Index: chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java |
index 4aa92215949f79398de65bd075f83915976c7a9d..1757c4da6ee683043d4f4515767cddc4e021413a 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java |
@@ -4,23 +4,46 @@ |
package org.chromium.chrome.browser.banners; |
+import android.app.Activity; |
+import android.content.ActivityNotFoundException; |
+import android.content.ContentResolver; |
+import android.content.Intent; |
+import android.content.IntentSender; |
+import android.content.pm.PackageManager; |
+import android.graphics.Bitmap; |
+import android.graphics.drawable.BitmapDrawable; |
+import android.os.AsyncTask; |
+import android.text.TextUtils; |
+import android.util.Log; |
+ |
+import org.chromium.base.CalledByNative; |
import org.chromium.chrome.browser.EmptyTabObserver; |
import org.chromium.chrome.browser.TabBase; |
import org.chromium.chrome.browser.TabObserver; |
import org.chromium.content.browser.ContentView; |
import org.chromium.content_public.browser.WebContents; |
+import org.chromium.ui.R; |
+import org.chromium.ui.base.WindowAndroid; |
+import org.chromium.ui.base.WindowAndroid.IntentCallback; |
/** |
* Manages an AppBannerView for a TabBase 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 observeration of the WebContents (which triggers the automatic creation |
+ * 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. |
+ * This Java-side class owns its native-side counterpart, which is basically used to grab resources |
+ * from the network. |
*/ |
-public class AppBannerManager { |
+public class AppBannerManager implements AppBannerView.Observer, InstallerDelegate.Observer, |
+ IntentCallback { |
+ private static final String TAG = "AppBannerManager"; |
+ |
+ /** Retrieves information about a given package. */ |
+ private static DetailsDelegate sDetailsDelegate; |
+ |
/** Pointer to the native side AppBannerManager. */ |
private final long mNativePointer; |
@@ -30,6 +53,29 @@ public class AppBannerManager { |
/** 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 setDetailsDelegate(DetailsDelegate delegate) { |
+ if (sDetailsDelegate != null) sDetailsDelegate.destroy(); |
+ sDetailsDelegate = delegate; |
+ } |
+ |
/** |
* Constructs an AppBannerManager for the given tab. |
* @param tab Tab that the AppBannerManager will be attached to. |
@@ -62,6 +108,7 @@ public class AppBannerManager { |
@Override |
public void onDestroyed(TabBase tab) { |
nativeDestroy(mNativePointer); |
+ resetState(); |
} |
}; |
} |
@@ -75,11 +122,166 @@ public class AppBannerManager { |
} |
/** |
- * Checks if app banners are enabled. |
- * @return True if banners are enabled, false otherwise. |
+ * 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. |
*/ |
- public static boolean isEnabled() { |
- return nativeIsEnabled(); |
+ @CalledByNative |
+ private void prepareBanner(final String url, final String packageName) { |
+ // Get rid of whatever banner is there currently. |
+ if (mBannerView != null) { |
+ dismissCurrentBanner(); |
+ mBannerView = null; |
+ } |
+ |
+ new AsyncTask<Void, Void, AppData>() { |
+ @Override |
+ protected AppData doInBackground(Void... args) { |
+ // Ask the delegate to provide information about the package. |
+ if (!isBannerForCurrentPage(url) || sDetailsDelegate == null) return null; |
Yaron
2014/02/19 22:41:36
Hmm. I think these url-related calls should be on
gone
2014/02/20 22:07:24
Done.
|
+ AppData data = new AppData(); |
+ data.siteUrl = mContentView.getUrl(); |
Yaron
2014/02/19 22:41:36
Same point here. If you need this on bg thread, gr
gone
2014/02/20 22:07:24
Done.
|
+ int iconSize = AppBannerView.getIconSize(mContentView.getContext()); |
+ return sDetailsDelegate.getAppDetails(packageName, iconSize, data) ? data : null; |
+ } |
+ |
+ @Override |
+ protected void onPostExecute(AppData result) { |
+ // Start grabbing the icon for the app. |
+ if (!isBannerForCurrentPage(url) || result == null) return; |
+ mAppData = result; |
+ nativeFetchIcon(mNativePointer, result.siteUrl, result.imageUrl); |
+ return; |
+ } |
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
+ } |
+ |
+ /** |
+ * 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. |
+ */ |
+ @CalledByNative |
+ private void createBanner(String imageUrl, Bitmap appIcon) { |
+ if (mAppData == null || !isBannerForCurrentPage(mAppData.siteUrl) |
+ || !TextUtils.equals(mAppData.imageUrl, imageUrl)) { |
+ return; |
+ } |
+ |
+ mAppData.icon = new BitmapDrawable(mContentView.getContext().getResources(), appIcon); |
+ mBannerView = AppBannerView.create(mContentView, this, mAppData); |
+ } |
+ |
+ /** |
+ * 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. |
+ */ |
+ @CalledByNative |
+ private void dismissCurrentBanner() { |
+ if (mBannerView != null) mBannerView.dismiss(); |
+ resetState(); |
+ } |
+ |
+ @Override |
+ public void onButtonClicked(AppBannerView banner) { |
+ if (mBannerView != banner) return; |
+ |
+ if (mAppData.installState == AppData.INSTALL_STATE_NOT_INSTALLED) { |
+ // The user initiated an install. |
+ WindowAndroid window = mTabBase.getWindowAndroid(); |
+ if (window.showIntent(mAppData.installIntent, this, R.string.low_memory_error)) { |
+ // Temporarily hide the banner. |
+ mBannerView.createVerticalSnapAnimation(false); |
+ } else { |
+ Log.e(TAG, "Failed to fire install intent."); |
+ dismissCurrentBanner(); |
+ } |
+ } else if (mAppData.installState == AppData.INSTALL_STATE_INSTALLED) { |
+ // The app is installed. Open it. |
+ String packageName = mAppData.packageName; |
+ PackageManager packageManager = mContentView.getContext().getPackageManager(); |
+ Intent appIntent = packageManager.getLaunchIntentForPackage(packageName); |
+ try { |
+ mContentView.getContext().startActivity(appIntent); |
+ } catch (ActivityNotFoundException e) { |
+ Log.e(TAG, "Failed to find app package: " + packageName); |
+ } |
+ dismissCurrentBanner(); |
+ } |
+ } |
+ |
+ @Override |
+ public void onBannerClicked(AppBannerView banner) { |
+ if (mContentView == null || mBannerView != banner) return; |
+ |
+ try { |
+ // Send the user to the app's Play store page. |
+ IntentSender sender = banner.getAppData().detailsIntent.getIntentSender(); |
+ mContentView.getContext().startIntentSender(sender, new Intent(), 0, 0, 0); |
+ } catch (IntentSender.SendIntentException e) { |
+ Log.e(TAG, "Failed to launch details intent."); |
+ } |
+ |
+ dismissCurrentBanner(); |
+ } |
+ |
+ @Override |
+ public void onIntentCompleted(WindowAndroid window, int resultCode, |
+ ContentResolver contentResolver, Intent data) { |
+ if (mContentView == null || mBannerView == null) return; |
+ |
+ mBannerView.createVerticalSnapAnimation(true); |
+ if (resultCode == Activity.RESULT_OK) { |
+ // The user chose to install the app. Watch the PackageManager to see when it finishes |
+ // installing it. |
+ PackageManager packageManager = mContentView.getContext().getPackageManager(); |
+ mAppData.installTask = |
+ new InstallerDelegate(packageManager, this, mAppData.packageName); |
+ mAppData.installTask.start(); |
+ mAppData.installState = AppData.INSTALL_STATE_INSTALLING; |
+ mBannerView.updateButtonState(); |
+ } |
+ } |
+ |
+ @Override |
+ public void onInstallFinished(InstallerDelegate monitor, boolean success) { |
+ if (mBannerView == null || mAppData.installTask != monitor) return; |
+ |
+ if (success) { |
+ // Let the user open the app from here. |
+ mAppData.installState = AppData.INSTALL_STATE_INSTALLED; |
+ mBannerView.updateButtonState(); |
+ } else { |
+ dismissCurrentBanner(); |
+ } |
+ } |
+ |
+ @Override |
+ public void onBannerDismissed(AppBannerView banner) { |
+ if (mBannerView != banner) return; |
+ |
+ // If the user swiped the banner off of the screen, block it from being shown again. |
+ boolean swipedAway = Math.abs(banner.getTranslationX()) >= banner.getWidth(); |
+ if (swipedAway) nativeBlockBanner(mNativePointer, mAppData.siteUrl, mAppData.packageName); |
+ |
+ resetState(); |
+ } |
+ |
+ private void resetState() { |
+ if (mAppData != null && mAppData.installTask != null) mAppData.installTask.cancel(); |
+ 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(); |
@@ -87,4 +289,7 @@ public class AppBannerManager { |
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 void nativeFetchIcon(long nativeAppBannerManager, String url, String imageUrl); |
} |