OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.chrome.browser.omaha; | |
6 | |
7 import android.content.ActivityNotFoundException; | |
8 import android.content.Context; | |
9 import android.content.Intent; | |
10 import android.graphics.Bitmap; | |
11 import android.graphics.BitmapFactory; | |
12 import android.graphics.Canvas; | |
13 import android.graphics.Paint; | |
14 import android.graphics.PorterDuff; | |
15 import android.graphics.PorterDuffXfermode; | |
16 import android.net.Uri; | |
17 import android.os.AsyncTask; | |
18 import android.text.TextUtils; | |
19 | |
20 import org.chromium.base.CommandLine; | |
21 import org.chromium.base.Log; | |
22 import org.chromium.base.ThreadUtils; | |
23 import org.chromium.base.metrics.RecordHistogram; | |
24 import org.chromium.chrome.R; | |
25 import org.chromium.chrome.browser.ChromeActivity; | |
26 import org.chromium.chrome.browser.ChromeSwitches; | |
27 import org.chromium.chrome.browser.appmenu.AppMenu; | |
28 import org.chromium.chrome.browser.preferences.PrefServiceBridge; | |
29 import org.chromium.components.variations.VariationsAssociatedData; | |
30 import org.chromium.ui.base.LocalizationUtils; | |
31 | |
32 /** | |
33 * Contains logic for whether the update menu item should be shown, whether the update toolbar badge | |
34 * should be shown, and UMA logging for the update menu item. | |
35 */ | |
36 public class UpdateMenuItemHelper { | |
37 private static UpdateMenuItemHelper sInstance; | |
38 private static Object sCreateInstanceLock = new Object(); | |
39 | |
40 private static final String TAG = "UpdateMenuItemHelper"; | |
gone
2015/12/10 21:45:09
nit: private static finals go above private static
Theresa
2015/12/11 19:44:46
Done.
| |
41 | |
42 // VariationsAssociatedData configs | |
43 private static final String FIELD_TRIAL_NAME = "UpdateMenuItem"; | |
44 private static final String ENABLED_VALUE = "true"; | |
45 private static final String ENABLE_UPDATE_MENU_ITEM = "enable_update_menu_it em"; | |
46 private static final String ENABLE_UPDATE_BADGE = "enable_update_badge"; | |
47 private static final String SHOW_SUMMARY = "show_summary"; | |
48 private static final String CUSTOM_SUMMARY = "custom_summary"; | |
49 | |
50 // UMA constants for logging whether the menu item was clicked. | |
51 private static final int ITEM_NOT_CLICKED = 0; | |
52 private static final int ITEM_CLICKED_INTENT_LAUNCHED = 1; | |
53 private static final int ITEM_CLICKED_INTENT_FAILED = 2; | |
54 private static final int ITEM_CLICKED_BOUNDARY = 3; | |
55 | |
56 // UMA constants for logging whether Chrome was updated after the menu item was clicked. | |
57 private static final int UPDATED = 0; | |
58 private static final int NOT_UPDATED = 1; | |
59 private static final int UPDATED_BOUNDARY = 2; | |
60 | |
61 // Whether OmahaClient has already been checked for an update. | |
62 private boolean mAlreadyCheckedForUpdates; | |
63 | |
64 // Whether an update is available. | |
65 private boolean mUpdateAvailable; | |
66 | |
67 // URL to direct the user to when Omaha detects a newer version available. | |
68 private String mUpdateUrl; | |
69 | |
70 // Whether the menu item was clicked. This is used to log the click-through rate. | |
71 private boolean mMenuItemClicked; | |
72 | |
73 // The bitmap to use for the menu button when the badge is showing. | |
74 private Bitmap mBadgedMenuButtonBitmap; | |
75 | |
76 /** | |
77 * @return The {@link UpdateMenuItemHelper} instance. | |
78 */ | |
79 public static UpdateMenuItemHelper getInstance() { | |
80 if (sInstance == null) { | |
81 synchronized (UpdateMenuItemHelper.sCreateInstanceLock) { | |
gone
2015/12/10 21:45:10
you should put the synchronized on the outside of
Theresa
2015/12/11 19:44:46
Done.
| |
82 sInstance = new UpdateMenuItemHelper(); | |
83 String testMarketUrl = getStringParamValue(ChromeSwitches.MARKET _URL_FOR_TESTING); | |
84 if (!TextUtils.isEmpty(testMarketUrl)) { | |
85 sInstance.mUpdateUrl = testMarketUrl; | |
86 } | |
87 } | |
88 } | |
89 return sInstance; | |
90 } | |
91 | |
92 /** | |
93 * Checks if the {@link OmahaClient} knows about an update. | |
94 * @param activity The current {@link ChromeActivity}. | |
95 */ | |
96 public void checkForUpdateOnBackgroundThread(final ChromeActivity activity) { | |
97 if (!getBooleanParam(ENABLE_UPDATE_MENU_ITEM)) return; | |
98 | |
99 ThreadUtils.assertOnUiThread(); | |
100 | |
101 if (mAlreadyCheckedForUpdates) { | |
102 if (activity.isActivityDestroyed()) return; | |
103 activity.onCheckForUpdate(mUpdateAvailable); | |
104 return; | |
105 } | |
106 | |
107 mAlreadyCheckedForUpdates = true; | |
108 | |
109 new AsyncTask<Void, Void, Void>() { | |
110 @Override | |
111 protected Void doInBackground(Void... params) { | |
112 if (OmahaClient.isNewerVersionAvailable(activity)) { | |
113 mUpdateUrl = OmahaClient.getMarketURL(activity); | |
114 mUpdateAvailable = true; | |
115 } else { | |
116 mUpdateAvailable = false; | |
117 } | |
118 return null; | |
119 } | |
120 | |
121 @Override | |
122 protected void onPostExecute(Void result) { | |
123 if (activity.isActivityDestroyed()) return; | |
124 activity.onCheckForUpdate(mUpdateAvailable); | |
125 recordUpdateHistogram(); | |
126 } | |
127 }.execute(); | |
128 } | |
129 | |
130 /** | |
131 * Logs whether an update was performed if the update menu item was clicked. | |
132 * Should be called from ChromeActivity#onStart(). | |
133 */ | |
134 public void onStart() { | |
135 if (mAlreadyCheckedForUpdates) { | |
136 recordUpdateHistogram(); | |
137 } | |
138 } | |
139 | |
140 /** | |
141 * @param activity The current {@link ChromeActivity}. | |
142 * @return Whether the update menu item should be shown. | |
143 */ | |
144 public boolean shouldShowMenuItem(ChromeActivity activity) { | |
145 if (getBooleanParam(ChromeSwitches.FORCE_SHOW_UPDATE_MENU_ITEM)) { | |
146 return true; | |
147 } | |
148 | |
149 if (!getBooleanParam(ENABLE_UPDATE_MENU_ITEM)) { | |
150 return false; | |
151 } | |
152 | |
153 return updateAvailable(activity); | |
154 } | |
155 | |
156 /** | |
157 * @param context The current {@link Context}. | |
158 * @return The string to use for summary text or the empty string if no summ ary should be shown. | |
159 */ | |
160 public String getMenuItemSummaryText(Context context) { | |
161 if (!getBooleanParam(SHOW_SUMMARY)) { | |
162 return ""; | |
163 } | |
164 | |
165 String customSummary = getStringParamValue(CUSTOM_SUMMARY); | |
166 if (!TextUtils.isEmpty(customSummary)) { | |
167 return customSummary; | |
168 } | |
169 | |
170 return context.getResources().getString(R.string.menu_update_summary_def ault); | |
171 } | |
172 | |
173 /** | |
174 * @param activity The current {@link ChromeActivity}. | |
175 * @return Whether the update badge should be shown in the toolbar. | |
176 */ | |
177 public boolean shouldShowToolbarBadge(ChromeActivity activity) { | |
178 if (getBooleanParam(ChromeSwitches.FORCE_SHOW_UPDATE_MENU_ITEM)) { | |
179 return true; | |
180 } | |
181 | |
182 if (!getBooleanParam(ENABLE_UPDATE_BADGE)) { | |
183 return false; | |
184 } | |
185 | |
186 return updateAvailable(activity); | |
187 } | |
188 | |
189 /** | |
190 * Handles a click on the update menu item. | |
191 * @param activity The current {@link ChromeActivity}. | |
192 */ | |
193 public void onMenuItemClicked(ChromeActivity activity) { | |
194 if (mUpdateUrl == null) return; | |
195 | |
196 // Fire an intent to open the URL. | |
197 try { | |
198 Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUpda teUrl)); | |
199 activity.startActivity(launchIntent); | |
200 recordItemClickedHistogram(ITEM_CLICKED_INTENT_LAUNCHED); | |
201 PrefServiceBridge.getInstance().setClickedUpdateMenuItem(true); | |
202 } catch (ActivityNotFoundException e) { | |
203 Log.e(TAG, "Failed to launch Activity for: " + mUpdateUrl); | |
204 recordItemClickedHistogram(ITEM_CLICKED_INTENT_FAILED); | |
205 } | |
206 } | |
207 | |
208 /** | |
209 * Returns a {@link Bitmap} to use for the menu button with part of the orig inal image removed | |
210 * to simulate a 1dp transparent border around the update badge. | |
211 * | |
212 * @param context The current {@link Context}. | |
213 * @return The {@link Bitmap} to use for the the menu button when showing th e update badge. | |
214 */ | |
215 public Bitmap getBadgedMenuButtonBitmap(Context context) { | |
216 if (mBadgedMenuButtonBitmap == null) { | |
217 // Punch a hole in the app menu button to create the illusion of a 1 dp transparent | |
218 // border around the update badge. | |
219 // Load btn_menu bitmap and use it to initialize a canvas. | |
220 BitmapFactory.Options opts = new BitmapFactory.Options(); | |
221 opts.inMutable = true; | |
222 mBadgedMenuButtonBitmap = BitmapFactory.decodeResource(context.getRe sources(), | |
223 R.drawable.btn_menu, opts); | |
224 Canvas canvas = new Canvas(); | |
225 canvas.setBitmap(mBadgedMenuButtonBitmap); | |
226 | |
227 // Calculate the dimensions and offsets for the update badge. | |
228 float menuBadgeBackgroundSize = context.getResources().getDimension( | |
229 R.dimen.menu_badge_background_size); | |
230 float radius = menuBadgeBackgroundSize / 2.f; | |
231 // The design specification calls for 4.5dp right offset and 9dp top offset. | |
232 float xPos = 4.5f * context.getResources().getDisplayMetrics().densi ty + radius; | |
233 float yPos = 9f * context.getResources().getDisplayMetrics().density + radius; | |
234 | |
235 if (LocalizationUtils.isLayoutRtl()) { | |
236 // In RTL layouts, the badge should be placed to the left of the menu button icon. | |
237 xPos = mBadgedMenuButtonBitmap.getWidth() - xPos; | |
238 } | |
239 | |
240 // Draw a transparent circle on top of the bitmap, creating a hole. | |
241 Paint paint = new Paint(); | |
242 paint.setFlags(Paint.ANTI_ALIAS_FLAG); | |
243 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); | |
244 canvas.drawCircle(xPos, yPos, radius, paint); | |
245 } | |
246 | |
247 return mBadgedMenuButtonBitmap; | |
248 } | |
249 | |
250 /** | |
251 * Should be called before the AppMenu is dismissed if the update menu item was clicked. | |
252 */ | |
253 public void setMenuItemClicked() { | |
254 mMenuItemClicked = true; | |
255 } | |
256 | |
257 /** | |
258 * Called when the {@link AppMenu} is dimissed. Logs a histogram immediately if the update menu | |
259 * item was not clicked. If it was clicked, logging is delayed until #onMenu ItemClicked(). | |
260 */ | |
261 public void onMenuDismissed() { | |
262 if (!mMenuItemClicked) { | |
263 recordItemClickedHistogram(ITEM_NOT_CLICKED); | |
264 } | |
265 mMenuItemClicked = false; | |
266 } | |
267 | |
268 private boolean updateAvailable(ChromeActivity activity) { | |
269 if (!mAlreadyCheckedForUpdates) { | |
270 checkForUpdateOnBackgroundThread(activity); | |
271 return false; | |
272 } | |
273 | |
274 return mUpdateAvailable; | |
275 } | |
276 | |
277 private void recordItemClickedHistogram(int action) { | |
278 RecordHistogram.recordEnumeratedHistogram("GoogleUpdate.MenuItem.ActionT akenOnMenuOpen", | |
279 action, ITEM_CLICKED_BOUNDARY); | |
280 } | |
281 | |
282 private void recordUpdateHistogram() { | |
283 if (PrefServiceBridge.getInstance().getClickedUpdateMenuItem()) { | |
284 RecordHistogram.recordEnumeratedHistogram( | |
285 "GoogleUpdate.MenuItem.ActionTakenAfterItemClicked", | |
286 mUpdateAvailable ? NOT_UPDATED : UPDATED, UPDATED_BOUNDARY); | |
287 PrefServiceBridge.getInstance().setClickedUpdateMenuItem(false); | |
288 } | |
289 } | |
290 | |
291 /** | |
292 * Gets a boolean VariationsAssociatedData parameter, assuming the <paramNam e>="true" format. | |
293 * Also checks for a command-line switch with the same name, for easy local testing. | |
294 * @param paramName The name of the parameter (or command-line switch) to ge t a value for. | |
295 * @return Whether the param is defined with a value "true", if there's a co mmand-line | |
296 * flag present with any value. | |
297 */ | |
298 private static boolean getBooleanParam(String paramName) { | |
299 if (CommandLine.getInstance().hasSwitch(paramName)) { | |
300 return true; | |
301 } | |
302 return TextUtils.equals(ENABLED_VALUE, | |
303 VariationsAssociatedData.getVariationParamValue(FIELD_TRIAL_NAME , paramName)); | |
304 } | |
305 | |
306 /** | |
307 * Gets a String VariationsAssociatedData parameter. Also checks for a comma nd-line switch with | |
308 * the same name, for easy local testing. | |
309 * @param paramName The name of the parameter (or command-line switch) to ge t a value for. | |
310 * @return The command-line flag value if present, or the param is value if present. | |
311 */ | |
312 private static String getStringParamValue(String paramName) { | |
313 String value = CommandLine.getInstance().getSwitchValue(paramName); | |
314 if (TextUtils.isEmpty(value)) { | |
315 value = VariationsAssociatedData.getVariationParamValue(FIELD_TRIAL_ NAME, paramName); | |
316 } | |
317 return value; | |
318 } | |
319 } | |
OLD | NEW |