blob: cf20d4b1172d5c7ad58a8b70a47a3d3fe6b7e380 [file] [log] [blame]
[email protected]c764b2822013-07-03 04:16:481// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/android/most_visited_sites.h"
6
7#include "base/android/jni_android.h"
8#include "base/android/jni_array.h"
9#include "base/android/jni_string.h"
10#include "base/android/scoped_java_ref.h"
[email protected]bc093e82014-03-04 21:49:1111#include "base/strings/utf_string_conversions.h"
[email protected]7ceb4e32013-12-06 04:13:0412#include "chrome/browser/chrome_notification_types.h"
[email protected]c764b2822013-07-03 04:16:4813#include "chrome/browser/history/history_types.h"
14#include "chrome/browser/history/top_sites.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/profiles/profile_android.h"
[email protected]bc093e82014-03-04 21:49:1117#include "chrome/browser/search/suggestions/proto/suggestions.pb.h"
18#include "chrome/browser/search/suggestions/suggestions_service.h"
19#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
[email protected]c764b2822013-07-03 04:16:4820#include "content/public/browser/browser_thread.h"
[email protected]7ceb4e32013-12-06 04:13:0421#include "content/public/browser/notification_source.h"
[email protected]c764b2822013-07-03 04:16:4822#include "jni/MostVisitedSites_jni.h"
23#include "third_party/skia/include/core/SkBitmap.h"
24#include "ui/gfx/android/java_bitmap.h"
25#include "ui/gfx/codec/jpeg_codec.h"
26
27using base::android::AttachCurrentThread;
28using base::android::ConvertUTF8ToJavaString;
29using base::android::ConvertJavaStringToUTF8;
30using base::android::ScopedJavaGlobalRef;
31using base::android::ToJavaArrayOfStrings;
32using base::android::CheckException;
33using content::BrowserThread;
34using history::TopSites;
[email protected]bc093e82014-03-04 21:49:1135using suggestions::ChromeSuggestion;
36using suggestions::SuggestionsProfile;
37using suggestions::SuggestionsService;
38using suggestions::SuggestionsServiceFactory;
[email protected]c764b2822013-07-03 04:16:4839
[email protected]c764b2822013-07-03 04:16:4840namespace {
41
[email protected]7ceb4e32013-12-06 04:13:0442void ExtractMostVisitedTitlesAndURLs(
43 const history::MostVisitedURLList& visited_list,
44 std::vector<base::string16>* titles,
45 std::vector<std::string>* urls,
46 int num_sites) {
47 size_t max = static_cast<size_t>(num_sites);
48 for (size_t i = 0; i < visited_list.size() && i < max; ++i) {
49 const history::MostVisitedURL& visited = visited_list[i];
50
51 if (visited.url.is_empty())
52 break; // This is the signal that there are no more real visited sites.
53
54 titles->push_back(visited.title);
55 urls->push_back(visited.url.spec());
[email protected]c764b2822013-07-03 04:16:4856 }
[email protected]7ceb4e32013-12-06 04:13:0457}
[email protected]c764b2822013-07-03 04:16:4858
[email protected]7ceb4e32013-12-06 04:13:0459void OnMostVisitedURLsAvailable(
60 ScopedJavaGlobalRef<jobject>* j_observer,
61 int num_sites,
62 const history::MostVisitedURLList& visited_list) {
63 std::vector<base::string16> titles;
64 std::vector<std::string> urls;
65 ExtractMostVisitedTitlesAndURLs(visited_list, &titles, &urls, num_sites);
[email protected]c764b2822013-07-03 04:16:4866
[email protected]7ceb4e32013-12-06 04:13:0467 JNIEnv* env = AttachCurrentThread();
68 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
69 env,
70 j_observer->obj(),
71 ToJavaArrayOfStrings(env, titles).obj(),
72 ToJavaArrayOfStrings(env, urls).obj());
73}
[email protected]c764b2822013-07-03 04:16:4874
75SkBitmap ExtractThumbnail(const base::RefCountedMemory& image_data) {
76 scoped_ptr<SkBitmap> image(gfx::JPEGCodec::Decode(
77 image_data.front(),
78 image_data.size()));
79 return image.get() ? *image : SkBitmap();
80}
81
82void OnObtainedThumbnail(
83 ScopedJavaGlobalRef<jobject>* bitmap,
[email protected]7ceb4e32013-12-06 04:13:0484 ScopedJavaGlobalRef<jobject>* j_callback) {
[email protected]c764b2822013-07-03 04:16:4885 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
86 JNIEnv* env = AttachCurrentThread();
87 Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
[email protected]7ceb4e32013-12-06 04:13:0488 env, j_callback->obj(), bitmap->obj());
[email protected]c764b2822013-07-03 04:16:4889}
90
91void GetUrlThumbnailTask(
92 std::string url_string,
93 scoped_refptr<TopSites> top_sites,
[email protected]7ceb4e32013-12-06 04:13:0494 ScopedJavaGlobalRef<jobject>* j_callback) {
[email protected]c764b2822013-07-03 04:16:4895 JNIEnv* env = AttachCurrentThread();
96
97 ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
98 new ScopedJavaGlobalRef<jobject>();
99
100 GURL gurl(url_string);
101
102 scoped_refptr<base::RefCountedMemory> data;
[email protected]420e6d92013-09-17 01:48:51103 if (top_sites->GetPageThumbnail(gurl, false, &data)) {
[email protected]c764b2822013-07-03 04:16:48104 SkBitmap thumbnail_bitmap = ExtractThumbnail(*data.get());
105 if (!thumbnail_bitmap.empty()) {
106 j_bitmap_ref->Reset(
107 env,
108 gfx::ConvertToJavaBitmap(&thumbnail_bitmap).obj());
109 }
110 }
111
[email protected]7ceb4e32013-12-06 04:13:04112 // Since j_callback is owned by this callback, when the callback falls out of
113 // scope it will be deleted. We need to pass ownership to the next callback.
114 ScopedJavaGlobalRef<jobject>* j_callback_pass =
115 new ScopedJavaGlobalRef<jobject>(*j_callback);
[email protected]c764b2822013-07-03 04:16:48116 BrowserThread::PostTask(
117 BrowserThread::UI, FROM_HERE,
118 base::Bind(
119 &OnObtainedThumbnail,
[email protected]7ceb4e32013-12-06 04:13:04120 base::Owned(j_bitmap_ref), base::Owned(j_callback_pass)));
[email protected]c764b2822013-07-03 04:16:48121}
122
123} // namespace
124
[email protected]7ceb4e32013-12-06 04:13:04125MostVisitedSites::MostVisitedSites(Profile* profile)
[email protected]bc093e82014-03-04 21:49:11126 : profile_(profile), num_sites_(0), weak_ptr_factory_(this) {
[email protected]7ceb4e32013-12-06 04:13:04127}
[email protected]c764b2822013-07-03 04:16:48128
[email protected]7ceb4e32013-12-06 04:13:04129MostVisitedSites::~MostVisitedSites() {
130}
[email protected]c764b2822013-07-03 04:16:48131
[email protected]7ceb4e32013-12-06 04:13:04132void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) {
133 delete this;
134}
[email protected]c764b2822013-07-03 04:16:48135
[email protected]7ceb4e32013-12-06 04:13:04136void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv* env,
137 jobject obj,
138 jobject j_observer,
139 jint num_sites) {
140 observer_.Reset(env, j_observer);
141 num_sites_ = num_sites;
[email protected]852fc792013-10-04 04:15:12142
[email protected]7ceb4e32013-12-06 04:13:04143 QueryMostVisitedURLs();
144
145 history::TopSites* top_sites = profile_->GetTopSites();
146 if (top_sites) {
147 // TopSites updates itself after a delay. To ensure up-to-date results,
148 // force an update now.
149 top_sites->SyncWithHistory();
150
151 // Register for notification when TopSites changes so that we can update
152 // ourself.
153 registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
154 content::Source<history::TopSites>(top_sites));
155 }
[email protected]c764b2822013-07-03 04:16:48156}
157
158// May be called from any thread
[email protected]7ceb4e32013-12-06 04:13:04159void MostVisitedSites::GetURLThumbnail(JNIEnv* env,
160 jobject obj,
161 jstring url,
162 jobject j_callback_obj) {
163 ScopedJavaGlobalRef<jobject>* j_callback =
[email protected]c764b2822013-07-03 04:16:48164 new ScopedJavaGlobalRef<jobject>();
[email protected]7ceb4e32013-12-06 04:13:04165 j_callback->Reset(env, j_callback_obj);
[email protected]c764b2822013-07-03 04:16:48166
167 std::string url_string = ConvertJavaStringToUTF8(env, url);
[email protected]7ceb4e32013-12-06 04:13:04168 scoped_refptr<TopSites> top_sites(profile_->GetTopSites());
[email protected]c764b2822013-07-03 04:16:48169 BrowserThread::PostTask(
170 BrowserThread::DB, FROM_HERE, base::Bind(
171 &GetUrlThumbnailTask,
172 url_string,
[email protected]7ceb4e32013-12-06 04:13:04173 top_sites, base::Owned(j_callback)));
[email protected]c764b2822013-07-03 04:16:48174}
[email protected]a4c2f282013-07-20 05:26:05175
[email protected]7ceb4e32013-12-06 04:13:04176void MostVisitedSites::BlacklistUrl(JNIEnv* env,
177 jobject obj,
178 jstring j_url) {
179 TopSites* top_sites = profile_->GetTopSites();
[email protected]a4c2f282013-07-20 05:26:05180 if (!top_sites)
181 return;
182
183 std::string url_string = ConvertJavaStringToUTF8(env, j_url);
184 top_sites->AddBlacklistedURL(GURL(url_string));
185}
[email protected]7ceb4e32013-12-06 04:13:04186
187void MostVisitedSites::Observe(int type,
188 const content::NotificationSource& source,
189 const content::NotificationDetails& details) {
190 DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED);
191
192 // Most visited urls changed, query again.
193 QueryMostVisitedURLs();
194}
195
196// static
197bool MostVisitedSites::Register(JNIEnv* env) {
198 return RegisterNativesImpl(env);
199}
200
201void MostVisitedSites::QueryMostVisitedURLs() {
[email protected]bc093e82014-03-04 21:49:11202 SuggestionsServiceFactory* suggestions_service_factory =
203 SuggestionsServiceFactory::GetInstance();
204 SuggestionsService* suggestions_service =
205 suggestions_service_factory->GetForProfile(profile_);
206 if (suggestions_service) {
207 // Suggestions service is enabled, initiate a query.
208 suggestions_service->FetchSuggestionsData(
209 base::Bind(
210 &MostVisitedSites::OnSuggestionsProfileAvailable,
211 weak_ptr_factory_.GetWeakPtr(),
212 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_))));
213 } else {
214 InitiateTopSitesQuery();
215 }
216}
217
218void MostVisitedSites::InitiateTopSitesQuery() {
[email protected]7ceb4e32013-12-06 04:13:04219 TopSites* top_sites = profile_->GetTopSites();
220 if (!top_sites)
221 return;
222
223 top_sites->GetMostVisitedURLs(
224 base::Bind(
225 &OnMostVisitedURLsAvailable,
226 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_)),
227 num_sites_),
228 false);
229}
230
[email protected]bc093e82014-03-04 21:49:11231void MostVisitedSites::OnSuggestionsProfileAvailable(
232 ScopedJavaGlobalRef<jobject>* j_observer,
233 const SuggestionsProfile& suggestions_profile) {
234 size_t size = suggestions_profile.suggestions_size();
235 if (size == 0) {
236 // No suggestions data available, initiate Top Sites query.
237 InitiateTopSitesQuery();
238 return;
239 }
240
241 std::vector<base::string16> titles;
242 std::vector<std::string> urls;
243 for (size_t i = 0; i < size; ++i) {
244 const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
245 titles.push_back(base::UTF8ToUTF16(suggestion.title()));
246 urls.push_back(suggestion.url());
247 }
248
249 JNIEnv* env = AttachCurrentThread();
250 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
251 env,
252 j_observer->obj(),
253 ToJavaArrayOfStrings(env, titles).obj(),
254 ToJavaArrayOfStrings(env, urls).obj());
255}
256
[email protected]7ceb4e32013-12-06 04:13:04257static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
258 MostVisitedSites* most_visited_sites =
259 new MostVisitedSites(ProfileAndroid::FromProfileAndroid(jprofile));
260 return reinterpret_cast<intptr_t>(most_visited_sites);
261}