root/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. JNINamespace
  2. isEnabled
  3. setAppDetailsDelegate
  4. createTabObserver
  5. updatePointers
  6. prepareBanner
  7. onAppDetailsRetrieved
  8. createBanner
  9. dismissCurrentBanner
  10. onBannerRemoved
  11. onBannerBlocked
  12. onBannerDismissEvent
  13. onBannerInstallEvent
  14. onFireIntent
  15. resetState
  16. isBannerForCurrentPage
  17. nativeIsEnabled
  18. nativeInit
  19. nativeDestroy
  20. nativeReplaceWebContents
  21. nativeBlockBanner
  22. nativeFetchIcon
  23. nativeRecordDismissEvent
  24. nativeRecordInstallEvent

// 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.ContentView;
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;

    /** ContentView that the AppBannerView/AppBannerManager is currently attached to. */
    private ContentView mContentView;

    /** 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);
                mContentView = null;
                resetState();
            }
        };
    }

    /**
     * Updates which ContentView and WebContents the AppBannerView is monitoring.
     */
    private void updatePointers() {
        if (mContentView != mTab.getContentView()) mContentView = mTab.getContentView();
        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(mContentView.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(mContentView.getContext().getResources(), appIcon));
        mBannerView = AppBannerView.create(mContentView, 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 mContentView != null && TextUtils.equals(mContentView.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);
}

/* [<][>][^][v][top][bottom][index][help] */