Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |