1 // Copyright 2014 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 package org.chromium.chrome.browser.banners;
7 import android.app.PendingIntent;
8 import android.graphics.Bitmap;
9 import android.graphics.drawable.BitmapDrawable;
10 import android.text.TextUtils;
12 import org.chromium.base.CalledByNative;
13 import org.chromium.base.JNINamespace;
14 import org.chromium.chrome.browser.EmptyTabObserver;
15 import org.chromium.chrome.browser.Tab;
16 import org.chromium.chrome.browser.TabObserver;
17 import org.chromium.content.browser.ContentViewCore;
18 import org.chromium.content_public.browser.WebContents;
19 import org.chromium.ui.R;
22 * Manages an AppBannerView for a Tab and its ContentView.
24 * The AppBannerManager manages a single AppBannerView, dismissing it when the user navigates to a
25 * new page or creating a new one when it detects that the current webpage is requesting a banner to
26 * be built. The actual observation of the WebContents (which triggers the automatic creation and
27 * removal of banners, among other things) is done by the native-side AppBannerManager.
29 * This Java-side class owns its native-side counterpart, which is basically used to grab resources
32 @JNINamespace("banners")
33 public class AppBannerManager implements AppBannerView.Observer, AppDetailsDelegate.Observer {
34 private static final String TAG = "AppBannerManager";
36 /** Retrieves information about a given package. */
37 private static AppDetailsDelegate sAppDetailsDelegate;
39 /** Pointer to the native side AppBannerManager. */
40 private final long mNativePointer;
42 /** Tab that the AppBannerView/AppBannerManager is owned by. */
43 private final Tab mTab;
45 /** ContentViewCore that the AppBannerView/AppBannerManager is currently attached to. */
46 private ContentViewCore mContentViewCore;
48 /** Current banner being shown. */
49 private AppBannerView mBannerView;
51 /** Data about the app being advertised. */
52 private AppData mAppData;
55 * Checks if app banners are enabled.
56 * @return True if banners are enabled, false otherwise.
58 public static boolean isEnabled() {
59 return nativeIsEnabled();
63 * Sets the delegate that provides information about a given package.
64 * @param delegate Delegate to use. Previously set ones are destroyed.
66 public static void setAppDetailsDelegate(AppDetailsDelegate delegate) {
67 if (sAppDetailsDelegate != null) sAppDetailsDelegate.destroy();
68 sAppDetailsDelegate = delegate;
72 * Constructs an AppBannerManager for the given tab.
73 * @param tab Tab that the AppBannerManager will be attached to.
75 public AppBannerManager(Tab tab) {
76 mNativePointer = nativeInit();
78 mTab.addObserver(createTabObserver());
83 * Creates a TabObserver for monitoring a Tab, used to react to changes in the ContentView
84 * or to trigger its own destruction.
85 * @return TabObserver that can be used to monitor a Tab.
87 private TabObserver createTabObserver() {
88 return new EmptyTabObserver() {
90 public void onWebContentsSwapped(Tab tab, boolean didStartLoad,
91 boolean didFinishLoad) {
96 public void onContentChanged(Tab tab) {
101 public void onDestroyed(Tab tab) {
102 nativeDestroy(mNativePointer);
103 mContentViewCore = null;
110 * Updates which ContentView and WebContents the AppBannerView is monitoring.
112 private void updatePointers() {
113 if (mContentViewCore != mTab.getContentViewCore())
114 mContentViewCore = mTab.getContentViewCore();
115 nativeReplaceWebContents(mNativePointer, mTab.getWebContents());
119 * Grabs package information for the banner asynchronously.
120 * @param url URL for the page that is triggering the banner.
121 * @param packageName Name of the package that is being advertised.
124 private void prepareBanner(String url, String packageName) {
125 // Get rid of whatever banner is there currently.
126 if (mBannerView != null) dismissCurrentBanner(AppBannerMetricsIds.DISMISS_ERROR);
128 if (sAppDetailsDelegate == null || !isBannerForCurrentPage(url)) return;
130 int iconSize = AppBannerView.getIconSize(mContentViewCore.getContext());
131 sAppDetailsDelegate.getAppDetailsAsynchronously(this, url, packageName, iconSize);
135 * Called when data about the package has been retrieved, which includes the url for the app's
136 * icon but not the icon Bitmap itself. Kicks off a background task to retrieve it.
137 * @param data Data about the app. Null if the task failed.
140 public void onAppDetailsRetrieved(AppData data) {
141 if (data == null || !isBannerForCurrentPage(data.siteUrl())) return;
144 String imageUrl = data.imageUrl();
145 if (TextUtils.isEmpty(imageUrl) || !nativeFetchIcon(mNativePointer, imageUrl)) resetState();
149 * Called when all the data required to show a banner has finally been retrieved.
150 * Creates the banner and shows it, as long as the banner is still meant for the current page.
151 * @param imageUrl URL of the icon.
152 * @param appIcon Bitmap containing the icon itself.
153 * @return Whether or not the banner was created.
156 private boolean createBanner(String imageUrl, Bitmap appIcon) {
157 if (mAppData == null || !isBannerForCurrentPage(mAppData.siteUrl())) return false;
159 if (!TextUtils.equals(mAppData.imageUrl(), imageUrl)) {
164 mAppData.setIcon(new BitmapDrawable(mContentViewCore.getContext().getResources(), appIcon));
165 mBannerView = AppBannerView.create(mContentViewCore, this, mAppData);
170 * Dismisses whatever banner is currently being displayed. This is treated as an automatic
171 * dismissal and not one that blocks the banner from appearing in the future.
172 * @param dismissalType What triggered the dismissal.
175 private void dismissCurrentBanner(int dismissalType) {
176 if (mBannerView != null) mBannerView.dismiss(dismissalType);
181 public void onBannerRemoved(AppBannerView banner) {
182 if (mBannerView != banner) return;
187 public void onBannerBlocked(AppBannerView banner, String url, String packageName) {
188 if (mBannerView != banner) return;
189 nativeBlockBanner(mNativePointer, url, packageName);
193 public void onBannerDismissEvent(AppBannerView banner, int eventType) {
194 if (mBannerView != banner) return;
195 nativeRecordDismissEvent(eventType);
199 public void onBannerInstallEvent(AppBannerView banner, int eventType) {
200 if (mBannerView != banner) return;
201 nativeRecordInstallEvent(eventType);
205 public boolean onFireIntent(AppBannerView banner, PendingIntent intent) {
206 if (mBannerView != banner) return false;
207 return mTab.getWindowAndroid().showIntent(intent, banner, R.string.low_memory_error);
211 * Resets all of the state, killing off any running tasks.
213 private void resetState() {
214 if (mBannerView != null) {
215 mBannerView.destroy();
223 * Checks to see if the banner is for the currently displayed page.
224 * @param bannerUrl URL that requested a banner.
225 * @return True if the user is still on the same page.
227 private boolean isBannerForCurrentPage(String bannerUrl) {
228 return mContentViewCore != null &&
229 TextUtils.equals(mContentViewCore.getUrl(), bannerUrl);
232 private static native boolean nativeIsEnabled();
233 private native long nativeInit();
234 private native void nativeDestroy(long nativeAppBannerManager);
235 private native void nativeReplaceWebContents(long nativeAppBannerManager,
236 WebContents webContents);
237 private native void nativeBlockBanner(
238 long nativeAppBannerManager, String url, String packageName);
239 private native boolean nativeFetchIcon(long nativeAppBannerManager, String imageUrl);
242 private static native void nativeRecordDismissEvent(int metric);
243 private static native void nativeRecordInstallEvent(int metric);