Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4295)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java

Issue 1505913003: Add update menu item and app menu icon badge (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rearrange field_trial.. again to facilitate command line testing Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..999689752532cfa22b2d76310893de515559930d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
@@ -0,0 +1,319 @@
+// 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.omaha;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.text.TextUtils;
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.Log;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.appmenu.AppMenu;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.components.variations.VariationsAssociatedData;
+import org.chromium.ui.base.LocalizationUtils;
+
+/**
+ * Contains logic for whether the update menu item should be shown, whether the update toolbar badge
+ * should be shown, and UMA logging for the update menu item.
+ */
+public class UpdateMenuItemHelper {
+ private static final String TAG = "UpdateMenuItemHelper";
+
+ // VariationsAssociatedData configs
+ private static final String FIELD_TRIAL_NAME = "UpdateMenuItem";
+ private static final String ENABLED_VALUE = "true";
+ private static final String ENABLE_UPDATE_MENU_ITEM = "enable_update_menu_item";
+ private static final String ENABLE_UPDATE_BADGE = "enable_update_badge";
+ private static final String SHOW_SUMMARY = "show_summary";
+ private static final String CUSTOM_SUMMARY = "custom_summary";
+
+ // UMA constants for logging whether the menu item was clicked.
+ private static final int ITEM_NOT_CLICKED = 0;
+ private static final int ITEM_CLICKED_INTENT_LAUNCHED = 1;
+ private static final int ITEM_CLICKED_INTENT_FAILED = 2;
+ private static final int ITEM_CLICKED_BOUNDARY = 3;
+
+ // UMA constants for logging whether Chrome was updated after the menu item was clicked.
+ private static final int UPDATED = 0;
+ private static final int NOT_UPDATED = 1;
+ private static final int UPDATED_BOUNDARY = 2;
+
+ private static UpdateMenuItemHelper sInstance;
+ private static Object sGetInstanceLock = new Object();
+
+ // Whether OmahaClient has already been checked for an update.
+ private boolean mAlreadyCheckedForUpdates;
+
+ // Whether an update is available.
+ private boolean mUpdateAvailable;
+
+ // URL to direct the user to when Omaha detects a newer version available.
+ private String mUpdateUrl;
+
+ // Whether the menu item was clicked. This is used to log the click-through rate.
+ private boolean mMenuItemClicked;
+
+ // The bitmap to use for the menu button when the badge is showing.
+ private Bitmap mBadgedMenuButtonBitmap;
+
+ /**
+ * @return The {@link UpdateMenuItemHelper} instance.
+ */
+ public static UpdateMenuItemHelper getInstance() {
+ synchronized (UpdateMenuItemHelper.sGetInstanceLock) {
+ if (sInstance == null) {
+ sInstance = new UpdateMenuItemHelper();
+ String testMarketUrl = getStringParamValue(ChromeSwitches.MARKET_URL_FOR_TESTING);
+ if (!TextUtils.isEmpty(testMarketUrl)) {
+ sInstance.mUpdateUrl = testMarketUrl;
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Checks if the {@link OmahaClient} knows about an update.
+ * @param activity The current {@link ChromeActivity}.
+ */
+ public void checkForUpdateOnBackgroundThread(final ChromeActivity activity) {
+ if (!getBooleanParam(ENABLE_UPDATE_MENU_ITEM)) return;
+
+ ThreadUtils.assertOnUiThread();
+
+ if (mAlreadyCheckedForUpdates) {
+ if (activity.isActivityDestroyed()) return;
+ activity.onCheckForUpdate(mUpdateAvailable);
+ return;
+ }
+
+ mAlreadyCheckedForUpdates = true;
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (OmahaClient.isNewerVersionAvailable(activity)) {
+ mUpdateUrl = OmahaClient.getMarketURL(activity);
+ mUpdateAvailable = true;
+ } else {
+ mUpdateAvailable = false;
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (activity.isActivityDestroyed()) return;
+ activity.onCheckForUpdate(mUpdateAvailable);
+ recordUpdateHistogram();
+ }
+ }.execute();
+ }
+
+ /**
+ * Logs whether an update was performed if the update menu item was clicked.
+ * Should be called from ChromeActivity#onStart().
+ */
+ public void onStart() {
+ if (mAlreadyCheckedForUpdates) {
+ recordUpdateHistogram();
+ }
+ }
+
+ /**
+ * @param activity The current {@link ChromeActivity}.
+ * @return Whether the update menu item should be shown.
+ */
+ public boolean shouldShowMenuItem(ChromeActivity activity) {
+ if (getBooleanParam(ChromeSwitches.FORCE_SHOW_UPDATE_MENU_ITEM)) {
+ return true;
+ }
+
+ if (!getBooleanParam(ENABLE_UPDATE_MENU_ITEM)) {
+ return false;
+ }
+
+ return updateAvailable(activity);
+ }
+
+ /**
+ * @param context The current {@link Context}.
+ * @return The string to use for summary text or the empty string if no summary should be shown.
+ */
+ public String getMenuItemSummaryText(Context context) {
+ if (!getBooleanParam(SHOW_SUMMARY)) {
+ return "";
+ }
+
+ String customSummary = getStringParamValue(CUSTOM_SUMMARY);
+ if (!TextUtils.isEmpty(customSummary)) {
+ return customSummary;
+ }
+
+ return context.getResources().getString(R.string.menu_update_summary_default);
+ }
+
+ /**
+ * @param activity The current {@link ChromeActivity}.
+ * @return Whether the update badge should be shown in the toolbar.
+ */
+ public boolean shouldShowToolbarBadge(ChromeActivity activity) {
+ if (getBooleanParam(ChromeSwitches.FORCE_SHOW_UPDATE_MENU_BADGE)) {
+ return true;
+ }
+
+ if (!getBooleanParam(ENABLE_UPDATE_BADGE)) {
+ return false;
+ }
+
+ return updateAvailable(activity);
+ }
+
+ /**
+ * Handles a click on the update menu item.
+ * @param activity The current {@link ChromeActivity}.
+ */
+ public void onMenuItemClicked(ChromeActivity activity) {
+ if (mUpdateUrl == null) return;
+
+ // Fire an intent to open the URL.
+ try {
+ Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUpdateUrl));
+ activity.startActivity(launchIntent);
+ recordItemClickedHistogram(ITEM_CLICKED_INTENT_LAUNCHED);
+ PrefServiceBridge.getInstance().setClickedUpdateMenuItem(true);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Failed to launch Activity for: " + mUpdateUrl);
+ recordItemClickedHistogram(ITEM_CLICKED_INTENT_FAILED);
+ }
+ }
+
+ /**
+ * Returns a {@link Bitmap} to use for the menu button with part of the original image removed
+ * to simulate a 1dp transparent border around the update badge.
+ *
+ * @param context The current {@link Context}.
+ * @return The {@link Bitmap} to use for the the menu button when showing the update badge.
+ */
+ public Bitmap getBadgedMenuButtonBitmap(Context context) {
+ if (mBadgedMenuButtonBitmap == null) {
+ // Punch a hole in the app menu button to create the illusion of a 1dp transparent
+ // border around the update badge.
+ // Load btn_menu bitmap and use it to initialize a canvas.
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inMutable = true;
+ mBadgedMenuButtonBitmap = BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.btn_menu, opts);
+ Canvas canvas = new Canvas();
+ canvas.setBitmap(mBadgedMenuButtonBitmap);
+
+ // Calculate the dimensions and offsets for the update badge.
+ float menuBadgeBackgroundSize = context.getResources().getDimension(
+ R.dimen.menu_badge_background_size);
+ float radius = menuBadgeBackgroundSize / 2.f;
+ // The design specification calls for 4.5dp right offset and 9dp top offset.
+ float xPos = 4.5f * context.getResources().getDisplayMetrics().density + radius;
+ float yPos = 9f * context.getResources().getDisplayMetrics().density + radius;
+
+ if (LocalizationUtils.isLayoutRtl()) {
+ // In RTL layouts, the badge should be placed to the left of the menu button icon.
+ xPos = mBadgedMenuButtonBitmap.getWidth() - xPos;
+ }
+
+ // Draw a transparent circle on top of the bitmap, creating a hole.
+ Paint paint = new Paint();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ canvas.drawCircle(xPos, yPos, radius, paint);
+ }
+
+ return mBadgedMenuButtonBitmap;
+ }
+
+ /**
+ * Should be called before the AppMenu is dismissed if the update menu item was clicked.
+ */
+ public void setMenuItemClicked() {
+ mMenuItemClicked = true;
+ }
+
+ /**
+ * Called when the {@link AppMenu} is dimissed. Logs a histogram immediately if the update menu
+ * item was not clicked. If it was clicked, logging is delayed until #onMenuItemClicked().
+ */
+ public void onMenuDismissed() {
+ if (!mMenuItemClicked) {
+ recordItemClickedHistogram(ITEM_NOT_CLICKED);
+ }
+ mMenuItemClicked = false;
+ }
+
+ private boolean updateAvailable(ChromeActivity activity) {
+ if (!mAlreadyCheckedForUpdates) {
+ checkForUpdateOnBackgroundThread(activity);
+ return false;
+ }
+
+ return mUpdateAvailable;
+ }
+
+ private void recordItemClickedHistogram(int action) {
+ RecordHistogram.recordEnumeratedHistogram("GoogleUpdate.MenuItem.ActionTakenOnMenuOpen",
+ action, ITEM_CLICKED_BOUNDARY);
+ }
+
+ private void recordUpdateHistogram() {
+ if (PrefServiceBridge.getInstance().getClickedUpdateMenuItem()) {
+ RecordHistogram.recordEnumeratedHistogram(
+ "GoogleUpdate.MenuItem.ActionTakenAfterItemClicked",
+ mUpdateAvailable ? NOT_UPDATED : UPDATED, UPDATED_BOUNDARY);
+ PrefServiceBridge.getInstance().setClickedUpdateMenuItem(false);
+ }
+ }
+
+ /**
+ * Gets a boolean VariationsAssociatedData parameter, assuming the <paramName>="true" format.
+ * Also checks for a command-line switch with the same name, for easy local testing.
+ * @param paramName The name of the parameter (or command-line switch) to get a value for.
+ * @return Whether the param is defined with a value "true", if there's a command-line
+ * flag present with any value.
+ */
+ private static boolean getBooleanParam(String paramName) {
+ if (CommandLine.getInstance().hasSwitch(paramName)) {
+ return true;
+ }
+ return TextUtils.equals(ENABLED_VALUE,
+ VariationsAssociatedData.getVariationParamValue(FIELD_TRIAL_NAME, paramName));
+ }
+
+ /**
+ * Gets a String VariationsAssociatedData parameter. Also checks for a command-line switch with
+ * the same name, for easy local testing.
+ * @param paramName The name of the parameter (or command-line switch) to get a value for.
+ * @return The command-line flag value if present, or the param is value if present.
+ */
+ private static String getStringParamValue(String paramName) {
+ String value = CommandLine.getInstance().getSwitchValue(paramName);
+ if (TextUtils.isEmpty(value)) {
+ value = VariationsAssociatedData.getVariationParamValue(FIELD_TRIAL_NAME, paramName);
+ }
+ return value;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698