diff --git a/OsmAnd/res/layout/data_storage_memory_used_item.xml b/OsmAnd/res/layout/data_storage_memory_used_item.xml index 0edf24f056..72f49226e8 100644 --- a/OsmAnd/res/layout/data_storage_memory_used_item.xml +++ b/OsmAnd/res/layout/data_storage_memory_used_item.xml @@ -9,7 +9,8 @@ + android:layout_height="wrap_content" + android:minHeight="@dimen/card_row_min_height"> + tools:text="Internal application memory" /> + Hillshade / Slope / Contour lines Select edits for upload Uploaded %1$d of %2$d Uploading %1$d of %2$d diff --git a/OsmAnd/res/xml/data_storage.xml b/OsmAnd/res/xml/data_storage.xml index a153ec4cd1..173907c8b0 100644 --- a/OsmAnd/res/xml/data_storage.xml +++ b/OsmAnd/res/xml/data_storage.xml @@ -14,10 +14,10 @@ android:title="@string/shared_string_maps"/> + android:title="@string/hillshade_slope_contour_lines"/> menuItems; - private ArrayList memoryItems; + private ArrayList storageItems; + private ArrayList memoryItems; private ArrayList dataStorageRadioButtonsGroup; private Preference changeButton; - private DataStorageMenuItem currentDataStorage; + private StorageItem currentDataStorage; private String tmpManuallySpecifiedPath; private DataStorageHelper dataStorageHelper; private boolean calculateTilesBtnPressed; - private DataStorageHelper.RefreshMemoryUsedInfo calculateMemoryTask; - private DataStorageHelper.RefreshMemoryUsedInfo calculateTilesMemoryTask; + private RefreshUsedMemoryTask calculateMemoryTask; + private RefreshUsedMemoryTask calculateTilesMemoryTask; private OsmandApplication app; private OsmandActionBarActivity activity; @@ -95,11 +94,11 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto return; } - menuItems = dataStorageHelper.getStorageItems(); + storageItems = dataStorageHelper.getStorageItems(); memoryItems = dataStorageHelper.getMemoryInfoItems(); dataStorageRadioButtonsGroup = new ArrayList<>(); - for (DataStorageMenuItem item : menuItems) { + for (StorageItem item : storageItems) { CheckBoxPreference preference = new CheckBoxPreference(activity); preference.setKey(item.getKey()); preference.setTitle(item.getTitle()); @@ -136,7 +135,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto Bundle resultData = (Bundle) newValue; if (resultData.containsKey(ChangeDataStorageBottomSheet.TAG)) { boolean moveMaps = resultData.getBoolean(MOVE_DATA); - DataStorageMenuItem newDataStorage = resultData.getParcelable(CHOSEN_DIRECTORY); + StorageItem newDataStorage = resultData.getParcelable(CHOSEN_DIRECTORY); if (newDataStorage != null) { if (tmpManuallySpecifiedPath != null) { String directory = tmpManuallySpecifiedPath; @@ -154,9 +153,9 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto if (pathChanged) { tmpManuallySpecifiedPath = resultData.getString(NEW_PATH); if (tmpManuallySpecifiedPath != null) { - DataStorageMenuItem manuallySpecified = null; + StorageItem manuallySpecified = null; try { - manuallySpecified = (DataStorageMenuItem) dataStorageHelper.getManuallySpecified().clone(); + manuallySpecified = (StorageItem) dataStorageHelper.getManuallySpecified().clone(); manuallySpecified.setDirectory(tmpManuallySpecifiedPath); } catch (CloneNotSupportedException e) { return false; @@ -170,7 +169,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto //show necessary dialog String key = preference.getKey(); if (key != null) { - DataStorageMenuItem newDataStorage = dataStorageHelper.getStorage(key); + StorageItem newDataStorage = dataStorageHelper.getStorage(key); if (newDataStorage != null) { if (!currentDataStorage.getKey().equals(newDataStorage.getKey())) { if (newDataStorage.getType() == OsmandSettings.EXTERNAL_STORAGE_TYPE_DEFAULT @@ -212,7 +211,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto final View itemView = holder.itemView; if (preference instanceof CheckBoxPreference) { - DataStorageMenuItem item = dataStorageHelper.getStorage(key); + StorageItem item = dataStorageHelper.getStorage(key); if (item != null) { TextView tvTitle = itemView.findViewById(android.R.id.title); TextView tvSummary = itemView.findViewById(R.id.summary); @@ -267,7 +266,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto TextView tvSummary = itemView.findViewById(R.id.summary); tvSummary.setText(DataStorageHelper.getFormattedMemoryInfo(totalUsageBytes, memoryUnitsFormats)); } else { - for (DataStorageMemoryItem mi : memoryItems) { + for (MemoryItem mi : memoryItems) { if (key.equals(mi.getKey())) { TextView tvMemory = itemView.findViewById(R.id.memory); String summary = ""; @@ -326,7 +325,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto } private void showFolderSelectionDialog() { - DataStorageMenuItem manuallySpecified = dataStorageHelper.getManuallySpecified(); + StorageItem manuallySpecified = dataStorageHelper.getManuallySpecified(); if (manuallySpecified != null) { SelectFolderBottomSheet.showInstance(getFragmentManager(), manuallySpecified.getKey(), manuallySpecified.getDirectory(), DataStorageFragment.this, @@ -335,11 +334,11 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto } } - private void moveData(final DataStorageMenuItem currentStorage, final DataStorageMenuItem newStorage) { + private void moveData(final StorageItem currentStorage, final StorageItem newStorage) { File fromDirectory = new File(currentStorage.getDirectory()); File toDirectory = new File(newStorage.getDirectory()); @SuppressLint("StaticFieldLeak") - MoveFilesToDifferentDirectory task = new MoveFilesToDifferentDirectory(activity, fromDirectory, toDirectory) { + MoveFilesTask task = new MoveFilesTask(activity, fromDirectory, toDirectory) { @NonNull @@ -405,7 +404,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } - private void confirm(OsmandApplication app, OsmandActionBarActivity activity, DataStorageMenuItem newStorageDirectory, boolean silentRestart) { + private void confirm(OsmandApplication app, OsmandActionBarActivity activity, StorageItem newStorageDirectory, boolean silentRestart) { String newDirectory = newStorageDirectory.getDirectory(); int type = newStorageDirectory.getType(); File newDirectoryFile = new File(newDirectory); @@ -454,7 +453,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto } protected void reloadData() { - new ReloadData(activity, app).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); + new ReloadDataTask(activity, app).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } @Override @@ -463,204 +462,10 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto } @Override - public void onFinishUpdating(String taskKey) { + public void onFinishUpdating(String tag) { updateAllSettings(); - if (taskKey != null && taskKey.equals(TILES_MEMORY)) { + if (tag != null && tag.equals(TILES_MEMORY)) { app.getSettings().OSMAND_USAGE_SPACE.set(dataStorageHelper.getTotalUsedBytes()); } } - - public static class MoveFilesToDifferentDirectory extends AsyncTask { - - protected WeakReference activity; - private WeakReference context; - private File from; - private File to; - protected ProgressImplementation progress; - private Runnable runOnSuccess; - private int movedCount; - private long movedSize; - private int copiedCount; - private long copiedSize; - private int failedCount; - private long failedSize; - private String exceptionMessage; - - public MoveFilesToDifferentDirectory(OsmandActionBarActivity activity, File from, File to) { - this.activity = new WeakReference<>(activity); - this.context = new WeakReference<>((Context) activity); - this.from = from; - this.to = to; - } - - public void setRunOnSuccess(Runnable runOnSuccess) { - this.runOnSuccess = runOnSuccess; - } - - public int getMovedCount() { - return movedCount; - } - - public int getCopiedCount() { - return copiedCount; - } - - public int getFailedCount() { - return failedCount; - } - - public long getMovedSize() { - return movedSize; - } - - public long getCopiedSize() { - return copiedSize; - } - - public long getFailedSize() { - return failedSize; - } - - @Override - protected void onPreExecute() { - Context ctx = context.get(); - if (context == null) { - return; - } - movedCount = 0; - copiedCount = 0; - failedCount = 0; - progress = ProgressImplementation.createProgressDialog( - ctx, ctx.getString(R.string.copying_osmand_files), - ctx.getString(R.string.copying_osmand_files_descr, to.getPath()), - ProgressDialog.STYLE_HORIZONTAL); - } - - @Override - protected void onPostExecute(Boolean result) { - Context ctx = context.get(); - if (ctx == null) { - return; - } - if (result != null) { - if (result.booleanValue() && runOnSuccess != null) { - runOnSuccess.run(); - } else if (!result.booleanValue()) { - Toast.makeText(ctx, ctx.getString(R.string.shared_string_io_error) + ": " + exceptionMessage, Toast.LENGTH_LONG).show(); - } - } - try { - if (progress.getDialog().isShowing()) { - progress.getDialog().dismiss(); - } - } catch (Exception e) { - //ignored - } - } - - private void movingFiles(File f, File t, int depth) throws IOException { - Context ctx = context.get(); - if (ctx == null) { - return; - } - if (depth <= 2) { - progress.startTask(ctx.getString(R.string.copying_osmand_one_file_descr, t.getName()), -1); - } - if (f.isDirectory()) { - t.mkdirs(); - File[] lf = f.listFiles(); - if (lf != null) { - for (int i = 0; i < lf.length; i++) { - if (lf[i] != null) { - movingFiles(lf[i], new File(t, lf[i].getName()), depth + 1); - } - } - } - f.delete(); - } else if (f.isFile()) { - if (t.exists()) { - Algorithms.removeAllFiles(t); - } - boolean rnm = false; - long fileSize = f.length(); - try { - rnm = f.renameTo(t); - movedCount++; - movedSize += fileSize; - } catch (RuntimeException e) { - } - if (!rnm) { - FileInputStream fin = new FileInputStream(f); - FileOutputStream fout = new FileOutputStream(t); - try { - progress.startTask(ctx.getString(R.string.copying_osmand_one_file_descr, t.getName()), (int) (f.length() / 1024)); - Algorithms.streamCopy(fin, fout, progress, 1024); - copiedCount++; - copiedSize += fileSize; - } catch (IOException e) { - failedCount++; - failedSize += fileSize; - } finally { - fin.close(); - fout.close(); - } - f.delete(); - } - } - if (depth <= 2) { - progress.finishTask(); - } - } - - @Override - protected Boolean doInBackground(Void... params) { - to.mkdirs(); - try { - movingFiles(from, to, 0); - } catch (IOException e) { - exceptionMessage = e.getMessage(); - return false; - } - return true; - } - - } - - public static class ReloadData extends AsyncTask { - private WeakReference ctx; - protected ProgressImplementation progress; - private OsmandApplication app; - - public ReloadData(Context ctx, OsmandApplication app) { - this.ctx = new WeakReference<>(ctx); - this.app = app; - } - - @Override - protected void onPreExecute() { - Context c = ctx.get(); - if (c == null) { - return; - } - progress = ProgressImplementation.createProgressDialog(c, c.getString(R.string.loading_data), - c.getString(R.string.loading_data), ProgressDialog.STYLE_HORIZONTAL); - } - - @Override - protected void onPostExecute(Boolean result) { - try { - if (progress.getDialog().isShowing()) { - progress.getDialog().dismiss(); - } - } catch (Exception e) { - //ignored - } - } - - @Override - protected Boolean doInBackground(Void... params) { - app.getResourceManager().reloadIndexes(progress, new ArrayList()); - return true; - } - } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java new file mode 100644 index 0000000000..ecc0f04842 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java @@ -0,0 +1,321 @@ +package net.osmand.plus.settings.datastorage; + +import android.os.Build; + +import androidx.annotation.NonNull; + +import net.osmand.IndexConstants; +import net.osmand.ValueHolder; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.R; +import net.osmand.plus.settings.datastorage.item.DirectoryItem; +import net.osmand.plus.settings.datastorage.item.DirectoryItem.CheckingType; +import net.osmand.plus.settings.datastorage.item.MemoryItem; +import net.osmand.plus.settings.datastorage.item.StorageItem; +import net.osmand.plus.settings.datastorage.task.RefreshUsedMemoryTask; + +import java.io.File; +import java.text.DecimalFormat; +import java.util.ArrayList; + +import static net.osmand.IndexConstants.AV_INDEX_DIR; +import static net.osmand.IndexConstants.BACKUP_INDEX_DIR; +import static net.osmand.IndexConstants.GPX_INDEX_DIR; +import static net.osmand.IndexConstants.MAPS_PATH; +import static net.osmand.IndexConstants.ROADS_INDEX_DIR; +import static net.osmand.IndexConstants.SRTM_INDEX_DIR; +import static net.osmand.IndexConstants.TILES_INDEX_DIR; +import static net.osmand.IndexConstants.WIKIVOYAGE_INDEX_DIR; +import static net.osmand.IndexConstants.WIKI_INDEX_DIR; +import static net.osmand.plus.settings.datastorage.item.DirectoryItem.CheckingType.EXTENSIONS; +import static net.osmand.plus.settings.datastorage.item.DirectoryItem.CheckingType.PREFIX; + +public class DataStorageHelper { + public final static String INTERNAL_STORAGE = "internal_storage"; + public final static String EXTERNAL_STORAGE = "external_storage"; + public final static String SHARED_STORAGE = "shared_storage"; + public final static String MULTIUSER_STORAGE = "multiuser_storage"; + public final static String MANUALLY_SPECIFIED = "manually_specified"; + + public final static String MAPS_MEMORY = "maps_memory_used"; + public final static String TERRAIN_MEMORY = "terrain_memory_used"; + public final static String TRACKS_MEMORY = "tracks_memory_used"; + public final static String NOTES_MEMORY = "notes_memory_used"; + public final static String TILES_MEMORY = "tiles_memory_used"; + public final static String OTHER_MEMORY = "other_memory_used"; + + private OsmandApplication app; + private ArrayList storageItems = new ArrayList<>(); + private StorageItem currentDataStorage; + private StorageItem manuallySpecified; + + private ArrayList memoryItems = new ArrayList<>(); + private MemoryItem mapsMemory; + private MemoryItem terrainMemory; + private MemoryItem tracksMemory; + private MemoryItem notesMemory; + private MemoryItem tilesMemory; + private MemoryItem otherMemory; + + private int currentStorageType; + private String currentStoragePath; + + public DataStorageHelper(@NonNull OsmandApplication app) { + this.app = app; + prepareData(); + } + + private void prepareData() { + initStorageItems(); + initUsedMemoryItems(); + } + + private void initStorageItems() { + OsmandSettings settings = app.getSettings(); + if (settings.getExternalStorageDirectoryTypeV19() >= 0) { + currentStorageType = settings.getExternalStorageDirectoryTypeV19(); + } else { + ValueHolder vh = new ValueHolder(); + if (vh.value != null && vh.value >= 0) { + currentStorageType = vh.value; + } else { + currentStorageType = 0; + } + } + currentStoragePath = settings.getExternalStorageDirectory().getAbsolutePath(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + + //internal storage + String path = settings.getInternalAppPath().getAbsolutePath(); + File dir = new File(path); + int iconId = R.drawable.ic_action_phone; + + StorageItem internalStorageItem = StorageItem.builder() + .setKey(INTERNAL_STORAGE) + .setTitle(app.getString(R.string.storage_directory_internal_app)) + .setDirectory(path) + .setDescription(app.getString(R.string.internal_app_storage_description)) + .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_INTERNAL_FILE) + .setIconResId(iconId) + .createItem(); + addItem(internalStorageItem); + + //shared storage + dir = settings.getDefaultInternalStorage(); + path = dir.getAbsolutePath(); + iconId = R.drawable.ic_action_phone; + + StorageItem sharedStorageItem = StorageItem.builder() + .setKey(SHARED_STORAGE) + .setTitle(app.getString(R.string.storage_directory_shared)) + .setDirectory(path) + .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_DEFAULT) + .setIconResId(iconId) + .createItem(); + addItem(sharedStorageItem); + + //external storage + File[] externals = app.getExternalFilesDirs(null); + if (externals != null) { + int i = 0; + for (File external : externals) { + if (external != null) { + ++i; + dir = external; + path = dir.getAbsolutePath(); + iconId = getIconForStorageType(dir); + StorageItem externalStorageItem = StorageItem.builder() + .setKey(EXTERNAL_STORAGE + i) + .setTitle(app.getString(R.string.storage_directory_external) + " " + i) + .setDirectory(path) + .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_EXTERNAL_FILE) + .setIconResId(iconId) + .createItem(); + addItem(externalStorageItem); + } + } + } + + //multi user storage + File[] obbDirs = app.getObbDirs(); + if (obbDirs != null) { + int i = 0; + for (File obb : obbDirs) { + if (obb != null) { + ++i; + dir = obb; + path = dir.getAbsolutePath(); + iconId = getIconForStorageType(dir); + StorageItem multiuserStorageItem = StorageItem.builder() + .setKey(MULTIUSER_STORAGE + i) + .setTitle(app.getString(R.string.storage_directory_multiuser) + " " + i) + .setDirectory(path) + .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_OBB) + .setIconResId(iconId) + .createItem(); + addItem(multiuserStorageItem); + } + } + } + } + + //manually specified storage + manuallySpecified = StorageItem.builder() + .setKey(MANUALLY_SPECIFIED) + .setTitle(app.getString(R.string.storage_directory_manual)) + .setDirectory(currentStoragePath) + .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_SPECIFIED) + .setIconResId(R.drawable.ic_action_folder) + .createItem(); + storageItems.add(manuallySpecified); + + if (currentDataStorage == null) { + currentDataStorage = manuallySpecified; + } + } + + private void initUsedMemoryItems() { + mapsMemory = MemoryItem.builder() + .setKey(MAPS_MEMORY) + .setExtensions(IndexConstants.BINARY_MAP_INDEX_EXT) + .setDirectories( + createDirectory(MAPS_PATH, false, EXTENSIONS, true), + createDirectory(ROADS_INDEX_DIR, true, EXTENSIONS, true), + createDirectory(WIKI_INDEX_DIR, true, EXTENSIONS, true), + createDirectory(WIKIVOYAGE_INDEX_DIR, true, EXTENSIONS, true), + createDirectory(BACKUP_INDEX_DIR, true, EXTENSIONS, true)) + .createItem(); + memoryItems.add(mapsMemory); + + terrainMemory = MemoryItem.builder() + .setKey(TERRAIN_MEMORY) + .setExtensions(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + .setDirectories( + createDirectory(SRTM_INDEX_DIR, true, EXTENSIONS, true), + createDirectory(TILES_INDEX_DIR, false, PREFIX, false)) + .setPrefixes("Hillshade", "Slope") + .createItem(); + memoryItems.add(terrainMemory); + + tracksMemory = MemoryItem.builder() + .setKey(TRACKS_MEMORY) +// .setExtensions(IndexConstants.GPX_FILE_EXT, ".gpx.bz2") + .setDirectories( + createDirectory(GPX_INDEX_DIR, true, EXTENSIONS, true)) + .createItem(); + memoryItems.add(tracksMemory); + + notesMemory = MemoryItem.builder() + .setKey(NOTES_MEMORY) +// .setExtensions("") + .setDirectories( + createDirectory(AV_INDEX_DIR, true, EXTENSIONS, true)) + .createItem(); + memoryItems.add(notesMemory); + + tilesMemory = MemoryItem.builder() + .setKey(TILES_MEMORY) +// .setExtensions("") + .setDirectories( + createDirectory(TILES_INDEX_DIR, true, EXTENSIONS, true)) + .createItem(); + memoryItems.add(tilesMemory); + + otherMemory = MemoryItem.builder() + .setKey(OTHER_MEMORY) + .createItem(); + memoryItems.add(otherMemory); + } + + public ArrayList getStorageItems() { + return storageItems; + } + + private int getIconForStorageType(File dir) { + return R.drawable.ic_action_folder; + } + + public StorageItem getCurrentStorage() { + return currentDataStorage; + } + + private void addItem(StorageItem item) { + if (currentStorageType == item.getType() && currentStoragePath.equals(item.getDirectory())) { + currentDataStorage = item; + } + storageItems.add(item); + } + + public StorageItem getManuallySpecified() { + return manuallySpecified; + } + + public StorageItem getStorage(String key) { + if (storageItems != null && key != null) { + for (StorageItem storageItem : storageItems) { + if (key.equals(storageItem.getKey())) { + return storageItem; + } + } + } + return null; + } + + public ArrayList getMemoryInfoItems() { + return memoryItems; + } + + public RefreshUsedMemoryTask calculateMemoryUsedInfo(UpdateMemoryInfoUIAdapter uiAdapter) { + File rootDir = new File(currentStoragePath); + RefreshUsedMemoryTask task = new RefreshUsedMemoryTask(uiAdapter, otherMemory, rootDir, null, null, OTHER_MEMORY); + task.execute(mapsMemory, terrainMemory, tracksMemory, notesMemory); + return task; + } + + public RefreshUsedMemoryTask calculateTilesMemoryUsed(UpdateMemoryInfoUIAdapter listener) { + File rootDir = new File(tilesMemory.getDirectories()[0].getAbsolutePath()); + RefreshUsedMemoryTask task = new RefreshUsedMemoryTask(listener, otherMemory, rootDir, null, terrainMemory.getPrefixes(), TILES_MEMORY); + task.execute(tilesMemory); + return task; + } + + public long getTotalUsedBytes() { + long total = 0; + if (memoryItems != null && memoryItems.size() > 0) { + for (MemoryItem mi : memoryItems) { + total += mi.getUsedMemoryBytes(); + } + return total; + } + return -1; + } + + public DirectoryItem createDirectory(@NonNull String relativePath, + boolean processInternalDirectories, + CheckingType checkingType, + boolean addUnmatchedToOtherMemory) { + String path = app.getAppPath(relativePath).getAbsolutePath(); + return new DirectoryItem(path, processInternalDirectories, checkingType, addUnmatchedToOtherMemory); + } + + public static String getFormattedMemoryInfo(long bytes, String[] formatStrings) { + int type = 0; + double memory = (double) bytes / 1024; + while (memory > 1024 && type < formatStrings.length) { + ++type; + memory = memory / 1024; + } + String formattedUsed = new DecimalFormat("#.##").format(memory); + return String.format(formatStrings[type], formattedUsed); + } + + public interface UpdateMemoryInfoUIAdapter { + + void onMemoryInfoUpdate(); + + void onFinishUpdating(String tag); + + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/item/DirectoryItem.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/item/DirectoryItem.java new file mode 100644 index 0000000000..f316a47920 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/item/DirectoryItem.java @@ -0,0 +1,40 @@ +package net.osmand.plus.settings.datastorage.item; + +public class DirectoryItem { + + private final String absolutePath; + private final boolean processInternalDirectories; + private final CheckingType checkingType; + private final boolean addUnmatchedToOtherMemory; + + public enum CheckingType { + EXTENSIONS, + PREFIX + } + + public DirectoryItem(String absolutePath, + boolean processInternalDirectories, + CheckingType checkingType, + boolean addUnmatchedToOtherMemory) { + this.absolutePath = absolutePath; + this.processInternalDirectories = processInternalDirectories; + this.checkingType = checkingType; + this.addUnmatchedToOtherMemory = addUnmatchedToOtherMemory; + } + + public String getAbsolutePath() { + return absolutePath; + } + + public boolean shouldProcessInternalDirectories() { + return processInternalDirectories; + } + + public CheckingType getCheckingType() { + return checkingType; + } + + public boolean shouldAddUnmatchedToOtherMemory() { + return addUnmatchedToOtherMemory; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageMemoryItem.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/item/MemoryItem.java similarity index 52% rename from OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageMemoryItem.java rename to OsmAnd/src/net/osmand/plus/settings/datastorage/item/MemoryItem.java index f7f955f89a..8fb65f99c3 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageMemoryItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/item/MemoryItem.java @@ -1,16 +1,18 @@ -package net.osmand.plus.settings.fragments; +package net.osmand.plus.settings.datastorage.item; + +public class MemoryItem { -public class DataStorageMemoryItem { - public final static int EXTENSIONS = 0; - public final static int PREFIX = 1; - private String key; - private String[] extensions; - private String[] prefixes; - private Directory[] directories; + private final String[] extensions; + private final String[] prefixes; + private final DirectoryItem[] directories; private long usedMemoryBytes; - private DataStorageMemoryItem(String key, String[] extensions, String[] prefixes, long usedMemoryBytes, Directory[] directories) { + private MemoryItem(String key, + String[] extensions, + String[] prefixes, + long usedMemoryBytes, + DirectoryItem[] directories) { this.key = key; this.extensions = extensions; this.prefixes = prefixes; @@ -42,7 +44,7 @@ public class DataStorageMemoryItem { return prefixes; } - public Directory[] getDirectories() { + public DirectoryItem[] getDirectories() { return directories; } @@ -54,7 +56,7 @@ public class DataStorageMemoryItem { private String key; private String[] extensions; private String[] prefixes; - private Directory[] directories; + private DirectoryItem[] directories; private long usedMemoryBytes; public DataStorageMemoryItemBuilder setKey(String key) { @@ -72,7 +74,7 @@ public class DataStorageMemoryItem { return this; } - public DataStorageMemoryItemBuilder setDirectories(Directory ... directories) { + public DataStorageMemoryItemBuilder setDirectories(DirectoryItem... directories) { this.directories = directories; return this; } @@ -82,38 +84,8 @@ public class DataStorageMemoryItem { return this; } - public DataStorageMemoryItem createItem() { - return new DataStorageMemoryItem(key, extensions, prefixes, usedMemoryBytes, directories); - } - } - - public static class Directory { - private String absolutePath; - private boolean goDeeper; - private int checkingType; - private boolean skipOther; - - public Directory(String absolutePath, boolean goDeeper, int checkingType, boolean skipOther) { - this.absolutePath = absolutePath; - this.goDeeper = goDeeper; - this.checkingType = checkingType; - this.skipOther = skipOther; - } - - public String getAbsolutePath() { - return absolutePath; - } - - public boolean isGoDeeper() { - return goDeeper; - } - - public int getCheckingType() { - return checkingType; - } - - public boolean isSkipOther() { - return skipOther; + public MemoryItem createItem() { + return new MemoryItem(key, extensions, prefixes, usedMemoryBytes, directories); } } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageMenuItem.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/item/StorageItem.java similarity index 78% rename from OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageMenuItem.java rename to OsmAnd/src/net/osmand/plus/settings/datastorage/item/StorageItem.java index f965b95fb1..717bcbd5e7 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageMenuItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/item/StorageItem.java @@ -1,11 +1,11 @@ -package net.osmand.plus.settings.fragments; +package net.osmand.plus.settings.datastorage.item; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.IdRes; -public class DataStorageMenuItem implements Parcelable, Cloneable { +public class StorageItem implements Parcelable, Cloneable { private String key; private int type; @@ -15,8 +15,12 @@ public class DataStorageMenuItem implements Parcelable, Cloneable { @IdRes private int iconResId; - private DataStorageMenuItem(String key, int type, String title, String description, - String directory, int iconResId) { + private StorageItem(String key, + int type, + String title, + String description, + String directory, + int iconResId) { this.key = key; this.type = type; this.title = title; @@ -25,7 +29,7 @@ public class DataStorageMenuItem implements Parcelable, Cloneable { this.iconResId = iconResId; } - private DataStorageMenuItem(Parcel in) { + private StorageItem(Parcel in) { key = in.readString(); type = in.readInt(); title = in.readString(); @@ -99,16 +103,16 @@ public class DataStorageMenuItem implements Parcelable, Cloneable { dest.writeString(directory); } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override - public DataStorageMenuItem createFromParcel(Parcel source) { - return new DataStorageMenuItem(source); + public StorageItem createFromParcel(Parcel source) { + return new StorageItem(source); } @Override - public DataStorageMenuItem[] newArray(int size) { - return new DataStorageMenuItem[size]; + public StorageItem[] newArray(int size) { + return new StorageItem[size]; } }; @@ -151,14 +155,14 @@ public class DataStorageMenuItem implements Parcelable, Cloneable { return this; } - public DataStorageMenuItem createItem() { - return new DataStorageMenuItem(key, type, title, description, directory, iconResId); + public StorageItem createItem() { + return new StorageItem(key, type, title, description, directory, iconResId); } } @Override public Object clone() throws CloneNotSupportedException { - return DataStorageMenuItem.builder() + return StorageItem.builder() .setKey(this.key) .setTitle(this.title) .setDescription(this.description) diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/task/MoveFilesTask.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/task/MoveFilesTask.java new file mode 100644 index 0000000000..b6fc45d93c --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/task/MoveFilesTask.java @@ -0,0 +1,173 @@ +package net.osmand.plus.settings.datastorage.task; + +import android.app.ProgressDialog; +import android.content.Context; +import android.os.AsyncTask; +import android.widget.Toast; + +import net.osmand.plus.ProgressImplementation; +import net.osmand.plus.R; +import net.osmand.plus.activities.OsmandActionBarActivity; +import net.osmand.util.Algorithms; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.ref.WeakReference; + +public class MoveFilesTask extends AsyncTask { + + protected WeakReference activity; + private WeakReference context; + private File from; + private File to; + protected ProgressImplementation progress; + private Runnable runOnSuccess; + private int movedCount; + private long movedSize; + private int copiedCount; + private long copiedSize; + private int failedCount; + private long failedSize; + private String exceptionMessage; + + public MoveFilesTask(OsmandActionBarActivity activity, File from, File to) { + this.activity = new WeakReference<>(activity); + this.context = new WeakReference<>((Context) activity); + this.from = from; + this.to = to; + } + + public void setRunOnSuccess(Runnable runOnSuccess) { + this.runOnSuccess = runOnSuccess; + } + + public int getMovedCount() { + return movedCount; + } + + public int getCopiedCount() { + return copiedCount; + } + + public int getFailedCount() { + return failedCount; + } + + public long getMovedSize() { + return movedSize; + } + + public long getCopiedSize() { + return copiedSize; + } + + public long getFailedSize() { + return failedSize; + } + + @Override + protected void onPreExecute() { + Context ctx = context.get(); + if (context == null) { + return; + } + movedCount = 0; + copiedCount = 0; + failedCount = 0; + progress = ProgressImplementation.createProgressDialog( + ctx, ctx.getString(R.string.copying_osmand_files), + ctx.getString(R.string.copying_osmand_files_descr, to.getPath()), + ProgressDialog.STYLE_HORIZONTAL); + } + + @Override + protected void onPostExecute(Boolean result) { + Context ctx = context.get(); + if (ctx == null) { + return; + } + if (result != null) { + if (result.booleanValue() && runOnSuccess != null) { + runOnSuccess.run(); + } else if (!result.booleanValue()) { + Toast.makeText(ctx, ctx.getString(R.string.shared_string_io_error) + ": " + exceptionMessage, Toast.LENGTH_LONG).show(); + } + } + try { + if (progress.getDialog().isShowing()) { + progress.getDialog().dismiss(); + } + } catch (Exception e) { + //ignored + } + } + + private void movingFiles(File f, File t, int depth) throws IOException { + Context ctx = context.get(); + if (ctx == null) { + return; + } + if (depth <= 2) { + progress.startTask(ctx.getString(R.string.copying_osmand_one_file_descr, t.getName()), -1); + } + if (f.isDirectory()) { + t.mkdirs(); + File[] lf = f.listFiles(); + if (lf != null) { + for (int i = 0; i < lf.length; i++) { + if (lf[i] != null) { + movingFiles(lf[i], new File(t, lf[i].getName()), depth + 1); + } + } + } + f.delete(); + } else if (f.isFile()) { + if (t.exists()) { + Algorithms.removeAllFiles(t); + } + boolean rnm = false; + long fileSize = f.length(); + try { + rnm = f.renameTo(t); + movedCount++; + movedSize += fileSize; + } catch (RuntimeException e) { + } + if (!rnm) { + FileInputStream fin = new FileInputStream(f); + FileOutputStream fout = new FileOutputStream(t); + try { + progress.startTask(ctx.getString(R.string.copying_osmand_one_file_descr, t.getName()), (int) (f.length() / 1024)); + Algorithms.streamCopy(fin, fout, progress, 1024); + copiedCount++; + copiedSize += fileSize; + } catch (IOException e) { + failedCount++; + failedSize += fileSize; + } finally { + fin.close(); + fout.close(); + } + f.delete(); + } + } + if (depth <= 2) { + progress.finishTask(); + } + } + + @Override + protected Boolean doInBackground(Void... params) { + to.mkdirs(); + try { + movingFiles(from, to, 0); + } catch (IOException e) { + exceptionMessage = e.getMessage(); + return false; + } + return true; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/task/RefreshUsedMemoryTask.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/task/RefreshUsedMemoryTask.java new file mode 100644 index 0000000000..123aac1b2b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/task/RefreshUsedMemoryTask.java @@ -0,0 +1,232 @@ +package net.osmand.plus.settings.datastorage.task; + +import android.os.AsyncTask; + +import androidx.annotation.NonNull; + +import net.osmand.plus.settings.datastorage.DataStorageHelper.UpdateMemoryInfoUIAdapter; +import net.osmand.plus.settings.datastorage.item.DirectoryItem; +import net.osmand.plus.settings.datastorage.item.DirectoryItem.CheckingType; +import net.osmand.plus.settings.datastorage.item.MemoryItem; + +import java.io.File; + +import static net.osmand.plus.settings.datastorage.DataStorageFragment.UI_REFRESH_TIME_MS; +import static net.osmand.util.Algorithms.objectEquals; + +public class RefreshUsedMemoryTask extends AsyncTask { + private final UpdateMemoryInfoUIAdapter uiAdapter; + private final File root; + private final MemoryItem otherMemoryItem; + private final String[] directoriesToSkip; + private final String[] filePrefixesToSkip; + private final String tag; + private long lastRefreshTime; + + public RefreshUsedMemoryTask(UpdateMemoryInfoUIAdapter uiAdapter, + MemoryItem otherMemoryItem, + File root, + String[] directoriesToSkip, + String[] filePrefixesToSkip, + String tag) { + this.uiAdapter = uiAdapter; + this.otherMemoryItem = otherMemoryItem; + this.root = root; + this.directoriesToSkip = directoriesToSkip; + this.filePrefixesToSkip = filePrefixesToSkip; + this.tag = tag; + } + + @Override + protected Void doInBackground(MemoryItem... items) { + lastRefreshTime = System.currentTimeMillis(); + if (root.canRead()) { + calculateMultiTypes(root, items); + } + return null; + } + + private void calculateMultiTypes(File rootDir, + MemoryItem... items) { + File[] subFiles = rootDir.listFiles(); + if (subFiles != null) { + for (File file : subFiles) { + if (isCancelled()) break; + + if (file.isDirectory()) { + if (!shouldSkipDirectory(file)) { + processDirectory(file, items); + } + + } else if (file.isFile()) { + if (!shouldSkipFile(file)) { + processFile(rootDir, file, items); + } + } + refreshUI(); + } + } + } + + private boolean shouldSkipDirectory(@NonNull File dir) { + if (directoriesToSkip != null) { + for (String dirToSkipPath : directoriesToSkip) { + String dirPath = dir.getAbsolutePath(); + if (objectEquals(dirPath, dirToSkipPath)) { + return true; + } + } + } + return false; + } + + private boolean shouldSkipFile(@NonNull File file) { + if (filePrefixesToSkip != null) { + String fileName = file.getName().toLowerCase(); + for (String prefixToAvoid : filePrefixesToSkip) { + String prefix = prefixToAvoid.toLowerCase(); + if (fileName.startsWith(prefix)) { + return true; + } + } + } + return false; + } + + private void processDirectory(@NonNull File directory, + @NonNull MemoryItem... items) { + String directoryPath = directory.getAbsolutePath(); + for (MemoryItem memoryItem : items) { + DirectoryItem[] targetDirectories = memoryItem.getDirectories(); + if (targetDirectories != null) { + for (DirectoryItem dir : targetDirectories) { + String allowedDirPath = dir.getAbsolutePath(); + boolean isPerfectlyMatch = objectEquals(directoryPath, allowedDirPath); + boolean isParentDirectory = !isPerfectlyMatch && (directoryPath.startsWith(allowedDirPath)); + boolean isMatchDirectory = isPerfectlyMatch || isParentDirectory; + if (isPerfectlyMatch) { + calculateMultiTypes(directory, items); + return; + } else if (isParentDirectory && dir.shouldProcessInternalDirectories()) { + calculateMultiTypes(directory, items); + return; + } else if (isMatchDirectory && !dir.shouldAddUnmatchedToOtherMemory()) { + return; + } + } + } + } + // Current directory did not match to any type + otherMemoryItem.addBytes(calculateFolderSize(directory)); + } + + private void processFile(@NonNull File rootDir, + @NonNull File file, + @NonNull MemoryItem... items) { + for (MemoryItem item : items) { + DirectoryItem[] targetDirectories = item.getDirectories(); + if (targetDirectories == null) continue; + String rootDirPath = rootDir.getAbsolutePath(); + + for (DirectoryItem targetDirectory : targetDirectories) { + String allowedDirPath = targetDirectory.getAbsolutePath(); + boolean processInternal = targetDirectory.shouldProcessInternalDirectories(); + if (objectEquals(rootDirPath, allowedDirPath) + || (rootDirPath.startsWith(allowedDirPath) && processInternal)) { + CheckingType checkingType = targetDirectory.getCheckingType(); + switch (checkingType) { + case EXTENSIONS: { + if (isSuitableExtension(file, item)) { + item.addBytes(file.length()); + return; + } + break; + } + case PREFIX: { + if (isSuitablePrefix(file, item)) { + item.addBytes(file.length()); + return; + } + break; + } + } + if (!targetDirectory.shouldAddUnmatchedToOtherMemory()) { + return; + } + } + } + } + // Current file did not match any type + otherMemoryItem.addBytes(file.length()); + } + + private boolean isSuitableExtension(@NonNull File file, + @NonNull MemoryItem item) { + String[] extensions = item.getExtensions(); + if (extensions != null) { + for (String extension : extensions) { + if (file.getAbsolutePath().endsWith(extension)) { + return true; + } + } + } + return extensions == null; + } + + private boolean isSuitablePrefix(@NonNull File file, + @NonNull MemoryItem item) { + String[] prefixes = item.getPrefixes(); + if (prefixes != null) { + for (String prefix : prefixes) { + if (file.getName().toLowerCase().startsWith(prefix.toLowerCase())) { + return true; + } + } + } + return prefixes == null; + } + + private long calculateFolderSize(@NonNull File dir) { + long bytes = 0; + if (dir.isDirectory()) { + File[] files = dir.listFiles(); + if (files == null) return 0; + + for (File file : files) { + if (isCancelled()) { + break; + } + if (file.isDirectory()) { + bytes += calculateFolderSize(file); + } else if (file.isFile()) { + bytes += file.length(); + } + } + } + return bytes; + } + + @Override + protected void onProgressUpdate(Void... values) { + super.onProgressUpdate(values); + if (uiAdapter != null) { + uiAdapter.onMemoryInfoUpdate(); + } + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + if (uiAdapter != null) { + uiAdapter.onFinishUpdating(tag); + } + } + + private void refreshUI() { + long currentTime = System.currentTimeMillis(); + if ((currentTime - lastRefreshTime) > UI_REFRESH_TIME_MS) { + lastRefreshTime = currentTime; + publishProgress(); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/task/ReloadDataTask.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/task/ReloadDataTask.java new file mode 100644 index 0000000000..4e097b9df2 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/task/ReloadDataTask.java @@ -0,0 +1,50 @@ +package net.osmand.plus.settings.datastorage.task; + +import android.app.ProgressDialog; +import android.content.Context; +import android.os.AsyncTask; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.ProgressImplementation; +import net.osmand.plus.R; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +public class ReloadDataTask extends AsyncTask { + private WeakReference ctx; + protected ProgressImplementation progress; + private OsmandApplication app; + + public ReloadDataTask(Context ctx, OsmandApplication app) { + this.ctx = new WeakReference<>(ctx); + this.app = app; + } + + @Override + protected void onPreExecute() { + Context c = ctx.get(); + if (c == null) { + return; + } + progress = ProgressImplementation.createProgressDialog(c, c.getString(R.string.loading_data), + c.getString(R.string.loading_data), ProgressDialog.STYLE_HORIZONTAL); + } + + @Override + protected void onPostExecute(Boolean result) { + try { + if (progress.getDialog().isShowing()) { + progress.getDialog().dismiss(); + } + } catch (Exception e) { + //ignored + } + } + + @Override + protected Boolean doInBackground(Void... params) { + app.getResourceManager().reloadIndexes(progress, new ArrayList()); + return true; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java index e2d3244f26..947a331290 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java @@ -73,6 +73,7 @@ import net.osmand.plus.settings.bottomsheets.ChangeGeneralProfilesPrefBottomShee import net.osmand.plus.settings.bottomsheets.EditTextPreferenceBottomSheet; import net.osmand.plus.settings.bottomsheets.MultiSelectPreferencesBottomSheet; import net.osmand.plus.settings.bottomsheets.SingleSelectPreferenceBottomSheet; +import net.osmand.plus.settings.datastorage.DataStorageFragment; import net.osmand.plus.settings.preferences.ListPreferenceEx; import net.osmand.plus.settings.preferences.MultiSelectBooleanPreference; import net.osmand.plus.settings.preferences.SwitchPreferenceEx; diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageHelper.java b/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageHelper.java deleted file mode 100644 index b270175d86..0000000000 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageHelper.java +++ /dev/null @@ -1,485 +0,0 @@ -package net.osmand.plus.settings.fragments; - -import android.os.AsyncTask; -import android.os.Build; - -import net.osmand.IndexConstants; -import net.osmand.ValueHolder; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.R; - -import java.io.File; -import java.text.DecimalFormat; -import java.util.ArrayList; - -import static net.osmand.plus.settings.fragments.DataStorageFragment.UI_REFRESH_TIME_MS; -import static net.osmand.plus.settings.fragments.DataStorageMemoryItem.Directory; -import static net.osmand.plus.settings.fragments.DataStorageMemoryItem.EXTENSIONS; -import static net.osmand.plus.settings.fragments.DataStorageMemoryItem.PREFIX; - -public class DataStorageHelper { - public final static String INTERNAL_STORAGE = "internal_storage"; - public final static String EXTERNAL_STORAGE = "external_storage"; - public final static String SHARED_STORAGE = "shared_storage"; - public final static String MULTIUSER_STORAGE = "multiuser_storage"; - public final static String MANUALLY_SPECIFIED = "manually_specified"; - - public final static String MAPS_MEMORY = "maps_memory_used"; - public final static String SRTM_AND_HILLSHADE_MEMORY = "contour_lines_and_hillshade_memory"; - public final static String TRACKS_MEMORY = "tracks_memory_used"; - public final static String NOTES_MEMORY = "notes_memory_used"; - public final static String TILES_MEMORY = "tiles_memory_used"; - public final static String OTHER_MEMORY = "other_memory_used"; - - private ArrayList menuItems = new ArrayList<>(); - private DataStorageMenuItem currentDataStorage; - private DataStorageMenuItem manuallySpecified; - - private ArrayList memoryItems = new ArrayList<>(); - private DataStorageMemoryItem mapsMemory; - private DataStorageMemoryItem srtmAndHillshadeMemory; - private DataStorageMemoryItem tracksMemory; - private DataStorageMemoryItem notesMemory; - private DataStorageMemoryItem tilesMemory; - private DataStorageMemoryItem otherMemory; - - private int currentStorageType; - private String currentStoragePath; - - public DataStorageHelper(OsmandApplication app) { - prepareData(app); - } - - private void prepareData(OsmandApplication app) { - - if (app == null) { - return; - } - - OsmandSettings settings = app.getSettings(); - - if (settings.getExternalStorageDirectoryTypeV19() >= 0) { - currentStorageType = settings.getExternalStorageDirectoryTypeV19(); - } else { - ValueHolder vh = new ValueHolder(); - if (vh.value != null && vh.value >= 0) { - currentStorageType = vh.value; - } else { - currentStorageType = 0; - } - } - currentStoragePath = settings.getExternalStorageDirectory().getAbsolutePath(); - - String path; - File dir; - int iconId; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - - //internal storage - path = settings.getInternalAppPath().getAbsolutePath(); - dir = new File(path); - iconId = R.drawable.ic_action_phone; - - DataStorageMenuItem internalStorageItem = DataStorageMenuItem.builder() - .setKey(INTERNAL_STORAGE) - .setTitle(app.getString(R.string.storage_directory_internal_app)) - .setDirectory(path) - .setDescription(app.getString(R.string.internal_app_storage_description)) - .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_INTERNAL_FILE) - .setIconResId(iconId) - .createItem(); - addItem(internalStorageItem); - - //shared_storage - dir = settings.getDefaultInternalStorage(); - path = dir.getAbsolutePath(); - iconId = R.drawable.ic_action_phone; - - DataStorageMenuItem sharedStorageItem = DataStorageMenuItem.builder() - .setKey(SHARED_STORAGE) - .setTitle(app.getString(R.string.storage_directory_shared)) - .setDirectory(path) - .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_DEFAULT) - .setIconResId(iconId) - .createItem(); - addItem(sharedStorageItem); - - //external storage - File[] externals = app.getExternalFilesDirs(null); - if (externals != null) { - int i = 0; - for (File external : externals) { - if (external != null) { - ++i; - dir = external; - path = dir.getAbsolutePath(); - iconId = getIconForStorageType(dir); - DataStorageMenuItem externalStorageItem = DataStorageMenuItem.builder() - .setKey(EXTERNAL_STORAGE + i) - .setTitle(app.getString(R.string.storage_directory_external) + " " + i) - .setDirectory(path) - .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_EXTERNAL_FILE) - .setIconResId(iconId) - .createItem(); - addItem(externalStorageItem); - } - } - } - - //multi user storage - File[] obbDirs = app.getObbDirs(); - if (obbDirs != null) { - int i = 0; - for (File obb : obbDirs) { - if (obb != null) { - ++i; - dir = obb; - path = dir.getAbsolutePath(); - iconId = getIconForStorageType(dir); - DataStorageMenuItem multiuserStorageItem = DataStorageMenuItem.builder() - .setKey(MULTIUSER_STORAGE + i) - .setTitle(app.getString(R.string.storage_directory_multiuser) + " " + i) - .setDirectory(path) - .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_OBB) - .setIconResId(iconId) - .createItem(); - addItem(multiuserStorageItem); - } - } - } - } - - //manually specified storage - manuallySpecified = DataStorageMenuItem.builder() - .setKey(MANUALLY_SPECIFIED) - .setTitle(app.getString(R.string.storage_directory_manual)) - .setDirectory(currentStoragePath) - .setType(OsmandSettings.EXTERNAL_STORAGE_TYPE_SPECIFIED) - .setIconResId(R.drawable.ic_action_folder) - .createItem(); - menuItems.add(manuallySpecified); - - if (currentDataStorage == null) { - currentDataStorage = manuallySpecified; - } - - initMemoryUsed(app); - } - - private void initMemoryUsed(OsmandApplication app) { - mapsMemory = DataStorageMemoryItem.builder() - .setKey(MAPS_MEMORY) - .setExtensions(IndexConstants.BINARY_MAP_INDEX_EXT) - .setDirectories( - new Directory(app.getAppPath(IndexConstants.MAPS_PATH).getAbsolutePath(), false, EXTENSIONS, false), - new Directory(app.getAppPath(IndexConstants.ROADS_INDEX_DIR).getAbsolutePath(), true, EXTENSIONS, false), - new Directory(app.getAppPath(IndexConstants.WIKI_INDEX_DIR).getAbsolutePath(), true, EXTENSIONS, false), - new Directory(app.getAppPath(IndexConstants.WIKIVOYAGE_INDEX_DIR).getAbsolutePath(), true, EXTENSIONS, false), - new Directory(app.getAppPath(IndexConstants.BACKUP_INDEX_DIR).getAbsolutePath(), true, EXTENSIONS, false)) - .createItem(); - memoryItems.add(mapsMemory); - - srtmAndHillshadeMemory = DataStorageMemoryItem.builder() - .setKey(SRTM_AND_HILLSHADE_MEMORY) - .setExtensions(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) - .setDirectories( - new Directory(app.getAppPath(IndexConstants.SRTM_INDEX_DIR).getAbsolutePath(), true, EXTENSIONS, false), - new Directory(app.getAppPath(IndexConstants.TILES_INDEX_DIR).getAbsolutePath(), false, PREFIX, true)) - .setPrefixes("Hillshade") - .createItem(); - memoryItems.add(srtmAndHillshadeMemory); - - tracksMemory = DataStorageMemoryItem.builder() - .setKey(TRACKS_MEMORY) -// .setExtensions(IndexConstants.GPX_FILE_EXT, ".gpx.bz2") - .setDirectories( - new Directory(app.getAppPath(IndexConstants.GPX_INDEX_DIR).getAbsolutePath(), true, EXTENSIONS, false)) - .createItem(); - memoryItems.add(tracksMemory); - - notesMemory = DataStorageMemoryItem.builder() - .setKey(NOTES_MEMORY) -// .setExtensions("") - .setDirectories( - new Directory(app.getAppPath(IndexConstants.AV_INDEX_DIR).getAbsolutePath(), true, EXTENSIONS, false)) - .createItem(); - memoryItems.add(notesMemory); - - tilesMemory = DataStorageMemoryItem.builder() - .setKey(TILES_MEMORY) -// .setExtensions("") - .setDirectories( - new Directory(app.getAppPath(IndexConstants.TILES_INDEX_DIR).getAbsolutePath(), true, EXTENSIONS, false)) - .createItem(); - memoryItems.add(tilesMemory); - - otherMemory = DataStorageMemoryItem.builder() - .setKey(OTHER_MEMORY) - .createItem(); - memoryItems.add(otherMemory); - } - - public ArrayList getStorageItems() { - return menuItems; - } - - private int getIconForStorageType(File dir) { - return R.drawable.ic_action_folder; - } - - public DataStorageMenuItem getCurrentStorage() { - return currentDataStorage; - } - - private void addItem(DataStorageMenuItem item) { - if (currentStorageType == item.getType() && currentStoragePath.equals(item.getDirectory())) { - currentDataStorage = item; - } - menuItems.add(item); - } - - public DataStorageMenuItem getManuallySpecified() { - return manuallySpecified; - } - - public DataStorageMenuItem getStorage(String key) { - if (menuItems != null && key != null) { - for (DataStorageMenuItem menuItem : menuItems) { - if (key.equals(menuItem.getKey())) { - return menuItem; - } - } - } - return null; - } - - public int getCurrentType() { - return currentStorageType; - } - - public String getCurrentPath() { - return currentStoragePath; - } - - public ArrayList getMemoryInfoItems() { - return memoryItems; - } - - public RefreshMemoryUsedInfo calculateMemoryUsedInfo(UpdateMemoryInfoUIAdapter listener) { - File rootDir = new File(currentStoragePath); - RefreshMemoryUsedInfo task = new RefreshMemoryUsedInfo(listener, otherMemory, rootDir, null, null, OTHER_MEMORY); - task.execute(mapsMemory, srtmAndHillshadeMemory, tracksMemory, notesMemory); - return task; - } - - public RefreshMemoryUsedInfo calculateTilesMemoryUsed(UpdateMemoryInfoUIAdapter listener) { - File rootDir = new File(tilesMemory.getDirectories()[0].getAbsolutePath()); - RefreshMemoryUsedInfo task = new RefreshMemoryUsedInfo(listener, otherMemory, rootDir, null, srtmAndHillshadeMemory.getPrefixes(), TILES_MEMORY); - task.execute(tilesMemory); - return task; - } - - public static class RefreshMemoryUsedInfo extends AsyncTask { - private UpdateMemoryInfoUIAdapter listener; - private File rootDir; - private DataStorageMemoryItem otherMemory; - private String[] directoriesToAvoid; - private String[] prefixesToAvoid; - private String taskKey; - private long lastRefreshTime; - - public RefreshMemoryUsedInfo(UpdateMemoryInfoUIAdapter listener, DataStorageMemoryItem otherMemory, File rootDir, String[] directoriesToAvoid, String[] prefixesToAvoid, String taskKey) { - this.listener = listener; - this.otherMemory = otherMemory; - this.rootDir = rootDir; - this.directoriesToAvoid = directoriesToAvoid; - this.prefixesToAvoid = prefixesToAvoid; - this.taskKey = taskKey; - } - - @Override - protected Void doInBackground(DataStorageMemoryItem... items) { - lastRefreshTime = System.currentTimeMillis(); - if (rootDir.canRead()) { - calculateMultiTypes(rootDir, items); - } - return null; - } - - private void calculateMultiTypes(File rootDir, DataStorageMemoryItem... items) { - File[] subFiles = rootDir.listFiles(); - - for (File file : subFiles) { - if (isCancelled()) { - break; - } - nextFile : { - if (file.isDirectory()) { - //check current directory should be avoid - if (directoriesToAvoid != null) { - for (String directoryToAvoid : directoriesToAvoid) { - if (file.getAbsolutePath().equals(directoryToAvoid)) { - break nextFile; - } - } - } - //check current directory matched items type - for (DataStorageMemoryItem item : items) { - Directory[] directories = item.getDirectories(); - if (directories == null) { - continue; - } - for (Directory dir : directories) { - if (file.getAbsolutePath().equals(dir.getAbsolutePath()) - || (file.getAbsolutePath().startsWith(dir.getAbsolutePath()))) { - if (dir.isGoDeeper()) { - calculateMultiTypes(file, items); - break nextFile; - } else if (dir.isSkipOther()) { - break nextFile; - } - } - } - } - //current directory did not match to any type - otherMemory.addBytes(getDirectorySize(file)); - } else if (file.isFile()) { - //check current file should be avoid - if (prefixesToAvoid != null) { - for (String prefixToAvoid : prefixesToAvoid) { - if (file.getName().toLowerCase().startsWith(prefixToAvoid.toLowerCase())) { - break nextFile; - } - } - } - //check current file matched items type - for (DataStorageMemoryItem item : items) { - Directory[] directories = item.getDirectories(); - if (directories == null) { - continue; - } - for (Directory dir : directories) { - if (rootDir.getAbsolutePath().equals(dir.getAbsolutePath()) - || (rootDir.getAbsolutePath().startsWith(dir.getAbsolutePath()) && dir.isGoDeeper())) { - int checkingType = dir.getCheckingType(); - switch (checkingType) { - case EXTENSIONS : { - String[] extensions = item.getExtensions(); - if (extensions != null) { - for (String extension : extensions) { - if (file.getAbsolutePath().endsWith(extension)) { - item.addBytes(file.length()); - break nextFile; - } - } - } else { - item.addBytes(file.length()); - break nextFile; - } - break ; - } - case PREFIX : { - String[] prefixes = item.getPrefixes(); - if (prefixes != null) { - for (String prefix : prefixes) { - if (file.getName().toLowerCase().startsWith(prefix.toLowerCase())) { - item.addBytes(file.length()); - break nextFile; - } - } - } else { - item.addBytes(file.length()); - break nextFile; - } - break ; - } - } - if (dir.isSkipOther()) { - break nextFile; - } - } - } - } - //current file did not match any type - otherMemory.addBytes(file.length()); - } - } - refreshUI(); - } - } - - private long getDirectorySize(File dir) { - long bytes = 0; - if (dir.isDirectory()) { - File[] files = dir.listFiles(); - for (File file : files) { - if (isCancelled()) { - break; - } - if (file.isDirectory()) { - bytes += getDirectorySize(file); - } else if (file.isFile()) { - bytes += file.length(); - } - } - } - return bytes; - } - - @Override - protected void onProgressUpdate(Void... values) { - super.onProgressUpdate(values); - if (listener != null) { - listener.onMemoryInfoUpdate(); - } - } - - @Override - protected void onPostExecute(Void aVoid) { - super.onPostExecute(aVoid); - if (listener != null) { - listener.onFinishUpdating(taskKey); - } - } - - private void refreshUI() { - long currentTime = System.currentTimeMillis(); - if ((currentTime - lastRefreshTime) > UI_REFRESH_TIME_MS) { - lastRefreshTime = currentTime; - publishProgress(); - } - } - } - - public long getTotalUsedBytes() { - long total = 0; - if (memoryItems != null && memoryItems.size() > 0) { - for (DataStorageMemoryItem mi : memoryItems) { - total += mi.getUsedMemoryBytes(); - } - return total; - } - return -1; - } - - public static String getFormattedMemoryInfo(long bytes, String[] formatStrings) { - int type = 0; - double memory = (double) bytes / 1024; - while (memory > 1024 && type < formatStrings.length) { - ++type; - memory = memory / 1024; - } - String formattedUsed = new DecimalFormat("#.##").format(memory); - return String.format(formatStrings[type], formattedUsed); - } - - public interface UpdateMemoryInfoUIAdapter { - - void onMemoryInfoUpdate(); - - void onFinishUpdating(String taskKey); - - } -} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/GlobalSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/GlobalSettingsFragment.java index fa7383228c..071d7be709 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/GlobalSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/GlobalSettingsFragment.java @@ -22,6 +22,8 @@ import net.osmand.plus.profiles.SelectProfileBottomSheet.DialogMode; import net.osmand.plus.profiles.SelectProfileBottomSheet.OnSelectProfileCallback; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.datastorage.DataStorageHelper; +import net.osmand.plus.settings.datastorage.item.StorageItem; import net.osmand.plus.settings.preferences.ListPreferenceEx; import net.osmand.plus.settings.preferences.SwitchPreferenceEx; @@ -181,7 +183,7 @@ public class GlobalSettingsFragment extends BaseSettingsFragment externalStorageDir.setIcon(getActiveIcon(R.drawable.ic_action_folder)); DataStorageHelper holder = new DataStorageHelper(app); - DataStorageMenuItem currentStorage = holder.getCurrentStorage(); + StorageItem currentStorage = holder.getCurrentStorage(); long totalUsed = app.getSettings().OSMAND_USAGE_SPACE.get(); if (totalUsed > 0) { String[] usedMemoryFormats = new String[] {