Commit 56a3dba9 authored by dfalcantara's avatar dfalcantara Committed by Commit bot

Start piping Android app promos through AppBannerInfoBars

* Use AppBannerInfoBars instead of AppBannerViews for Android
  app promos.

- Button doesn't install anything.

- Layout is still incorrect.

BUG=453170

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

Cr-Commit-Position: refs/heads/master@{#315104}
parent f0fdb821
......@@ -4,7 +4,6 @@
package org.chromium.chrome.browser.banners;
import android.app.PendingIntent;
import android.text.TextUtils;
import org.chromium.base.ApplicationStatus;
......@@ -13,23 +12,21 @@ import org.chromium.base.JNINamespace;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.EmptyTabObserver;
import org.chromium.chrome.browser.Tab;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.content_public.browser.WebContents;
/**
* Manages an AppBannerView for a Tab and its ContentView.
* Manages an AppBannerInfoBar for a Tab.
*
* The AppBannerManager manages a single AppBannerView, dismissing it when the user navigates to a
* new page or creating a new one when it detects that the current webpage is requesting a banner to
* be built. The actual observation of the WebContents (which triggers the automatic creation and
* removal of banners, among other things) is done by the native-side AppBannerManager.
* The AppBannerManager manages a single AppBannerInfoBar, creating a new one when it detects that
* the current webpage is requesting a banner to be built. The actual observation of the WebContents
* (which triggers the automatic creation and removal of banners, among other things) is done by the
* native-side AppBannerManager.
*
* This Java-side class owns its native-side counterpart, which is basically used to grab resources
* from the network.
*/
@JNINamespace("banners")
public class AppBannerManager extends EmptyTabObserver
implements AppBannerView.Observer, AppDetailsDelegate.Observer {
public class AppBannerManager extends EmptyTabObserver implements AppDetailsDelegate.Observer {
private static final String TAG = "AppBannerManager";
/** Retrieves information about a given package. */
......@@ -41,15 +38,6 @@ public class AppBannerManager extends EmptyTabObserver
/** Tab that the AppBannerView/AppBannerManager is owned by. */
private final Tab mTab;
/** ContentViewCore that the AppBannerView/AppBannerManager is currently attached to. */
private ContentViewCore mContentViewCore;
/** Current banner being shown. */
private AppBannerView mBannerView;
/** Data about the app being advertised. */
private AppData mAppData;
/**
* Checks if app banners are enabled.
* @return True if banners are enabled, false otherwise.
......@@ -93,15 +81,12 @@ public class AppBannerManager extends EmptyTabObserver
*/
public void destroy() {
nativeDestroy(mNativePointer);
resetState();
}
/**
* Updates which ContentView and WebContents the AppBannerView is monitoring.
* Updates which WebContents the native AppBannerManager is monitoring.
*/
private void updatePointers() {
if (mContentViewCore != mTab.getContentViewCore())
mContentViewCore = mTab.getContentViewCore();
nativeReplaceWebContents(mNativePointer, mTab.getWebContents());
}
......@@ -117,9 +102,8 @@ public class AppBannerManager extends EmptyTabObserver
* @param packageName Name of the package that is being advertised.
*/
@CalledByNative
private void prepareBanner(String url, String packageName) {
if (sAppDetailsDelegate == null || !isBannerForCurrentPage(url)) return;
private void fetchAppDetails(String url, String packageName) {
if (sAppDetailsDelegate == null) return;
int iconSize = getPreferredIconSize();
sAppDetailsDelegate.getAppDetailsAsynchronously(this, url, packageName, iconSize);
}
......@@ -131,63 +115,13 @@ public class AppBannerManager extends EmptyTabObserver
*/
@Override
public void onAppDetailsRetrieved(AppData data) {
if (data == null || !isBannerForCurrentPage(data.siteUrl())) return;
if (data == null) return;
mAppData = data;
String imageUrl = data.imageUrl();
if (TextUtils.isEmpty(imageUrl) || !nativeFetchIcon(mNativePointer, imageUrl)) resetState();
}
@Override
public void onBannerRemoved(AppBannerView banner) {
if (mBannerView != banner) return;
resetState();
}
@Override
public void onBannerBlocked(AppBannerView banner, String url, String packageName) {
if (mBannerView != banner) return;
nativeBlockBanner(mNativePointer, url, packageName);
}
@Override
public void onBannerDismissEvent(AppBannerView banner, int eventType) {
if (mBannerView != banner) return;
nativeRecordDismissEvent(eventType);
}
if (TextUtils.isEmpty(imageUrl)) return;
@Override
public void onBannerInstallEvent(AppBannerView banner, int eventType) {
if (mBannerView != banner) return;
nativeRecordInstallEvent(eventType);
}
@Override
public boolean onFireIntent(AppBannerView banner, PendingIntent intent) {
if (mBannerView != banner) return false;
return mTab.getWindowAndroid().showIntent(intent, banner, R.string.low_memory_error);
}
/**
* Resets all of the state, killing off any running tasks.
*/
private void resetState() {
if (mBannerView != null) {
mBannerView.destroy();
mBannerView = null;
}
mAppData = null;
}
/**
* Checks to see if the banner is for the currently displayed page.
* @param bannerUrl URL that requested a banner.
* @return True if the user is still on the same page.
*/
private boolean isBannerForCurrentPage(String bannerUrl) {
return mContentViewCore != null
&& TextUtils.equals(mContentViewCore.getWebContents().getUrl(), bannerUrl);
nativeOnAppDetailsRetrieved(
mNativePointer, data, data.title(), data.packageName(), data.imageUrl());
}
private static native boolean nativeIsEnabled();
......@@ -195,9 +129,8 @@ public class AppBannerManager extends EmptyTabObserver
private native void nativeDestroy(long nativeAppBannerManager);
private native void nativeReplaceWebContents(long nativeAppBannerManager,
WebContents webContents);
private native void nativeBlockBanner(
long nativeAppBannerManager, String url, String packageName);
private native boolean nativeFetchIcon(long nativeAppBannerManager, String imageUrl);
private native boolean nativeOnAppDetailsRetrieved(long nativeAppBannerManager, AppData data,
String title, String packageName, String imageUrl);
// UMA tracking.
private static native void nativeRecordDismissEvent(int metric);
......
......@@ -61,6 +61,8 @@ import org.chromium.ui.base.WindowAndroid.IntentCallback;
* The layout of the banner, which includes its widget sizes, may change when the screen is rotated
* to account for less screen real estate. This means that all of the View's widgets and cached
* dimensions must be rebuilt from scratch.
*
* TODO(dfalcantara): Nuke this file.
*/
public class AppBannerView extends SwipableOverlayView
implements View.OnClickListener, InstallerDelegate.Observer, IntentCallback {
......@@ -244,9 +246,7 @@ public class AppBannerView extends SwipableOverlayView
// Configure the controls with the package information.
mTitleView.setText(mAppData.title());
mIconView.setImageDrawable(mAppData.icon());
mRatingView.initialize(mAppData.rating());
setAccessibilityInformation();
// Determine how much the user can drag sideways before their touch is considered a scroll.
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
......@@ -255,15 +255,6 @@ public class AppBannerView extends SwipableOverlayView
updateButtonStatus();
}
/**
* Creates a succinct description about the app being advertised.
*/
private void setAccessibilityInformation() {
String bannerText = getContext().getString(
R.string.app_banner_view_accessibility, mAppData.title(), mAppData.rating());
setContentDescription(bannerText);
}
@Override
public void onClick(View view) {
if (mObserver == null) return;
......
......@@ -5,7 +5,6 @@
package org.chromium.chrome.browser.banners;
import android.app.PendingIntent;
import android.graphics.drawable.Drawable;
/**
* Stores information about a particular app.
......@@ -23,9 +22,6 @@ public class AppData {
private PendingIntent mDetailsIntent;
private PendingIntent mInstallIntent;
// Data that can be updated asynchronously.
private Drawable mIcon;
/**
* Creates a new AppData for the given page and package.
* @param siteUrl URL for the site requesting the banner.
......@@ -56,7 +52,7 @@ public class AppData {
* Returns the title to display for the app in the banner.
* @return The String to display.
*/
String title() {
public String title() {
return mTitle;
}
......@@ -68,19 +64,11 @@ public class AppData {
return mImageUrl;
}
/**
* Returns the Drawable depicting the app's icon.
* @return The Drawable to use as the app icon.
*/
Drawable icon() {
return mIcon;
}
/**
* Returns how well the app was rated, on a scale from 0 to 5.
* @return The rating of the app.
*/
float rating() {
public float rating() {
return mRating;
}
......@@ -88,7 +76,7 @@ public class AppData {
* Returns text to display on the install button when the app is not installed on the system.
* @return The String to display.
*/
String installButtonText() {
public String installButtonText() {
return mInstallButtonText;
}
......@@ -97,7 +85,7 @@ public class AppData {
* The IntentSender stored inside dictates what package needs to be launched.
* @return Intent that triggers the details page.
*/
PendingIntent detailsIntent() {
public PendingIntent detailsIntent() {
return mDetailsIntent;
}
......@@ -128,12 +116,4 @@ public class AppData {
mDetailsIntent = detailsIntent;
mInstallIntent = installIntent;
}
/**
* Sets the icon used to depict the app.
* @param Drawable App icon in Drawable form.
*/
void setIcon(Drawable icon) {
mIcon = icon;
}
}
......@@ -4,41 +4,79 @@
package org.chromium.chrome.browser.infobar;
import android.content.Context;
import android.graphics.Bitmap;
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 {
/** Web app: URL pointing to the web app. */
private final String mAppTitle;
// Data for native app installs.
private final AppData mAppData;
// 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;
}
// Banner for web apps.
public AppBannerInfoBar(long nativeInfoBar, String appTitle, Bitmap iconBitmap, String url) {
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;
}
@Override
public void createContent(InfoBarLayout layout) {
TextView url = new TextView(layout.getContext());
url.setText(mAppUrl);
layout.setCustomContent(url);
if (mAppUrl != null) {
TextView url = new TextView(layout.getContext());
url.setText(mAppUrl);
layout.setCustomContent(url);
}
super.createContent(layout);
// Set up accessibility text.
Context context = getContext();
if (mAppData != null) {
layout.setContentDescription(context.getString(
R.string.app_banner_view_native_app_accessibility, mAppTitle,
mAppData.rating()));
} else {
layout.setContentDescription(context.getString(
R.string.app_banner_view_web_app_accessibility, mAppTitle,
mAppUrl));
}
}
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);
}
}
\ No newline at end of file
}
......@@ -829,7 +829,10 @@ You are signing in with a managed account and giving its administrator control o
</message>
<!-- App banner accessibility strings, used for touch exploration -->
<message name="IDS_APP_BANNER_VIEW_ACCESSIBILITY" desc="Accessibililty text: Describes the banner content, including the app name and rating.">
<message name="IDS_APP_BANNER_VIEW_WEB_APP_ACCESSIBILITY" desc="Accessibililty text: Describes the banner content, including the app name and its URL.">
Prompt to add page to the homescreen. App name: <ph name="APP_NAME">%1$s<ex>Gmail</ex></ph>. App URL: <ph name="APP_URL">%2$s<ex>http://gmail.com</ex></ph>.
</message>
<message name="IDS_APP_BANNER_VIEW_NATIVE_APP_ACCESSIBILITY" desc="Accessibililty text: Describes the banner content, including the app name and rating.">
Prompt to get app from the Google Play Store. App name: <ph name="APP_NAME">%1$s<ex>Gmail</ex></ph>. App average rating: <ph name="APP_RATING">%2$.1f<ex>4.2</ex></ph>.
</message>
<message name="IDS_APP_BANNER_INSTALL_ACCESSIBILITY" desc="Accessibility text: Indicates that clicking on the button will purchase the app or install it.">
......
......@@ -14,6 +14,18 @@
namespace banners {
// static
AppBannerInfoBar* AppBannerInfoBarDelegate::CreateForNativeApp(
infobars::InfoBarManager* infobar_manager,
AppDelegate* app_delegate,
const base::android::ScopedJavaGlobalRef<jobject>& japp_data) {
scoped_ptr<AppBannerInfoBarDelegate> delegate(new AppBannerInfoBarDelegate(
app_delegate));
AppBannerInfoBar* infobar = new AppBannerInfoBar(delegate.Pass(), japp_data);
return infobar_manager->AddInfoBar(make_scoped_ptr(infobar))
? infobar : nullptr;
}
// static
AppBannerInfoBar* AppBannerInfoBarDelegate::CreateForWebApp(
infobars::InfoBarManager* infobar_manager,
......
......@@ -43,6 +43,13 @@ class AppBannerInfoBarDelegate : public ConfirmInfoBarDelegate {
virtual gfx::Image GetIcon() const = 0;
};
// Creates a banner for the current page that promotes a native app.
// May return nullptr if the the infobar couldn't be created.
static AppBannerInfoBar* CreateForNativeApp(
infobars::InfoBarManager* infobar_manager,
AppDelegate* delegate,
const base::android::ScopedJavaGlobalRef<jobject>& japp_data);
// Creates a banner for the current page that promotes a web app.
// May return nullptr if the the infobar couldn't be created.
static AppBannerInfoBar* CreateForWebApp(
......
......@@ -78,7 +78,12 @@ void AppBannerManager::Block() const {
if (!web_contents())
return;
if (!web_app_data_.IsEmpty()) {
if (!native_app_data_.is_null()) {
AppBannerSettingsHelper::RecordBannerEvent(
web_contents(), web_contents()->GetURL(),
native_app_package_,
AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK, base::Time::Now());
} else if (!web_app_data_.IsEmpty()) {
AppBannerSettingsHelper::RecordBannerEvent(
web_contents(), web_contents()->GetURL(),
web_app_data_.start_url.spec(),
......@@ -132,6 +137,8 @@ void AppBannerManager::DidNavigateMainFrame(
app_title_ = base::string16();
app_icon_.reset();
web_app_data_ = content::Manifest();
native_app_data_.Reset();
native_app_package_ = std::string();
}
void AppBannerManager::DidFinishLoad(
......@@ -247,7 +254,12 @@ void AppBannerManager::OnFetchComplete(const GURL url, const SkBitmap* bitmap) {
InfoBarService* service = InfoBarService::FromWebContents(web_contents());
weak_infobar_ptr_ = nullptr;
if (!web_app_data_.IsEmpty()){
if (!native_app_data_.is_null()) {
weak_infobar_ptr_ = AppBannerInfoBarDelegate::CreateForNativeApp(
service,
this,
native_app_data_);
} else if (!web_app_data_.IsEmpty()){
weak_infobar_ptr_ = AppBannerInfoBarDelegate::CreateForWebApp(
service,
this,
......@@ -285,16 +297,25 @@ void AppBannerManager::OnDidRetrieveMetaTagContent(
ConvertUTF8ToJavaString(env, expected_url.spec()));
ScopedJavaLocalRef<jstring> jpackage(
ConvertUTF8ToJavaString(env, tag_content));
Java_AppBannerManager_prepareBanner(env,
jobj.obj(),
jurl.obj(),
jpackage.obj());
Java_AppBannerManager_fetchAppDetails(env,
jobj.obj(),
jurl.obj(),
jpackage.obj());
}
bool AppBannerManager::FetchIcon(JNIEnv* env,
jobject obj,
jstring jimage_url) {
std::string image_url = ConvertJavaStringToUTF8(env, jimage_url);
bool AppBannerManager::OnAppDetailsRetrieved(JNIEnv* env,
jobject obj,
jobject japp_data,
jstring japp_title,
jstring japp_package,
jstring jicon_url) {
if (validated_url_ != web_contents()->GetURL())
return false;
std::string image_url = ConvertJavaStringToUTF8(env, jicon_url);
app_title_ = ConvertJavaStringToUTF16(env, japp_title);
native_app_package_ = ConvertJavaStringToUTF8(env, japp_package);
native_app_data_.Reset(env, japp_data);
return FetchIcon(GURL(image_url));
}
......
......@@ -85,11 +85,14 @@ class AppBannerManager : public chrome::BitmapFetcherDelegate,
jobject obj,
jobject jweb_contents);
// Fetches the icon at the given URL asynchronously.
// Returns |false| if this couldn't be kicked off.
bool FetchIcon(JNIEnv* env,
jobject obj,
jstring jimage_url);
// Called when the Java-side has retrieved information for the app.
// Returns |false| if an icon fetch couldn't be kicked off.
bool OnAppDetailsRetrieved(JNIEnv* env,
jobject obj,
jobject japp_data,
jstring japp_title,
jstring japp_package,
jstring jicon_url);
// Fetches the icon at the given URL asynchronously.
// Returns |false| if this couldn't be kicked off.
......@@ -155,6 +158,9 @@ class AppBannerManager : public chrome::BitmapFetcherDelegate,
content::Manifest web_app_data_;
base::android::ScopedJavaGlobalRef<jobject> native_app_data_;
std::string native_app_package_;
// Weak pointer to the InfoBar that is being managed.
AppBannerInfoBar* weak_infobar_ptr_;
......
......@@ -13,6 +13,14 @@
#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)
......@@ -38,22 +46,30 @@ AppBannerInfoBar::CreateRenderInfoBar(JNIEnv* env) {
}
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);
// 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);
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()));
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;
......
......@@ -17,6 +17,11 @@ class AppBannerInfoBarDelegate;
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,
......@@ -29,6 +34,9 @@ class AppBannerInfoBar : public ConfirmInfoBar {
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_;
......
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