| package net.osmand.plus.settings; |
| |
| 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.OsmandSettings; |
| import net.osmand.plus.R; |
| |
| import java.io.File; |
| import java.text.DecimalFormat; |
| import java.util.ArrayList; |
| |
| import static net.osmand.plus.settings.DataStorageFragment.UI_REFRESH_TIME_MS; |
| import static net.osmand.plus.settings.DataStorageMemoryItem.Directory; |
| import static net.osmand.plus.settings.DataStorageMemoryItem.EXTENSIONS; |
| import static net.osmand.plus.settings.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<DataStorageMenuItem> menuItems = new ArrayList<>(); |
| private DataStorageMenuItem currentDataStorage; |
| private DataStorageMenuItem manuallySpecified; |
| |
| private ArrayList<DataStorageMemoryItem> 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; |
| |
| private DataStorageHelper(OsmandApplication app) { |
| prepareData(app); |
| } |
| |
| public static DataStorageHelper refreshInfo(OsmandApplication app) { |
| return new DataStorageHelper(app); |
| } |
| |
| private void prepareData(OsmandApplication app) { |
| |
| if (app == null) { |
| return; |
| } |
| |
| OsmandSettings settings = app.getSettings(); |
| |
| if (settings.getExternalStorageDirectoryTypeV19() >= 0) { |
| currentStorageType = settings.getExternalStorageDirectoryTypeV19(); |
| } else { |
| ValueHolder<Integer> vh = new ValueHolder<Integer>(); |
| 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(".gpx", ".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<DataStorageMenuItem> 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<DataStorageMemoryItem> 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<DataStorageMemoryItem, Void, Void> { |
| 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); |
| |
| } |
| } |