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"
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"
14 #include "base/task/cancelable_task_tracker.h"
15 #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"
19 #include "chrome/common/chrome_constants.h"
20 #include "chrome/common/render_messages.h"
21 #include "chrome/common/web_application_info.h"
22 #include "content/public/browser/user_metrics.h"
23 #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"
26 #include "content/public/common/manifest.h"
27 #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"
31 #include "ui/gfx/favicon_size.h"
34 jlong
Initialize(JNIEnv
* env
, jobject obj
, jlong tab_android_ptr
) {
35 TabAndroid
* tab
= reinterpret_cast<TabAndroid
*>(tab_android_ptr
);
37 ShortcutHelper
* shortcut_helper
=
38 new ShortcutHelper(env
, obj
, tab
->web_contents());
39 shortcut_helper
->Initialize();
41 return reinterpret_cast<intptr_t>(shortcut_helper
);
44 ShortcutHelper::ShortcutHelper(JNIEnv
* env
,
46 content::WebContents
* web_contents
)
47 : WebContentsObserver(web_contents
),
49 url_(web_contents
->GetURL()),
50 display_(content::Manifest::DISPLAY_MODE_BROWSER
),
51 orientation_(blink::WebScreenOrientationLockDefault
),
52 weak_ptr_factory_(this) {
55 void ShortcutHelper::Initialize() {
56 // Send a message to the renderer to retrieve information about the page.
57 Send(new ChromeViewMsg_GetWebApplicationInfo(routing_id()));
60 ShortcutHelper::~ShortcutHelper() {
63 void ShortcutHelper::OnDidGetWebApplicationInfo(
64 const WebApplicationInfo
& received_web_app_info
) {
65 // Sanitize received_web_app_info.
66 WebApplicationInfo web_app_info
= received_web_app_info
;
68 web_app_info
.title
.substr(0, chrome::kMaxMetaTagAttributeLength
);
69 web_app_info
.description
=
70 web_app_info
.description
.substr(0, chrome::kMaxMetaTagAttributeLength
);
72 title_
= web_app_info
.title
.empty() ? web_contents()->GetTitle()
75 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
;
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"));
86 case WebApplicationInfo::MOBILE_CAPABLE_APPLE
:
87 content::RecordAction(
88 base::UserMetricsAction("webapps.AddShortcut.AppShortcutApple"));
90 case WebApplicationInfo::MOBILE_CAPABLE_UNSPECIFIED
:
91 content::RecordAction(
92 base::UserMetricsAction("webapps.AddShortcut.Bookmark"));
96 web_contents()->GetManifest(base::Bind(&ShortcutHelper::OnDidGetManifest
,
97 weak_ptr_factory_
.GetWeakPtr()));
100 void 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();
107 // Set the url based on the manifest value, if any.
108 if (manifest
.start_url
.is_valid())
109 url_
= manifest
.start_url
;
111 // Set the display based on the manifest value, if any.
112 if (manifest
.display
!= content::Manifest::DISPLAY_MODE_UNSPECIFIED
)
113 display_
= manifest
.display
;
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
;
122 // 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
126 // TODO(mlamouri): send a message to the developer console about this.
127 if (display_
== content::Manifest::DISPLAY_MODE_STANDALONE
)
128 orientation_
= manifest
.orientation
;
131 // 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.
135 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_
);
140 Java_ShortcutHelper_onInitialized(env
, j_obj
.obj(), j_title
.obj());
143 void ShortcutHelper::TearDown(JNIEnv
*, jobject
) {
147 void ShortcutHelper::Destroy() {
151 void ShortcutHelper::AddShortcut(
155 jint launcher_large_icon_size
) {
156 base::string16 title
= base::android::ConvertJavaStringToUTF16(env
, jtitle
);
161 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
163 // 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.
166 std::vector
<int> icon_types
;
167 icon_types
.push_back(favicon_base::FAVICON
);
168 icon_types
.push_back(favicon_base::TOUCH_PRECOMPOSED_ICON
|
169 favicon_base::TOUCH_ICON
);
170 FaviconService
* favicon_service
= FaviconServiceFactory::GetForProfile(
171 profile
, Profile::EXPLICIT_ACCESS
);
173 // 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;
176 favicon_service
->GetLargestRawFaviconForPageURL(url_
, icon_types
,
177 threshold_to_get_any_largest_icon
,
178 base::Bind(&ShortcutHelper::FinishAddingShortcut
,
179 base::Unretained(this)),
180 &cancelable_task_tracker_
);
183 void ShortcutHelper::FinishAddingShortcut(
184 const favicon_base::FaviconRawBitmapResult
& bitmap_result
) {
185 icon_
= bitmap_result
;
187 // Stop observing so we don't get destroyed while doing the last steps.
190 base::WorkerPool::PostTask(
192 base::Bind(&ShortcutHelper::AddShortcutInBackground
,
203 bool ShortcutHelper::OnMessageReceived(const IPC::Message
& message
) {
206 IPC_BEGIN_MESSAGE_MAP(ShortcutHelper
, message
)
207 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidGetWebApplicationInfo
,
208 OnDidGetWebApplicationInfo
)
209 IPC_MESSAGE_UNHANDLED(handled
= false)
210 IPC_END_MESSAGE_MAP()
215 void ShortcutHelper::WebContentsDestroyed() {
219 bool ShortcutHelper::RegisterShortcutHelper(JNIEnv
* env
) {
220 return RegisterNativesImpl(env
);
223 void ShortcutHelper::AddShortcutInBackground(
225 const base::string16
& title
,
226 content::Manifest::DisplayMode display
,
227 const favicon_base::FaviconRawBitmapResult
& bitmap_result
,
228 blink::WebScreenOrientationLockType orientation
) {
229 DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
231 // Grab the average color from the bitmap.
232 SkColor color
= SK_ColorWHITE
;
233 SkBitmap favicon_bitmap
;
234 if (bitmap_result
.is_valid()) {
235 if (gfx::PNGCodec::Decode(bitmap_result
.bitmap_data
->front(),
236 bitmap_result
.bitmap_data
->size(),
238 color
= color_utils::CalculateKMeanColorOfBitmap(favicon_bitmap
);
241 int r_value
= SkColorGetR(color
);
242 int g_value
= SkColorGetG(color
);
243 int b_value
= SkColorGetB(color
);
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
);
255 Java_ShortcutHelper_addShortcut(
257 base::android::GetApplicationContext(),
264 display
== content::Manifest::DISPLAY_MODE_STANDALONE
,