[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 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. |
| 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] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame^] | 11 | #include "base/strings/utf_string_conversions.h" |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 12 | #include "chrome/browser/chrome_notification_types.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 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" |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame^] | 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" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 20 | #include "content/public/browser/browser_thread.h" |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 21 | #include "content/public/browser/notification_source.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 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" |
| 26 | |
| 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; |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame^] | 35 | using suggestions::ChromeSuggestion; |
| 36 | using suggestions::SuggestionsProfile; |
| 37 | using suggestions::SuggestionsService; |
| 38 | using suggestions::SuggestionsServiceFactory; |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 39 | |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 40 | namespace { |
| 41 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 42 | void 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] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 56 | } |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 57 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 58 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 59 | void 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] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 66 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 67 | 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] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 74 | |
| 75 | SkBitmap 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 | |
| 82 | void OnObtainedThumbnail( |
| 83 | ScopedJavaGlobalRef<jobject>* bitmap, |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 84 | ScopedJavaGlobalRef<jobject>* j_callback) { |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 85 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 86 | JNIEnv* env = AttachCurrentThread(); |
| 87 | Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable( |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 88 | env, j_callback->obj(), bitmap->obj()); |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 89 | } |
| 90 | |
| 91 | void GetUrlThumbnailTask( |
| 92 | std::string url_string, |
| 93 | scoped_refptr<TopSites> top_sites, |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 94 | ScopedJavaGlobalRef<jobject>* j_callback) { |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 95 | 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] | 420e6d9 | 2013-09-17 01:48:51 | [diff] [blame] | 103 | if (top_sites->GetPageThumbnail(gurl, false, &data)) { |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 104 | 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] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 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); |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 116 | BrowserThread::PostTask( |
| 117 | BrowserThread::UI, FROM_HERE, |
| 118 | base::Bind( |
| 119 | &OnObtainedThumbnail, |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 120 | base::Owned(j_bitmap_ref), base::Owned(j_callback_pass))); |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | } // namespace |
| 124 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 125 | MostVisitedSites::MostVisitedSites(Profile* profile) |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame^] | 126 | : profile_(profile), num_sites_(0), weak_ptr_factory_(this) { |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 127 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 128 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 129 | MostVisitedSites::~MostVisitedSites() { |
| 130 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 131 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 132 | void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) { |
| 133 | delete this; |
| 134 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 135 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 136 | void 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] | 852fc79 | 2013-10-04 04:15:12 | [diff] [blame] | 142 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 143 | 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] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | // May be called from any thread |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 159 | void MostVisitedSites::GetURLThumbnail(JNIEnv* env, |
| 160 | jobject obj, |
| 161 | jstring url, |
| 162 | jobject j_callback_obj) { |
| 163 | ScopedJavaGlobalRef<jobject>* j_callback = |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 164 | new ScopedJavaGlobalRef<jobject>(); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 165 | j_callback->Reset(env, j_callback_obj); |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 166 | |
| 167 | std::string url_string = ConvertJavaStringToUTF8(env, url); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 168 | scoped_refptr<TopSites> top_sites(profile_->GetTopSites()); |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 169 | BrowserThread::PostTask( |
| 170 | BrowserThread::DB, FROM_HERE, base::Bind( |
| 171 | &GetUrlThumbnailTask, |
| 172 | url_string, |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 173 | top_sites, base::Owned(j_callback))); |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 174 | } |
[email protected] | a4c2f28 | 2013-07-20 05:26:05 | [diff] [blame] | 175 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 176 | void MostVisitedSites::BlacklistUrl(JNIEnv* env, |
| 177 | jobject obj, |
| 178 | jstring j_url) { |
| 179 | TopSites* top_sites = profile_->GetTopSites(); |
[email protected] | a4c2f28 | 2013-07-20 05:26:05 | [diff] [blame] | 180 | 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] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 186 | |
| 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); |
| 191 | |
| 192 | // Most visited urls changed, query again. |
| 193 | QueryMostVisitedURLs(); |
| 194 | } |
| 195 | |
| 196 | // static |
| 197 | bool MostVisitedSites::Register(JNIEnv* env) { |
| 198 | return RegisterNativesImpl(env); |
| 199 | } |
| 200 | |
| 201 | void MostVisitedSites::QueryMostVisitedURLs() { |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame^] | 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( |
| 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 | |
| 218 | void MostVisitedSites::InitiateTopSitesQuery() { |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 219 | 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] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame^] | 231 | void 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] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 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); |
| 261 | } |