From 583a6c2a684d55ab7d2fc7f758e3af1bb4adf244 Mon Sep 17 00:00:00 2001
From: dominickn <dominickn@google.com>
Date: Fri, 29 May 2015 03:25:59 -0700
Subject: Add 'Android' suffix to mobile app banner implementation.

The existing app banner info bars are Android only, but the classes do not have Android
suffixes in the banner namespace. This complicates the naming of
desktop-specific app banner infobars.

This CL renames the existing app
banner info bar classes and their corresponding Java classes as a precursor for the desktop implementation.

BUG=491001

Review URL: https://codereview.chromium.org/1149873007

Cr-Commit-Position: refs/heads/master@{#331938}
---
 .../chrome/browser/infobar/AppBannerInfoBar.java   | 180 -------------
 .../browser/infobar/AppBannerInfoBarAndroid.java   | 182 +++++++++++++
 .../browser/infobar/AppBannerInfoBarDelegate.java  | 155 -----------
 .../infobar/AppBannerInfoBarDelegateAndroid.java   | 155 +++++++++++
 .../browser/banners/AppBannerManagerTest.java      |   8 +-
 .../banners/app_banner_data_fetcher_android.cc     |  27 +-
 .../android/banners/app_banner_infobar_delegate.cc | 280 --------------------
 .../android/banners/app_banner_infobar_delegate.h  |  93 -------
 .../banners/app_banner_infobar_delegate_android.cc | 283 +++++++++++++++++++++
 .../banners/app_banner_infobar_delegate_android.h  |  93 +++++++
 chrome/browser/android/chrome_jni_registrar.cc     |   9 +-
 .../ui/android/infobars/app_banner_infobar.cc      |  89 -------
 .../ui/android/infobars/app_banner_infobar.h       |  56 ----
 .../android/infobars/app_banner_infobar_android.cc |  88 +++++++
 .../android/infobars/app_banner_infobar_android.h  |  56 ++++
 chrome/chrome_browser.gypi                         |   8 +-
 chrome/chrome_browser_ui.gypi                      |   4 +-
 17 files changed, 884 insertions(+), 882 deletions(-)
 delete mode 100644 chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java
 create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarAndroid.java
 delete mode 100644 chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java
 create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegateAndroid.java
 delete mode 100644 chrome/browser/android/banners/app_banner_infobar_delegate.cc
 delete mode 100644 chrome/browser/android/banners/app_banner_infobar_delegate.h
 create mode 100644 chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
 create mode 100644 chrome/browser/android/banners/app_banner_infobar_delegate_android.h
 delete mode 100644 chrome/browser/ui/android/infobars/app_banner_infobar.cc
 delete mode 100644 chrome/browser/ui/android/infobars/app_banner_infobar.h
 create mode 100644 chrome/browser/ui/android/infobars/app_banner_infobar_android.cc
 create mode 100644 chrome/browser/ui/android/infobars/app_banner_infobar_android.h

diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java
deleted file mode 100644
index 970a51c..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2015 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.infobar;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.support.v4.view.ViewCompat;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.RatingBar;
-import android.widget.TextView;
-
-import org.chromium.base.ApplicationStatus;
-import org.chromium.base.CalledByNative;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.banners.AppData;
-
-/**
- * Infobar informing the user about an app related to this page.
- */
-public class AppBannerInfoBar extends ConfirmInfoBar implements View.OnClickListener {
-    // Installation states.
-    public static final int INSTALL_STATE_NOT_INSTALLED = 0;
-    public static final int INSTALL_STATE_INSTALLING = 1;
-    public static final int INSTALL_STATE_INSTALLED = 2;
-
-    // Views composing the infobar.
-    private Button mButton;
-    private ViewGroup mTitleView;
-    private View mIconView;
-
-    private final String mAppTitle;
-
-    // Data for native app installs.
-    private final AppData mAppData;
-    private int mInstallState;
-
-    // Data for web app installs.
-    private final String mAppUrl;
-
-    // Banner for native apps.
-    private AppBannerInfoBar(long nativeInfoBar, String appTitle, Bitmap iconBitmap, AppData data) {
-        super(nativeInfoBar, null, 0, iconBitmap, appTitle, null, data.installButtonText(), null);
-        mAppTitle = appTitle;
-        mAppData = data;
-        mAppUrl = null;
-        mInstallState = INSTALL_STATE_NOT_INSTALLED;
-    }
-
-    // Banner for web apps.
-    private AppBannerInfoBar(long nativeInfoBar, String appTitle, Bitmap iconBitmap, String url) {
-        super(nativeInfoBar, null, 0, iconBitmap, appTitle, null, getAddToHomescreenText(), null);
-        mAppTitle = appTitle;
-        mAppData = null;
-        mAppUrl = url;
-        mInstallState = INSTALL_STATE_NOT_INSTALLED;
-    }
-
-    @Override
-    public void createContent(InfoBarLayout layout) {
-        super.createContent(layout);
-
-        mButton = layout.getPrimaryButton();
-        mIconView = layout.getIcon();
-
-        Resources res = getContext().getResources();
-        int iconSize = res.getDimensionPixelSize(R.dimen.app_banner_icon_size);
-        int iconSpacing = res.getDimensionPixelSize(R.dimen.app_banner_icon_spacing);
-        layout.setIconSizeAndSpacing(iconSize, iconSize, iconSpacing);
-
-        mTitleView = (ViewGroup) LayoutInflater.from(getContext()).inflate(
-                R.layout.app_banner_title, null);
-        TextView appName = (TextView) mTitleView.findViewById(R.id.app_name);
-        RatingBar ratingView = (RatingBar) mTitleView.findViewById(R.id.rating_bar);
-        TextView webAppUrl = (TextView) mTitleView.findViewById(R.id.web_app_url);
-        appName.setText(mAppTitle);
-        layout.setMessageView(mTitleView);
-
-        Context context = getContext();
-        if (mAppData != null) {
-            // Native app.
-            ImageView playLogo = new ImageView(layout.getContext());
-            playLogo.setImageResource(R.drawable.google_play);
-            layout.setCustomViewInButtonRow(playLogo);
-
-            ratingView.setRating(mAppData.rating());
-            layout.getPrimaryButton().setButtonColor(getContext().getResources().getColor(
-                    R.color.app_banner_install_button_bg));
-            mTitleView.setContentDescription(context.getString(
-                    R.string.app_banner_view_native_app_accessibility, mAppTitle,
-                    mAppData.rating()));
-            mTitleView.removeView(webAppUrl);
-            updateButton();
-        } else {
-            // Web app.
-            webAppUrl.setText(mAppUrl);
-            mTitleView.setContentDescription(context.getString(
-                    R.string.app_banner_view_web_app_accessibility, mAppTitle,
-                    mAppUrl));
-            mTitleView.removeView(ratingView);
-
-        }
-
-        // Hide uninteresting views from accessibility.
-        ViewCompat.setImportantForAccessibility(ratingView, View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-        if (mIconView != null) {
-            ViewCompat.setImportantForAccessibility(mIconView, View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-        }
-
-        // Set up clicking on the controls to bring up the app details.
-        mTitleView.setOnClickListener(this);
-        if (mIconView != null) mIconView.setOnClickListener(this);
-    }
-
-    @Override
-    public void onButtonClicked(boolean isPrimaryButton) {
-        if (mInstallState == INSTALL_STATE_INSTALLING) {
-            setControlsEnabled(true);
-            updateButton();
-            return;
-        }
-        super.onButtonClicked(isPrimaryButton);
-    }
-
-    @CalledByNative
-    public void onInstallStateChanged(int newState) {
-        setControlsEnabled(true);
-        mInstallState = newState;
-        updateButton();
-    }
-
-    private void updateButton() {
-        assert mAppData != null;
-
-        String text;
-        String accessibilityText = null;
-        boolean enabled = true;
-        if (mInstallState == INSTALL_STATE_NOT_INSTALLED) {
-            text = mAppData.installButtonText();
-            accessibilityText = getContext().getString(
-                    R.string.app_banner_view_native_app_install_accessibility, text);
-        } else if (mInstallState == INSTALL_STATE_INSTALLING) {
-            text = getContext().getString(R.string.app_banner_installing);
-            enabled = false;
-        } else {
-            text = getContext().getString(R.string.app_banner_open);
-        }
-
-        mButton.setText(text);
-        mButton.setContentDescription(accessibilityText);
-        mButton.setEnabled(enabled);
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mTitleView || v == mIconView) onLinkClicked();
-    }
-
-    private static String getAddToHomescreenText() {
-        return ApplicationStatus.getApplicationContext().getString(R.string.menu_add_to_homescreen);
-    }
-
-    @CalledByNative
-    private static InfoBar createNativeAppInfoBar(
-            long nativeInfoBar, String appTitle, Bitmap iconBitmap, AppData appData) {
-        return new AppBannerInfoBar(nativeInfoBar, appTitle, iconBitmap, appData);
-    }
-
-    @CalledByNative
-    private static InfoBar createWebAppInfoBar(
-            long nativeInfoBar, String appTitle, Bitmap iconBitmap, String url) {
-        return new AppBannerInfoBar(nativeInfoBar, appTitle, iconBitmap, url);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarAndroid.java
new file mode 100644
index 0000000..f566636
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarAndroid.java
@@ -0,0 +1,182 @@
+// Copyright 2015 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.infobar;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.support.v4.view.ViewCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.RatingBar;
+import android.widget.TextView;
+
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.CalledByNative;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.banners.AppData;
+
+/**
+ * Infobar informing the user about an app related to this page.
+ */
+public class AppBannerInfoBarAndroid extends ConfirmInfoBar implements View.OnClickListener {
+    // Installation states.
+    public static final int INSTALL_STATE_NOT_INSTALLED = 0;
+    public static final int INSTALL_STATE_INSTALLING = 1;
+    public static final int INSTALL_STATE_INSTALLED = 2;
+
+    // Views composing the infobar.
+    private Button mButton;
+    private ViewGroup mTitleView;
+    private View mIconView;
+
+    private final String mAppTitle;
+
+    // Data for native app installs.
+    private final AppData mAppData;
+    private int mInstallState;
+
+    // Data for web app installs.
+    private final String mAppUrl;
+
+    // Banner for native apps.
+    private AppBannerInfoBarAndroid(
+            long nativeInfoBar, String appTitle, Bitmap iconBitmap, AppData data) {
+        super(nativeInfoBar, null, 0, iconBitmap, appTitle, null, data.installButtonText(), null);
+        mAppTitle = appTitle;
+        mAppData = data;
+        mAppUrl = null;
+        mInstallState = INSTALL_STATE_NOT_INSTALLED;
+    }
+
+    // Banner for web apps.
+    private AppBannerInfoBarAndroid(
+            long nativeInfoBar, String appTitle, Bitmap iconBitmap, String url) {
+        super(nativeInfoBar, null, 0, iconBitmap, appTitle, null, getAddToHomescreenText(), null);
+        mAppTitle = appTitle;
+        mAppData = null;
+        mAppUrl = url;
+        mInstallState = INSTALL_STATE_NOT_INSTALLED;
+    }
+
+    @Override
+    public void createContent(InfoBarLayout layout) {
+        super.createContent(layout);
+
+        mButton = layout.getPrimaryButton();
+        mIconView = layout.getIcon();
+
+        Resources res = getContext().getResources();
+        int iconSize = res.getDimensionPixelSize(R.dimen.app_banner_icon_size);
+        int iconSpacing = res.getDimensionPixelSize(R.dimen.app_banner_icon_spacing);
+        layout.setIconSizeAndSpacing(iconSize, iconSize, iconSpacing);
+
+        mTitleView = (ViewGroup) LayoutInflater.from(getContext()).inflate(
+                R.layout.app_banner_title, null);
+        TextView appName = (TextView) mTitleView.findViewById(R.id.app_name);
+        RatingBar ratingView = (RatingBar) mTitleView.findViewById(R.id.rating_bar);
+        TextView webAppUrl = (TextView) mTitleView.findViewById(R.id.web_app_url);
+        appName.setText(mAppTitle);
+        layout.setMessageView(mTitleView);
+
+        Context context = getContext();
+        if (mAppData != null) {
+            // Native app.
+            ImageView playLogo = new ImageView(layout.getContext());
+            playLogo.setImageResource(R.drawable.google_play);
+            layout.setCustomViewInButtonRow(playLogo);
+
+            ratingView.setRating(mAppData.rating());
+            layout.getPrimaryButton().setButtonColor(getContext().getResources().getColor(
+                    R.color.app_banner_install_button_bg));
+            mTitleView.setContentDescription(context.getString(
+                    R.string.app_banner_view_native_app_accessibility, mAppTitle,
+                    mAppData.rating()));
+            mTitleView.removeView(webAppUrl);
+            updateButton();
+        } else {
+            // Web app.
+            webAppUrl.setText(mAppUrl);
+            mTitleView.setContentDescription(context.getString(
+                    R.string.app_banner_view_web_app_accessibility, mAppTitle,
+                    mAppUrl));
+            mTitleView.removeView(ratingView);
+
+        }
+
+        // Hide uninteresting views from accessibility.
+        ViewCompat.setImportantForAccessibility(ratingView, View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+        if (mIconView != null) {
+            ViewCompat.setImportantForAccessibility(mIconView, View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+        }
+
+        // Set up clicking on the controls to bring up the app details.
+        mTitleView.setOnClickListener(this);
+        if (mIconView != null) mIconView.setOnClickListener(this);
+    }
+
+    @Override
+    public void onButtonClicked(boolean isPrimaryButton) {
+        if (mInstallState == INSTALL_STATE_INSTALLING) {
+            setControlsEnabled(true);
+            updateButton();
+            return;
+        }
+        super.onButtonClicked(isPrimaryButton);
+    }
+
+    @CalledByNative
+    public void onInstallStateChanged(int newState) {
+        setControlsEnabled(true);
+        mInstallState = newState;
+        updateButton();
+    }
+
+    private void updateButton() {
+        assert mAppData != null;
+
+        String text;
+        String accessibilityText = null;
+        boolean enabled = true;
+        if (mInstallState == INSTALL_STATE_NOT_INSTALLED) {
+            text = mAppData.installButtonText();
+            accessibilityText = getContext().getString(
+                    R.string.app_banner_view_native_app_install_accessibility, text);
+        } else if (mInstallState == INSTALL_STATE_INSTALLING) {
+            text = getContext().getString(R.string.app_banner_installing);
+            enabled = false;
+        } else {
+            text = getContext().getString(R.string.app_banner_open);
+        }
+
+        mButton.setText(text);
+        mButton.setContentDescription(accessibilityText);
+        mButton.setEnabled(enabled);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mTitleView || v == mIconView) onLinkClicked();
+    }
+
+    private static String getAddToHomescreenText() {
+        return ApplicationStatus.getApplicationContext().getString(R.string.menu_add_to_homescreen);
+    }
+
+    @CalledByNative
+    private static InfoBar createNativeAppInfoBar(
+            long nativeInfoBar, String appTitle, Bitmap iconBitmap, AppData appData) {
+        return new AppBannerInfoBarAndroid(nativeInfoBar, appTitle, iconBitmap, appData);
+    }
+
+    @CalledByNative
+    private static InfoBar createWebAppInfoBar(
+            long nativeInfoBar, String appTitle, Bitmap iconBitmap, String url) {
+        return new AppBannerInfoBarAndroid(nativeInfoBar, appTitle, iconBitmap, url);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java
deleted file mode 100644
index 686fe8f..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2015 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.infobar;
-
-import android.app.Activity;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Looper;
-
-import org.chromium.base.ApplicationStatus;
-import org.chromium.base.CalledByNative;
-import org.chromium.base.JNINamespace;
-import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.Tab;
-import org.chromium.chrome.browser.banners.AppData;
-import org.chromium.chrome.browser.banners.InstallerDelegate;
-import org.chromium.ui.base.WindowAndroid;
-
-/**
- * Handles the promotion and installation of an app specified by the current web page.  This Java
- * object is created by and owned by the native AppBannerInfoBarDelegate.
- */
-@JNINamespace("banners")
-public class AppBannerInfoBarDelegate {
-    /** PackageManager to use in place of the real one. */
-    private static PackageManager sPackageManagerForTests;
-
-    /** Weak pointer to the native AppBannerInfoBarDelegate. */
-    private long mNativePointer;
-
-    /** Monitors an installation in progress. */
-    private InstallerDelegate mInstallTask;
-
-    /** Monitors for application state changes. */
-    private final ApplicationStatus.ApplicationStateListener mListener;
-
-    /** Overrides the PackageManager for testing. */
-    @VisibleForTesting
-    public static void setPackageManagerForTesting(PackageManager manager) {
-        sPackageManagerForTests = manager;
-    }
-
-    private AppBannerInfoBarDelegate(long nativePtr) {
-        mNativePointer = nativePtr;
-        mListener = createApplicationStateListener();
-        ApplicationStatus.registerApplicationStateListener(mListener);
-    }
-
-    private ApplicationStatus.ApplicationStateListener createApplicationStateListener() {
-        return new ApplicationStatus.ApplicationStateListener() {
-            @Override
-            public void onApplicationStateChange(int newState) {
-                if (!ApplicationStatus.hasVisibleActivities()) return;
-                nativeUpdateInstallState(mNativePointer);
-            }
-        };
-    }
-
-    @CalledByNative
-    private void destroy() {
-        if (mInstallTask != null) {
-            mInstallTask.cancel();
-            mInstallTask = null;
-        }
-        ApplicationStatus.unregisterApplicationStateListener(mListener);
-        mNativePointer = 0;
-    }
-
-    @CalledByNative
-    private boolean installOrOpenNativeApp(Tab tab, AppData appData) {
-        Context context = ApplicationStatus.getApplicationContext();
-        String packageName = appData.packageName();
-        PackageManager packageManager = getPackageManager(context);
-
-        if (InstallerDelegate.isInstalled(packageManager, packageName)) {
-            // Open the app.
-            Intent launchIntent = packageManager.getLaunchIntentForPackage(packageName);
-            if (launchIntent == null) return true;
-            context.startActivity(launchIntent);
-            return true;
-        } else {
-            // Try installing the app.  If the installation was kicked off, return false to prevent
-            // the infobar from disappearing.
-            return !tab.getWindowAndroid().showIntent(
-                    appData.installIntent(), createIntentCallback(appData), null);
-        }
-    }
-
-    private WindowAndroid.IntentCallback createIntentCallback(final AppData appData) {
-        return new WindowAndroid.IntentCallback() {
-            @Override
-            public void onIntentCompleted(WindowAndroid window, int resultCode,
-                    ContentResolver contentResolver, Intent data) {
-                boolean isInstalling = resultCode == Activity.RESULT_OK;
-                if (isInstalling) {
-                    // Start monitoring the install.
-                    PackageManager pm =
-                            getPackageManager(ApplicationStatus.getApplicationContext());
-                    mInstallTask = new InstallerDelegate(
-                            Looper.getMainLooper(), pm, createInstallerDelegateObserver(),
-                            appData.packageName());
-                    mInstallTask.start();
-                }
-
-                nativeOnInstallIntentReturned(mNativePointer, isInstalling);
-            }
-        };
-    }
-
-    private InstallerDelegate.Observer createInstallerDelegateObserver() {
-        return new InstallerDelegate.Observer() {
-            @Override
-            public void onInstallFinished(InstallerDelegate task, boolean success) {
-                if (mInstallTask != task) return;
-                mInstallTask = null;
-                nativeOnInstallFinished(mNativePointer, success);
-            }
-        };
-    }
-
-    @CalledByNative
-    private void showAppDetails(Tab tab, AppData appData) {
-        tab.getWindowAndroid().showIntent(appData.detailsIntent(), null, null);
-    }
-
-    @CalledByNative
-    private int determineInstallState(AppData data) {
-        if (mInstallTask != null) return AppBannerInfoBar.INSTALL_STATE_INSTALLING;
-
-        PackageManager pm = getPackageManager(ApplicationStatus.getApplicationContext());
-        boolean isInstalled = InstallerDelegate.isInstalled(pm, data.packageName());
-        return isInstalled ? AppBannerInfoBar.INSTALL_STATE_INSTALLED
-                : AppBannerInfoBar.INSTALL_STATE_NOT_INSTALLED;
-    }
-
-    private PackageManager getPackageManager(Context context) {
-        if (sPackageManagerForTests != null) return sPackageManagerForTests;
-        return context.getPackageManager();
-    }
-
-    @CalledByNative
-    private static AppBannerInfoBarDelegate create(long nativePtr) {
-        return new AppBannerInfoBarDelegate(nativePtr);
-    }
-
-    private native void nativeOnInstallIntentReturned(
-            long nativeAppBannerInfoBarDelegate, boolean isInstalling);
-    private native void nativeOnInstallFinished(
-            long nativeAppBannerInfoBarDelegate, boolean success);
-    private native void nativeUpdateInstallState(long nativeAppBannerInfoBarDelegate);
-}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegateAndroid.java
new file mode 100644
index 0000000..99343c9
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegateAndroid.java
@@ -0,0 +1,155 @@
+// Copyright 2015 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.infobar;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Looper;
+
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.Tab;
+import org.chromium.chrome.browser.banners.AppData;
+import org.chromium.chrome.browser.banners.InstallerDelegate;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * Handles the promotion and installation of an app specified by the current web page.  This Java
+ * object is created by and owned by the native AppBannerInfoBarDelegateAndroid.
+ */
+@JNINamespace("banners")
+public class AppBannerInfoBarDelegateAndroid {
+    /** PackageManager to use in place of the real one. */
+    private static PackageManager sPackageManagerForTests;
+
+    /** Weak pointer to the native AppBannerInfoBarDelegateAndroid. */
+    private long mNativePointer;
+
+    /** Monitors an installation in progress. */
+    private InstallerDelegate mInstallTask;
+
+    /** Monitors for application state changes. */
+    private final ApplicationStatus.ApplicationStateListener mListener;
+
+    /** Overrides the PackageManager for testing. */
+    @VisibleForTesting
+    public static void setPackageManagerForTesting(PackageManager manager) {
+        sPackageManagerForTests = manager;
+    }
+
+    private AppBannerInfoBarDelegateAndroid(long nativePtr) {
+        mNativePointer = nativePtr;
+        mListener = createApplicationStateListener();
+        ApplicationStatus.registerApplicationStateListener(mListener);
+    }
+
+    private ApplicationStatus.ApplicationStateListener createApplicationStateListener() {
+        return new ApplicationStatus.ApplicationStateListener() {
+            @Override
+            public void onApplicationStateChange(int newState) {
+                if (!ApplicationStatus.hasVisibleActivities()) return;
+                nativeUpdateInstallState(mNativePointer);
+            }
+        };
+    }
+
+    @CalledByNative
+    private void destroy() {
+        if (mInstallTask != null) {
+            mInstallTask.cancel();
+            mInstallTask = null;
+        }
+        ApplicationStatus.unregisterApplicationStateListener(mListener);
+        mNativePointer = 0;
+    }
+
+    @CalledByNative
+    private boolean installOrOpenNativeApp(Tab tab, AppData appData) {
+        Context context = ApplicationStatus.getApplicationContext();
+        String packageName = appData.packageName();
+        PackageManager packageManager = getPackageManager(context);
+
+        if (InstallerDelegate.isInstalled(packageManager, packageName)) {
+            // Open the app.
+            Intent launchIntent = packageManager.getLaunchIntentForPackage(packageName);
+            if (launchIntent == null) return true;
+            context.startActivity(launchIntent);
+            return true;
+        } else {
+            // Try installing the app.  If the installation was kicked off, return false to prevent
+            // the infobar from disappearing.
+            return !tab.getWindowAndroid().showIntent(
+                    appData.installIntent(), createIntentCallback(appData), null);
+        }
+    }
+
+    private WindowAndroid.IntentCallback createIntentCallback(final AppData appData) {
+        return new WindowAndroid.IntentCallback() {
+            @Override
+            public void onIntentCompleted(WindowAndroid window, int resultCode,
+                    ContentResolver contentResolver, Intent data) {
+                boolean isInstalling = resultCode == Activity.RESULT_OK;
+                if (isInstalling) {
+                    // Start monitoring the install.
+                    PackageManager pm =
+                            getPackageManager(ApplicationStatus.getApplicationContext());
+                    mInstallTask = new InstallerDelegate(
+                            Looper.getMainLooper(), pm, createInstallerDelegateObserver(),
+                            appData.packageName());
+                    mInstallTask.start();
+                }
+
+                nativeOnInstallIntentReturned(mNativePointer, isInstalling);
+            }
+        };
+    }
+
+    private InstallerDelegate.Observer createInstallerDelegateObserver() {
+        return new InstallerDelegate.Observer() {
+            @Override
+            public void onInstallFinished(InstallerDelegate task, boolean success) {
+                if (mInstallTask != task) return;
+                mInstallTask = null;
+                nativeOnInstallFinished(mNativePointer, success);
+            }
+        };
+    }
+
+    @CalledByNative
+    private void showAppDetails(Tab tab, AppData appData) {
+        tab.getWindowAndroid().showIntent(appData.detailsIntent(), null, null);
+    }
+
+    @CalledByNative
+    private int determineInstallState(AppData data) {
+        if (mInstallTask != null) return AppBannerInfoBarAndroid.INSTALL_STATE_INSTALLING;
+
+        PackageManager pm = getPackageManager(ApplicationStatus.getApplicationContext());
+        boolean isInstalled = InstallerDelegate.isInstalled(pm, data.packageName());
+        return isInstalled ? AppBannerInfoBarAndroid.INSTALL_STATE_INSTALLED
+                : AppBannerInfoBarAndroid.INSTALL_STATE_NOT_INSTALLED;
+    }
+
+    private PackageManager getPackageManager(Context context) {
+        if (sPackageManagerForTests != null) return sPackageManagerForTests;
+        return context.getPackageManager();
+    }
+
+    @CalledByNative
+    private static AppBannerInfoBarDelegateAndroid create(long nativePtr) {
+        return new AppBannerInfoBarDelegateAndroid(nativePtr);
+    }
+
+    private native void nativeOnInstallIntentReturned(
+            long nativeAppBannerInfoBarDelegateAndroid, boolean isInstalling);
+    private native void nativeOnInstallFinished(
+            long nativeAppBannerInfoBarDelegateAndroid, boolean success);
+    private native void nativeUpdateInstallState(long nativeAppBannerInfoBarDelegateAndroid);
+}
diff --git a/chrome/android/javatests_shell/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/android/javatests_shell/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index 6107c0b..6e2e8c9 100644
--- a/chrome/android/javatests_shell/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/android/javatests_shell/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -25,8 +25,8 @@ import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.infobar.AnimationHelper;
-import org.chromium.chrome.browser.infobar.AppBannerInfoBar;
-import org.chromium.chrome.browser.infobar.AppBannerInfoBarDelegate;
+import org.chromium.chrome.browser.infobar.AppBannerInfoBarAndroid;
+import org.chromium.chrome.browser.infobar.AppBannerInfoBarDelegateAndroid;
 import org.chromium.chrome.browser.infobar.InfoBar;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.shell.ChromeShellTestBase;
@@ -127,7 +127,7 @@ public class AppBannerManagerTest extends ChromeShellTestBase {
         mPackageManager = new TestPackageManager();
         AppBannerManager.setAppDetailsDelegate(mDetailsDelegate);
         AppBannerManager.setIsEnabledForTesting(true);
-        AppBannerInfoBarDelegate.setPackageManagerForTesting(mPackageManager);
+        AppBannerInfoBarDelegateAndroid.setPackageManagerForTesting(mPackageManager);
         clearAppData();
 
         super.setUp();
@@ -166,7 +166,7 @@ public class AppBannerManagerTest extends ChromeShellTestBase {
                 InfoBarContainer container = getActivity().getActiveTab().getInfoBarContainer();
                 ArrayList<InfoBar> infobars = container.getInfoBars();
                 if (infobars.size() != 1) return false;
-                if (!(infobars.get(0) instanceof AppBannerInfoBar)) return false;
+                if (!(infobars.get(0) instanceof AppBannerInfoBarAndroid)) return false;
 
                 TextView textView =
                         (TextView) infobars.get(0).getContentWrapper().findViewById(R.id.app_name);
diff --git a/chrome/browser/android/banners/app_banner_data_fetcher_android.cc b/chrome/browser/android/banners/app_banner_data_fetcher_android.cc
index bc0662e..29934f4 100644
--- a/chrome/browser/android/banners/app_banner_data_fetcher_android.cc
+++ b/chrome/browser/android/banners/app_banner_data_fetcher_android.cc
@@ -4,8 +4,8 @@
 
 #include "chrome/browser/android/banners/app_banner_data_fetcher_android.h"
 
-#include "chrome/browser/android/banners/app_banner_infobar_delegate.h"
-#include "chrome/browser/ui/android/infobars/app_banner_infobar.h"
+#include "chrome/browser/android/banners/app_banner_infobar_delegate_android.h"
+#include "chrome/browser/ui/android/infobars/app_banner_infobar_android.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace banners {
@@ -49,23 +49,20 @@ infobars::InfoBar* AppBannerDataFetcherAndroid::CreateBanner(
 
   infobars::InfoBar* infobar = nullptr;
   if (native_app_data_.is_null()) {
-    scoped_ptr<AppBannerInfoBarDelegate> delegate(
-        new AppBannerInfoBarDelegate(event_request_id(),
-                                     title,
-                                     new SkBitmap(*icon),
-                                     web_app_data()));
+    scoped_ptr<AppBannerInfoBarDelegateAndroid> delegate(
+        new AppBannerInfoBarDelegateAndroid(
+            event_request_id(), title, new SkBitmap(*icon), web_app_data()));
 
-    infobar = new AppBannerInfoBar(delegate.Pass(), web_app_data().start_url);
+    infobar =
+        new AppBannerInfoBarAndroid(delegate.Pass(), web_app_data().start_url);
     if (infobar)
       RecordDidShowBanner("AppBanner.WebApp.Shown");
   } else {
-    scoped_ptr<AppBannerInfoBarDelegate> delegate(
-        new AppBannerInfoBarDelegate(event_request_id(),
-                                     title,
-                                     new SkBitmap(*icon),
-                                     native_app_data_,
-                                     native_app_package_));
-    infobar = new AppBannerInfoBar(delegate.Pass(), native_app_data_);
+    scoped_ptr<AppBannerInfoBarDelegateAndroid> delegate(
+        new AppBannerInfoBarDelegateAndroid(
+            event_request_id(), title, new SkBitmap(*icon), native_app_data_,
+            native_app_package_));
+    infobar = new AppBannerInfoBarAndroid(delegate.Pass(), native_app_data_);
     if (infobar)
       RecordDidShowBanner("AppBanner.NativeApp.Shown");
   }
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate.cc b/chrome/browser/android/banners/app_banner_infobar_delegate.cc
deleted file mode 100644
index 86534b2..0000000
--- a/chrome/browser/android/banners/app_banner_infobar_delegate.cc
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2015 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.
-
-#include "chrome/browser/android/banners/app_banner_infobar_delegate.h"
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
-#include "base/location.h"
-#include "base/strings/string16.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/android/shortcut_helper.h"
-#include "chrome/browser/android/shortcut_info.h"
-#include "chrome/browser/android/tab_android.h"
-#include "chrome/browser/banners/app_banner_data_fetcher.h"
-#include "chrome/browser/banners/app_banner_metrics.h"
-#include "chrome/browser/banners/app_banner_settings_helper.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/infobars/infobar_service.h"
-#include "chrome/browser/ui/android/infobars/app_banner_infobar.h"
-#include "chrome/common/render_messages.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/rappor/rappor_utils.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/manifest.h"
-#include "jni/AppBannerInfoBarDelegate_jni.h"
-#include "ui/gfx/android/java_bitmap.h"
-
-using base::android::ConvertJavaStringToUTF8;
-using base::android::ConvertJavaStringToUTF16;
-using base::android::ConvertUTF8ToJavaString;
-using base::android::ConvertUTF16ToJavaString;
-
-namespace banners {
-
-AppBannerInfoBarDelegate::AppBannerInfoBarDelegate(
-    int event_request_id,
-    const base::string16& app_title,
-    SkBitmap* app_icon,
-    const content::Manifest& web_app_data)
-    : app_title_(app_title),
-      app_icon_(app_icon),
-      event_request_id_(event_request_id),
-      web_app_data_(web_app_data) {
-  DCHECK(!web_app_data.IsEmpty());
-  CreateJavaDelegate();
-}
-
-AppBannerInfoBarDelegate::AppBannerInfoBarDelegate(
-    int event_request_id,
-    const base::string16& app_title,
-    SkBitmap* app_icon,
-    const base::android::ScopedJavaGlobalRef<jobject>& native_app_data,
-    const std::string& native_app_package)
-    : app_title_(app_title),
-      app_icon_(app_icon),
-      event_request_id_(event_request_id),
-      native_app_data_(native_app_data),
-      native_app_package_(native_app_package) {
-  DCHECK(!native_app_data_.is_null());
-  CreateJavaDelegate();
-}
-
-AppBannerInfoBarDelegate::~AppBannerInfoBarDelegate() {
-  TrackDismissEvent(DISMISS_EVENT_DISMISSED);
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_AppBannerInfoBarDelegate_destroy(env,
-                                        java_delegate_.obj());
-  java_delegate_.Reset();
-}
-
-void AppBannerInfoBarDelegate::UpdateInstallState(JNIEnv* env, jobject obj) {
-  if (native_app_data_.is_null())
-    return;
-
-  int newState = Java_AppBannerInfoBarDelegate_determineInstallState(
-      env,
-      java_delegate_.obj(),
-      native_app_data_.obj());
-  static_cast<AppBannerInfoBar*>(infobar())->OnInstallStateChanged(newState);
-}
-
-void AppBannerInfoBarDelegate::OnInstallIntentReturned(
-    JNIEnv* env,
-    jobject obj,
-    jboolean jis_installing) {
-  if (!infobar())
-    return;
-
-  content::WebContents* web_contents =
-      InfoBarService::WebContentsFromInfoBar(infobar());
-  if (!web_contents)
-    return;
-
-  if (jis_installing) {
-    AppBannerSettingsHelper::RecordBannerEvent(
-        web_contents,
-        web_contents->GetURL(),
-        native_app_package_,
-        AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
-        AppBannerDataFetcher::GetCurrentTime());
-
-    TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_STARTED);
-    rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(),
-                                            "AppBanner.NativeApp.Installed",
-                                            web_contents->GetURL());
-  }
-
-  UpdateInstallState(env, obj);
-}
-
-void AppBannerInfoBarDelegate::OnInstallFinished(JNIEnv* env,
-                                                 jobject obj,
-                                                 jboolean success) {
-  if (!infobar())
-    return;
-
-  if (success) {
-    TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_COMPLETED);
-    UpdateInstallState(env, obj);
-  } else if (infobar()->owner()) {
-    TrackDismissEvent(DISMISS_EVENT_INSTALL_TIMEOUT);
-    infobar()->owner()->RemoveInfoBar(infobar());
-  }
-}
-
-void AppBannerInfoBarDelegate::CreateJavaDelegate() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  java_delegate_.Reset(Java_AppBannerInfoBarDelegate_create(
-      env,
-      reinterpret_cast<intptr_t>(this)));
-}
-
-void AppBannerInfoBarDelegate::SendBannerAccepted(
-    content::WebContents* web_contents,
-    const std::string& platform) {
-  web_contents->GetMainFrame()->Send(
-      new ChromeViewMsg_AppBannerAccepted(
-          web_contents->GetMainFrame()->GetRoutingID(),
-          event_request_id_,
-          platform));
-}
-
-gfx::Image AppBannerInfoBarDelegate::GetIcon() const {
-  return gfx::Image::CreateFrom1xBitmap(*app_icon_.get());
-}
-
-void AppBannerInfoBarDelegate::InfoBarDismissed() {
-  content::WebContents* web_contents =
-      InfoBarService::WebContentsFromInfoBar(infobar());
-  if (!web_contents)
-    return;
-
-  TrackDismissEvent(DISMISS_EVENT_CLOSE_BUTTON);
-
-  web_contents->GetMainFrame()->Send(
-      new ChromeViewMsg_AppBannerDismissed(
-          web_contents->GetMainFrame()->GetRoutingID(),
-          event_request_id_));
-
-  if (!native_app_data_.is_null()) {
-    AppBannerSettingsHelper::RecordBannerEvent(
-        web_contents, web_contents->GetURL(),
-        native_app_package_,
-        AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK,
-        AppBannerDataFetcher::GetCurrentTime());
-
-    rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(),
-                                            "AppBanner.NativeApp.Dismissed",
-                                            web_contents->GetURL());
-  } else if (!web_app_data_.IsEmpty()) {
-    AppBannerSettingsHelper::RecordBannerEvent(
-        web_contents, web_contents->GetURL(),
-        web_app_data_.start_url.spec(),
-        AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK,
-        AppBannerDataFetcher::GetCurrentTime());
-
-    rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(),
-                                            "AppBanner.WebApp.Dismissed",
-                                            web_contents->GetURL());
-  }
-}
-
-base::string16 AppBannerInfoBarDelegate::GetMessageText() const {
-  return app_title_;
-}
-
-int AppBannerInfoBarDelegate::GetButtons() const {
-  return BUTTON_OK;
-}
-
-bool AppBannerInfoBarDelegate::Accept() {
-  content::WebContents* web_contents =
-      InfoBarService::WebContentsFromInfoBar(infobar());
-  if (!web_contents) {
-    TrackDismissEvent(DISMISS_EVENT_ERROR);
-    return true;
-  }
-
-  if (!native_app_data_.is_null()) {
-    JNIEnv* env = base::android::AttachCurrentThread();
-
-    TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
-    if (tab == nullptr) {
-      TrackDismissEvent(DISMISS_EVENT_ERROR);
-      return true;
-    }
-
-    bool was_opened = Java_AppBannerInfoBarDelegate_installOrOpenNativeApp(
-        env,
-        java_delegate_.obj(),
-        tab->GetJavaObject().obj(),
-        native_app_data_.obj());
-
-    if (was_opened) {
-      TrackDismissEvent(DISMISS_EVENT_APP_OPEN);
-    } else {
-      TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_TRIGGERED);
-    }
-    SendBannerAccepted(web_contents, "play");
-    return was_opened;
-  } else if (!web_app_data_.IsEmpty()) {
-    AppBannerSettingsHelper::RecordBannerEvent(
-        web_contents, web_contents->GetURL(),
-        web_app_data_.start_url.spec(),
-        AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
-        AppBannerDataFetcher::GetCurrentTime());
-
-    ShortcutInfo info;
-    info.UpdateFromManifest(web_app_data_);
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO,
-        FROM_HERE,
-        base::Bind(&ShortcutHelper::AddShortcutInBackgroundWithSkBitmap,
-                   info,
-                   *app_icon_.get()));
-
-    TrackInstallEvent(INSTALL_EVENT_WEB_APP_INSTALLED);
-    SendBannerAccepted(web_contents, "web");
-    rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(),
-                                            "AppBanner.WebApp.Installed",
-                                            web_contents->GetURL());
-    return true;
-  }
-
-  return true;
-}
-
-bool AppBannerInfoBarDelegate::LinkClicked(WindowOpenDisposition disposition) {
-  if (native_app_data_.is_null())
-    return false;
-
-  // Try to show the details for the native app.
-  JNIEnv* env = base::android::AttachCurrentThread();
-
-  content::WebContents* web_contents =
-      InfoBarService::WebContentsFromInfoBar(infobar());
-  TabAndroid* tab = web_contents ? TabAndroid::FromWebContents(web_contents)
-                                 : nullptr;
-  if (tab == nullptr) {
-    TrackDismissEvent(DISMISS_EVENT_ERROR);
-    return true;
-  }
-
-  Java_AppBannerInfoBarDelegate_showAppDetails(env,
-                                               java_delegate_.obj(),
-                                               tab->GetJavaObject().obj(),
-                                               native_app_data_.obj());
-
-  TrackDismissEvent(DISMISS_EVENT_BANNER_CLICK);
-  return true;
-}
-
-bool RegisterAppBannerInfoBarDelegate(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
-}  // namespace banners
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate.h b/chrome/browser/android/banners/app_banner_infobar_delegate.h
deleted file mode 100644
index c91177c..0000000
--- a/chrome/browser/android/banners/app_banner_infobar_delegate.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2015 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.
-
-#ifndef CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_INFOBAR_DELEGATE_H_
-#define CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_INFOBAR_DELEGATE_H_
-
-#include "base/android/scoped_java_ref.h"
-#include "base/strings/string16.h"
-#include "components/infobars/core/confirm_infobar_delegate.h"
-#include "content/public/common/manifest.h"
-#include "ui/gfx/image/image.h"
-#include "url/gurl.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace infobars {
-class InfoBarManager;
-}  // namespace infobars
-
-class AppBannerInfoBar;
-
-namespace banners {
-
-// Manages installation of an app being promoted by a webpage.
-class AppBannerInfoBarDelegate : public ConfirmInfoBarDelegate {
- public:
-  // Delegate for promoting a web app.
-  AppBannerInfoBarDelegate(
-      int event_request_id,
-      const base::string16& app_title,
-      SkBitmap* app_icon,
-      const content::Manifest& web_app_data);
-
-  // Delegate for promoting an Android app.
-  AppBannerInfoBarDelegate(
-      int event_request_id,
-      const base::string16& app_title,
-      SkBitmap* app_icon,
-      const base::android::ScopedJavaGlobalRef<jobject>& native_app_data,
-      const std::string& native_app_package);
-
-  ~AppBannerInfoBarDelegate() override;
-
-  // Called when the AppBannerInfoBar's button needs to be updated.
-  void UpdateInstallState(JNIEnv* env, jobject obj);
-
-  // Called when the installation Intent has been handled and focus has been
-  // returned to Chrome.
-  void OnInstallIntentReturned(JNIEnv* env,
-                               jobject obj,
-                               jboolean jis_installing);
-
-  // Called when the InstallerDelegate task has finished.
-  void OnInstallFinished(JNIEnv* env,
-                         jobject obj,
-                         jboolean success);
-
- private:
-  void CreateJavaDelegate();
-  void SendBannerAccepted(content::WebContents* web_contents,
-                          const std::string& platform);
-
-  // ConfirmInfoBarDelegate:
-  gfx::Image GetIcon() const override;
-  void InfoBarDismissed() override;
-  base::string16 GetMessageText() const override;
-  int GetButtons() const override;
-  bool Accept() override;
-  bool LinkClicked(WindowOpenDisposition disposition) override;
-
-  base::android::ScopedJavaGlobalRef<jobject> java_delegate_;
-
-  base::string16 app_title_;
-  scoped_ptr<SkBitmap> app_icon_;
-
-  int event_request_id_;
-  content::Manifest web_app_data_;
-
-  base::android::ScopedJavaGlobalRef<jobject> native_app_data_;
-  std::string native_app_package_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppBannerInfoBarDelegate);
-};  // AppBannerInfoBarDelegate
-
-// Register native methods.
-bool RegisterAppBannerInfoBarDelegate(JNIEnv* env);
-
-}  // namespace banners
-
-#endif  // CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_INFOBAR_DELEGATE_H_
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
new file mode 100644
index 0000000..ac212f5
--- /dev/null
+++ b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
@@ -0,0 +1,283 @@
+// Copyright 2015 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.
+
+#include "chrome/browser/android/banners/app_banner_infobar_delegate_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/location.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/android/shortcut_helper.h"
+#include "chrome/browser/android/shortcut_info.h"
+#include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/banners/app_banner_data_fetcher.h"
+#include "chrome/browser/banners/app_banner_metrics.h"
+#include "chrome/browser/banners/app_banner_settings_helper.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/ui/android/infobars/app_banner_infobar_android.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/rappor/rappor_utils.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/manifest.h"
+#include "jni/AppBannerInfoBarDelegateAndroid_jni.h"
+#include "ui/gfx/android/java_bitmap.h"
+
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertJavaStringToUTF16;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ConvertUTF16ToJavaString;
+
+namespace banners {
+
+AppBannerInfoBarDelegateAndroid::AppBannerInfoBarDelegateAndroid(
+    int event_request_id,
+    const base::string16& app_title,
+    SkBitmap* app_icon,
+    const content::Manifest& web_app_data)
+    : app_title_(app_title),
+      app_icon_(app_icon),
+      event_request_id_(event_request_id),
+      web_app_data_(web_app_data) {
+  DCHECK(!web_app_data.IsEmpty());
+  CreateJavaDelegate();
+}
+
+AppBannerInfoBarDelegateAndroid::AppBannerInfoBarDelegateAndroid(
+    int event_request_id,
+    const base::string16& app_title,
+    SkBitmap* app_icon,
+    const base::android::ScopedJavaGlobalRef<jobject>& native_app_data,
+    const std::string& native_app_package)
+    : app_title_(app_title),
+      app_icon_(app_icon),
+      event_request_id_(event_request_id),
+      native_app_data_(native_app_data),
+      native_app_package_(native_app_package) {
+  DCHECK(!native_app_data_.is_null());
+  CreateJavaDelegate();
+}
+
+AppBannerInfoBarDelegateAndroid::~AppBannerInfoBarDelegateAndroid() {
+  TrackDismissEvent(DISMISS_EVENT_DISMISSED);
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_AppBannerInfoBarDelegateAndroid_destroy(env,
+                                        java_delegate_.obj());
+  java_delegate_.Reset();
+}
+
+void AppBannerInfoBarDelegateAndroid::UpdateInstallState(JNIEnv* env,
+                                                         jobject obj) {
+  if (native_app_data_.is_null())
+    return;
+
+  int newState = Java_AppBannerInfoBarDelegateAndroid_determineInstallState(
+      env,
+      java_delegate_.obj(),
+      native_app_data_.obj());
+  static_cast<AppBannerInfoBarAndroid*>(infobar())
+      ->OnInstallStateChanged(newState);
+}
+
+void AppBannerInfoBarDelegateAndroid::OnInstallIntentReturned(
+    JNIEnv* env,
+    jobject obj,
+    jboolean jis_installing) {
+  if (!infobar())
+    return;
+
+  content::WebContents* web_contents =
+      InfoBarService::WebContentsFromInfoBar(infobar());
+  if (!web_contents)
+    return;
+
+  if (jis_installing) {
+    AppBannerSettingsHelper::RecordBannerEvent(
+        web_contents,
+        web_contents->GetURL(),
+        native_app_package_,
+        AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
+        AppBannerDataFetcher::GetCurrentTime());
+
+    TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_STARTED);
+    rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(),
+                                            "AppBanner.NativeApp.Installed",
+                                            web_contents->GetURL());
+  }
+
+  UpdateInstallState(env, obj);
+}
+
+void AppBannerInfoBarDelegateAndroid::OnInstallFinished(JNIEnv* env,
+                                                 jobject obj,
+                                                 jboolean success) {
+  if (!infobar())
+    return;
+
+  if (success) {
+    TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_COMPLETED);
+    UpdateInstallState(env, obj);
+  } else if (infobar()->owner()) {
+    TrackDismissEvent(DISMISS_EVENT_INSTALL_TIMEOUT);
+    infobar()->owner()->RemoveInfoBar(infobar());
+  }
+}
+
+void AppBannerInfoBarDelegateAndroid::CreateJavaDelegate() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  java_delegate_.Reset(Java_AppBannerInfoBarDelegateAndroid_create(
+      env,
+      reinterpret_cast<intptr_t>(this)));
+}
+
+void AppBannerInfoBarDelegateAndroid::SendBannerAccepted(
+    content::WebContents* web_contents,
+    const std::string& platform) {
+  web_contents->GetMainFrame()->Send(
+      new ChromeViewMsg_AppBannerAccepted(
+          web_contents->GetMainFrame()->GetRoutingID(),
+          event_request_id_,
+          platform));
+}
+
+gfx::Image AppBannerInfoBarDelegateAndroid::GetIcon() const {
+  return gfx::Image::CreateFrom1xBitmap(*app_icon_.get());
+}
+
+void AppBannerInfoBarDelegateAndroid::InfoBarDismissed() {
+  content::WebContents* web_contents =
+      InfoBarService::WebContentsFromInfoBar(infobar());
+  if (!web_contents)
+    return;
+
+  TrackDismissEvent(DISMISS_EVENT_CLOSE_BUTTON);
+
+  web_contents->GetMainFrame()->Send(
+      new ChromeViewMsg_AppBannerDismissed(
+          web_contents->GetMainFrame()->GetRoutingID(),
+          event_request_id_));
+
+  if (!native_app_data_.is_null()) {
+    AppBannerSettingsHelper::RecordBannerEvent(
+        web_contents, web_contents->GetURL(),
+        native_app_package_,
+        AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK,
+        AppBannerDataFetcher::GetCurrentTime());
+
+    rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(),
+                                            "AppBanner.NativeApp.Dismissed",
+                                            web_contents->GetURL());
+  } else if (!web_app_data_.IsEmpty()) {
+    AppBannerSettingsHelper::RecordBannerEvent(
+        web_contents, web_contents->GetURL(),
+        web_app_data_.start_url.spec(),
+        AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK,
+        AppBannerDataFetcher::GetCurrentTime());
+
+    rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(),
+                                            "AppBanner.WebApp.Dismissed",
+                                            web_contents->GetURL());
+  }
+}
+
+base::string16 AppBannerInfoBarDelegateAndroid::GetMessageText() const {
+  return app_title_;
+}
+
+int AppBannerInfoBarDelegateAndroid::GetButtons() const {
+  return BUTTON_OK;
+}
+
+bool AppBannerInfoBarDelegateAndroid::Accept() {
+  content::WebContents* web_contents =
+      InfoBarService::WebContentsFromInfoBar(infobar());
+  if (!web_contents) {
+    TrackDismissEvent(DISMISS_EVENT_ERROR);
+    return true;
+  }
+
+  if (!native_app_data_.is_null()) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+
+    TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
+    if (tab == nullptr) {
+      TrackDismissEvent(DISMISS_EVENT_ERROR);
+      return true;
+    }
+
+    bool was_opened = Java_AppBannerInfoBarDelegateAndroid_installOrOpenNativeApp(
+        env,
+        java_delegate_.obj(),
+        tab->GetJavaObject().obj(),
+        native_app_data_.obj());
+
+    if (was_opened) {
+      TrackDismissEvent(DISMISS_EVENT_APP_OPEN);
+    } else {
+      TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_TRIGGERED);
+    }
+    SendBannerAccepted(web_contents, "play");
+    return was_opened;
+  } else if (!web_app_data_.IsEmpty()) {
+    AppBannerSettingsHelper::RecordBannerEvent(
+        web_contents, web_contents->GetURL(),
+        web_app_data_.start_url.spec(),
+        AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
+        AppBannerDataFetcher::GetCurrentTime());
+
+    ShortcutInfo info;
+    info.UpdateFromManifest(web_app_data_);
+    content::BrowserThread::PostTask(
+        content::BrowserThread::IO,
+        FROM_HERE,
+        base::Bind(&ShortcutHelper::AddShortcutInBackgroundWithSkBitmap,
+                   info,
+                   *app_icon_.get()));
+
+    TrackInstallEvent(INSTALL_EVENT_WEB_APP_INSTALLED);
+    SendBannerAccepted(web_contents, "web");
+    rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(),
+                                            "AppBanner.WebApp.Installed",
+                                            web_contents->GetURL());
+    return true;
+  }
+
+  return true;
+}
+
+bool AppBannerInfoBarDelegateAndroid::LinkClicked(
+    WindowOpenDisposition disposition) {
+  if (native_app_data_.is_null())
+    return false;
+
+  // Try to show the details for the native app.
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  content::WebContents* web_contents =
+      InfoBarService::WebContentsFromInfoBar(infobar());
+  TabAndroid* tab = web_contents ? TabAndroid::FromWebContents(web_contents)
+                                 : nullptr;
+  if (tab == nullptr) {
+    TrackDismissEvent(DISMISS_EVENT_ERROR);
+    return true;
+  }
+
+  Java_AppBannerInfoBarDelegateAndroid_showAppDetails(env,
+                                               java_delegate_.obj(),
+                                               tab->GetJavaObject().obj(),
+                                               native_app_data_.obj());
+
+  TrackDismissEvent(DISMISS_EVENT_BANNER_CLICK);
+  return true;
+}
+
+bool RegisterAppBannerInfoBarDelegateAndroid(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+}  // namespace banners
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate_android.h b/chrome/browser/android/banners/app_banner_infobar_delegate_android.h
new file mode 100644
index 0000000..f912dfc
--- /dev/null
+++ b/chrome/browser/android/banners/app_banner_infobar_delegate_android.h
@@ -0,0 +1,93 @@
+// Copyright 2015 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.
+
+#ifndef CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_INFOBAR_DELEGATE_ANDROID_H_
+#define CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_INFOBAR_DELEGATE_ANDROID_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "base/strings/string16.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
+#include "content/public/common/manifest.h"
+#include "ui/gfx/image/image.h"
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace infobars {
+class InfoBarManager;
+}  // namespace infobars
+
+class AppBannerInfoBar;
+
+namespace banners {
+
+// Manages installation of an app being promoted by a webpage.
+class AppBannerInfoBarDelegateAndroid : public ConfirmInfoBarDelegate {
+ public:
+  // Delegate for promoting a web app.
+  AppBannerInfoBarDelegateAndroid(
+      int event_request_id,
+      const base::string16& app_title,
+      SkBitmap* app_icon,
+      const content::Manifest& web_app_data);
+
+  // Delegate for promoting an Android app.
+  AppBannerInfoBarDelegateAndroid(
+      int event_request_id,
+      const base::string16& app_title,
+      SkBitmap* app_icon,
+      const base::android::ScopedJavaGlobalRef<jobject>& native_app_data,
+      const std::string& native_app_package);
+
+  ~AppBannerInfoBarDelegateAndroid() override;
+
+  // Called when the AppBannerInfoBar's button needs to be updated.
+  void UpdateInstallState(JNIEnv* env, jobject obj);
+
+  // Called when the installation Intent has been handled and focus has been
+  // returned to Chrome.
+  void OnInstallIntentReturned(JNIEnv* env,
+                               jobject obj,
+                               jboolean jis_installing);
+
+  // Called when the InstallerDelegate task has finished.
+  void OnInstallFinished(JNIEnv* env,
+                         jobject obj,
+                         jboolean success);
+
+ private:
+  void CreateJavaDelegate();
+  void SendBannerAccepted(content::WebContents* web_contents,
+                          const std::string& platform);
+
+  // ConfirmInfoBarDelegate:
+  gfx::Image GetIcon() const override;
+  void InfoBarDismissed() override;
+  base::string16 GetMessageText() const override;
+  int GetButtons() const override;
+  bool Accept() override;
+  bool LinkClicked(WindowOpenDisposition disposition) override;
+
+  base::android::ScopedJavaGlobalRef<jobject> java_delegate_;
+
+  base::string16 app_title_;
+  scoped_ptr<SkBitmap> app_icon_;
+
+  int event_request_id_;
+  content::Manifest web_app_data_;
+
+  base::android::ScopedJavaGlobalRef<jobject> native_app_data_;
+  std::string native_app_package_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppBannerInfoBarDelegateAndroid);
+};  // AppBannerInfoBarDelegateAndroid
+
+// Register native methods.
+bool RegisterAppBannerInfoBarDelegateAndroid(JNIEnv* env);
+
+}  // namespace banners
+
+#endif  // CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_INFOBAR_DELEGATE_ANDROID_H_
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index c4e333b..8d3206e 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/android/accessibility/font_size_prefs_android.h"
 #include "chrome/browser/android/accessibility_util.h"
 #include "chrome/browser/android/appmenu/app_menu_drag_helper.h"
-#include "chrome/browser/android/banners/app_banner_infobar_delegate.h"
+#include "chrome/browser/android/banners/app_banner_infobar_delegate_android.h"
 #include "chrome/browser/android/banners/app_banner_manager_android.h"
 #include "chrome/browser/android/bookmarks/bookmarks_bridge.h"
 #include "chrome/browser/android/bookmarks/partner_bookmarks_reader.h"
@@ -95,7 +95,7 @@
 #include "chrome/browser/ui/android/connection_info_popup_android.h"
 #include "chrome/browser/ui/android/context_menu_helper.h"
 #include "chrome/browser/ui/android/infobars/account_chooser_infobar.h"
-#include "chrome/browser/ui/android/infobars/app_banner_infobar.h"
+#include "chrome/browser/ui/android/infobars/app_banner_infobar_android.h"
 #include "chrome/browser/ui/android/infobars/confirm_infobar.h"
 #include "chrome/browser/ui/android/infobars/data_reduction_proxy_infobar.h"
 #include "chrome/browser/ui/android/infobars/download_overwrite_infobar.h"
@@ -148,8 +148,9 @@ static base::android::RegistrationMethod kChromeRegisteredMethods[] = {
     {"AndroidProfileOAuth2TokenService",
      AndroidProfileOAuth2TokenService::Register},
     {"AnswersImageBridge", RegisterAnswersImageBridge},
-    {"AppBannerInfoBar", RegisterAppBannerInfoBar},
-    {"AppBannerInfoBarDelegate", banners::RegisterAppBannerInfoBarDelegate},
+    {"AppBannerInfoBarAndroid", RegisterAppBannerInfoBarAndroid},
+    {"AppBannerInfoBarDelegateAndroid",
+     banners::RegisterAppBannerInfoBarDelegateAndroid},
     {"AppBannerManagerAndroid", banners::AppBannerManagerAndroid::Register},
     {"ApplicationLifetime", RegisterApplicationLifetimeAndroid},
     {"AutocompleteControllerAndroid", RegisterAutocompleteControllerAndroid},
diff --git a/chrome/browser/ui/android/infobars/app_banner_infobar.cc b/chrome/browser/ui/android/infobars/app_banner_infobar.cc
deleted file mode 100644
index bdc868f..0000000
--- a/chrome/browser/ui/android/infobars/app_banner_infobar.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2015 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.
-
-#include "chrome/browser/ui/android/infobars/app_banner_infobar.h"
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
-#include "base/android/scoped_java_ref.h"
-#include "chrome/browser/android/banners/app_banner_infobar_delegate.h"
-#include "jni/AppBannerInfoBar_jni.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "ui/gfx/android/java_bitmap.h"
-#include "ui/gfx/image/image.h"
-
-
-AppBannerInfoBar::AppBannerInfoBar(
-    scoped_ptr<banners::AppBannerInfoBarDelegate> delegate,
-    const base::android::ScopedJavaGlobalRef<jobject>& japp_data)
-    : ConfirmInfoBar(delegate.Pass()),
-      japp_data_(japp_data) {
-}
-
-AppBannerInfoBar::AppBannerInfoBar(
-    scoped_ptr<banners::AppBannerInfoBarDelegate> delegate,
-    const GURL& app_url)
-    : ConfirmInfoBar(delegate.Pass()),
-      app_url_(app_url) {
-}
-
-AppBannerInfoBar::~AppBannerInfoBar() {
-}
-
-base::android::ScopedJavaLocalRef<jobject>
-AppBannerInfoBar::CreateRenderInfoBar(JNIEnv* env) {
-  ConfirmInfoBarDelegate* app_banner_infobar_delegate = GetDelegate();
-
-  base::android::ScopedJavaLocalRef<jstring> app_title =
-      base::android::ConvertUTF16ToJavaString(
-          env, app_banner_infobar_delegate->GetMessageText());
-
-  base::android::ScopedJavaLocalRef<jobject> java_bitmap;
-  if (!app_banner_infobar_delegate->GetIcon().IsEmpty()) {
-    java_bitmap = gfx::ConvertToJavaBitmap(
-        app_banner_infobar_delegate->GetIcon().ToSkBitmap());
-  }
-
-  base::android::ScopedJavaLocalRef<jobject> infobar;
-  if (!japp_data_.is_null()) {
-    infobar.Reset(Java_AppBannerInfoBar_createNativeAppInfoBar(
-        env,
-        reinterpret_cast<intptr_t>(this),
-        app_title.obj(),
-        java_bitmap.obj(),
-        japp_data_.obj()));
-  } else {
-    // Trim down the app URL to the domain and registry.
-    std::string trimmed_url =
-        net::registry_controlled_domains::GetDomainAndRegistry(
-            app_url_,
-            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-
-    base::android::ScopedJavaLocalRef<jstring> app_url =
-        base::android::ConvertUTF8ToJavaString(env, trimmed_url);
-
-    infobar.Reset(Java_AppBannerInfoBar_createWebAppInfoBar(
-        env,
-        reinterpret_cast<intptr_t>(this),
-        app_title.obj(),
-        java_bitmap.obj(),
-        app_url.obj()));
-  }
-
-  java_infobar_.Reset(env, infobar.obj());
-  return infobar;
-}
-
-void AppBannerInfoBar::OnInstallStateChanged(int new_state) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_AppBannerInfoBar_onInstallStateChanged(env,
-                                              java_infobar_.obj(),
-                                              new_state);
-}
-
-// Native JNI methods ---------------------------------------------------------
-
-bool RegisterAppBannerInfoBar(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
diff --git a/chrome/browser/ui/android/infobars/app_banner_infobar.h b/chrome/browser/ui/android/infobars/app_banner_infobar.h
deleted file mode 100644
index 72ef28e..0000000
--- a/chrome/browser/ui/android/infobars/app_banner_infobar.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2015 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.
-
-#ifndef CHROME_BROWSER_UI_ANDROID_INFOBARS_APP_BANNER_INFOBAR_H_
-#define CHROME_BROWSER_UI_ANDROID_INFOBARS_APP_BANNER_INFOBAR_H_
-
-#include "base/android/scoped_java_ref.h"
-#include "base/basictypes.h"
-#include "chrome/browser/ui/android/infobars/confirm_infobar.h"
-#include "url/gurl.h"
-
-namespace banners {
-class AppBannerInfoBarDelegate;
-}  // namespace banners
-
-
-class AppBannerInfoBar : public ConfirmInfoBar {
- public:
-  // Constructs an AppBannerInfoBar promoting a native app.
-  AppBannerInfoBar(
-      scoped_ptr<banners::AppBannerInfoBarDelegate> delegate,
-      const base::android::ScopedJavaGlobalRef<jobject>& japp_data);
-
-  // Constructs an AppBannerInfoBar promoting a web app.
-  AppBannerInfoBar(
-      scoped_ptr<banners::AppBannerInfoBarDelegate> delegate,
-      const GURL& app_url);
-
-  ~AppBannerInfoBar() override;
-
-  // Called when the installation state of the app may have changed.
-  // Updates the InfoBar visuals to match the new state and re-enables controls
-  // that may have been disabled.
-  void OnInstallStateChanged(int new_state);
-
- private:
-  // InfoBarAndroid overrides.
-  base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
-      JNIEnv* env) override;
-
-  // Native app: Details about the app.
-  base::android::ScopedJavaGlobalRef<jobject> japp_data_;
-
-  // Web app: URL for the app.
-  GURL app_url_;
-
-  base::android::ScopedJavaGlobalRef<jobject> java_infobar_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppBannerInfoBar);
-};
-
-// Register native methods.
-bool RegisterAppBannerInfoBar(JNIEnv* env);
-
-#endif  // CHROME_BROWSER_UI_ANDROID_INFOBARS_APP_BANNER_INFOBAR_H_
diff --git a/chrome/browser/ui/android/infobars/app_banner_infobar_android.cc b/chrome/browser/ui/android/infobars/app_banner_infobar_android.cc
new file mode 100644
index 0000000..16e8f14
--- /dev/null
+++ b/chrome/browser/ui/android/infobars/app_banner_infobar_android.cc
@@ -0,0 +1,88 @@
+// Copyright 2015 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.
+
+#include "chrome/browser/ui/android/infobars/app_banner_infobar_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "chrome/browser/android/banners/app_banner_infobar_delegate_android.h"
+#include "jni/AppBannerInfoBarAndroid_jni.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "ui/gfx/android/java_bitmap.h"
+#include "ui/gfx/image/image.h"
+
+
+AppBannerInfoBarAndroid::AppBannerInfoBarAndroid(
+    scoped_ptr<banners::AppBannerInfoBarDelegateAndroid> delegate,
+    const base::android::ScopedJavaGlobalRef<jobject>& japp_data)
+    : ConfirmInfoBar(delegate.Pass()),
+      japp_data_(japp_data) {
+}
+
+AppBannerInfoBarAndroid::AppBannerInfoBarAndroid(
+    scoped_ptr<banners::AppBannerInfoBarDelegateAndroid> delegate,
+    const GURL& app_url)
+    : ConfirmInfoBar(delegate.Pass()),
+      app_url_(app_url) {
+}
+
+AppBannerInfoBarAndroid::~AppBannerInfoBarAndroid() {
+}
+
+base::android::ScopedJavaLocalRef<jobject>
+AppBannerInfoBarAndroid::CreateRenderInfoBar(JNIEnv* env) {
+  ConfirmInfoBarDelegate* app_banner_infobar_delegate = GetDelegate();
+
+  base::android::ScopedJavaLocalRef<jstring> app_title =
+      base::android::ConvertUTF16ToJavaString(
+          env, app_banner_infobar_delegate->GetMessageText());
+
+  base::android::ScopedJavaLocalRef<jobject> java_bitmap;
+  if (!app_banner_infobar_delegate->GetIcon().IsEmpty()) {
+    java_bitmap = gfx::ConvertToJavaBitmap(
+        app_banner_infobar_delegate->GetIcon().ToSkBitmap());
+  }
+
+  base::android::ScopedJavaLocalRef<jobject> infobar;
+  if (!japp_data_.is_null()) {
+    infobar.Reset(Java_AppBannerInfoBarAndroid_createNativeAppInfoBar(
+        env,
+        reinterpret_cast<intptr_t>(this),
+        app_title.obj(),
+        java_bitmap.obj(),
+        japp_data_.obj()));
+  } else {
+    // Trim down the app URL to the domain and registry.
+    std::string trimmed_url =
+        net::registry_controlled_domains::GetDomainAndRegistry(
+            app_url_,
+            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+
+    base::android::ScopedJavaLocalRef<jstring> app_url =
+        base::android::ConvertUTF8ToJavaString(env, trimmed_url);
+
+    infobar.Reset(Java_AppBannerInfoBarAndroid_createWebAppInfoBar(
+        env,
+        reinterpret_cast<intptr_t>(this),
+        app_title.obj(),
+        java_bitmap.obj(),
+        app_url.obj()));
+  }
+
+  java_infobar_.Reset(env, infobar.obj());
+  return infobar;
+}
+
+void AppBannerInfoBarAndroid::OnInstallStateChanged(int new_state) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_AppBannerInfoBarAndroid_onInstallStateChanged(env, java_infobar_.obj(),
+                                                     new_state);
+}
+
+// Native JNI methods ---------------------------------------------------------
+
+bool RegisterAppBannerInfoBarAndroid(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
diff --git a/chrome/browser/ui/android/infobars/app_banner_infobar_android.h b/chrome/browser/ui/android/infobars/app_banner_infobar_android.h
new file mode 100644
index 0000000..9cc364ec
--- /dev/null
+++ b/chrome/browser/ui/android/infobars/app_banner_infobar_android.h
@@ -0,0 +1,56 @@
+// Copyright 2015 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.
+
+#ifndef CHROME_BROWSER_UI_ANDROID_INFOBARS_APP_BANNER_INFOBAR_ANDROID_H_
+#define CHROME_BROWSER_UI_ANDROID_INFOBARS_APP_BANNER_INFOBAR_ANDROID_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "chrome/browser/ui/android/infobars/confirm_infobar.h"
+#include "url/gurl.h"
+
+namespace banners {
+class AppBannerInfoBarDelegateAndroid;
+}  // namespace banners
+
+
+class AppBannerInfoBarAndroid : public ConfirmInfoBar {
+ public:
+  // Constructs an AppBannerInfoBarAndroid promoting a native app.
+  AppBannerInfoBarAndroid(
+      scoped_ptr<banners::AppBannerInfoBarDelegateAndroid> delegate,
+      const base::android::ScopedJavaGlobalRef<jobject>& japp_data);
+
+  // Constructs an AppBannerInfoBarAndroid promoting a web app.
+  AppBannerInfoBarAndroid(
+      scoped_ptr<banners::AppBannerInfoBarDelegateAndroid> delegate,
+      const GURL& app_url);
+
+  ~AppBannerInfoBarAndroid() override;
+
+  // Called when the installation state of the app may have changed.
+  // Updates the InfoBar visuals to match the new state and re-enables controls
+  // that may have been disabled.
+  void OnInstallStateChanged(int new_state);
+
+ private:
+  // InfoBarAndroid overrides.
+  base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
+      JNIEnv* env) override;
+
+  // Native app: Details about the app.
+  base::android::ScopedJavaGlobalRef<jobject> japp_data_;
+
+  // Web app: URL for the app.
+  GURL app_url_;
+
+  base::android::ScopedJavaGlobalRef<jobject> java_infobar_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppBannerInfoBarAndroid);
+};
+
+// Register native methods.
+bool RegisterAppBannerInfoBarAndroid(JNIEnv* env);
+
+#endif  // CHROME_BROWSER_UI_ANDROID_INFOBARS_APP_BANNER_INFOBAR_ANDROID_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 2a17889..468e138 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -33,8 +33,8 @@
       'browser/android/appmenu/app_menu_drag_helper.h',
       'browser/android/banners/app_banner_data_fetcher_android.cc',
       'browser/android/banners/app_banner_data_fetcher_android.h',
-      'browser/android/banners/app_banner_infobar_delegate.cc',
-      'browser/android/banners/app_banner_infobar_delegate.h',
+      'browser/android/banners/app_banner_infobar_delegate_android.cc',
+      'browser/android/banners/app_banner_infobar_delegate_android.h',
       'browser/android/banners/app_banner_manager_android.cc',
       'browser/android/banners/app_banner_manager_android.h',
       'browser/android/bookmarks/bookmarks_bridge.cc',
@@ -1731,9 +1731,9 @@
       'android/java/src/org/chromium/chrome/browser/WarmupManager.java',
       'android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java',
       'android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java',
-      'android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java',
+      'android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarAndroid.java',
       'android/java/src/org/chromium/chrome/browser/infobar/AccountChooserInfoBar.java',
-      'android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java',
+      'android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegateAndroid.java',
       'android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBarDelegate.java',
       'android/java/src/org/chromium/chrome/browser/infobar/DataReductionProxyInfoBarDelegate.java',
       'android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBarDelegate.java',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 7b72c67..f2508a4 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -39,8 +39,8 @@
       'browser/ui/android/context_menu_helper.cc',
       'browser/ui/android/context_menu_helper.h',
       'browser/ui/android/external_protocol_dialog_android.cc',
-      'browser/ui/android/infobars/app_banner_infobar.cc',
-      'browser/ui/android/infobars/app_banner_infobar.h',
+      'browser/ui/android/infobars/app_banner_infobar_android.cc',
+      'browser/ui/android/infobars/app_banner_infobar_android.h',
       'browser/ui/android/infobars/confirm_infobar.cc',
       'browser/ui/android/infobars/confirm_infobar.h',
       'browser/ui/android/infobars/data_reduction_proxy_infobar.cc',
-- 
cgit v1.1