blob: a485d40ec62cb6b12304e5e7130fc5b5da01f1d8 [file] [log] [blame]
[email protected]51208252013-08-19 21:05:301// 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/shortcut_helper.h"
6
7#include <jni.h>
8
9#include "base/android/jni_android.h"
10#include "base/android/jni_string.h"
11#include "base/basictypes.h"
12#include "base/location.h"
13#include "base/strings/string16.h"
[email protected]e95b717f2014-02-06 13:47:1314#include "base/task/cancelable_task_tracker.h"
[email protected]51208252013-08-19 21:05:3015#include "base/threading/worker_pool.h"
16#include "chrome/browser/android/tab_android.h"
17#include "chrome/browser/favicon/favicon_service.h"
18#include "chrome/browser/favicon/favicon_service_factory.h"
mlamouri89ccc632014-09-16 19:29:5819#include "chrome/common/chrome_constants.h"
[email protected]51208252013-08-19 21:05:3020#include "chrome/common/render_messages.h"
mlamouri89ccc632014-09-16 19:29:5821#include "chrome/common/web_application_info.h"
[email protected]0dd45112013-10-04 21:40:3722#include "content/public/browser/user_metrics.h"
[email protected]51208252013-08-19 21:05:3023#include "content/public/browser/web_contents.h"
24#include "content/public/browser/web_contents_observer.h"
25#include "content/public/common/frame_navigate_params.h"
mlamourib225ae562014-09-17 11:51:3726#include "content/public/common/manifest.h"
[email protected]51208252013-08-19 21:05:3027#include "jni/ShortcutHelper_jni.h"
28#include "ui/gfx/android/java_bitmap.h"
29#include "ui/gfx/codec/png_codec.h"
30#include "ui/gfx/color_analysis.h"
[email protected]d320d852013-10-26 10:51:2231#include "ui/gfx/favicon_size.h"
[email protected]51208252013-08-19 21:05:3032#include "url/gurl.h"
33
mlamouriecb9b4082014-09-12 13:04:1534jlong Initialize(JNIEnv* env, jobject obj, jlong tab_android_ptr) {
35 TabAndroid* tab = reinterpret_cast<TabAndroid*>(tab_android_ptr);
[email protected]51208252013-08-19 21:05:3036
mlamouriecb9b4082014-09-12 13:04:1537 ShortcutHelper* shortcut_helper =
38 new ShortcutHelper(env, obj, tab->web_contents());
39 shortcut_helper->Initialize();
40
41 return reinterpret_cast<intptr_t>(shortcut_helper);
42}
43
44ShortcutHelper::ShortcutHelper(JNIEnv* env,
45 jobject obj,
46 content::WebContents* web_contents)
47 : WebContentsObserver(web_contents),
48 java_ref_(env, obj),
49 url_(web_contents->GetURL()),
mlamouri9317a292014-09-18 11:49:0750 display_(content::Manifest::DISPLAY_MODE_BROWSER),
mlamouri350ce8a2014-09-18 18:17:2151 orientation_(blink::WebScreenOrientationLockDefault),
mlamourib225ae562014-09-17 11:51:3752 weak_ptr_factory_(this) {
mlamouriecb9b4082014-09-12 13:04:1553}
54
55void ShortcutHelper::Initialize() {
[email protected]51208252013-08-19 21:05:3056 // Send a message to the renderer to retrieve information about the page.
mlamouri89ccc632014-09-16 19:29:5857 Send(new ChromeViewMsg_GetWebApplicationInfo(routing_id()));
[email protected]51208252013-08-19 21:05:3058}
59
mlamouriecb9b4082014-09-12 13:04:1560ShortcutHelper::~ShortcutHelper() {
61}
62
mlamouri89ccc632014-09-16 19:29:5863void ShortcutHelper::OnDidGetWebApplicationInfo(
64 const WebApplicationInfo& received_web_app_info) {
65 // Sanitize received_web_app_info.
66 WebApplicationInfo web_app_info = received_web_app_info;
67 web_app_info.title =
68 web_app_info.title.substr(0, chrome::kMaxMetaTagAttributeLength);
69 web_app_info.description =
70 web_app_info.description.substr(0, chrome::kMaxMetaTagAttributeLength);
[email protected]0dd45112013-10-04 21:40:3771
mlamouri89ccc632014-09-16 19:29:5872 title_ = web_app_info.title.empty() ? web_contents()->GetTitle()
73 : web_app_info.title;
mlamouriecb9b4082014-09-12 13:04:1574
mlamouri9317a292014-09-18 11:49:0775 if (web_app_info.mobile_capable == WebApplicationInfo::MOBILE_CAPABLE ||
76 web_app_info.mobile_capable == WebApplicationInfo::MOBILE_CAPABLE_APPLE) {
77 display_ = content::Manifest::DISPLAY_MODE_STANDALONE;
78 }
79
80 // Record what type of shortcut was added by the user.
81 switch (web_app_info.mobile_capable) {
82 case WebApplicationInfo::MOBILE_CAPABLE:
83 content::RecordAction(
84 base::UserMetricsAction("webapps.AddShortcut.AppShortcut"));
85 break;
86 case WebApplicationInfo::MOBILE_CAPABLE_APPLE:
87 content::RecordAction(
88 base::UserMetricsAction("webapps.AddShortcut.AppShortcutApple"));
89 break;
90 case WebApplicationInfo::MOBILE_CAPABLE_UNSPECIFIED:
91 content::RecordAction(
92 base::UserMetricsAction("webapps.AddShortcut.Bookmark"));
93 break;
94 }
95
mlamourib225ae562014-09-17 11:51:3796 web_contents()->GetManifest(base::Bind(&ShortcutHelper::OnDidGetManifest,
97 weak_ptr_factory_.GetWeakPtr()));
98}
99
100void ShortcutHelper::OnDidGetManifest(const content::Manifest& manifest) {
101 // Set the title based on the manifest value, if any.
102 if (!manifest.short_name.is_null())
103 title_ = manifest.short_name.string();
104 else if (!manifest.name.is_null())
105 title_ = manifest.name.string();
106
107 // Set the url based on the manifest value, if any.
108 if (manifest.start_url.is_valid())
109 url_ = manifest.start_url;
110
mlamouri9317a292014-09-18 11:49:07111 // Set the display based on the manifest value, if any.
112 if (manifest.display != content::Manifest::DISPLAY_MODE_UNSPECIFIED)
113 display_ = manifest.display;
114
115 // 'fullscreen' and 'minimal-ui' are not yet supported, fallback to the right
116 // mode in those cases.
117 if (manifest.display == content::Manifest::DISPLAY_MODE_FULLSCREEN)
118 display_ = content::Manifest::DISPLAY_MODE_STANDALONE;
119 if (manifest.display == content::Manifest::DISPLAY_MODE_MINIMAL_UI)
120 display_ = content::Manifest::DISPLAY_MODE_BROWSER;
121
mlamouri350ce8a2014-09-18 18:17:21122 // Set the orientation based on the manifest value, if any.
123 if (manifest.orientation != blink::WebScreenOrientationLockDefault) {
124 // Ignore the orientation if the display mode is different from
125 // 'standalone'.
126 // TODO(mlamouri): send a message to the developer console about this.
127 if (display_ == content::Manifest::DISPLAY_MODE_STANDALONE)
128 orientation_ = manifest.orientation;
129 }
130
mlamourib225ae562014-09-17 11:51:37131 // The ShortcutHelper is now able to notify its Java counterpart that it is
132 // initialized. OnInitialized method is not conceptually part of getting the
133 // manifest data but it happens that the initialization is finalized when
134 // these data are available.
mlamouriecb9b4082014-09-12 13:04:15135 JNIEnv* env = base::android::AttachCurrentThread();
136 ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
137 ScopedJavaLocalRef<jstring> j_title =
138 base::android::ConvertUTF16ToJavaString(env, title_);
139
140 Java_ShortcutHelper_onInitialized(env, j_obj.obj(), j_title.obj());
141}
142
143void ShortcutHelper::TearDown(JNIEnv*, jobject) {
144 Destroy();
145}
146
147void ShortcutHelper::Destroy() {
148 delete this;
149}
150
151void ShortcutHelper::AddShortcut(
152 JNIEnv* env,
153 jobject obj,
154 jstring jtitle,
155 jint launcher_large_icon_size) {
156 base::string16 title = base::android::ConvertJavaStringToUTF16(env, jtitle);
157 if (!title.empty())
158 title_ = title;
159
160 Profile* profile =
161 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
162
[email protected]51208252013-08-19 21:05:30163 // Grab the best, largest icon we can find to represent this bookmark.
164 // TODO(dfalcantara): Try combining with the new BookmarksHandler once its
165 // rewrite is further along.
[email protected]d320d852013-10-26 10:51:22166 std::vector<int> icon_types;
[email protected]7627e0b42014-04-17 17:20:53167 icon_types.push_back(favicon_base::FAVICON);
168 icon_types.push_back(favicon_base::TOUCH_PRECOMPOSED_ICON |
169 favicon_base::TOUCH_ICON);
[email protected]51208252013-08-19 21:05:30170 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
[email protected]5820d32c2013-08-28 10:03:50171 profile, Profile::EXPLICIT_ACCESS);
[email protected]51208252013-08-19 21:05:30172
[email protected]d320d852013-10-26 10:51:22173 // Using favicon if its size is not smaller than platform required size,
174 // otherwise using the largest icon among all avaliable icons.
175 int threshold_to_get_any_largest_icon = launcher_large_icon_size_ - 1;
[email protected]b29844e2014-06-25 19:11:21176 favicon_service->GetLargestRawFaviconForPageURL(url_, icon_types,
[email protected]d320d852013-10-26 10:51:22177 threshold_to_get_any_largest_icon,
mlamouriecb9b4082014-09-12 13:04:15178 base::Bind(&ShortcutHelper::FinishAddingShortcut,
[email protected]5820d32c2013-08-28 10:03:50179 base::Unretained(this)),
[email protected]51208252013-08-19 21:05:30180 &cancelable_task_tracker_);
181}
182
mlamouriecb9b4082014-09-12 13:04:15183void ShortcutHelper::FinishAddingShortcut(
[email protected]12b54c72014-06-14 00:38:48184 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
mlamouriecb9b4082014-09-12 13:04:15185 icon_ = bitmap_result;
186
187 // Stop observing so we don't get destroyed while doing the last steps.
188 Observe(NULL);
189
[email protected]5820d32c2013-08-28 10:03:50190 base::WorkerPool::PostTask(
191 FROM_HERE,
192 base::Bind(&ShortcutHelper::AddShortcutInBackground,
193 url_,
194 title_,
mlamouri9317a292014-09-18 11:49:07195 display_,
mlamouri350ce8a2014-09-18 18:17:21196 icon_,
197 orientation_),
[email protected]5820d32c2013-08-28 10:03:50198 true);
mlamouriecb9b4082014-09-12 13:04:15199
[email protected]5820d32c2013-08-28 10:03:50200 Destroy();
[email protected]51208252013-08-19 21:05:30201}
202
mlamouriecb9b4082014-09-12 13:04:15203bool ShortcutHelper::OnMessageReceived(const IPC::Message& message) {
[email protected]51208252013-08-19 21:05:30204 bool handled = true;
mlamouriecb9b4082014-09-12 13:04:15205
206 IPC_BEGIN_MESSAGE_MAP(ShortcutHelper, message)
mlamouri89ccc632014-09-16 19:29:58207 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidGetWebApplicationInfo,
208 OnDidGetWebApplicationInfo)
[email protected]51208252013-08-19 21:05:30209 IPC_MESSAGE_UNHANDLED(handled = false)
210 IPC_END_MESSAGE_MAP()
mlamouriecb9b4082014-09-12 13:04:15211
[email protected]51208252013-08-19 21:05:30212 return handled;
213}
214
mlamouriecb9b4082014-09-12 13:04:15215void ShortcutHelper::WebContentsDestroyed() {
[email protected]5820d32c2013-08-28 10:03:50216 Destroy();
217}
218
[email protected]51208252013-08-19 21:05:30219bool ShortcutHelper::RegisterShortcutHelper(JNIEnv* env) {
220 return RegisterNativesImpl(env);
221}
222
[email protected]5820d32c2013-08-28 10:03:50223void ShortcutHelper::AddShortcutInBackground(
[email protected]51208252013-08-19 21:05:30224 const GURL& url,
[email protected]96920152013-12-04 21:00:16225 const base::string16& title,
mlamouri9317a292014-09-18 11:49:07226 content::Manifest::DisplayMode display,
mlamouri350ce8a2014-09-18 18:17:21227 const favicon_base::FaviconRawBitmapResult& bitmap_result,
228 blink::WebScreenOrientationLockType orientation) {
[email protected]51208252013-08-19 21:05:30229 DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
230
231 // Grab the average color from the bitmap.
232 SkColor color = SK_ColorWHITE;
233 SkBitmap favicon_bitmap;
234 if (bitmap_result.is_valid()) {
[email protected]cc668c32014-03-12 04:48:34235 if (gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
236 bitmap_result.bitmap_data->size(),
237 &favicon_bitmap))
238 color = color_utils::CalculateKMeanColorOfBitmap(favicon_bitmap);
[email protected]51208252013-08-19 21:05:30239 }
240
241 int r_value = SkColorGetR(color);
242 int g_value = SkColorGetG(color);
243 int b_value = SkColorGetB(color);
244
245 // Send the data to the Java side to create the shortcut.
246 JNIEnv* env = base::android::AttachCurrentThread();
247 ScopedJavaLocalRef<jstring> java_url =
248 base::android::ConvertUTF8ToJavaString(env, url.spec());
249 ScopedJavaLocalRef<jstring> java_title =
250 base::android::ConvertUTF16ToJavaString(env, title);
251 ScopedJavaLocalRef<jobject> java_bitmap;
252 if (favicon_bitmap.getSize())
253 java_bitmap = gfx::ConvertToJavaBitmap(&favicon_bitmap);
254
mlamouri89ccc632014-09-16 19:29:58255 Java_ShortcutHelper_addShortcut(
256 env,
257 base::android::GetApplicationContext(),
258 java_url.obj(),
259 java_title.obj(),
260 java_bitmap.obj(),
261 r_value,
262 g_value,
263 b_value,
mlamouri350ce8a2014-09-18 18:17:21264 display == content::Manifest::DISPLAY_MODE_STANDALONE,
265 orientation);
[email protected]51208252013-08-19 21:05:30266}