blob: 994cc8eb0d1e58b901e4d7b82142030b5c0a7ac2 [file] [log] [blame]
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);
}
}