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

Unified 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: Rebased and test update added 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 side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
index 106027043db0a10c3070b964590dfbeb35bdde21..c5c9eee5995b132a31517a0c1223be235692117b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
@@ -4,12 +4,17 @@
package org.chromium.chrome.browser.offlinepages;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.AsyncTask;
import android.os.BatteryManager;
import android.os.Environment;
+import org.chromium.base.Callback;
import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordHistogram;
@@ -17,6 +22,7 @@ import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.snackbar.Snackbar;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
@@ -29,6 +35,12 @@ import org.chromium.net.ConnectionType;
import org.chromium.net.NetworkChangeNotifier;
import org.chromium.ui.base.PageTransition;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
import java.util.concurrent.TimeUnit;
/**
@@ -39,6 +51,8 @@ public class OfflinePageUtils {
/** Background task tag to differentiate from other task types */
public static final String TASK_TAG = "OfflinePageUtils";
+ public static final String EXTERNAL_MHTML_FILE_PATH = "offline-pages";
+
private static final int SNACKBAR_DURATION = 6 * 1000; // 6 second
private static final long STORAGE_ALMOST_FULL_THRESHOLD_BYTES = 10L * (1 << 20); // 10M
@@ -250,6 +264,163 @@ public class OfflinePageUtils {
TimeUnit.MILLISECONDS);
}
+ /**
+ * Share saved offline page.
+ * @param shareDirectly Whether it should share directly with the activity that was most
+ * recently used to share.
+ * @param mainActivity Activity that is used to access package manager
+ * @param onlineUrl Online URL associated with the offline page that is used to access the
+ * offline page file path.
+ * @param bitmap Screenshot of the page to be shared.
+ * @param mContext The application context.
+ * @param currentTab Tab that is used to access offlineUrl and tile.
+ */
+ public static void shareOfflinePage(final boolean shareDirectly, final boolean saveLastUsed,
+ final Activity mainActivity, final String text, final String onlineUrl,
+ final Bitmap bitmap, final ShareHelper.TargetChosenCallback callback,
+ final Context mContext, final Tab currentTab) {
+ final String offlineUrl = currentTab.getUrl();
+ final String title = currentTab.getTitle();
+ OfflinePageBridge offlinePageBridge =
+ OfflinePageBridge.getForProfile(currentTab.getProfile());
+ if (offlinePageBridge != null) {
gone 2016/08/12 01:26:12 Avoid indentation by early exiting: if (offlinePa
Vivian 2016/08/12 23:07:12 Done.
+ offlinePageBridge.getPageByOfflineUrl(offlineUrl, new Callback<OfflinePageItem>() {
+ @Override
+ public void onResult(OfflinePageItem item) {
gone 2016/08/12 01:26:13 nit: When indentation gets deep in simple function
Vivian 2016/08/12 23:07:12 Done.
+ if (item != null) {
+ String offlineFilePath = item.getFilePath();
+ prepareForSharing(shareDirectly, saveLastUsed, mainActivity, title, text,
+ onlineUrl, bitmap, callback, offlineFilePath, mContext);
+ }
+ }
+ });
+ } else {
+ Log.e(TAG, "Unable to perform sharing on current tab.");
+ }
+ }
+
+ private static void prepareForSharing(final boolean shareDirectly, final boolean saveLastUsed,
+ final Activity activity, final String title, final String text, final String onlineUrl,
+ final Bitmap bitmap, final ShareHelper.TargetChosenCallback callback,
+ final String filePath, final Context context) {
+ new AsyncTask<Void, Void, File>() {
+ @Override
+ protected File doInBackground(Void... params) {
+ File offlinePageOriginal = new File(filePath);
+ File shareableDir = getDirectoryForOfflineSharing(context);
+ if (shareableDir != null) {
+ String fileName = rewriteOfflineFileName(offlinePageOriginal.getName());
+ File offlinePageShareable = new File(shareableDir, fileName);
Theresa 2016/08/12 17:36:44 Do we really need to rewrite the offline page file
Vivian 2016/08/12 23:07:12 This was my initial approach. The problem is, we w
Theresa 2016/08/13 15:58:48 That really surprises me. The Android documentatio
Theresa 2016/08/13 16:05:55 To expand a little more on why I'm pushing on this
+
+ if (offlinePageShareable.exists()) {
+ try {
+ offlinePageShareable.delete();
+ } catch (SecurityException e) {
+ Log.e(TAG,
+ "failed to delete the file: " + offlinePageOriginal.getName());
gone 2016/08/12 01:26:12 nit: Might be worth printing the exception: Log.e
Vivian 2016/08/12 23:07:12 Done.
+ }
+ }
+ if (copyToShareableLocation(offlinePageOriginal, offlinePageShareable)) {
+ return offlinePageShareable;
+ }
+ } else {
+ Log.e(TAG, "Unable to create subdirectory in shareable directory");
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(File offlinePageShareable) {
+ if (offlinePageShareable == null) return;
+ Uri offlineUri = Uri.fromFile(offlinePageShareable);
+ ShareHelper.share(shareDirectly, saveLastUsed, activity, title, text, onlineUrl,
+ offlineUri, bitmap, callback);
gone 2016/08/12 01:26:12 If sharing the offline page fails, you should fall
Vivian 2016/08/12 23:07:12 Done.
+ }
+ }.execute();
+ }
+
+ /**
+ * This method copies the file from internal storage to a sharable directory.
+ * @param src file path of the original file to be copied
+ * @param dst file path of the destination
+ */
+ @VisibleForTesting
+ static boolean copyToShareableLocation(File src, File dst) {
+ try {
+ FileChannel inChannel = new FileInputStream(src).getChannel();
+ FileChannel outChannel = new FileOutputStream(dst).getChannel();
+
+ inChannel.transferTo(0, inChannel.size(), outChannel);
+
+ if (inChannel != null) inChannel.close();
+ if (outChannel != null) outChannel.close();
gone 2016/08/12 01:26:12 1) Log.e takes an Exception as the third argument,
Vivian 2016/08/12 23:07:12 Done.
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "failed to copy the file: " + src.getName() + ", " + e);
+ return false;
+ } catch (IOException e) {
+ Log.e(TAG, "failed to copy the file: " + src.getName() + ", " + e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get a directory for offline page sharing operation.
+ * @param context Context that is used to access external cache directory.
+ * @return path to directory for the shared file to be stored
+ */
+ private static File getDirectoryForOfflineSharing(Context context) {
+ File path = new File(context.getExternalCacheDir(), EXTERNAL_MHTML_FILE_PATH);
+ boolean success = true;
gone 2016/08/12 01:26:13 Useless variable?
Vivian 2016/08/12 23:07:12 Yes. Nice catch! Deleted it.
+ if (!path.exists() && !path.mkdir()) {
+ path = null;
+ }
+ return path;
+ }
+
+ /**
+ * Rewrite file name so that it does not contain periods except the one to separate the file
+ * extension.
+ * This step is used to ensure that file name can be recognized by intent filter (.*\\.mhtml").
+ * As Android's path pattern only matches the first dot that appears in a file path.
+ * @pram fileName Name of the offline page file.
+ */
+ @VisibleForTesting
+ static String rewriteOfflineFileName(String fileName) {
+ fileName = fileName.replaceAll("\\s+", "");
+ return fileName.replaceAll("\\.(?=.*\\.)", "_");
+ }
+
+ /**
+ * Delete a shared mhtml file. If the file path is a directory, delete all files in directory.
+ * @param file File object of the offline page.
+ */
+ @VisibleForTesting
+ static void deleteSharedOfflineFiles(File file) {
gone 2016/08/12 01:26:13 Just use FileUtils.recursivelyDeleteFile(File file
Vivian 2016/08/12 23:07:12 Done.
+ if (!file.exists()) return;
+ if (file.isDirectory()) {
+ for (File f : file.listFiles()) deleteSharedOfflineFiles(f);
+ }
+ if (!file.delete()) {
+ Log.w(TAG, "Failed to delete shared offline file: %s", file.getAbsolutePath());
+ }
+ }
+
+ /**
+ * Clears all shared mhtml files.
+ * @param context Context that is used to access external cache directory.
+ */
+ public static void clearSharedOfflineFiles(final Context context) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ File offlinePath = getDirectoryForOfflineSharing(context);
+ deleteSharedOfflineFiles(offlinePath);
+ return null;
+ }
+ }.execute();
gone 2016/08/12 01:26:12 AsyncTask#execute() does different things, dependi
Vivian 2016/08/12 23:07:12 Done.
+ }
+
private static boolean isPowerConnected(Intent batteryStatus) {
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isConnected = (status == BatteryManager.BATTERY_STATUS_CHARGING

Powered by Google App Engine
This is Rietveld 408576698