Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / android / shortcut_helper.cc
blob779e2728bf99d50f1c0902e8d5aef18775c448a6
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/shortcut_helper.h"
7 #include <jni.h>
8 #include <limits>
10 #include "base/android/jni_android.h"
11 #include "base/android/jni_string.h"
12 #include "base/basictypes.h"
13 #include "base/location.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/task/cancelable_task_tracker.h"
17 #include "base/threading/worker_pool.h"
18 #include "chrome/browser/banners/app_banner_settings_helper.h"
19 #include "chrome/browser/favicon/favicon_service_factory.h"
20 #include "chrome/browser/manifest/manifest_icon_selector.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/chrome_constants.h"
23 #include "chrome/common/render_messages.h"
24 #include "chrome/common/web_application_info.h"
25 #include "components/dom_distiller/core/url_utils.h"
26 #include "components/favicon/core/favicon_service.h"
27 #include "content/public/browser/user_metrics.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/browser/web_contents_observer.h"
30 #include "content/public/common/frame_navigate_params.h"
31 #include "content/public/common/manifest.h"
32 #include "jni/ShortcutHelper_jni.h"
33 #include "net/base/mime_util.h"
34 #include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h"
35 #include "ui/gfx/android/java_bitmap.h"
36 #include "ui/gfx/codec/png_codec.h"
37 #include "ui/gfx/color_analysis.h"
38 #include "ui/gfx/favicon_size.h"
39 #include "ui/gfx/screen.h"
40 #include "url/gurl.h"
42 using content::Manifest;
44 // Android's preferred icon size in DP is 48, as defined in
45 // http://developer.android.com/design/style/iconography.html
46 const int ShortcutHelper::kPreferredIconSizeInDp = 48;
48 jlong Initialize(JNIEnv* env, jobject obj, jobject java_web_contents) {
49 content::WebContents* web_contents =
50 content::WebContents::FromJavaWebContents(java_web_contents);
51 ShortcutHelper* shortcut_helper = new ShortcutHelper(env, obj, web_contents);
52 shortcut_helper->Initialize();
54 return reinterpret_cast<intptr_t>(shortcut_helper);
57 ShortcutHelper::ShortcutHelper(JNIEnv* env,
58 jobject obj,
59 content::WebContents* web_contents)
60 : WebContentsObserver(web_contents),
61 java_ref_(env, obj),
62 shortcut_info_(dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(
63 web_contents->GetURL())),
64 add_shortcut_requested_(false),
65 manifest_icon_status_(MANIFEST_ICON_STATUS_NONE),
66 preferred_icon_size_in_px_(kPreferredIconSizeInDp *
67 gfx::Screen::GetScreenFor(web_contents->GetNativeView())->
68 GetPrimaryDisplay().device_scale_factor()),
69 weak_ptr_factory_(this) {
72 void ShortcutHelper::Initialize() {
73 // Send a message to the renderer to retrieve information about the page.
74 Send(new ChromeViewMsg_GetWebApplicationInfo(routing_id()));
77 ShortcutHelper::~ShortcutHelper() {
80 void ShortcutHelper::OnDidGetWebApplicationInfo(
81 const WebApplicationInfo& received_web_app_info) {
82 // Sanitize received_web_app_info.
83 WebApplicationInfo web_app_info = received_web_app_info;
84 web_app_info.title =
85 web_app_info.title.substr(0, chrome::kMaxMetaTagAttributeLength);
86 web_app_info.description =
87 web_app_info.description.substr(0, chrome::kMaxMetaTagAttributeLength);
89 shortcut_info_.title = web_app_info.title.empty() ? web_contents()->GetTitle()
90 : web_app_info.title;
92 if (web_app_info.mobile_capable == WebApplicationInfo::MOBILE_CAPABLE ||
93 web_app_info.mobile_capable == WebApplicationInfo::MOBILE_CAPABLE_APPLE) {
94 shortcut_info_.display = content::Manifest::DISPLAY_MODE_STANDALONE;
97 // Record what type of shortcut was added by the user.
98 switch (web_app_info.mobile_capable) {
99 case WebApplicationInfo::MOBILE_CAPABLE:
100 content::RecordAction(
101 base::UserMetricsAction("webapps.AddShortcut.AppShortcut"));
102 break;
103 case WebApplicationInfo::MOBILE_CAPABLE_APPLE:
104 content::RecordAction(
105 base::UserMetricsAction("webapps.AddShortcut.AppShortcutApple"));
106 break;
107 case WebApplicationInfo::MOBILE_CAPABLE_UNSPECIFIED:
108 content::RecordAction(
109 base::UserMetricsAction("webapps.AddShortcut.Bookmark"));
110 break;
113 web_contents()->GetManifest(base::Bind(&ShortcutHelper::OnDidGetManifest,
114 weak_ptr_factory_.GetWeakPtr()));
117 void ShortcutHelper::OnDidGetManifest(const content::Manifest& manifest) {
118 if (!manifest.IsEmpty()) {
119 content::RecordAction(
120 base::UserMetricsAction("webapps.AddShortcut.Manifest"));
123 shortcut_info_.UpdateFromManifest(manifest);
125 GURL icon_src = ManifestIconSelector::FindBestMatchingIcon(
126 manifest.icons,
127 kPreferredIconSizeInDp,
128 gfx::Screen::GetScreenFor(web_contents()->GetNativeView()));
129 if (icon_src.is_valid()) {
130 web_contents()->DownloadImage(icon_src,
131 false,
132 preferred_icon_size_in_px_,
133 false,
134 base::Bind(&ShortcutHelper::OnDidDownloadIcon,
135 weak_ptr_factory_.GetWeakPtr()));
136 manifest_icon_status_ = MANIFEST_ICON_STATUS_FETCHING;
139 // The ShortcutHelper is now able to notify its Java counterpart that it is
140 // initialized. OnInitialized method is not conceptually part of getting the
141 // manifest data but it happens that the initialization is finalized when
142 // these data are available.
143 JNIEnv* env = base::android::AttachCurrentThread();
144 ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
145 ScopedJavaLocalRef<jstring> j_title =
146 base::android::ConvertUTF16ToJavaString(env, shortcut_info_.title);
148 Java_ShortcutHelper_onInitialized(env, j_obj.obj(), j_title.obj());
151 void ShortcutHelper::OnDidDownloadIcon(int id,
152 int http_status_code,
153 const GURL& url,
154 const std::vector<SkBitmap>& bitmaps,
155 const std::vector<gfx::Size>& sizes) {
156 // If getting the candidate manifest icon failed, the ShortcutHelper should
157 // fallback to the favicon.
158 // If the user already requested to add the shortcut, it will do so but use
159 // the favicon instead.
160 // Otherwise, it sets the state as if there was no manifest icon pending.
161 if (bitmaps.empty()) {
162 if (add_shortcut_requested_)
163 AddShortcutUsingFavicon();
164 else
165 manifest_icon_status_ = MANIFEST_ICON_STATUS_NONE;
166 return;
169 // There might be multiple bitmaps returned. The one to pick is bigger or
170 // equal to the preferred size. |bitmaps| is ordered from bigger to smaller.
171 int preferred_bitmap_index = 0;
172 for (size_t i = 0; i < bitmaps.size(); ++i) {
173 if (bitmaps[i].height() < preferred_icon_size_in_px_)
174 break;
175 preferred_bitmap_index = i;
178 manifest_icon_ = bitmaps[preferred_bitmap_index];
179 manifest_icon_status_ = MANIFEST_ICON_STATUS_DONE;
181 if (add_shortcut_requested_)
182 AddShortcutUsingManifestIcon();
185 void ShortcutHelper::TearDown(JNIEnv*, jobject) {
186 Destroy();
189 void ShortcutHelper::Destroy() {
190 delete this;
193 void ShortcutHelper::AddShortcut(
194 JNIEnv* env,
195 jobject obj,
196 jstring jtitle,
197 jint launcher_large_icon_size) {
198 add_shortcut_requested_ = true;
200 base::string16 title = base::android::ConvertJavaStringToUTF16(env, jtitle);
201 if (!title.empty())
202 shortcut_info_.title = title;
204 switch (manifest_icon_status_) {
205 case MANIFEST_ICON_STATUS_NONE:
206 AddShortcutUsingFavicon();
207 break;
208 case MANIFEST_ICON_STATUS_FETCHING:
209 // ::OnDidDownloadIcon() will call AddShortcutUsingManifestIcon().
210 break;
211 case MANIFEST_ICON_STATUS_DONE:
212 AddShortcutUsingManifestIcon();
213 break;
217 void ShortcutHelper::AddShortcutUsingManifestIcon() {
218 RecordAddToHomescreen();
220 // Stop observing so we don't get destroyed while doing the last steps.
221 Observe(NULL);
223 base::WorkerPool::PostTask(
224 FROM_HERE,
225 base::Bind(&ShortcutHelper::AddShortcutInBackgroundWithSkBitmap,
226 shortcut_info_,
227 manifest_icon_,
228 true),
229 true);
231 Destroy();
234 void ShortcutHelper::AddShortcutUsingFavicon() {
235 RecordAddToHomescreen();
237 Profile* profile =
238 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
240 // Grab the best, largest icon we can find to represent this bookmark.
241 // TODO(dfalcantara): Try combining with the new BookmarksHandler once its
242 // rewrite is further along.
243 std::vector<int> icon_types;
244 icon_types.push_back(favicon_base::FAVICON);
245 icon_types.push_back(favicon_base::TOUCH_PRECOMPOSED_ICON |
246 favicon_base::TOUCH_ICON);
247 favicon::FaviconService* favicon_service =
248 FaviconServiceFactory::GetForProfile(profile,
249 ServiceAccessType::EXPLICIT_ACCESS);
251 // Using favicon if its size is not smaller than platform required size,
252 // otherwise using the largest icon among all avaliable icons.
253 int threshold_to_get_any_largest_icon = preferred_icon_size_in_px_ - 1;
254 favicon_service->GetLargestRawFaviconForPageURL(
255 shortcut_info_.url,
256 icon_types,
257 threshold_to_get_any_largest_icon,
258 base::Bind(&ShortcutHelper::OnDidGetFavicon,
259 base::Unretained(this)),
260 &cancelable_task_tracker_);
263 void ShortcutHelper::OnDidGetFavicon(
264 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
265 // Stop observing so we don't get destroyed while doing the last steps.
266 Observe(NULL);
268 base::WorkerPool::PostTask(
269 FROM_HERE,
270 base::Bind(&ShortcutHelper::AddShortcutInBackgroundWithRawBitmap,
271 shortcut_info_,
272 bitmap_result),
273 true);
275 Destroy();
278 bool ShortcutHelper::OnMessageReceived(const IPC::Message& message) {
279 bool handled = true;
281 IPC_BEGIN_MESSAGE_MAP(ShortcutHelper, message)
282 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidGetWebApplicationInfo,
283 OnDidGetWebApplicationInfo)
284 IPC_MESSAGE_UNHANDLED(handled = false)
285 IPC_END_MESSAGE_MAP()
287 return handled;
290 void ShortcutHelper::WebContentsDestroyed() {
291 Destroy();
294 bool ShortcutHelper::RegisterShortcutHelper(JNIEnv* env) {
295 return RegisterNativesImpl(env);
298 void ShortcutHelper::AddShortcutInBackgroundWithRawBitmap(
299 const ShortcutInfo& info,
300 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
301 DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
303 SkBitmap icon_bitmap;
304 if (bitmap_result.is_valid()) {
305 gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
306 bitmap_result.bitmap_data->size(),
307 &icon_bitmap);
310 AddShortcutInBackgroundWithSkBitmap(info, icon_bitmap, true);
313 void ShortcutHelper::AddShortcutInBackgroundWithSkBitmap(
314 const ShortcutInfo& info,
315 const SkBitmap& icon_bitmap,
316 const bool return_to_homescreen) {
317 DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
319 SkColor color = color_utils::CalculateKMeanColorOfBitmap(icon_bitmap);
320 int r_value = SkColorGetR(color);
321 int g_value = SkColorGetG(color);
322 int b_value = SkColorGetB(color);
324 // Send the data to the Java side to create the shortcut.
325 JNIEnv* env = base::android::AttachCurrentThread();
326 ScopedJavaLocalRef<jstring> java_url =
327 base::android::ConvertUTF8ToJavaString(env, info.url.spec());
328 ScopedJavaLocalRef<jstring> java_title =
329 base::android::ConvertUTF16ToJavaString(env, info.title);
330 ScopedJavaLocalRef<jobject> java_bitmap;
331 if (icon_bitmap.getSize())
332 java_bitmap = gfx::ConvertToJavaBitmap(&icon_bitmap);
334 Java_ShortcutHelper_addShortcut(
335 env,
336 base::android::GetApplicationContext(),
337 java_url.obj(),
338 java_title.obj(),
339 java_bitmap.obj(),
340 r_value,
341 g_value,
342 b_value,
343 info.display == content::Manifest::DISPLAY_MODE_STANDALONE,
344 info.orientation,
345 return_to_homescreen);
348 void ShortcutHelper::RecordAddToHomescreen() {
349 // Record that the shortcut has been added, so no banners will be shown
350 // for this app.
351 AppBannerSettingsHelper::RecordBannerEvent(
352 web_contents(), shortcut_info_.url, shortcut_info_.url.spec(),
353 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
354 base::Time::Now());