1 // 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.
5 #include "chrome/browser/android/most_visited_sites.h"
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"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #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"
17 #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"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/notification_source.h"
22 #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"
27 using base::android::AttachCurrentThread
;
28 using base::android::ConvertUTF8ToJavaString
;
29 using base::android::ConvertJavaStringToUTF8
;
30 using base::android::ScopedJavaGlobalRef
;
31 using base::android::ToJavaArrayOfStrings
;
32 using base::android::CheckException
;
33 using content::BrowserThread
;
34 using history::TopSites
;
35 using suggestions::ChromeSuggestion
;
36 using suggestions::SuggestionsProfile
;
37 using suggestions::SuggestionsService
;
38 using suggestions::SuggestionsServiceFactory
;
42 void ExtractMostVisitedTitlesAndURLs(
43 const history::MostVisitedURLList
& visited_list
,
44 std::vector
<base::string16
>* titles
,
45 std::vector
<std::string
>* urls
,
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
];
51 if (visited
.url
.is_empty())
52 break; // This is the signal that there are no more real visited sites.
54 titles
->push_back(visited
.title
);
55 urls
->push_back(visited
.url
.spec());
59 void OnMostVisitedURLsAvailable(
60 ScopedJavaGlobalRef
<jobject
>* j_observer
,
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
);
67 JNIEnv
* env
= AttachCurrentThread();
68 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
71 ToJavaArrayOfStrings(env
, titles
).obj(),
72 ToJavaArrayOfStrings(env
, urls
).obj());
75 SkBitmap
ExtractThumbnail(const base::RefCountedMemory
& image_data
) {
76 scoped_ptr
<SkBitmap
> image(gfx::JPEGCodec::Decode(
79 return image
.get() ? *image
: SkBitmap();
82 void OnObtainedThumbnail(
83 ScopedJavaGlobalRef
<jobject
>* bitmap
,
84 ScopedJavaGlobalRef
<jobject
>* j_callback
) {
85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
86 JNIEnv
* env
= AttachCurrentThread();
87 Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
88 env
, j_callback
->obj(), bitmap
->obj());
91 void GetUrlThumbnailTask(
92 std::string url_string
,
93 scoped_refptr
<TopSites
> top_sites
,
94 ScopedJavaGlobalRef
<jobject
>* j_callback
) {
95 JNIEnv
* env
= AttachCurrentThread();
97 ScopedJavaGlobalRef
<jobject
>* j_bitmap_ref
=
98 new ScopedJavaGlobalRef
<jobject
>();
100 GURL
gurl(url_string
);
102 scoped_refptr
<base::RefCountedMemory
> data
;
103 if (top_sites
->GetPageThumbnail(gurl
, false, &data
)) {
104 SkBitmap thumbnail_bitmap
= ExtractThumbnail(*data
.get());
105 if (!thumbnail_bitmap
.empty()) {
108 gfx::ConvertToJavaBitmap(&thumbnail_bitmap
).obj());
112 // 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
);
116 BrowserThread::PostTask(
117 BrowserThread::UI
, FROM_HERE
,
119 &OnObtainedThumbnail
,
120 base::Owned(j_bitmap_ref
), base::Owned(j_callback_pass
)));
125 MostVisitedSites::MostVisitedSites(Profile
* profile
)
126 : profile_(profile
), num_sites_(0), weak_ptr_factory_(this) {
129 MostVisitedSites::~MostVisitedSites() {
132 void MostVisitedSites::Destroy(JNIEnv
* env
, jobject obj
) {
136 void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv
* env
,
140 observer_
.Reset(env
, j_observer
);
141 num_sites_
= num_sites
;
143 QueryMostVisitedURLs();
145 history::TopSites
* top_sites
= profile_
->GetTopSites();
147 // TopSites updates itself after a delay. To ensure up-to-date results,
148 // force an update now.
149 top_sites
->SyncWithHistory();
151 // Register for notification when TopSites changes so that we can update
153 registrar_
.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED
,
154 content::Source
<history::TopSites
>(top_sites
));
158 // May be called from any thread
159 void MostVisitedSites::GetURLThumbnail(JNIEnv
* env
,
162 jobject j_callback_obj
) {
163 ScopedJavaGlobalRef
<jobject
>* j_callback
=
164 new ScopedJavaGlobalRef
<jobject
>();
165 j_callback
->Reset(env
, j_callback_obj
);
167 std::string url_string
= ConvertJavaStringToUTF8(env
, url
);
168 scoped_refptr
<TopSites
> top_sites(profile_
->GetTopSites());
169 BrowserThread::PostTask(
170 BrowserThread::DB
, FROM_HERE
, base::Bind(
171 &GetUrlThumbnailTask
,
173 top_sites
, base::Owned(j_callback
)));
176 void MostVisitedSites::BlacklistUrl(JNIEnv
* env
,
179 TopSites
* top_sites
= profile_
->GetTopSites();
183 std::string url_string
= ConvertJavaStringToUTF8(env
, j_url
);
184 top_sites
->AddBlacklistedURL(GURL(url_string
));
187 void MostVisitedSites::Observe(int type
,
188 const content::NotificationSource
& source
,
189 const content::NotificationDetails
& details
) {
190 DCHECK_EQ(type
, chrome::NOTIFICATION_TOP_SITES_CHANGED
);
192 // Most visited urls changed, query again.
193 QueryMostVisitedURLs();
197 bool MostVisitedSites::Register(JNIEnv
* env
) {
198 return RegisterNativesImpl(env
);
201 void MostVisitedSites::QueryMostVisitedURLs() {
202 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(
210 &MostVisitedSites::OnSuggestionsProfileAvailable
,
211 weak_ptr_factory_
.GetWeakPtr(),
212 base::Owned(new ScopedJavaGlobalRef
<jobject
>(observer_
))));
214 InitiateTopSitesQuery();
218 void MostVisitedSites::InitiateTopSitesQuery() {
219 TopSites
* top_sites
= profile_
->GetTopSites();
223 top_sites
->GetMostVisitedURLs(
225 &OnMostVisitedURLsAvailable
,
226 base::Owned(new ScopedJavaGlobalRef
<jobject
>(observer_
)),
231 void MostVisitedSites::OnSuggestionsProfileAvailable(
232 ScopedJavaGlobalRef
<jobject
>* j_observer
,
233 const SuggestionsProfile
& suggestions_profile
) {
234 size_t size
= suggestions_profile
.suggestions_size();
236 // No suggestions data available, initiate Top Sites query.
237 InitiateTopSitesQuery();
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());
249 JNIEnv
* env
= AttachCurrentThread();
250 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
253 ToJavaArrayOfStrings(env
, titles
).obj(),
254 ToJavaArrayOfStrings(env
, urls
).obj());
257 static 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
);