// Copyright 2014 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.banners;
import android.content.Context;
import android.text.TextUtils;
import org.chromium.base.ContextUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.ShortcutHelper;
import org.chromium.content_public.browser.WebContents;
/**
* Manages an AppBannerInfoBar for a Tab.
*
* The AppBannerManager is responsible for fetching details about native apps to display in the
* banner. The actual observation of the WebContents (which triggers the automatic creation and
* removal of banners, among other things) is done by the native-side AppBannerManagerAndroid.
*/
@JNINamespace("banners")
public class AppBannerManager {
private static final String TAG = "AppBannerManager";
/** Retrieves information about a given package. */
private static AppDetailsDelegate sAppDetailsDelegate;
/** Whether add to home screen is permitted by the system. */
private static Boolean sIsSupported;
/** Whether the tab to which this manager is attached to is permitted to show banners. */
private boolean mIsEnabledForTab;
/** Pointer to the native side AppBannerManager. */
private long mNativePointer;
/**
* Checks if the add to home screen intent is supported.
* @return true if add to home screen is supported, false otherwise.
*/
public static boolean isSupported() {
if (sIsSupported == null) {
Context context = ContextUtils.getApplicationContext();
sIsSupported = ShortcutHelper.isAddToHomeIntentSupported(context);
}
return sIsSupported;
}
/**
* Checks if app banners are enabled for the tab which this manager is attached to.
* @return true if app banners can be shown for this tab, false otherwise.
*/
@CalledByNative
private boolean isEnabledForTab() {
return isSupported() && mIsEnabledForTab;
}
/**
* Sets the delegate that provides information about a given package.
* @param delegate Delegate to use. Previously set ones are destroyed.
*/
public static void setAppDetailsDelegate(AppDetailsDelegate delegate) {
if (sAppDetailsDelegate != null) sAppDetailsDelegate.destroy();
sAppDetailsDelegate = delegate;
}
/**
* Constructs an AppBannerManager.
* @param nativePointer the native-side object that owns this AppBannerManager.
*/
private AppBannerManager(long nativePointer) {
mNativePointer = nativePointer;
mIsEnabledForTab = isSupported();
}
@CalledByNative
private static AppBannerManager create(long nativePointer) {
return new AppBannerManager(nativePointer);
}
@CalledByNative
private void destroy() {
mNativePointer = 0;
}
/**
* Grabs package information for the banner asynchronously.
* @param url URL for the page that is triggering the banner.
* @param packageName Name of the package that is being advertised.
*/
@CalledByNative
private void fetchAppDetails(
String url, String packageName, String referrer, int iconSizeInDp) {
if (sAppDetailsDelegate == null) return;
Context context = ContextUtils.getApplicationContext();
int iconSizeInPx = Math.round(
context.getResources().getDisplayMetrics().density * iconSizeInDp);
sAppDetailsDelegate.getAppDetailsAsynchronously(
createAppDetailsObserver(), url, packageName, referrer, iconSizeInPx);
}
private AppDetailsDelegate.Observer createAppDetailsObserver() {
return new AppDetailsDelegate.Observer() {
/**
* Called when data about the package has been retrieved, which includes the url for the
* app's icon but not the icon Bitmap itself.
* @param data Data about the app. Null if the task failed.
*/
@Override
public void onAppDetailsRetrieved(AppData data) {
if (data == null || mNativePointer == 0) return;
String imageUrl = data.imageUrl();
if (TextUtils.isEmpty(imageUrl)) return;
nativeOnAppDetailsRetrieved(
mNativePointer, data, data.title(), data.packageName(), data.imageUrl());
}
};
}
/** Enables or disables app banners. */
public void setIsEnabledForTab(boolean state) {
mIsEnabledForTab = state;
}
/** Overrides whether the system supports add to home screen. Used in testing. */
@VisibleForTesting
public static void setIsSupported(boolean state) {
sIsSupported = state;
}
/** Sets a constant (in days) that gets added to the time when the current time is requested. */
@VisibleForTesting
static void setTimeDeltaForTesting(int days) {
nativeSetTimeDeltaForTesting(days);
}
/** Disables the HTTPS scheme requirement for testing. */
@VisibleForTesting
static void disableSecureSchemeCheckForTesting() {
nativeDisableSecureSchemeCheckForTesting();
}
/** Sets the weights of direct and indirect page navigations for testing. */
@VisibleForTesting
static void setEngagementWeights(double directEngagement, double indirectEngagement) {
nativeSetEngagementWeights(directEngagement, indirectEngagement);
}
/** Returns whether the native AppBannerManager is working. */
@VisibleForTesting
public boolean isActiveForTesting() {
return nativeIsActiveForTesting(mNativePointer);
}
/** Returns the AppBannerManager object. This is owned by the C++ banner manager. */
public static AppBannerManager getAppBannerManagerForWebContents(WebContents webContents) {
return nativeGetJavaBannerManagerForWebContents(webContents);
}
private static native AppBannerManager nativeGetJavaBannerManagerForWebContents(
WebContents webContents);
private native boolean nativeOnAppDetailsRetrieved(long nativeAppBannerManagerAndroid,
AppData data, String title, String packageName, String imageUrl);
// Testing methods.
private static native void nativeSetTimeDeltaForTesting(int days);
private static native void nativeDisableSecureSchemeCheckForTesting();
private static native void nativeSetEngagementWeights(double directEngagement,
double indirectEngagement);
private native boolean nativeIsActiveForTesting(long nativeAppBannerManagerAndroid);
}