OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include <utility> |
| 6 |
5 #include "chrome/browser/android/banners/app_banner_manager_android.h" | 7 #include "chrome/browser/android/banners/app_banner_manager_android.h" |
6 | 8 |
7 #include "base/android/jni_android.h" | 9 #include "base/android/jni_android.h" |
8 #include "base/android/jni_string.h" | 10 #include "base/android/jni_string.h" |
9 #include "chrome/browser/android/banners/app_banner_data_fetcher_android.h" | 11 #include "base/memory/ptr_util.h" |
| 12 #include "base/strings/utf_string_conversions.h" |
| 13 #include "chrome/browser/android/banners/app_banner_infobar_delegate_android.h" |
10 #include "chrome/browser/android/shortcut_helper.h" | 14 #include "chrome/browser/android/shortcut_helper.h" |
11 #include "chrome/browser/banners/app_banner_metrics.h" | 15 #include "chrome/browser/banners/app_banner_metrics.h" |
| 16 #include "chrome/browser/infobars/infobar_service.h" |
| 17 #include "chrome/browser/manifest/manifest_icon_downloader.h" |
| 18 #include "chrome/browser/manifest/manifest_icon_selector.h" |
| 19 #include "chrome/browser/ui/android/infobars/app_banner_infobar_android.h" |
12 #include "chrome/common/chrome_constants.h" | 20 #include "chrome/common/chrome_constants.h" |
13 #include "content/public/browser/web_contents.h" | 21 #include "content/public/browser/web_contents.h" |
14 #include "content/public/common/frame_navigate_params.h" | 22 #include "content/public/common/frame_navigate_params.h" |
15 #include "jni/AppBannerManager_jni.h" | 23 #include "jni/AppBannerManager_jni.h" |
| 24 #include "third_party/skia/include/core/SkBitmap.h" |
16 | 25 |
17 using base::android::ConvertJavaStringToUTF8; | 26 using base::android::ConvertJavaStringToUTF8; |
18 using base::android::ConvertJavaStringToUTF16; | 27 using base::android::ConvertJavaStringToUTF16; |
19 using base::android::ConvertUTF8ToJavaString; | 28 using base::android::ConvertUTF8ToJavaString; |
20 using base::android::ConvertUTF16ToJavaString; | |
21 using base::android::JavaParamRef; | 29 using base::android::JavaParamRef; |
22 using base::android::ScopedJavaLocalRef; | 30 using base::android::ScopedJavaLocalRef; |
23 | 31 |
24 DEFINE_WEB_CONTENTS_USER_DATA_KEY(banners::AppBannerManagerAndroid); | 32 DEFINE_WEB_CONTENTS_USER_DATA_KEY(banners::AppBannerManagerAndroid); |
25 | 33 |
26 namespace { | 34 namespace { |
27 | 35 |
28 const char kPlayPlatform[] = "play"; | 36 const char kPlayPlatform[] = "play"; |
29 const char kReferrerName[] = "referrer"; | 37 const char kReferrerName[] = "referrer"; |
30 const char kIdName[] = "id"; | 38 const char kIdName[] = "id"; |
31 const char kPlayInlineReferrer[] = "playinline=chrome_inline"; | 39 const char kPlayInlineReferrer[] = "playinline=chrome_inline"; |
32 | 40 |
33 } // anonymous namespace | 41 } // anonymous namespace |
34 | 42 |
35 namespace banners { | 43 namespace banners { |
36 | 44 |
37 AppBannerManagerAndroid::AppBannerManagerAndroid( | 45 AppBannerManagerAndroid::AppBannerManagerAndroid( |
38 content::WebContents* web_contents) | 46 content::WebContents* web_contents) |
39 : AppBannerManager(web_contents) { | 47 : AppBannerManager(web_contents) { |
40 CreateJavaBannerManager(); | 48 CreateJavaBannerManager(); |
41 } | 49 } |
42 | 50 |
43 AppBannerManagerAndroid::~AppBannerManagerAndroid() { | 51 AppBannerManagerAndroid::~AppBannerManagerAndroid() { |
44 JNIEnv* env = base::android::AttachCurrentThread(); | 52 JNIEnv* env = base::android::AttachCurrentThread(); |
45 Java_AppBannerManager_destroy(env, java_banner_manager_.obj()); | 53 Java_AppBannerManager_destroy(env, java_banner_manager_.obj()); |
46 java_banner_manager_.Reset(); | 54 java_banner_manager_.Reset(); |
47 } | 55 } |
48 | 56 |
| 57 base::Closure AppBannerManagerAndroid::FetchWebappSplashScreenImageCallback( |
| 58 const std::string& webapp_id) { |
| 59 content::WebContents* contents = web_contents(); |
| 60 DCHECK(contents); |
| 61 |
| 62 int ideal_splash_image_size_in_dp = |
| 63 ShortcutHelper::GetIdealSplashImageSizeInDp(); |
| 64 int minimum_splash_image_size_in_dp = |
| 65 ShortcutHelper::GetMinimumSplashImageSizeInDp(); |
| 66 GURL image_url = ManifestIconSelector::FindBestMatchingIcon( |
| 67 manifest_.icons, ideal_splash_image_size_in_dp, |
| 68 minimum_splash_image_size_in_dp); |
| 69 |
| 70 return base::Bind(&ShortcutHelper::FetchSplashScreenImage, contents, |
| 71 image_url, ideal_splash_image_size_in_dp, |
| 72 minimum_splash_image_size_in_dp, webapp_id); |
| 73 } |
| 74 |
49 const base::android::ScopedJavaGlobalRef<jobject>& | 75 const base::android::ScopedJavaGlobalRef<jobject>& |
50 AppBannerManagerAndroid::GetJavaBannerManager() const { | 76 AppBannerManagerAndroid::GetJavaBannerManager() const { |
51 return java_banner_manager_; | 77 return java_banner_manager_; |
52 } | 78 } |
53 | 79 |
54 bool AppBannerManagerAndroid::IsFetcherActive( | 80 bool AppBannerManagerAndroid::IsActiveForTesting( |
55 JNIEnv* env, | 81 JNIEnv* env, |
56 const JavaParamRef<jobject>& obj) { | 82 const JavaParamRef<jobject>& obj) { |
57 return AppBannerManager::IsFetcherActive(); | 83 return is_active(); |
58 } | 84 } |
59 | 85 |
60 bool AppBannerManagerAndroid::OnAppDetailsRetrieved( | 86 bool AppBannerManagerAndroid::OnAppDetailsRetrieved( |
61 JNIEnv* env, | 87 JNIEnv* env, |
62 const JavaParamRef<jobject>& obj, | 88 const JavaParamRef<jobject>& obj, |
63 const JavaParamRef<jobject>& japp_data, | 89 const JavaParamRef<jobject>& japp_data, |
64 const JavaParamRef<jstring>& japp_title, | 90 const JavaParamRef<jstring>& japp_title, |
65 const JavaParamRef<jstring>& japp_package, | 91 const JavaParamRef<jstring>& japp_package, |
66 const JavaParamRef<jstring>& jicon_url) { | 92 const JavaParamRef<jstring>& jicon_url) { |
67 AppBannerDataFetcherAndroid* android_fetcher = | 93 native_app_data_.Reset(japp_data); |
68 static_cast<AppBannerDataFetcherAndroid*>(data_fetcher().get()); | 94 app_title_ = ConvertJavaStringToUTF16(env, japp_title); |
69 if (!CheckFetcherMatchesContents(android_fetcher->is_debug_mode())) | 95 native_app_package_ = ConvertJavaStringToUTF8(env, japp_package); |
70 return false; | 96 icon_url_ = GURL(ConvertJavaStringToUTF8(env, jicon_url)); |
71 | 97 |
72 GURL image_url = GURL(ConvertJavaStringToUTF8(env, jicon_url)); | 98 return ManifestIconDownloader::Download( |
73 | 99 web_contents(), icon_url_, GetIdealIconSizeInDp(), |
74 return android_fetcher->ContinueFetching( | 100 GetMinimumIconSizeInDp(), |
75 ConvertJavaStringToUTF16(env, japp_title), | 101 base::Bind(&AppBannerManager::OnAppIconFetched, GetWeakPtr())); |
76 ConvertJavaStringToUTF8(env, japp_package), japp_data, image_url); | |
77 } | 102 } |
78 | 103 |
79 void AppBannerManagerAndroid::RequestAppBanner(const GURL& validated_url, | 104 void AppBannerManagerAndroid::RequestAppBanner(const GURL& validated_url, |
80 bool is_debug_mode) { | 105 bool is_debug_mode) { |
81 JNIEnv* env = base::android::AttachCurrentThread(); | 106 JNIEnv* env = base::android::AttachCurrentThread(); |
82 if (!Java_AppBannerManager_isEnabledForTab(env, java_banner_manager_.obj())) | 107 if (!Java_AppBannerManager_isEnabledForTab(env, java_banner_manager_.obj())) |
83 return; | 108 return; |
84 | 109 |
85 AppBannerManager::RequestAppBanner(validated_url, is_debug_mode); | 110 AppBannerManager::RequestAppBanner(validated_url, is_debug_mode); |
86 } | 111 } |
87 | 112 |
88 AppBannerDataFetcher* AppBannerManagerAndroid::CreateAppBannerDataFetcher( | 113 std::string AppBannerManagerAndroid::GetAppIdentifier() { |
89 base::WeakPtr<Delegate> weak_delegate, | 114 return native_app_data_.is_null() ? AppBannerManager::GetAppIdentifier() |
90 bool is_debug_mode) { | 115 : native_app_package_; |
91 return new AppBannerDataFetcherAndroid( | |
92 web_contents(), weak_delegate, | |
93 ShortcutHelper::GetIdealHomescreenIconSizeInDp(), | |
94 ShortcutHelper::GetMinimumHomescreenIconSizeInDp(), | |
95 ShortcutHelper::GetIdealSplashImageSizeInDp(), | |
96 ShortcutHelper::GetMinimumSplashImageSizeInDp(), is_debug_mode); | |
97 } | 116 } |
98 | 117 |
99 bool AppBannerManagerAndroid::HandleNonWebApp(const std::string& platform, | 118 std::string AppBannerManagerAndroid::GetBannerType() { |
| 119 return native_app_data_.is_null() ? AppBannerManager::GetBannerType() |
| 120 : "android"; |
| 121 } |
| 122 |
| 123 int AppBannerManagerAndroid::GetIdealIconSizeInDp() { |
| 124 return ShortcutHelper::GetIdealHomescreenIconSizeInDp(); |
| 125 } |
| 126 |
| 127 int AppBannerManagerAndroid::GetMinimumIconSizeInDp() { |
| 128 return ShortcutHelper::GetMinimumHomescreenIconSizeInDp(); |
| 129 } |
| 130 |
| 131 bool AppBannerManagerAndroid::IsWebAppInstalled( |
| 132 content::BrowserContext* browser_context, |
| 133 const GURL& start_url) { |
| 134 // Returns true if a WebAPK is installed. Does not check whether a non-WebAPK |
| 135 // web app is installed: this is detected by the content settings check in |
| 136 // AppBannerSettingsHelper::ShouldShowBanner (due to the lack of an API to |
| 137 // detect what is and isn't on the Android homescreen). |
| 138 // This method will still detect the presence of a WebAPK even if Chrome's |
| 139 // data is cleared. |
| 140 return ShortcutHelper::IsWebApkInstalled(start_url); |
| 141 } |
| 142 |
| 143 void AppBannerManagerAndroid::PerformInstallableCheck() { |
| 144 // Check if the manifest prefers that we show a native app banner. If so, call |
| 145 // to Java to verify the details. |
| 146 if (manifest_.prefer_related_applications && |
| 147 manifest_.related_applications.size()) { |
| 148 for (const auto& application : manifest_.related_applications) { |
| 149 std::string platform = base::UTF16ToUTF8(application.platform.string()); |
| 150 std::string id = base::UTF16ToUTF8(application.id.string()); |
| 151 if (CanHandleNonWebApp(platform, application.url, id)) |
| 152 return; |
| 153 } |
| 154 } |
| 155 |
| 156 // No native app banner was requested. Continue checking for a web app banner. |
| 157 AppBannerManager::PerformInstallableCheck(); |
| 158 } |
| 159 |
| 160 void AppBannerManagerAndroid::OnAppIconFetched(const SkBitmap& bitmap) { |
| 161 if (bitmap.drawsNothing()) { |
| 162 ReportError(web_contents(), NO_ICON_AVAILABLE); |
| 163 Stop(); |
| 164 } |
| 165 |
| 166 if (!is_active()) |
| 167 return; |
| 168 |
| 169 icon_.reset(new SkBitmap(bitmap)); |
| 170 SendBannerPromptRequest(); |
| 171 } |
| 172 |
| 173 void AppBannerManagerAndroid::ShowBanner() { |
| 174 content::WebContents* contents = web_contents(); |
| 175 DCHECK(contents); |
| 176 |
| 177 infobars::InfoBar* infobar = nullptr; |
| 178 if (native_app_data_.is_null()) { |
| 179 std::unique_ptr<AppBannerInfoBarDelegateAndroid> delegate( |
| 180 new AppBannerInfoBarDelegateAndroid( |
| 181 GetWeakPtr(), app_title_, manifest_url_, manifest_, icon_url_, |
| 182 std::move(icon_), event_request_id())); |
| 183 |
| 184 infobar = new AppBannerInfoBarAndroid(std::move(delegate), |
| 185 manifest_.start_url); |
| 186 if (infobar) { |
| 187 RecordDidShowBanner("AppBanner.WebApp.Shown"); |
| 188 TrackDisplayEvent(DISPLAY_EVENT_WEB_APP_BANNER_CREATED); |
| 189 } |
| 190 } else { |
| 191 std::unique_ptr<AppBannerInfoBarDelegateAndroid> delegate( |
| 192 new AppBannerInfoBarDelegateAndroid( |
| 193 app_title_, native_app_data_, std::move(icon_), native_app_package_, |
| 194 referrer_, event_request_id())); |
| 195 infobar = |
| 196 new AppBannerInfoBarAndroid(std::move(delegate), native_app_data_); |
| 197 if (infobar) { |
| 198 RecordDidShowBanner("AppBanner.NativeApp.Shown"); |
| 199 TrackDisplayEvent(DISPLAY_EVENT_NATIVE_APP_BANNER_CREATED); |
| 200 } |
| 201 } |
| 202 |
| 203 if (infobar) { |
| 204 InfoBarService::FromWebContents(contents)->AddInfoBar( |
| 205 base::WrapUnique(infobar)); |
| 206 } |
| 207 } |
| 208 |
| 209 bool AppBannerManagerAndroid::CanHandleNonWebApp(const std::string& platform, |
100 const GURL& url, | 210 const GURL& url, |
101 const std::string& id, | 211 const std::string& id) { |
102 bool is_debug_mode) { | 212 if (!CheckPlatformAndId(platform, id)) |
103 if (!CheckPlatformAndId(platform, id, is_debug_mode)) | |
104 return false; | 213 return false; |
105 | 214 |
106 banners::TrackDisplayEvent(DISPLAY_EVENT_NATIVE_APP_BANNER_REQUESTED); | 215 banners::TrackDisplayEvent(DISPLAY_EVENT_NATIVE_APP_BANNER_REQUESTED); |
107 | 216 |
108 // Send the info to the Java side to get info about the app. | 217 // Send the info to the Java side to get info about the app. |
109 JNIEnv* env = base::android::AttachCurrentThread(); | 218 JNIEnv* env = base::android::AttachCurrentThread(); |
110 if (java_banner_manager_.is_null()) | 219 if (java_banner_manager_.is_null()) |
111 return false; | 220 return false; |
112 | 221 |
113 std::string id_from_app_url = ExtractQueryValueForName(url, kIdName); | 222 std::string id_from_app_url = ExtractQueryValueForName(url, kIdName); |
114 if (id_from_app_url.size() && id != id_from_app_url) { | 223 if (id_from_app_url.size() && id != id_from_app_url) { |
115 banners::OutputDeveloperNotShownMessage( | 224 ReportError(web_contents(), IDS_DO_NOT_MATCH); |
116 web_contents(), banners::kIgnoredIdsDoNotMatch, is_debug_mode); | |
117 return false; | 225 return false; |
118 } | 226 } |
119 | 227 |
120 std::string referrer = | 228 std::string referrer = ExtractQueryValueForName(url, kReferrerName); |
121 ExtractQueryValueForName(url, kReferrerName); | |
122 | 229 |
123 // Attach the chrome_inline referrer value, prefixed with "&" if the referrer | 230 // Attach the chrome_inline referrer value, prefixed with "&" if the referrer |
124 // is non empty. | 231 // is non empty. |
125 if (referrer.empty()) | 232 if (referrer.empty()) |
126 referrer = kPlayInlineReferrer; | 233 referrer = kPlayInlineReferrer; |
127 else | 234 else |
128 referrer.append("&").append(kPlayInlineReferrer); | 235 referrer.append("&").append(kPlayInlineReferrer); |
129 | 236 |
130 ScopedJavaLocalRef<jstring> jurl( | 237 ScopedJavaLocalRef<jstring> jurl( |
131 ConvertUTF8ToJavaString(env, data_fetcher()->validated_url().spec())); | 238 ConvertUTF8ToJavaString(env, validated_url_.spec())); |
132 ScopedJavaLocalRef<jstring> jpackage( | 239 ScopedJavaLocalRef<jstring> jpackage(ConvertUTF8ToJavaString(env, id)); |
133 ConvertUTF8ToJavaString(env, id)); | 240 ScopedJavaLocalRef<jstring> jreferrer(ConvertUTF8ToJavaString(env, referrer)); |
134 ScopedJavaLocalRef<jstring> jreferrer( | |
135 ConvertUTF8ToJavaString(env, referrer)); | |
136 Java_AppBannerManager_fetchAppDetails( | 241 Java_AppBannerManager_fetchAppDetails( |
137 env, java_banner_manager_.obj(), jurl.obj(), | 242 env, java_banner_manager_.obj(), jurl.obj(), jpackage.obj(), |
138 jpackage.obj(), jreferrer.obj(), | 243 jreferrer.obj(), GetIdealIconSizeInDp()); |
139 ShortcutHelper::GetIdealHomescreenIconSizeInDp()); | |
140 return true; | 244 return true; |
141 } | 245 } |
142 | 246 |
143 void AppBannerManagerAndroid::CreateJavaBannerManager() { | 247 void AppBannerManagerAndroid::CreateJavaBannerManager() { |
144 JNIEnv* env = base::android::AttachCurrentThread(); | 248 JNIEnv* env = base::android::AttachCurrentThread(); |
145 java_banner_manager_.Reset( | 249 java_banner_manager_.Reset( |
146 Java_AppBannerManager_create(env, reinterpret_cast<intptr_t>(this))); | 250 Java_AppBannerManager_create(env, reinterpret_cast<intptr_t>(this))); |
147 } | 251 } |
148 | 252 |
149 bool AppBannerManagerAndroid::CheckFetcherMatchesContents(bool is_debug_mode) { | 253 bool AppBannerManagerAndroid::CheckPlatformAndId(const std::string& platform, |
150 if (!web_contents()) | 254 const std::string& id) { |
| 255 if (platform != kPlayPlatform) { |
| 256 ReportError(web_contents(), PLATFORM_NOT_SUPPORTED_ON_ANDROID); |
151 return false; | 257 return false; |
152 | 258 } |
153 if (!data_fetcher() || | 259 if (id.empty()) { |
154 data_fetcher()->validated_url() != web_contents()->GetURL()) { | 260 ReportError(web_contents(), NO_ID_SPECIFIED); |
155 banners::OutputDeveloperNotShownMessage( | |
156 web_contents(), banners::kUserNavigatedBeforeBannerShown, | |
157 is_debug_mode); | |
158 return false; | 261 return false; |
159 } | 262 } |
160 return true; | 263 return true; |
161 } | |
162 | |
163 bool AppBannerManagerAndroid::CheckPlatformAndId(const std::string& platform, | |
164 const std::string& id, | |
165 bool is_debug_mode) { | |
166 if (platform != kPlayPlatform) { | |
167 banners::OutputDeveloperNotShownMessage( | |
168 web_contents(), banners::kIgnoredNotSupportedOnAndroid, platform, | |
169 is_debug_mode); | |
170 return false; | |
171 } | |
172 if (id.empty()) { | |
173 banners::OutputDeveloperNotShownMessage( | |
174 web_contents(), banners::kIgnoredNoId, is_debug_mode); | |
175 return false; | |
176 } | |
177 return true; | |
178 } | 264 } |
179 | 265 |
180 std::string AppBannerManagerAndroid::ExtractQueryValueForName( | 266 std::string AppBannerManagerAndroid::ExtractQueryValueForName( |
181 const GURL& url, | 267 const GURL& url, |
182 const std::string& name) { | 268 const std::string& name) { |
183 url::Component query = url.parsed_for_possibly_invalid_spec().query; | 269 url::Component query = url.parsed_for_possibly_invalid_spec().query; |
184 url::Component key, value; | 270 url::Component key, value; |
185 const char* url_spec = url.spec().c_str(); | 271 const char* url_spec = url.spec().c_str(); |
186 | 272 |
187 while (url::ExtractQueryKeyValue(url_spec, &query, &key, &value)) { | 273 while (url::ExtractQueryKeyValue(url_spec, &query, &key, &value)) { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
223 jdouble direct_engagement, | 309 jdouble direct_engagement, |
224 jdouble indirect_engagement) { | 310 jdouble indirect_engagement) { |
225 AppBannerManager::SetEngagementWeights(direct_engagement, | 311 AppBannerManager::SetEngagementWeights(direct_engagement, |
226 indirect_engagement); | 312 indirect_engagement); |
227 } | 313 } |
228 | 314 |
229 // static | 315 // static |
230 void SetTimeDeltaForTesting(JNIEnv* env, | 316 void SetTimeDeltaForTesting(JNIEnv* env, |
231 const JavaParamRef<jclass>& clazz, | 317 const JavaParamRef<jclass>& clazz, |
232 jint days) { | 318 jint days) { |
233 AppBannerDataFetcher::SetTimeDeltaForTesting(days); | 319 AppBannerManager::SetTimeDeltaForTesting(days); |
234 } | 320 } |
235 | 321 |
236 } // namespace banners | 322 } // namespace banners |
OLD | NEW |