blob: f3311ebec7825eb01afab792e34a4da84298d8d1 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/android/ntp/most_visited_sites_bridge.h"
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/bind.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
#include "components/favicon_base/favicon_types.h"
#include "components/history/core/browser/history_service.h"
#include "components/ntp_tiles/metrics.h"
#include "components/ntp_tiles/most_visited_sites.h"
#include "components/ntp_tiles/section_type.h"
#include "components/rappor/rappor_service_impl.h"
#include "jni/MostVisitedSitesBridge_jni.h"
#include "jni/MostVisitedSites_jni.h"
#include "ui/gfx/android/java_bitmap.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaArrayOfStrings;
using base::android::ToJavaIntArray;
using base::android::ToJavaLongArray;
using ntp_tiles::MostVisitedSites;
using ntp_tiles::NTPTilesVector;
using ntp_tiles::SectionType;
using ntp_tiles::TileTitleSource;
using ntp_tiles::TileSource;
using ntp_tiles::TileVisualType;
namespace {
class JavaHomepageClient : public MostVisitedSites::HomepageClient {
public:
JavaHomepageClient(JNIEnv* env,
const JavaParamRef<jobject>& obj,
Profile* profile);
bool IsHomepageTileEnabled() const override;
GURL GetHomepageUrl() const override;
void QueryHomepageTitle(TitleCallback title_callback) override;
private:
void OnTitleEntryFound(TitleCallback title_callback,
bool success,
const history::URLRow& row,
const history::VisitVector& visits);
ScopedJavaGlobalRef<jobject> client_;
Profile* profile_;
// Used in loading titles.
base::CancelableTaskTracker task_tracker_;
DISALLOW_COPY_AND_ASSIGN(JavaHomepageClient);
};
JavaHomepageClient::JavaHomepageClient(JNIEnv* env,
const JavaParamRef<jobject>& obj,
Profile* profile)
: client_(env, obj), profile_(profile) {
DCHECK(profile);
}
void JavaHomepageClient::QueryHomepageTitle(TitleCallback title_callback) {
DCHECK(!title_callback.is_null());
GURL url = GetHomepageUrl();
if (url.is_empty()) {
std::move(title_callback).Run(base::nullopt);
return;
}
history::HistoryService* const history_service =
HistoryServiceFactory::GetForProfileIfExists(
profile_, ServiceAccessType::EXPLICIT_ACCESS);
if (!history_service) {
std::move(title_callback).Run(base::nullopt);
return;
}
// If the client is destroyed, the tracker will cancel this task automatically
// and the callback will not be called. Therefore, base::Unretained works.
history_service->QueryURL(
url,
/*want_visits=*/false,
base::BindOnce(&JavaHomepageClient::OnTitleEntryFound,
base::Unretained(this), std::move(title_callback)),
&task_tracker_);
}
void JavaHomepageClient::OnTitleEntryFound(TitleCallback title_callback,
bool success,
const history::URLRow& row,
const history::VisitVector& visits) {
if (!success) {
std::move(title_callback).Run(base::nullopt);
return;
}
std::move(title_callback).Run(row.title());
}
bool JavaHomepageClient::IsHomepageTileEnabled() const {
return Java_HomepageClient_isHomepageTileEnabled(AttachCurrentThread(),
client_);
}
GURL JavaHomepageClient::GetHomepageUrl() const {
base::android::ScopedJavaLocalRef<jstring> url =
Java_HomepageClient_getHomepageUrl(AttachCurrentThread(), client_);
if (url.is_null()) {
return GURL();
}
return GURL(ConvertJavaStringToUTF8(url));
}
} // namespace
class MostVisitedSitesBridge::JavaObserver : public MostVisitedSites::Observer {
public:
JavaObserver(JNIEnv* env, const JavaParamRef<jobject>& obj);
void OnURLsAvailable(
const std::map<SectionType, NTPTilesVector>& sections) override;
void OnIconMadeAvailable(const GURL& site_url) override;
private:
ScopedJavaGlobalRef<jobject> observer_;
DISALLOW_COPY_AND_ASSIGN(JavaObserver);
};
MostVisitedSitesBridge::JavaObserver::JavaObserver(
JNIEnv* env,
const JavaParamRef<jobject>& obj)
: observer_(env, obj) {}
void MostVisitedSitesBridge::JavaObserver::OnURLsAvailable(
const std::map<SectionType, NTPTilesVector>& sections) {
JNIEnv* env = AttachCurrentThread();
std::vector<base::string16> titles;
std::vector<std::string> urls;
std::vector<std::string> whitelist_icons;
std::vector<int> title_sources;
std::vector<int> sources;
std::vector<int> section_types;
std::vector<int64_t> data_generation_times;
for (const auto& section : sections) {
const NTPTilesVector& tiles = section.second;
section_types.resize(section_types.size() + tiles.size(),
static_cast<int>(section.first));
for (const auto& tile : tiles) {
titles.emplace_back(tile.title);
urls.emplace_back(tile.url.spec());
whitelist_icons.emplace_back(tile.whitelist_icon_path.value());
title_sources.emplace_back(static_cast<int>(tile.title_source));
sources.emplace_back(static_cast<int>(tile.source));
data_generation_times.emplace_back(
tile.data_generation_time.ToJavaTime());
}
}
Java_MostVisitedSitesBridge_onURLsAvailable(
env, observer_, ToJavaArrayOfStrings(env, titles),
ToJavaArrayOfStrings(env, urls), ToJavaIntArray(env, section_types),
ToJavaArrayOfStrings(env, whitelist_icons),
ToJavaIntArray(env, title_sources), ToJavaIntArray(env, sources),
ToJavaLongArray(env, data_generation_times));
}
void MostVisitedSitesBridge::JavaObserver::OnIconMadeAvailable(
const GURL& site_url) {
JNIEnv* env = AttachCurrentThread();
Java_MostVisitedSitesBridge_onIconMadeAvailable(
env, observer_, ConvertUTF8ToJavaString(env, site_url.spec()));
}
MostVisitedSitesBridge::MostVisitedSitesBridge(Profile* profile)
: most_visited_(ChromeMostVisitedSitesFactory::NewForProfile(profile)),
profile_(profile) {
DCHECK(!profile->IsOffTheRecord());
}
MostVisitedSitesBridge::~MostVisitedSitesBridge() {}
void MostVisitedSitesBridge::Destroy(JNIEnv* env,
const JavaParamRef<jobject>& obj) {
delete this;
}
void MostVisitedSitesBridge::OnHomepageStateChanged(
JNIEnv* env,
const JavaParamRef<jobject>& obj) {
most_visited_->RefreshTiles();
}
void MostVisitedSitesBridge::SetHomepageClient(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jobject>& j_client) {
most_visited_->SetHomepageClient(
std::make_unique<JavaHomepageClient>(env, j_client, profile_));
}
void MostVisitedSitesBridge::SetObserver(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& j_observer,
jint num_sites) {
java_observer_.reset(new JavaObserver(env, j_observer));
most_visited_->SetMostVisitedURLsObserver(java_observer_.get(), num_sites);
}
void MostVisitedSitesBridge::AddOrRemoveBlacklistedUrl(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jstring>& j_url,
jboolean add_url) {
GURL url(ConvertJavaStringToUTF8(env, j_url));
most_visited_->AddOrRemoveBlacklistedUrl(url, add_url);
}
void MostVisitedSitesBridge::RecordPageImpression(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jint jtiles_count) {
ntp_tiles::metrics::RecordPageImpression(jtiles_count);
}
void MostVisitedSitesBridge::RecordTileImpression(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jint jindex,
jint jvisual_type,
jint jicon_type,
jint jtitle_source,
jint jsource,
jlong jdata_generation_time_ms,
const JavaParamRef<jstring>& jurl) {
GURL url(ConvertJavaStringToUTF8(env, jurl));
TileTitleSource title_source = static_cast<TileTitleSource>(jtitle_source);
TileSource source = static_cast<TileSource>(jsource);
TileVisualType visual_type = static_cast<TileVisualType>(jvisual_type);
favicon_base::IconType icon_type =
static_cast<favicon_base::IconType>(jicon_type);
ntp_tiles::metrics::RecordTileImpression(
ntp_tiles::NTPTileImpression(
jindex, source, title_source, visual_type, icon_type,
base::Time::FromJavaTime(jdata_generation_time_ms), url),
g_browser_process->rappor_service());
}
void MostVisitedSitesBridge::RecordOpenedMostVisitedItem(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jint index,
jint tile_type,
jint title_source,
jint source,
jlong jdata_generation_time_ms) {
ntp_tiles::metrics::RecordTileClick(ntp_tiles::NTPTileImpression(
index, static_cast<TileSource>(source),
static_cast<TileTitleSource>(title_source),
static_cast<TileVisualType>(tile_type), favicon_base::IconType::kInvalid,
base::Time::FromJavaTime(jdata_generation_time_ms),
/*url_for_rappor=*/GURL()));
}
static jlong JNI_MostVisitedSitesBridge_Init(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& jprofile) {
MostVisitedSitesBridge* most_visited_sites =
new MostVisitedSitesBridge(ProfileAndroid::FromProfileAndroid(jprofile));
return reinterpret_cast<intptr_t>(most_visited_sites);
}