Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / banners / AppBannerManager.java
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.
4
5 package org.chromium.chrome.browser.banners;
6
7 import android.app.PendingIntent;
8 import android.graphics.Bitmap;
9 import android.graphics.drawable.BitmapDrawable;
10 import android.text.TextUtils;
11
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;
20
21 /**
22  * Manages an AppBannerView for a Tab and its ContentView.
23  *
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.
28  *
29  * This Java-side class owns its native-side counterpart, which is basically used to grab resources
30  * from the network.
31  */
32 @JNINamespace("banners")
33 public class AppBannerManager implements AppBannerView.Observer, AppDetailsDelegate.Observer {
34     private static final String TAG = "AppBannerManager";
35
36     /** Retrieves information about a given package. */
37     private static AppDetailsDelegate sAppDetailsDelegate;
38
39     /** Pointer to the native side AppBannerManager. */
40     private final long mNativePointer;
41
42     /** Tab that the AppBannerView/AppBannerManager is owned by. */
43     private final Tab mTab;
44
45     /** ContentViewCore that the AppBannerView/AppBannerManager is currently attached to. */
46     private ContentViewCore mContentViewCore;
47
48     /** Current banner being shown. */
49     private AppBannerView mBannerView;
50
51     /** Data about the app being advertised. */
52     private AppData mAppData;
53
54     /**
55      * Checks if app banners are enabled.
56      * @return True if banners are enabled, false otherwise.
57      */
58     public static boolean isEnabled() {
59         return nativeIsEnabled();
60     }
61
62     /**
63      * Sets the delegate that provides information about a given package.
64      * @param delegate Delegate to use.  Previously set ones are destroyed.
65      */
66     public static void setAppDetailsDelegate(AppDetailsDelegate delegate) {
67         if (sAppDetailsDelegate != null) sAppDetailsDelegate.destroy();
68         sAppDetailsDelegate = delegate;
69     }
70
71     /**
72      * Constructs an AppBannerManager for the given tab.
73      * @param tab Tab that the AppBannerManager will be attached to.
74      */
75     public AppBannerManager(Tab tab) {
76         mNativePointer = nativeInit();
77         mTab = tab;
78         mTab.addObserver(createTabObserver());
79         updatePointers();
80     }
81
82     /**
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.
86      */
87     private TabObserver createTabObserver() {
88         return new EmptyTabObserver() {
89             @Override
90             public void onWebContentsSwapped(Tab tab, boolean didStartLoad,
91                     boolean didFinishLoad) {
92                 updatePointers();
93             }
94
95             @Override
96             public void onContentChanged(Tab tab) {
97                 updatePointers();
98             }
99
100             @Override
101             public void onDestroyed(Tab tab) {
102                 nativeDestroy(mNativePointer);
103                 mContentViewCore = null;
104                 resetState();
105             }
106         };
107     }
108
109     /**
110      * Updates which ContentView and WebContents the AppBannerView is monitoring.
111      */
112     private void updatePointers() {
113         if (mContentViewCore != mTab.getContentViewCore())
114             mContentViewCore = mTab.getContentViewCore();
115         nativeReplaceWebContents(mNativePointer, mTab.getWebContents());
116     }
117
118     /**
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.
122      */
123     @CalledByNative
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);
127
128         if (sAppDetailsDelegate == null || !isBannerForCurrentPage(url)) return;
129
130         int iconSize = AppBannerView.getIconSize(mContentViewCore.getContext());
131         sAppDetailsDelegate.getAppDetailsAsynchronously(this, url, packageName, iconSize);
132     }
133
134     /**
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.
138      */
139     @Override
140     public void onAppDetailsRetrieved(AppData data) {
141         if (data == null || !isBannerForCurrentPage(data.siteUrl())) return;
142
143         mAppData = data;
144         String imageUrl = data.imageUrl();
145         if (TextUtils.isEmpty(imageUrl) || !nativeFetchIcon(mNativePointer, imageUrl)) resetState();
146     }
147
148     /**
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.
154      */
155     @CalledByNative
156     private boolean createBanner(String imageUrl, Bitmap appIcon) {
157         if (mAppData == null || !isBannerForCurrentPage(mAppData.siteUrl())) return false;
158
159         if (!TextUtils.equals(mAppData.imageUrl(), imageUrl)) {
160             resetState();
161             return false;
162         }
163
164         mAppData.setIcon(new BitmapDrawable(mContentViewCore.getContext().getResources(), appIcon));
165         mBannerView = AppBannerView.create(mContentViewCore, this, mAppData);
166         return true;
167     }
168
169     /**
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.
173      */
174     @CalledByNative
175     private void dismissCurrentBanner(int dismissalType) {
176         if (mBannerView != null) mBannerView.dismiss(dismissalType);
177         resetState();
178     }
179
180     @Override
181     public void onBannerRemoved(AppBannerView banner) {
182         if (mBannerView != banner) return;
183         resetState();
184     }
185
186     @Override
187     public void onBannerBlocked(AppBannerView banner, String url, String packageName) {
188         if (mBannerView != banner) return;
189         nativeBlockBanner(mNativePointer, url, packageName);
190     }
191
192     @Override
193     public void onBannerDismissEvent(AppBannerView banner, int eventType) {
194         if (mBannerView != banner) return;
195         nativeRecordDismissEvent(eventType);
196     }
197
198     @Override
199     public void onBannerInstallEvent(AppBannerView banner, int eventType) {
200         if (mBannerView != banner) return;
201         nativeRecordInstallEvent(eventType);
202     }
203
204     @Override
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);
208     }
209
210     /**
211      * Resets all of the state, killing off any running tasks.
212      */
213     private void resetState() {
214         if (mBannerView != null) {
215             mBannerView.destroy();
216             mBannerView = null;
217         }
218
219         mAppData = null;
220     }
221
222     /**
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.
226      */
227     private boolean isBannerForCurrentPage(String bannerUrl) {
228         return mContentViewCore != null &&
229                TextUtils.equals(mContentViewCore.getUrl(), bannerUrl);
230     }
231
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);
240
241     // UMA tracking.
242     private static native void nativeRecordDismissEvent(int metric);
243     private static native void nativeRecordInstallEvent(int metric);
244 }