Commit 3791e101 authored by dfalcantara's avatar dfalcantara Committed by Commit bot

Allow installing apps via the AppBannerInfoBar

* Button cycles between "Install", "Installing", and "Open",
  depending on the installation status of the app being promoted.

* Clicking on the icon or the title of the AppBannerInfoBar should
  send the user to see details about the app that is being promoted.

* Open up access to LinkClicked() from ConfirmInfoBar.
  Piggyback on LinkClicked() to determine that the user wants to
  see the link related to the app.

* Add an ID to the icon of the infobar so it can be retrieved and
  have an OnClickListener attached to it.

* Update the status of the button when the user hops in and out
  of the Chrome application.

BUG=453170

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

Cr-Commit-Position: refs/heads/master@{#315182}
parent 451a7802
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
package org.chromium.chrome.browser.banners; package org.chromium.chrome.browser.banners;
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 android.text.TextUtils; import android.text.TextUtils;
import org.chromium.base.ApplicationStatus; import org.chromium.base.ApplicationStatus;
...@@ -12,7 +18,9 @@ import org.chromium.base.JNINamespace; ...@@ -12,7 +18,9 @@ import org.chromium.base.JNINamespace;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.EmptyTabObserver; import org.chromium.chrome.browser.EmptyTabObserver;
import org.chromium.chrome.browser.Tab; import org.chromium.chrome.browser.Tab;
import org.chromium.chrome.browser.infobar.AppBannerInfoBar;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid;
/** /**
* Manages an AppBannerInfoBar for a Tab. * Manages an AppBannerInfoBar for a Tab.
...@@ -26,7 +34,7 @@ import org.chromium.content_public.browser.WebContents; ...@@ -26,7 +34,7 @@ import org.chromium.content_public.browser.WebContents;
* from the network. * from the network.
*/ */
@JNINamespace("banners") @JNINamespace("banners")
public class AppBannerManager extends EmptyTabObserver implements AppDetailsDelegate.Observer { public class AppBannerManager extends EmptyTabObserver {
private static final String TAG = "AppBannerManager"; private static final String TAG = "AppBannerManager";
/** Retrieves information about a given package. */ /** Retrieves information about a given package. */
...@@ -38,6 +46,12 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele ...@@ -38,6 +46,12 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele
/** Tab that the AppBannerView/AppBannerManager is owned by. */ /** Tab that the AppBannerView/AppBannerManager is owned by. */
private final Tab mTab; private final Tab mTab;
/** Monitors an installation in progress. */
private InstallerDelegate mInstallTask;
/** Monitors for application state changes. */
private final ApplicationStatus.ApplicationStateListener mListener;
/** /**
* Checks if app banners are enabled. * Checks if app banners are enabled.
* @return True if banners are enabled, false otherwise. * @return True if banners are enabled, false otherwise.
...@@ -63,6 +77,18 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele ...@@ -63,6 +77,18 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele
mNativePointer = nativeInit(); mNativePointer = nativeInit();
mTab = tab; mTab = tab;
updatePointers(); updatePointers();
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);
}
};
} }
@Override @Override
...@@ -80,6 +106,11 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele ...@@ -80,6 +106,11 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele
* Destroys the native AppBannerManager. * Destroys the native AppBannerManager.
*/ */
public void destroy() { public void destroy() {
if (mInstallTask != null) {
mInstallTask.cancel();
mInstallTask = null;
}
ApplicationStatus.unregisterApplicationStateListener(mListener);
nativeDestroy(mNativePointer); nativeDestroy(mNativePointer);
} }
...@@ -105,23 +136,105 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele ...@@ -105,23 +136,105 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele
private void fetchAppDetails(String url, String packageName) { private void fetchAppDetails(String url, String packageName) {
if (sAppDetailsDelegate == null) return; if (sAppDetailsDelegate == null) return;
int iconSize = getPreferredIconSize(); int iconSize = getPreferredIconSize();
sAppDetailsDelegate.getAppDetailsAsynchronously(this, url, packageName, iconSize); sAppDetailsDelegate.getAppDetailsAsynchronously(
createAppDetailsObserver(), url, packageName, iconSize);
} }
/** private AppDetailsDelegate.Observer createAppDetailsObserver() {
* Called when data about the package has been retrieved, which includes the url for the app's return new AppDetailsDelegate.Observer() {
* icon but not the icon Bitmap itself. Kicks off a background task to retrieve it. /**
* @param data Data about the app. Null if the task failed. * Called when data about the package has been retrieved, which includes the url for the
*/ * app's icon but not the icon Bitmap itself.
@Override * @param data Data about the app. Null if the task failed.
public void onAppDetailsRetrieved(AppData data) { */
if (data == null) return; @Override
public void onAppDetailsRetrieved(AppData data) {
if (data == null) return;
String imageUrl = data.imageUrl();
if (TextUtils.isEmpty(imageUrl)) return;
nativeOnAppDetailsRetrieved(
mNativePointer, data, data.title(), data.packageName(), data.imageUrl());
}
};
}
String imageUrl = data.imageUrl(); @CalledByNative
if (TextUtils.isEmpty(imageUrl)) return; private boolean installOrOpenNativeApp(AppData appData) {
Context context = ApplicationStatus.getApplicationContext();
String packageName = appData.packageName();
PackageManager packageManager = context.getPackageManager();
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 !mTab.getWindowAndroid().showIntent(
appData.installIntent(), createIntentCallback(appData),
R.string.low_memory_error);
}
}
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 =
ApplicationStatus.getApplicationContext().getPackageManager();
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(AppData appData) {
WindowAndroid.IntentCallback emptyCallback = new WindowAndroid.IntentCallback() {
@Override
public void onIntentCompleted(WindowAndroid window, int resultCode,
ContentResolver contentResolver, Intent data) {
// Do nothing.
}
};
mTab.getWindowAndroid().showIntent(
appData.detailsIntent(), emptyCallback, R.string.low_memory_error);
}
@CalledByNative
private int determineInstallState(AppData data) {
if (mInstallTask != null) return AppBannerInfoBar.INSTALL_STATE_INSTALLING;
nativeOnAppDetailsRetrieved( PackageManager pm = ApplicationStatus.getApplicationContext().getPackageManager();
mNativePointer, data, data.title(), data.packageName(), data.imageUrl()); boolean isInstalled = InstallerDelegate.isInstalled(pm, data.packageName());
return isInstalled ? AppBannerInfoBar.INSTALL_STATE_INSTALLED
: AppBannerInfoBar.INSTALL_STATE_NOT_INSTALLED;
} }
private static native boolean nativeIsEnabled(); private static native boolean nativeIsEnabled();
...@@ -131,6 +244,10 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele ...@@ -131,6 +244,10 @@ public class AppBannerManager extends EmptyTabObserver implements AppDetailsDele
WebContents webContents); WebContents webContents);
private native boolean nativeOnAppDetailsRetrieved(long nativeAppBannerManager, AppData data, private native boolean nativeOnAppDetailsRetrieved(long nativeAppBannerManager, AppData data,
String title, String packageName, String imageUrl); String title, String packageName, String imageUrl);
private native void nativeOnInstallIntentReturned(
long nativeAppBannerManager, boolean isInstalling);
private native void nativeOnInstallFinished(long nativeAppBannerManager, boolean success);
private native void nativeUpdateInstallState(long nativeAppBannerManager);
// UMA tracking. // UMA tracking.
private static native void nativeRecordDismissEvent(int metric); private static native void nativeRecordDismissEvent(int metric);
......
...@@ -6,6 +6,8 @@ package org.chromium.chrome.browser.infobar; ...@@ -6,6 +6,8 @@ package org.chromium.chrome.browser.infobar;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.view.View;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.base.ApplicationStatus; import org.chromium.base.ApplicationStatus;
...@@ -16,11 +18,22 @@ import org.chromium.chrome.browser.banners.AppData; ...@@ -16,11 +18,22 @@ import org.chromium.chrome.browser.banners.AppData;
/** /**
* Infobar informing the user about an app related to this page. * Infobar informing the user about an app related to this page.
*/ */
public class AppBannerInfoBar extends ConfirmInfoBar { 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 View mTitleView;
private View mIconView;
private final String mAppTitle; private final String mAppTitle;
// Data for native app installs. // Data for native app installs.
private final AppData mAppData; private final AppData mAppData;
private int mInstallState;
// Data for web app installs. // Data for web app installs.
private final String mAppUrl; private final String mAppUrl;
...@@ -31,6 +44,7 @@ public class AppBannerInfoBar extends ConfirmInfoBar { ...@@ -31,6 +44,7 @@ public class AppBannerInfoBar extends ConfirmInfoBar {
mAppTitle = appTitle; mAppTitle = appTitle;
mAppData = data; mAppData = data;
mAppUrl = null; mAppUrl = null;
mInstallState = INSTALL_STATE_NOT_INSTALLED;
} }
// Banner for web apps. // Banner for web apps.
...@@ -39,6 +53,7 @@ public class AppBannerInfoBar extends ConfirmInfoBar { ...@@ -39,6 +53,7 @@ public class AppBannerInfoBar extends ConfirmInfoBar {
mAppTitle = appTitle; mAppTitle = appTitle;
mAppData = null; mAppData = null;
mAppUrl = url; mAppUrl = url;
mInstallState = INSTALL_STATE_NOT_INSTALLED;
} }
@Override @Override
...@@ -51,6 +66,14 @@ public class AppBannerInfoBar extends ConfirmInfoBar { ...@@ -51,6 +66,14 @@ public class AppBannerInfoBar extends ConfirmInfoBar {
super.createContent(layout); super.createContent(layout);
mButton = (Button) layout.findViewById(R.id.button_primary);
mTitleView = layout.findViewById(R.id.infobar_message);
// TODO(dfalcantara): Grab the icon from the layout.
// mIconView = layout.findViewById(R.id.infobar_icon);
assert mButton != null && mTitleView != null;
// Set up accessibility text. // Set up accessibility text.
Context context = getContext(); Context context = getContext();
if (mAppData != null) { if (mAppData != null) {
...@@ -62,6 +85,52 @@ public class AppBannerInfoBar extends ConfirmInfoBar { ...@@ -62,6 +85,52 @@ public class AppBannerInfoBar extends ConfirmInfoBar {
R.string.app_banner_view_web_app_accessibility, mAppTitle, R.string.app_banner_view_web_app_accessibility, mAppTitle,
mAppUrl)); mAppUrl));
} }
// 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() {
String text;
String accessibilityText = null;
boolean enabled = true;
if (mInstallState == INSTALL_STATE_NOT_INSTALLED) {
text = mAppData.installButtonText();
accessibilityText =
getContext().getString(R.string.app_banner_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() { private static String getAddToHomescreenText() {
......
...@@ -71,4 +71,9 @@ bool AppBannerInfoBarDelegate::Accept() { ...@@ -71,4 +71,9 @@ bool AppBannerInfoBarDelegate::Accept() {
return delegate_->OnButtonClicked(); return delegate_->OnButtonClicked();
} }
bool AppBannerInfoBarDelegate::LinkClicked(WindowOpenDisposition disposition) {
DCHECK(delegate_);
return delegate_->OnLinkClicked();
}
} // namespace banners } // namespace banners
...@@ -33,6 +33,10 @@ class AppBannerInfoBarDelegate : public ConfirmInfoBarDelegate { ...@@ -33,6 +33,10 @@ class AppBannerInfoBarDelegate : public ConfirmInfoBarDelegate {
// Returns true if the infobar should be dismissed. // Returns true if the infobar should be dismissed.
virtual bool OnButtonClicked() const = 0; virtual bool OnButtonClicked() const = 0;
// User has clicked the link.
// Returns true if the infobar should be dismissed.
virtual bool OnLinkClicked() const = 0;
// Called when the infobar has been destroyed. // Called when the infobar has been destroyed.
virtual void OnInfoBarDestroyed() = 0; virtual void OnInfoBarDestroyed() = 0;
...@@ -67,6 +71,7 @@ class AppBannerInfoBarDelegate : public ConfirmInfoBarDelegate { ...@@ -67,6 +71,7 @@ class AppBannerInfoBarDelegate : public ConfirmInfoBarDelegate {
base::string16 GetMessageText() const override; base::string16 GetMessageText() const override;
int GetButtons() const override; int GetButtons() const override;
bool Accept() override; bool Accept() override;
bool LinkClicked(WindowOpenDisposition disposition) override;
private: private:
explicit AppBannerInfoBarDelegate(AppDelegate* delegate); explicit AppBannerInfoBarDelegate(AppDelegate* delegate);
......
...@@ -99,7 +99,16 @@ bool AppBannerManager::OnButtonClicked() const { ...@@ -99,7 +99,16 @@ bool AppBannerManager::OnButtonClicked() const {
if (!web_contents()) if (!web_contents())
return true; return true;
if (!web_app_data_.IsEmpty()) { if (!native_app_data_.is_null()) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> jobj = weak_java_banner_view_manager_.get(env);
if (jobj.is_null())
return true;
return Java_AppBannerManager_installOrOpenNativeApp(env,
jobj.obj(),
native_app_data_.obj());
} else if (!web_app_data_.IsEmpty()) {
AppBannerSettingsHelper::RecordBannerEvent( AppBannerSettingsHelper::RecordBannerEvent(
web_contents(), web_contents()->GetURL(), web_contents(), web_contents()->GetURL(),
web_app_data_.start_url.spec(), web_app_data_.start_url.spec(),
...@@ -113,6 +122,27 @@ bool AppBannerManager::OnButtonClicked() const { ...@@ -113,6 +122,27 @@ bool AppBannerManager::OnButtonClicked() const {
return true; return true;
} }
bool AppBannerManager::OnLinkClicked() const {
if (!web_contents())
return true;
if (!native_app_data_.is_null()) {
// Try to show the details for the native app.
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> jobj = weak_java_banner_view_manager_.get(env);
if (jobj.is_null())
return true;
Java_AppBannerManager_showAppDetails(env,
jobj.obj(),
native_app_data_.obj());
return true;
} else {
// Nothing should happen if the user is installing a web app.
return false;
}
}
base::string16 AppBannerManager::GetTitle() const { base::string16 AppBannerManager::GetTitle() const {
return app_title_; return app_title_;
} }
...@@ -319,6 +349,53 @@ bool AppBannerManager::OnAppDetailsRetrieved(JNIEnv* env, ...@@ -319,6 +349,53 @@ bool AppBannerManager::OnAppDetailsRetrieved(JNIEnv* env,
return FetchIcon(GURL(image_url)); return FetchIcon(GURL(image_url));
} }
void AppBannerManager::OnInstallIntentReturned(JNIEnv* env,
jobject obj,
jboolean jis_installing) {
if (!weak_infobar_ptr_)
return;
if (jis_installing) {
AppBannerSettingsHelper::RecordBannerEvent(
web_contents(),
web_contents()->GetURL(),
native_app_package_,
AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
base::Time::Now());
}
UpdateInstallState(env, obj);
}
void AppBannerManager::OnInstallFinished(JNIEnv* env,
jobject obj,
jboolean success) {
if (!weak_infobar_ptr_)
return;
if (success) {
UpdateInstallState(env, obj);
} else {
InfoBarService* service = InfoBarService::FromWebContents(web_contents());
service->RemoveInfoBar(weak_infobar_ptr_);
}
}
void AppBannerManager::UpdateInstallState(JNIEnv* env, jobject obj) {
if (!weak_infobar_ptr_ || native_app_data_.is_null())
return;
ScopedJavaLocalRef<jobject> jobj = weak_java_banner_view_manager_.get(env);
if (jobj.is_null())
return;
int newState = Java_AppBannerManager_determineInstallState(
env,
jobj.obj(),
native_app_data_.obj());
weak_infobar_ptr_->OnInstallStateChanged(newState);
}
bool AppBannerManager::FetchIcon(const GURL& image_url) { bool AppBannerManager::FetchIcon(const GURL& image_url) {
if (!web_contents()) if (!web_contents())
return false; return false;
......
...@@ -94,6 +94,17 @@ class AppBannerManager : public chrome::BitmapFetcherDelegate, ...@@ -94,6 +94,17 @@ class AppBannerManager : public chrome::BitmapFetcherDelegate,
jstring japp_package, jstring japp_package,
jstring jicon_url); jstring jicon_url);
// 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);
// Fetches the icon at the given URL asynchronously. // Fetches the icon at the given URL asynchronously.
// Returns |false| if this couldn't be kicked off. // Returns |false| if this couldn't be kicked off.
bool FetchIcon(const GURL& image_url); bool FetchIcon(const GURL& image_url);
...@@ -103,6 +114,9 @@ class AppBannerManager : public chrome::BitmapFetcherDelegate, ...@@ -103,6 +114,9 @@ class AppBannerManager : public chrome::BitmapFetcherDelegate,
static void InstallManifestApp(const content::Manifest& manifest, static void InstallManifestApp(const content::Manifest& manifest,
const SkBitmap& icon); const SkBitmap& icon);
// Called when the AppBannerInfoBar's button needs to be updated.
void UpdateInstallState(JNIEnv* env, jobject obj);
// WebContentsObserver overrides. // WebContentsObserver overrides.
void DidNavigateMainFrame( void DidNavigateMainFrame(
const content::LoadCommittedDetails& details, const content::LoadCommittedDetails& details,
...@@ -117,6 +131,7 @@ class AppBannerManager : public chrome::BitmapFetcherDelegate, ...@@ -117,6 +131,7 @@ class AppBannerManager : public chrome::BitmapFetcherDelegate,
// AppBannerInfoBarDelegate::AppDelegate overrides. // AppBannerInfoBarDelegate::AppDelegate overrides.
void Block() const override; void Block() const override;
bool OnButtonClicked() const override; bool OnButtonClicked() const override;
bool OnLinkClicked() const override;
void OnInfoBarDestroyed() override; void OnInfoBarDestroyed() override;
base::string16 GetTitle() const override; base::string16 GetTitle() const override;
gfx::Image GetIcon() const override; gfx::Image GetIcon() const override;
......
...@@ -75,6 +75,13 @@ AppBannerInfoBar::CreateRenderInfoBar(JNIEnv* env) { ...@@ -75,6 +75,13 @@ AppBannerInfoBar::CreateRenderInfoBar(JNIEnv* env) {
return infobar; 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 --------------------------------------------------------- // Native JNI methods ---------------------------------------------------------
bool RegisterAppBannerInfoBarDelegate(JNIEnv* env) { bool RegisterAppBannerInfoBarDelegate(JNIEnv* env) {
......
...@@ -29,6 +29,11 @@ class AppBannerInfoBar : public ConfirmInfoBar { ...@@ -29,6 +29,11 @@ class AppBannerInfoBar : public ConfirmInfoBar {
~AppBannerInfoBar() override; ~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: private:
// InfoBarAndroid overrides. // InfoBarAndroid overrides.
base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar( base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
......
...@@ -18,13 +18,13 @@ class ConfirmInfoBar : public InfoBarAndroid { ...@@ -18,13 +18,13 @@ class ConfirmInfoBar : public InfoBarAndroid {
protected: protected:
base::string16 GetTextFor(ConfirmInfoBarDelegate::InfoBarButton button); base::string16 GetTextFor(ConfirmInfoBarDelegate::InfoBarButton button);
ConfirmInfoBarDelegate* GetDelegate(); ConfirmInfoBarDelegate* GetDelegate();
void OnLinkClicked(JNIEnv* env, jobject obj) override;
// InfoBarAndroid overrides. // InfoBarAndroid overrides.
base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar( base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
JNIEnv* env) override; JNIEnv* env) override;
private: private:
void OnLinkClicked(JNIEnv* env, jobject obj) override;
void ProcessButton(int action, const std::string& action_value) override; void ProcessButton(int action, const std::string& action_value) override;
base::android::ScopedJavaGlobalRef<jobject> java_confirm_delegate_; base::android::ScopedJavaGlobalRef<jobject> java_confirm_delegate_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment