From 0a134c430ce3b8eb43e950db4ea0bfba3a59bcd5 Mon Sep 17 00:00:00 2001 From: Roman Tsisyk Date: Sun, 16 May 2021 11:58:50 +0300 Subject: [PATCH] [android]: Merge two StorageUtils classes into one No semantic changes intended. This patch is pure refactoring. Signed-off-by: Roman Tsisyk --- .../MapDownloadCompletedProcessor.java | 33 +--- .../maps/settings/StoragePathFragment.java | 20 ++- .../maps/settings/StoragePathManager.java | 1 + .../maps/settings/StorageUtils.java | 165 ----------------- .../src/com/mapswithme/util/StorageUtils.java | 167 ++++++++++++++++++ 5 files changed, 196 insertions(+), 190 deletions(-) delete mode 100644 android/src/com/mapswithme/maps/settings/StorageUtils.java diff --git a/android/src/com/mapswithme/maps/downloader/MapDownloadCompletedProcessor.java b/android/src/com/mapswithme/maps/downloader/MapDownloadCompletedProcessor.java index 4eb5b2359d..5408eda1bd 100644 --- a/android/src/com/mapswithme/maps/downloader/MapDownloadCompletedProcessor.java +++ b/android/src/com/mapswithme/maps/downloader/MapDownloadCompletedProcessor.java @@ -4,22 +4,21 @@ import android.app.DownloadManager; import android.content.Context; import android.database.Cursor; import android.net.Uri; -import android.os.FileUtils; import android.text.TextUtils; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; + import com.mapswithme.maps.MwmApplication; +import com.mapswithme.util.StorageUtils; import com.mapswithme.util.concurrency.UiThread; import com.mapswithme.util.log.Logger; import com.mapswithme.util.log.LoggerFactory; -import java.io.FileOutputStream; +import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; public class MapDownloadCompletedProcessor { @@ -67,31 +66,17 @@ public class MapDownloadCompletedProcessor } String dstPath = MapManager.nativeGetFilePathByUrl(targetUriPath); - return copyFile(context, downloadedFileUri, dstPath); - } - - private static boolean copyFile(@NonNull Context context, @NonNull Uri from, @NonNull String to) - { - try (InputStream in = context.getContentResolver().openInputStream(from)) + try { - if (in == null) + if (!StorageUtils.copyFile(context, downloadedFileUri, new File(dstPath))) return false; - - try (OutputStream out = new FileOutputStream(to)) - { - byte[] buf = new byte[4 * 1024]; - int len; - while ((len = in.read(buf)) > 0) - out.write(buf, 0, len); - - context.getContentResolver().delete(from, null, null); - return true; - } + context.getContentResolver().delete(downloadedFileUri, null, null); + return true; } catch (IOException e) { - LOGGER.e(TAG, "Failed to copy or delete downloaded map file from " + from.toString() + - " to " + to + ". Exception ", e); + LOGGER.e(TAG, "Failed to copy or delete downloaded map file from " + downloadedFileUri.toString() + + " to " + dstPath + ". Exception ", e); return false; } } diff --git a/android/src/com/mapswithme/maps/settings/StoragePathFragment.java b/android/src/com/mapswithme/maps/settings/StoragePathFragment.java index 59a43a1674..748fa245a8 100644 --- a/android/src/com/mapswithme/maps/settings/StoragePathFragment.java +++ b/android/src/com/mapswithme/maps/settings/StoragePathFragment.java @@ -11,11 +11,15 @@ import android.widget.TextView; import androidx.appcompat.app.AlertDialog; +import com.mapswithme.maps.BuildConfig; +import com.mapswithme.maps.Framework; import com.mapswithme.maps.R; import com.mapswithme.maps.base.OnBackPressListener; import com.mapswithme.util.Constants; +import com.mapswithme.util.StorageUtils; import com.mapswithme.util.Utils; +import java.io.File; import java.util.List; import java.util.Locale; @@ -81,9 +85,23 @@ public class StoragePathFragment extends BaseSettingsFragment mPathManager.stopExternalStorageWatching(); } + static long getWritableDirSize() + { + final File writableDir = new File(Framework.nativeGetWritableDir()); + if (BuildConfig.DEBUG) + { + if (!writableDir.exists()) + throw new IllegalStateException("Writable directory doesn't exits, can't get size."); + if (!writableDir.isDirectory()) + throw new IllegalStateException("Writable directory isn't a directory, can't get size."); + } + + return StorageUtils.getDirSizeRecursively(writableDir, StoragePathManager.MOVABLE_FILES_FILTER); + } + private void updateList() { - long dirSize = StorageUtils.getWritableDirSize(); + long dirSize = getWritableDirSize(); mHeader.setText(getString(R.string.maps) + ": " + getSizeString(dirSize)); if (mAdapter != null) diff --git a/android/src/com/mapswithme/maps/settings/StoragePathManager.java b/android/src/com/mapswithme/maps/settings/StoragePathManager.java index 9397f2985b..9895a80aca 100644 --- a/android/src/com/mapswithme/maps/settings/StoragePathManager.java +++ b/android/src/com/mapswithme/maps/settings/StoragePathManager.java @@ -22,6 +22,7 @@ import com.mapswithme.maps.R; import com.mapswithme.maps.dialog.DialogUtils; import com.mapswithme.maps.downloader.MapManager; import com.mapswithme.util.Config; +import com.mapswithme.util.StorageUtils; import com.mapswithme.util.concurrency.ThreadPool; import com.mapswithme.util.concurrency.UiThread; import com.mapswithme.util.log.Logger; diff --git a/android/src/com/mapswithme/maps/settings/StorageUtils.java b/android/src/com/mapswithme/maps/settings/StorageUtils.java deleted file mode 100644 index 6d00ad9c05..0000000000 --- a/android/src/com/mapswithme/maps/settings/StorageUtils.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.mapswithme.maps.settings; - -import com.mapswithme.maps.BuildConfig; -import com.mapswithme.maps.Framework; -import com.mapswithme.util.Constants; -import com.mapswithme.util.Utils; -import com.mapswithme.util.log.Logger; -import com.mapswithme.util.log.LoggerFactory; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.nio.channels.FileChannel; -import java.util.ArrayList; - -final class StorageUtils -{ - private StorageUtils() {} - - private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.STORAGE); - private static final String TAG = StorageUtils.class.getSimpleName(); - - /** - * Check if directory is writable. On some devices with KitKat (eg, Samsung S4) simple File.canWrite() returns - * true for some actually read only directories on sdcard. - * see https://code.google.com/p/android/issues/detail?id=66369 for details - * - * @param path path to ckeck - * @return result - */ - @SuppressWarnings("ResultOfMethodCallIgnored") - static boolean isDirWritable(String path) - { - File f = new File(path, "mapsme_test_dir"); - f.mkdir(); - if (!f.exists()) - return false; - - f.delete(); - return true; - } - - static long getFreeBytesAtPath(String path) - { - long size = 0; - try - { - size = new File(path).getFreeSpace(); - } catch (RuntimeException e) - { - e.printStackTrace(); - } - - return size; - } - - static void copyFile(File source, File dest) throws IOException - { - int maxChunkSize = 10 * Constants.MB; // move file by smaller chunks to avoid OOM. - FileChannel inputChannel = null, outputChannel = null; - try - { - inputChannel = new FileInputStream(source).getChannel(); - outputChannel = new FileOutputStream(dest).getChannel(); - long totalSize = inputChannel.size(); - - for (long currentPosition = 0; currentPosition < totalSize; currentPosition += maxChunkSize) - { - outputChannel.position(currentPosition); - outputChannel.transferFrom(inputChannel, currentPosition, maxChunkSize); - } - } finally - { - Utils.closeSafely(inputChannel); - Utils.closeSafely(outputChannel); - } - } - - private static long getDirSizeRecursively(File file, FilenameFilter fileFilter) - { - if (file.isDirectory()) - { - long dirSize = 0; - for (File child : file.listFiles()) - dirSize += getDirSizeRecursively(child, fileFilter); - - return dirSize; - } - - if (fileFilter.accept(file.getParentFile(), file.getName())) - return file.length(); - - return 0; - } - - static long getWritableDirSize() - { - final File writableDir = new File(Framework.nativeGetWritableDir()); - if (BuildConfig.DEBUG) - { - if (!writableDir.exists()) - throw new IllegalStateException("Writable directory doesn't exits, can't get size."); - if (!writableDir.isDirectory()) - throw new IllegalStateException("Writable directory isn't a directory, can't get size."); - } - - return getDirSizeRecursively(writableDir, StoragePathManager.MOVABLE_FILES_FILTER); - } - - /** - * Recursively lists all movable files in the directory. - */ - static void listFilesRecursively(File dir, String prefix, FilenameFilter filter, ArrayList relPaths) - { - File[] list = dir.listFiles(); - if (list == null) - return; - - for (File file : list) - { - if (file.isDirectory()) - { - listFilesRecursively(file, prefix + file.getName() + File.separator, filter, relPaths); - continue; - } - String name = file.getName(); - if (filter.accept(dir, name)) - relPaths.add(prefix + name); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private static void removeEmptyDirectories(File dir) - { - for (File file : dir.listFiles()) - { - if (!file.isDirectory()) - continue; - removeEmptyDirectories(file); - file.delete(); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - static boolean removeFilesInDirectory(File dir, File[] files) - { - try - { - for (File file : files) - { - if (file != null) - file.delete(); - } - removeEmptyDirectories(dir); - return true; - } catch (Exception e) - { - e.printStackTrace(); - return false; - } - } - -} diff --git a/android/src/com/mapswithme/util/StorageUtils.java b/android/src/com/mapswithme/util/StorageUtils.java index 7245230379..d29a2a7b24 100644 --- a/android/src/com/mapswithme/util/StorageUtils.java +++ b/android/src/com/mapswithme/util/StorageUtils.java @@ -17,7 +17,15 @@ import com.mapswithme.util.log.Logger; import com.mapswithme.util.log.LoggerFactory; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FilenameFilter; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.FileChannel; +import java.util.ArrayList; public class StorageUtils { @@ -163,4 +171,163 @@ public class StorageUtils return FileProvider.getUriForFile(context.getApplicationContext(), BuildConfig.FILE_PROVIDER_AUTHORITY, new File(path)); } + + /** + * Copy data from a URI into a local file. + * @param context context + * @param from a source URI. + * @param to a destination file + * @return true on success and false if the provider recently crashed. + * @throws IOException - if I/O error occurs. + */ + public static boolean copyFile(@NonNull Context context, @NonNull Uri from, @NonNull File to) throws IOException + { + try (InputStream in = context.getContentResolver().openInputStream(from)) + { + if (in == null) + return false; + + try (OutputStream out = new FileOutputStream(to)) + { + byte[] buf = new byte[4 * 1024]; + int len; + while ((len = in.read(buf)) > 0) + out.write(buf, 0, len); + + return true; + } + } + } + + /** + * Copy the contents of the source file to the target file. + * @param from a source file + * @param to a destination file + * @throws IOException - if I/O error occurs. + */ + static void copyFile(File from, File to) throws IOException + { + int maxChunkSize = 10 * Constants.MB; // move file by smaller chunks to avoid OOM. + FileChannel inputChannel = null, outputChannel = null; + try + { + inputChannel = new FileInputStream(from).getChannel(); + outputChannel = new FileOutputStream(to).getChannel(); + long totalSize = inputChannel.size(); + + for (long currentPosition = 0; currentPosition < totalSize; currentPosition += maxChunkSize) + { + outputChannel.position(currentPosition); + outputChannel.transferFrom(inputChannel, currentPosition, maxChunkSize); + } + } finally + { + Utils.closeSafely(inputChannel); + Utils.closeSafely(outputChannel); + } + } + + /** + * Recursively lists all movable files in the directory. + */ + public static void listFilesRecursively(File dir, String prefix, FilenameFilter filter, ArrayList relPaths) + { + File[] list = dir.listFiles(); + if (list == null) + return; + + for (File file : list) + { + if (file.isDirectory()) + { + listFilesRecursively(file, prefix + file.getName() + File.separator, filter, relPaths); + continue; + } + String name = file.getName(); + if (filter.accept(dir, name)) + relPaths.add(prefix + name); + } + } + + /** + * Check if directory is writable. On some devices with KitKat (eg, Samsung S4) simple File.canWrite() returns + * true for some actually read only directories on sdcard. + * see https://code.google.com/p/android/issues/detail?id=66369 for details + * + * @param path path to ckeck + * @return result + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public static boolean isDirWritable(String path) + { + File f = new File(path, "mapsme_test_dir"); + f.mkdir(); + if (!f.exists()) + return false; + + f.delete(); + return true; + } + + public static long getFreeBytesAtPath(String path) + { + long size = 0; + try + { + size = new File(path).getFreeSpace(); + } catch (RuntimeException e) + { + e.printStackTrace(); + } + + return size; + } + + public static long getDirSizeRecursively(File file, FilenameFilter fileFilter) + { + if (file.isDirectory()) + { + long dirSize = 0; + for (File child : file.listFiles()) + dirSize += getDirSizeRecursively(child, fileFilter); + + return dirSize; + } + + if (fileFilter.accept(file.getParentFile(), file.getName())) + return file.length(); + + return 0; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static void removeEmptyDirectories(File dir) + { + for (File file : dir.listFiles()) + { + if (!file.isDirectory()) + continue; + removeEmptyDirectories(file); + file.delete(); + } + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static boolean removeFilesInDirectory(File dir, File[] files) + { + try + { + for (File file : files) + { + if (file != null) + file.delete(); + } + removeEmptyDirectories(dir); + return true; + } catch (Exception e) + { + e.printStackTrace(); + return false; + } + } }