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

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: Adjustments according to Filip's 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;
13 import org.chromium.base.Log; 18 import org.chromium.base.Log;
14 import org.chromium.base.VisibleForTesting; 19 import org.chromium.base.VisibleForTesting;
15 import org.chromium.base.metrics.RecordHistogram; 20 import org.chromium.base.metrics.RecordHistogram;
16 import org.chromium.base.metrics.RecordUserAction; 21 import org.chromium.base.metrics.RecordUserAction;
17 import org.chromium.chrome.R; 22 import org.chromium.chrome.R;
18 import org.chromium.chrome.browser.ChromeActivity; 23 import org.chromium.chrome.browser.ChromeActivity;
19 import org.chromium.chrome.browser.profiles.Profile; 24 import org.chromium.chrome.browser.profiles.Profile;
25 import org.chromium.chrome.browser.share.ShareHelper;
20 import org.chromium.chrome.browser.snackbar.Snackbar; 26 import org.chromium.chrome.browser.snackbar.Snackbar;
21 import org.chromium.chrome.browser.snackbar.SnackbarManager; 27 import org.chromium.chrome.browser.snackbar.SnackbarManager;
22 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController; 28 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
23 import org.chromium.chrome.browser.tab.Tab; 29 import org.chromium.chrome.browser.tab.Tab;
24 import org.chromium.chrome.browser.tabmodel.TabModelSelector; 30 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
25 import org.chromium.components.bookmarks.BookmarkId; 31 import org.chromium.components.bookmarks.BookmarkId;
26 import org.chromium.content_public.browser.LoadUrlParams; 32 import org.chromium.content_public.browser.LoadUrlParams;
27 import org.chromium.content_public.browser.WebContents; 33 import org.chromium.content_public.browser.WebContents;
28 import org.chromium.net.ConnectionType; 34 import org.chromium.net.ConnectionType;
29 import org.chromium.net.NetworkChangeNotifier; 35 import org.chromium.net.NetworkChangeNotifier;
30 import org.chromium.ui.base.PageTransition; 36 import org.chromium.ui.base.PageTransition;
31 37
38 import java.io.File;
39 import java.io.FileInputStream;
40 import java.io.FileNotFoundException;
41 import java.io.FileOutputStream;
42 import java.io.IOException;
43 import java.nio.channels.FileChannel;
32 import java.util.concurrent.TimeUnit; 44 import java.util.concurrent.TimeUnit;
33 45
34 /** 46 /**
35 * A class holding static util functions for offline pages. 47 * A class holding static util functions for offline pages.
36 */ 48 */
37 public class OfflinePageUtils { 49 public class OfflinePageUtils {
38 private static final String TAG = "OfflinePageUtils"; 50 private static final String TAG = "OfflinePageUtils";
39 /** Background task tag to differentiate from other task types */ 51 /** Background task tag to differentiate from other task types */
40 public static final String TASK_TAG = "OfflinePageUtils"; 52 public static final String TASK_TAG = "OfflinePageUtils";
41 53
54 public static final String EXTERNAL_MHTML_FILE_PATH = "offline-pages";
55
42 private static final int SNACKBAR_DURATION = 6 * 1000; // 6 second 56 private static final int SNACKBAR_DURATION = 6 * 1000; // 6 second
43 57
44 private static final long STORAGE_ALMOST_FULL_THRESHOLD_BYTES = 10L * (1 << 20); // 10M 58 private static final long STORAGE_ALMOST_FULL_THRESHOLD_BYTES = 10L * (1 << 20); // 10M
45 59
46 private static OfflinePageUtils sInstance; 60 private static OfflinePageUtils sInstance;
47 61
48 private static OfflinePageUtils getInstance() { 62 private static OfflinePageUtils getInstance() {
49 if (sInstance == null) { 63 if (sInstance == null) {
50 sInstance = new OfflinePageUtils(); 64 sInstance = new OfflinePageUtils();
51 } 65 }
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 long delayInMilliseconds = nowMillis - taskScheduledTimeMillis; 257 long delayInMilliseconds = nowMillis - taskScheduledTimeMillis;
244 if (delayInMilliseconds <= 0) { 258 if (delayInMilliseconds <= 0) {
245 return; 259 return;
246 } 260 }
247 RecordHistogram.recordLongTimesHistogram( 261 RecordHistogram.recordLongTimesHistogram(
248 "OfflinePages.Wakeup.DelayTime", 262 "OfflinePages.Wakeup.DelayTime",
249 delayInMilliseconds, 263 delayInMilliseconds,
250 TimeUnit.MILLISECONDS); 264 TimeUnit.MILLISECONDS);
251 } 265 }
252 266
267 /**
268 * Share saved offline page.
269 * @param shareDirectly Whether it should share directly with the activity t hat was most
270 * recently used to share.
271 * @param mainActivity Activity that is used to access package manager
272 * @param onlineUrl Online URL associated with the offline page that is used to access the
273 * offline page file path.
274 * @param bitmap Screenshot of the page to be shared.
275 * @param mContext The application context.
276 * @param currentTab Tab that is used to access offlineUrl and tile.
277 */
278 public static void shareOfflinePage(final boolean shareDirectly, final Activ ity mainActivity,
279 final String onlineUrl, final Bitmap bitmap, final Context mContext,
280 final Tab currentTab) {
281 final String offlineUrl = currentTab.getUrl();
282 final String title = currentTab.getTitle();
283 OfflinePageBridge offlinePageBridge =
284 OfflinePageBridge.getForProfile(currentTab.getProfile());
285 if (offlinePageBridge != null) {
286 offlinePageBridge.getPageByOfflineUrl(offlineUrl, new Callback<Offli nePageItem>() {
287 @Override
288 public void onResult(OfflinePageItem item) {
289 if (item != null) {
290 String offlineFilePath = item.getFilePath();
291 prepareForSharing(shareDirectly, mainActivity, title, on lineUrl, bitmap,
292 offlineFilePath, mContext);
293 }
294 }
295 });
296 } else {
297 Log.e(TAG, "Unable to perform sharing on current tab.");
298 }
299 }
300
301 private static void prepareForSharing(final boolean shareDirectly, final Act ivity activity,
302 final String title, final String onlineUrl, final Bitmap bitmap, fin al String filePath,
303 final Context context) {
304 new AsyncTask<Void, Void, File>() {
305 @Override
306 protected File doInBackground(Void... params) {
307 File offlinePageOriginal = new File(filePath);
308 File shareableDir = getDirectoryForOfflineSharing(context);
309 if (shareableDir != null) {
310 String fileName = rewriteOfflineFileName(offlinePageOriginal .getName());
311 File offlinePageShareable = new File(shareableDir, fileName) ;
312
313 if (offlinePageShareable.exists()) {
314 try {
315 offlinePageShareable.delete();
316 } catch (SecurityException e) {
317 Log.e(TAG,
318 "failed to delete the file: " + offlinePageO riginal.getName());
319 }
320 }
321 if (copyToShareableLocation(offlinePageOriginal, offlinePage Shareable)) {
322 return offlinePageShareable;
323 }
324 } else {
325 Log.e(TAG, "Unable to create subdirectory in shareable direc tory");
326 }
327 return null;
328 }
329
330 @Override
331 protected void onPostExecute(File offlinePageShareable) {
332 if (offlinePageShareable == null) return;
333 Uri offlineUri = Uri.fromFile(offlinePageShareable);
334 ShareHelper.share(shareDirectly, activity, title, onlineUrl, off lineUri, bitmap);
335 }
336 }.execute();
337 }
338
339 /**
340 * This method copies the file from internal storage to a sharable directory .
341 * @param src file path of the original file to be copied
342 * @param dst file path of the destination
343 */
344 @VisibleForTesting
345 static boolean copyToShareableLocation(File src, File dst) {
346 try {
347 FileChannel inChannel = new FileInputStream(src).getChannel();
348 FileChannel outChannel = new FileOutputStream(dst).getChannel();
349
350 inChannel.transferTo(0, inChannel.size(), outChannel);
351
352 if (inChannel != null) inChannel.close();
353 if (outChannel != null) outChannel.close();
354 } catch (FileNotFoundException e) {
355 Log.e(TAG, "failed to copy the file: " + src.getName() + ", " + e);
356 return false;
357 } catch (IOException e) {
358 Log.e(TAG, "failed to copy the file: " + src.getName() + ", " + e);
359 return false;
360 }
361 return true;
362 }
363
364 /**
365 * Get a directory for offline page sharing operation.
366 * @param context Context that is used to access external cache directory.
367 * @return path to directory for the shared file to be stored
368 * */
fgorski 2016/08/11 16:57:53 nit: * still needs removing
Vivian 2016/08/11 23:18:29 Done.
369 private static File getDirectoryForOfflineSharing(Context context) {
370 File path = new File(context.getExternalCacheDir(), EXTERNAL_MHTML_FILE_ PATH);
371 boolean success = true;
372 if (!path.exists() && !path.mkdir()) {
373 path = null;
374 }
375 return path;
376 }
377
378 /**
379 * Rewrite file name so that it does not contain periods except the one to s eparate the file
380 * extension.
381 * This step is used to ensure that file name can be recognized by intent fi lter (.*\\.mhtml").
382 * As Android's path pattern only matches the first dot that appears in a fi le path.
383 * @pram fileName Name of the offline page file.
384 */
385 @VisibleForTesting
386 static String rewriteOfflineFileName(String fileName) {
387 fileName = fileName.replaceAll("\\s+", "");
388 return fileName.replaceAll("\\.(?=.*\\.)", "_");
389 }
390
391 /**
392 * Delete a shared mhtml file. If the file path is a directory, delete all f iles in directory.
393 * @param file File object of the offline page.
394 */
395 @VisibleForTesting
396 static void deleteSharedOfflineFiles(File file) {
397 if (!file.exists()) return;
398 if (file.isDirectory()) {
399 for (File f : file.listFiles()) deleteSharedOfflineFiles(f);
400 }
401 if (!file.delete()) {
402 Log.w(TAG, "Failed to delete shared offline file: %s", file.getAbsol utePath());
403 }
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 deleteSharedOfflineFiles(offlinePath);
416 return null;
417 }
418 }.execute();
419 }
420
253 private static boolean isPowerConnected(Intent batteryStatus) { 421 private static boolean isPowerConnected(Intent batteryStatus) {
254 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); 422 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
255 boolean isConnected = (status == BatteryManager.BATTERY_STATUS_CHARGING 423 boolean isConnected = (status == BatteryManager.BATTERY_STATUS_CHARGING
256 || status == BatteryManager.BATTERY_STATUS_FULL); 424 || status == BatteryManager.BATTERY_STATUS_FULL);
257 Log.d(TAG, "Power connected is " + isConnected); 425 Log.d(TAG, "Power connected is " + isConnected);
258 return isConnected; 426 return isConnected;
259 } 427 }
260 428
261 private static int batteryPercentage(Intent batteryStatus) { 429 private static int batteryPercentage(Intent batteryStatus) {
262 int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 430 int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
(...skipping 20 matching lines...) Expand all
283 return new DeviceConditions(isPowerConnected(batteryStatus), 451 return new DeviceConditions(isPowerConnected(batteryStatus),
284 batteryPercentage(batteryStatus), 452 batteryPercentage(batteryStatus),
285 NetworkChangeNotifier.getInstance().getCurrentConnectionType()); 453 NetworkChangeNotifier.getInstance().getCurrentConnectionType());
286 } 454 }
287 455
288 @VisibleForTesting 456 @VisibleForTesting
289 static void setInstanceForTesting(OfflinePageUtils instance) { 457 static void setInstanceForTesting(OfflinePageUtils instance) {
290 sInstance = instance; 458 sInstance = instance;
291 } 459 }
292 } 460 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698