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

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java

Issue 2081153005: [Offline Page] Offline page sharing implementation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address comments Created 4 years, 4 months 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.offlinepages; 5 package org.chromium.chrome.browser.offlinepages;
6 6
7 import android.app.Activity;
7 import android.content.Context; 8 import android.content.Context;
8 import android.content.Intent; 9 import android.content.Intent;
9 import android.content.IntentFilter; 10 import android.content.IntentFilter;
11 import android.graphics.Bitmap;
12 import android.net.Uri;
13 import android.os.AsyncTask;
10 import android.os.BatteryManager; 14 import android.os.BatteryManager;
11 import android.os.Environment; 15 import android.os.Environment;
12 16
17 import org.chromium.base.Callback;
18 import org.chromium.base.FileUtils;
13 import org.chromium.base.Log; 19 import org.chromium.base.Log;
20 import org.chromium.base.StreamUtil;
14 import org.chromium.base.VisibleForTesting; 21 import org.chromium.base.VisibleForTesting;
15 import org.chromium.base.metrics.RecordHistogram; 22 import org.chromium.base.metrics.RecordHistogram;
16 import org.chromium.base.metrics.RecordUserAction; 23 import org.chromium.base.metrics.RecordUserAction;
17 import org.chromium.chrome.R; 24 import org.chromium.chrome.R;
18 import org.chromium.chrome.browser.ChromeActivity; 25 import org.chromium.chrome.browser.ChromeActivity;
19 import org.chromium.chrome.browser.profiles.Profile; 26 import org.chromium.chrome.browser.profiles.Profile;
27 import org.chromium.chrome.browser.share.ShareHelper;
20 import org.chromium.chrome.browser.snackbar.Snackbar; 28 import org.chromium.chrome.browser.snackbar.Snackbar;
21 import org.chromium.chrome.browser.snackbar.SnackbarManager; 29 import org.chromium.chrome.browser.snackbar.SnackbarManager;
22 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController; 30 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
23 import org.chromium.chrome.browser.tab.Tab; 31 import org.chromium.chrome.browser.tab.Tab;
24 import org.chromium.chrome.browser.tabmodel.TabModelSelector; 32 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
25 import org.chromium.components.bookmarks.BookmarkId; 33 import org.chromium.components.bookmarks.BookmarkId;
26 import org.chromium.content_public.browser.LoadUrlParams; 34 import org.chromium.content_public.browser.LoadUrlParams;
27 import org.chromium.content_public.browser.WebContents; 35 import org.chromium.content_public.browser.WebContents;
28 import org.chromium.net.ConnectionType; 36 import org.chromium.net.ConnectionType;
29 import org.chromium.net.NetworkChangeNotifier; 37 import org.chromium.net.NetworkChangeNotifier;
30 import org.chromium.ui.base.PageTransition; 38 import org.chromium.ui.base.PageTransition;
31 39
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.FileNotFoundException;
43 import java.io.FileOutputStream;
44 import java.io.IOException;
45 import java.nio.channels.FileChannel;
32 import java.util.concurrent.TimeUnit; 46 import java.util.concurrent.TimeUnit;
33 47
34 /** 48 /**
35 * A class holding static util functions for offline pages. 49 * A class holding static util functions for offline pages.
36 */ 50 */
37 public class OfflinePageUtils { 51 public class OfflinePageUtils {
38 private static final String TAG = "OfflinePageUtils"; 52 private static final String TAG = "OfflinePageUtils";
39 /** Background task tag to differentiate from other task types */ 53 /** Background task tag to differentiate from other task types */
40 public static final String TASK_TAG = "OfflinePageUtils"; 54 public static final String TASK_TAG = "OfflinePageUtils";
41 55
56 public static final String EXTERNAL_MHTML_FILE_PATH = "offline-pages";
57
42 private static final int DEFAULT_SNACKBAR_DURATION_MS = 6 * 1000; // 6 secon d 58 private static final int DEFAULT_SNACKBAR_DURATION_MS = 6 * 1000; // 6 secon d
43 59
44 private static final long STORAGE_ALMOST_FULL_THRESHOLD_BYTES = 10L * (1 << 20); // 10M 60 private static final long STORAGE_ALMOST_FULL_THRESHOLD_BYTES = 10L * (1 << 20); // 10M
45 61
46 // Used instead of the constant so tests can override the value. 62 // Used instead of the constant so tests can override the value.
47 private static int sSnackbarDurationMs = DEFAULT_SNACKBAR_DURATION_MS; 63 private static int sSnackbarDurationMs = DEFAULT_SNACKBAR_DURATION_MS;
48 64
49 private static OfflinePageUtils sInstance; 65 private static OfflinePageUtils sInstance;
50 66
51 private static OfflinePageUtils getInstance() { 67 private static OfflinePageUtils getInstance() {
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
246 long delayInMilliseconds = nowMillis - taskScheduledTimeMillis; 262 long delayInMilliseconds = nowMillis - taskScheduledTimeMillis;
247 if (delayInMilliseconds <= 0) { 263 if (delayInMilliseconds <= 0) {
248 return; 264 return;
249 } 265 }
250 RecordHistogram.recordLongTimesHistogram( 266 RecordHistogram.recordLongTimesHistogram(
251 "OfflinePages.Wakeup.DelayTime", 267 "OfflinePages.Wakeup.DelayTime",
252 delayInMilliseconds, 268 delayInMilliseconds,
253 TimeUnit.MILLISECONDS); 269 TimeUnit.MILLISECONDS);
254 } 270 }
255 271
272 /**
273 * Share saved offline page.
274 * @param shareDirectly Whether it should share directly with the activity t hat was most
275 * recently used to share.
276 * @param mainActivity Activity that is used to access package manager
Theresa 2016/08/15 20:50:00 nit: missing period at the end of this line "...
Vivian 2016/08/16 17:40:12 Done.
277 * @param onlineUrl Online URL associated with the offline page that is used to access the
278 * offline page file path.
279 * @param bitmap Screenshot of the page to be shared.
280 * @param currentTab Tab that is used to access offlineUrl and tile.
281 */
282 public static void shareOfflinePage(final boolean shareDirectly, final boole an saveLastUsed,
Theresa 2016/08/15 20:50:00 The JavaDoc is missing some params - saveLastUsed,
Vivian 2016/08/16 17:40:11 Done.
283 final Activity mainActivity, final String text, final String onlineU rl,
284 final Bitmap bitmap, final ShareHelper.TargetChosenCallback callback ,
285 final Tab currentTab) {
286 final String offlineUrl = currentTab.getUrl();
287 final String title = currentTab.getTitle();
288 OfflinePageBridge offlinePageBridge =
289 OfflinePageBridge.getForProfile(currentTab.getProfile());
290
291 if (offlinePageBridge != null) {
292 Log.e(TAG, "Unable to perform sharing on current tab.");
293 return;
294 }
295
296 offlinePageBridge.getPageByOfflineUrl(offlineUrl, new Callback<OfflinePa geItem>() {
297 @Override
298 public void onResult(OfflinePageItem item) {
299 if (item == null) return;
300
301 String offlineFilePath = item.getFilePath();
302 prepareForSharing(shareDirectly, saveLastUsed, mainActivity, tit le, text, onlineUrl,
303 bitmap, callback, offlineFilePath);
304 }
305 });
306 }
307
308 private static void prepareForSharing(final boolean shareDirectly, final boo lean saveLastUsed,
309 final Activity activity, final String title, final String text, fina l String onlineUrl,
310 final Bitmap bitmap, final ShareHelper.TargetChosenCallback callback ,
311 final String filePath) {
312 new AsyncTask<Void, Void, File>() {
313 @Override
314 protected File doInBackground(Void... params) {
315 File offlinePageOriginal = new File(filePath);
316 File shareableDir = getDirectoryForOfflineSharing(activity);
317 if (shareableDir != null) {
Theresa 2016/08/15 20:50:00 early exit here too: if (shareableDir == null) {
Vivian 2016/08/16 17:40:11 Done.
318 String fileName = rewriteOfflineFileName(offlinePageOriginal .getName());
319 File offlinePageShareable = new File(shareableDir, fileName) ;
320
321 if (offlinePageShareable.exists()) {
Theresa 2016/08/15 20:49:59 Please add a comment explaining why the old offlin
Vivian 2016/08/16 17:40:11 Done.
322 try {
323 offlinePageShareable.delete();
324 } catch (SecurityException e) {
325 Log.e(TAG, "Failed to delete: " + offlinePageOrigina l.getName(), e);
Theresa 2016/08/15 20:50:00 If we fail to delete the old shareable file, what
Vivian 2016/08/16 17:40:12 Good question! It will overwrite the some of the e
Theresa 2016/08/16 18:47:20 Sounds good, thanks!
326 }
327 }
328 if (copyToShareableLocation(offlinePageOriginal, offlinePage Shareable)) {
329 return offlinePageShareable;
330 }
331 } else {
332 Log.e(TAG, "Unable to create subdirectory in shareable direc tory");
333 }
334 return null;
335 }
336
337 @Override
338 protected void onPostExecute(File offlinePageShareable) {
339 Uri offlineUri = null;
Theresa 2016/08/13 15:58:48 Eventually we'll want to be able to share the mhtm
Theresa 2016/08/15 20:50:00 I think we can skip this for now.
Vivian 2016/08/16 17:40:11 Acknowledged.
340 if (offlinePageShareable != null) {
341 offlineUri = Uri.fromFile(offlinePageShareable);
342 }
343 ShareHelper.share(shareDirectly, saveLastUsed, activity, title, text, onlineUrl,
344 offlineUri, bitmap, callback);
345 }
346 }.execute();
Theresa 2016/08/15 20:50:00 Add AsyncTask.SERIAL_EXECUTOR here. Not specifying
Vivian 2016/08/16 17:40:11 Done.
347 }
348
349 /**
350 * This method copies the file from internal storage to a sharable directory .
351 * @param src file path of the original file to be copied
Theresa 2016/08/15 20:50:00 nits: remove "This method" capitalize the first w
Vivian 2016/08/16 17:40:11 Done.
352 * @param dst file path of the destination
353 */
354 @VisibleForTesting
355 static boolean copyToShareableLocation(File src, File dst) {
356 FileInputStream inputStream = null;
357 FileOutputStream outputStream = null;
358
359 try {
360 inputStream = new FileInputStream(src);
361 outputStream = new FileOutputStream(dst);
362
363 FileChannel inChannel = inputStream.getChannel();
364 FileChannel outChannel = outputStream.getChannel();
365 inChannel.transferTo(0, inChannel.size(), outChannel);
366 } catch (FileNotFoundException e) {
367 Log.e(TAG, "Failed to copy the file: " + src.getName(), e);
368 return false;
369 } catch (IOException e) {
Theresa 2016/08/15 20:50:00 This can be combined with the catch above: ... }
Vivian 2016/08/16 17:40:12 Done.
370 Log.e(TAG, "Failed to copy the file: " + src.getName(), e);
371 return false;
372 } finally {
373 StreamUtil.closeQuietly(inputStream);
374 StreamUtil.closeQuietly(outputStream);
375 }
376 return true;
377 }
378
379 /**
380 * Get a directory for offline page sharing operation.
Theresa 2016/08/15 20:50:00 nit: "Gets the directory to use for sharing offlin
Vivian 2016/08/16 17:40:12 Done.
381 * @param context Context that is used to access external cache directory.
382 * @return path to directory for the shared file to be stored
Theresa 2016/08/15 20:50:00 nit: capitalize Path
Vivian 2016/08/16 17:40:12 Done.
383 */
384 @VisibleForTesting
385 static File getDirectoryForOfflineSharing(Context context) {
386 File path = new File(context.getExternalCacheDir(), EXTERNAL_MHTML_FILE_ PATH);
Theresa 2016/08/15 20:49:59 I think we should create a static variable that ca
Theresa 2016/08/16 03:00:10 *sOfflineSharingDirectory (since it'd be static)
Vivian 2016/08/16 17:40:12 Done.
387 if (!path.exists() && !path.mkdir()) {
388 path = null;
389 }
390 return path;
391 }
392
393 /**
394 * Rewrite file name so that it does not contain periods except the one to s eparate the file
395 * extension.
396 * This step is used to ensure that file name can be recognized by intent fi lter (.*\\.mhtml").
Theresa 2016/08/15 20:50:00 nit: "... (.*\\.mhtml) as Android's path..."
Vivian 2016/08/16 17:40:12 Done.
397 * As Android's path pattern only matches the first dot that appears in a fi le path.
398 * @pram fileName Name of the offline page file.
399 */
400 @VisibleForTesting
401 static String rewriteOfflineFileName(String fileName) {
402 fileName = fileName.replaceAll("\\s+", "");
403 return fileName.replaceAll("\\.(?=.*\\.)", "_");
404 }
405
406 /**
407 * Clears all shared mhtml files.
408 * @param context Context that is used to access external cache directory.
409 */
410 public static void clearSharedOfflineFiles(final Context context) {
411 new AsyncTask<Void, Void, Void>() {
412 @Override
413 protected Void doInBackground(Void... params) {
414 File offlinePath = getDirectoryForOfflineSharing(context);
415 FileUtils.recursivelyDeleteFile(offlinePath);
416 return null;
417 }
418 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Theresa 2016/08/15 20:50:00 This should be done on the SERIAL_EXECUTOR so that
Vivian 2016/08/16 17:40:11 Done.
419 }
420
256 private static boolean isPowerConnected(Intent batteryStatus) { 421 private static boolean isPowerConnected(Intent batteryStatus) {
257 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); 422 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
258 boolean isConnected = (status == BatteryManager.BATTERY_STATUS_CHARGING 423 boolean isConnected = (status == BatteryManager.BATTERY_STATUS_CHARGING
259 || status == BatteryManager.BATTERY_STATUS_FULL); 424 || status == BatteryManager.BATTERY_STATUS_FULL);
260 Log.d(TAG, "Power connected is " + isConnected); 425 Log.d(TAG, "Power connected is " + isConnected);
261 return isConnected; 426 return isConnected;
262 } 427 }
263 428
264 private static int batteryPercentage(Intent batteryStatus) { 429 private static int batteryPercentage(Intent batteryStatus) {
265 int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 430 int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
(...skipping 25 matching lines...) Expand all
291 @VisibleForTesting 456 @VisibleForTesting
292 static void setInstanceForTesting(OfflinePageUtils instance) { 457 static void setInstanceForTesting(OfflinePageUtils instance) {
293 sInstance = instance; 458 sInstance = instance;
294 } 459 }
295 460
296 @VisibleForTesting 461 @VisibleForTesting
297 public static void setSnackbarDurationForTesting(int durationMs) { 462 public static void setSnackbarDurationForTesting(int durationMs) {
298 sSnackbarDurationMs = durationMs; 463 sSnackbarDurationMs = durationMs;
299 } 464 }
300 } 465 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698