| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.chrome.browser.banners; |
| |
| import android.app.PendingIntent; |
| import android.graphics.Bitmap; |
| import android.graphics.drawable.BitmapDrawable; |
| import android.text.TextUtils; |
| |
| import org.chromium.base.CalledByNative; |
| import org.chromium.base.JNINamespace; |
| import org.chromium.chrome.browser.EmptyTabObserver; |
| import org.chromium.chrome.browser.Tab; |
| import org.chromium.chrome.browser.TabObserver; |
| import org.chromium.content.browser.ContentViewCore; |
| import org.chromium.content_public.browser.WebContents; |
| import org.chromium.ui.R; |
| |
| /** |
| * Manages an AppBannerView for a Tab and its ContentView. |
| * |
| * The AppBannerManager manages a single AppBannerView, dismissing it when the user navigates to a |
| * new page or creating a new one when it detects that the current webpage is requesting a banner to |
| * be built. The actual observation of the WebContents (which triggers the automatic creation and |
| * removal of banners, among other things) is done by the native-side AppBannerManager. |
| * |
| * This Java-side class owns its native-side counterpart, which is basically used to grab resources |
| * from the network. |
| */ |
| @JNINamespace("banners") |
| public class AppBannerManager implements AppBannerView.Observer, AppDetailsDelegate.Observer { |
| private static final String TAG = "AppBannerManager"; |
| |
| /** Retrieves information about a given package. */ |
| private static AppDetailsDelegate sAppDetailsDelegate; |
| |
| /** Pointer to the native side AppBannerManager. */ |
| private final long mNativePointer; |
| |
| /** Tab that the AppBannerView/AppBannerManager is owned by. */ |
| private final Tab mTab; |
| |
| /** ContentViewCore that the AppBannerView/AppBannerManager is currently attached to. */ |
| private ContentViewCore mContentViewCore; |
| |
| /** Current banner being shown. */ |
| private AppBannerView mBannerView; |
| |
| /** Data about the app being advertised. */ |
| private AppData mAppData; |
| |
| /** |
| * Checks if app banners are enabled. |
| * @return True if banners are enabled, false otherwise. |
| */ |
| public static boolean isEnabled() { |
| return nativeIsEnabled(); |
| } |
| |
| /** |
| * Sets the delegate that provides information about a given package. |
| * @param delegate Delegate to use. Previously set ones are destroyed. |
| */ |
| public static void setAppDetailsDelegate(AppDetailsDelegate delegate) { |
| if (sAppDetailsDelegate != null) sAppDetailsDelegate.destroy(); |
| sAppDetailsDelegate = delegate; |
| } |
| |
| /** |
| * Constructs an AppBannerManager for the given tab. |
| * @param tab Tab that the AppBannerManager will be attached to. |
| */ |
| public AppBannerManager(Tab tab) { |
| mNativePointer = nativeInit(); |
| mTab = tab; |
| mTab.addObserver(createTabObserver()); |
| updatePointers(); |
| } |
| |
| /** |
| * Creates a TabObserver for monitoring a Tab, used to react to changes in the ContentView |
| * or to trigger its own destruction. |
| * @return TabObserver that can be used to monitor a Tab. |
| */ |
| private TabObserver createTabObserver() { |
| return new EmptyTabObserver() { |
| @Override |
| public void onWebContentsSwapped(Tab tab, boolean didStartLoad, |
| boolean didFinishLoad) { |
| updatePointers(); |
| } |
| |
| @Override |
| public void onContentChanged(Tab tab) { |
| updatePointers(); |
| } |
| |
| @Override |
| public void onDestroyed(Tab tab) { |
| nativeDestroy(mNativePointer); |
| mContentViewCore = null; |
| resetState(); |
| } |
| }; |
| } |
| |
| /** |
| * Updates which ContentView and WebContents the AppBannerView is monitoring. |
| */ |
| private void updatePointers() { |
| if (mContentViewCore != mTab.getContentViewCore()) |
| mContentViewCore = mTab.getContentViewCore(); |
| nativeReplaceWebContents(mNativePointer, mTab.getWebContents()); |
| } |
| |
| /** |
| * Grabs package information for the banner asynchronously. |
| * @param url URL for the page that is triggering the banner. |
| * @param packageName Name of the package that is being advertised. |
| */ |
| @CalledByNative |
| private void prepareBanner(String url, String packageName) { |
| // Get rid of whatever banner is there currently. |
| if (mBannerView != null) dismissCurrentBanner(AppBannerMetricsIds.DISMISS_ERROR); |
| |
| if (sAppDetailsDelegate == null || !isBannerForCurrentPage(url)) return; |
| |
| int iconSize = AppBannerView.getIconSize(mContentViewCore.getContext()); |
| sAppDetailsDelegate.getAppDetailsAsynchronously(this, url, packageName, iconSize); |
| } |
| |
| /** |
| * Called when data about the package has been retrieved, which includes the url for the app's |
| * icon but not the icon Bitmap itself. Kicks off a background task to retrieve it. |
| * @param data Data about the app. Null if the task failed. |
| */ |
| @Override |
| public void onAppDetailsRetrieved(AppData data) { |
| if (data == null || !isBannerForCurrentPage(data.siteUrl())) return; |
| |
| mAppData = data; |
| String imageUrl = data.imageUrl(); |
| if (TextUtils.isEmpty(imageUrl) || !nativeFetchIcon(mNativePointer, imageUrl)) resetState(); |
| } |
| |
| /** |
| * Called when all the data required to show a banner has finally been retrieved. |
| * Creates the banner and shows it, as long as the banner is still meant for the current page. |
| * @param imageUrl URL of the icon. |
| * @param appIcon Bitmap containing the icon itself. |
| * @return Whether or not the banner was created. |
| */ |
| @CalledByNative |
| private boolean createBanner(String imageUrl, Bitmap appIcon) { |
| if (mAppData == null || !isBannerForCurrentPage(mAppData.siteUrl())) return false; |
| |
| if (!TextUtils.equals(mAppData.imageUrl(), imageUrl)) { |
| resetState(); |
| return false; |
| } |
| |
| mAppData.setIcon(new BitmapDrawable(mContentViewCore.getContext().getResources(), appIcon)); |
| mBannerView = AppBannerView.create(mContentViewCore, this, mAppData); |
| return true; |
| } |
| |
| /** |
| * Dismisses whatever banner is currently being displayed. This is treated as an automatic |
| * dismissal and not one that blocks the banner from appearing in the future. |
| * @param dismissalType What triggered the dismissal. |
| */ |
| @CalledByNative |
| private void dismissCurrentBanner(int dismissalType) { |
| if (mBannerView != null) mBannerView.dismiss(dismissalType); |
| resetState(); |
| } |
| |
| @Override |
| public void onBannerRemoved(AppBannerView banner) { |
| if (mBannerView != banner) return; |
| resetState(); |
| } |
| |
| @Override |
| public void onBannerBlocked(AppBannerView banner, String url, String packageName) { |
| if (mBannerView != banner) return; |
| nativeBlockBanner(mNativePointer, url, packageName); |
| } |
| |
| @Override |
| public void onBannerDismissEvent(AppBannerView banner, int eventType) { |
| if (mBannerView != banner) return; |
| nativeRecordDismissEvent(eventType); |
| } |
| |
| @Override |
| public void onBannerInstallEvent(AppBannerView banner, int eventType) { |
| if (mBannerView != banner) return; |
| nativeRecordInstallEvent(eventType); |
| } |
| |
| @Override |
| public boolean onFireIntent(AppBannerView banner, PendingIntent intent) { |
| if (mBannerView != banner) return false; |
| return mTab.getWindowAndroid().showIntent(intent, banner, R.string.low_memory_error); |
| } |
| |
| /** |
| * Resets all of the state, killing off any running tasks. |
| */ |
| private void resetState() { |
| if (mBannerView != null) { |
| mBannerView.destroy(); |
| mBannerView = null; |
| } |
| |
| mAppData = null; |
| } |
| |
| /** |
| * Checks to see if the banner is for the currently displayed page. |
| * @param bannerUrl URL that requested a banner. |
| * @return True if the user is still on the same page. |
| */ |
| private boolean isBannerForCurrentPage(String bannerUrl) { |
| return mContentViewCore != null && |
| TextUtils.equals(mContentViewCore.getUrl(), bannerUrl); |
| } |
| |
| private static native boolean nativeIsEnabled(); |
| private native long nativeInit(); |
| private native void nativeDestroy(long nativeAppBannerManager); |
| private native void nativeReplaceWebContents(long nativeAppBannerManager, |
| WebContents webContents); |
| private native void nativeBlockBanner( |
| long nativeAppBannerManager, String url, String packageName); |
| private native boolean nativeFetchIcon(long nativeAppBannerManager, String imageUrl); |
| |
| // UMA tracking. |
| private static native void nativeRecordDismissEvent(int metric); |
| private static native void nativeRecordInstallEvent(int metric); |
| } |