Data storage refactoring, step 1

This commit is contained in:
nazar-kutz 2021-02-01 17:43:20 +02:00
parent 7fa01ec95d
commit 353c2d1676
14 changed files with 841 additions and 777 deletions

View file

@ -22,7 +22,7 @@
android:layout="@layout/preference_with_descr" android:layout="@layout/preference_with_descr"
android:persistent="false" android:persistent="false"
android:title="@string/application_dir" android:title="@string/application_dir"
app:fragment="net.osmand.plus.settings.fragments.DataStorageFragment" app:fragment="net.osmand.plus.settings.datastorage.DataStorageFragment"
tools:icon="@drawable/ic_action_folder" /> tools:icon="@drawable/ic_action_folder" />
<Preference <Preference

View file

@ -139,7 +139,7 @@ import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.plus.settings.fragments.BaseSettingsFragment;
import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType;
import net.osmand.plus.settings.fragments.ConfigureProfileFragment; import net.osmand.plus.settings.fragments.ConfigureProfileFragment;
import net.osmand.plus.settings.fragments.DataStorageFragment; import net.osmand.plus.settings.datastorage.DataStorageFragment;
import net.osmand.plus.track.TrackAppearanceFragment; import net.osmand.plus.track.TrackAppearanceFragment;
import net.osmand.plus.track.TrackMenuFragment; import net.osmand.plus.track.TrackMenuFragment;
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint; import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;

View file

@ -19,13 +19,13 @@ import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.plus.settings.fragments.BaseSettingsFragment;
import net.osmand.plus.settings.fragments.DataStorageMenuItem; import net.osmand.plus.settings.datastorage.item.StorageItem;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import java.io.File; import java.io.File;
import static net.osmand.plus.settings.fragments.DataStorageHelper.MANUALLY_SPECIFIED; import static net.osmand.plus.settings.datastorage.DataStorageHelper.MANUALLY_SPECIFIED;
public class ChangeDataStorageBottomSheet extends BasePreferenceBottomSheet { public class ChangeDataStorageBottomSheet extends BasePreferenceBottomSheet {
@ -39,8 +39,8 @@ public class ChangeDataStorageBottomSheet extends BasePreferenceBottomSheet {
public final static String MOVE_DATA = "move_data"; public final static String MOVE_DATA = "move_data";
public final static String CHOSEN_DIRECTORY = "chosen_storage"; public final static String CHOSEN_DIRECTORY = "chosen_storage";
private DataStorageMenuItem currentDirectory; private StorageItem currentDirectory;
private DataStorageMenuItem newDirectory; private StorageItem newDirectory;
@Override @Override
public void createMenuItems(Bundle savedInstanceState) { public void createMenuItems(Bundle savedInstanceState) {
@ -126,11 +126,11 @@ public class ChangeDataStorageBottomSheet extends BasePreferenceBottomSheet {
items.add(baseItem); items.add(baseItem);
} }
public void setCurrentDirectory(DataStorageMenuItem currentDirectory) { public void setCurrentDirectory(StorageItem currentDirectory) {
this.currentDirectory = currentDirectory; this.currentDirectory = currentDirectory;
} }
public void setNewDirectory(DataStorageMenuItem newDirectory) { public void setNewDirectory(StorageItem newDirectory) {
this.newDirectory = newDirectory; this.newDirectory = newDirectory;
} }
@ -158,8 +158,8 @@ public class ChangeDataStorageBottomSheet extends BasePreferenceBottomSheet {
return true; return true;
} }
public static boolean showInstance(FragmentManager fm, String prefId, DataStorageMenuItem currentDirectory, public static boolean showInstance(FragmentManager fm, String prefId, StorageItem currentDirectory,
DataStorageMenuItem newDirectory, Fragment target, boolean usedOnMap) { StorageItem newDirectory, Fragment target, boolean usedOnMap) {
try { try {
if (fm.findFragmentByTag(TAG) == null) { if (fm.findFragmentByTag(TAG) == null) {
Bundle args = new Bundle(); Bundle args = new Bundle();

View file

@ -1,8 +1,7 @@
package net.osmand.plus.settings.fragments; package net.osmand.plus.settings.datastorage;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@ -26,7 +25,6 @@ import net.osmand.AndroidUtils;
import net.osmand.FileUtils; import net.osmand.FileUtils;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.ProgressImplementation;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
@ -34,20 +32,21 @@ import net.osmand.plus.activities.OsmandActionBarActivity;
import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.download.DownloadActivity;
import net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet; import net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet;
import net.osmand.plus.settings.bottomsheets.SelectFolderBottomSheet; import net.osmand.plus.settings.bottomsheets.SelectFolderBottomSheet;
import net.osmand.util.Algorithms; import net.osmand.plus.settings.datastorage.item.MemoryItem;
import net.osmand.plus.settings.datastorage.item.StorageItem;
import net.osmand.plus.settings.datastorage.task.MoveFilesTask;
import net.osmand.plus.settings.datastorage.task.RefreshUsedMemoryTask;
import net.osmand.plus.settings.datastorage.task.ReloadDataTask;
import net.osmand.plus.settings.fragments.BaseSettingsFragment;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import static net.osmand.plus.settings.fragments.DataStorageHelper.INTERNAL_STORAGE; import static net.osmand.plus.settings.datastorage.DataStorageHelper.INTERNAL_STORAGE;
import static net.osmand.plus.settings.fragments.DataStorageHelper.MANUALLY_SPECIFIED; import static net.osmand.plus.settings.datastorage.DataStorageHelper.MANUALLY_SPECIFIED;
import static net.osmand.plus.settings.fragments.DataStorageHelper.OTHER_MEMORY; import static net.osmand.plus.settings.datastorage.DataStorageHelper.OTHER_MEMORY;
import static net.osmand.plus.settings.fragments.DataStorageHelper.TILES_MEMORY; import static net.osmand.plus.settings.datastorage.DataStorageHelper.TILES_MEMORY;
import static net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet.CHOSEN_DIRECTORY; import static net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet.CHOSEN_DIRECTORY;
import static net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet.MOVE_DATA; import static net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet.MOVE_DATA;
import static net.osmand.plus.settings.bottomsheets.SelectFolderBottomSheet.NEW_PATH; import static net.osmand.plus.settings.bottomsheets.SelectFolderBottomSheet.NEW_PATH;
@ -60,17 +59,17 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
private final static String CHANGE_DIRECTORY_BUTTON = "change_directory"; private final static String CHANGE_DIRECTORY_BUTTON = "change_directory";
private final static String OSMAND_USAGE = "osmand_usage"; private final static String OSMAND_USAGE = "osmand_usage";
private ArrayList<DataStorageMenuItem> menuItems; private ArrayList<StorageItem> storageItems;
private ArrayList<DataStorageMemoryItem> memoryItems; private ArrayList<MemoryItem> memoryItems;
private ArrayList<CheckBoxPreference> dataStorageRadioButtonsGroup; private ArrayList<CheckBoxPreference> dataStorageRadioButtonsGroup;
private Preference changeButton; private Preference changeButton;
private DataStorageMenuItem currentDataStorage; private StorageItem currentDataStorage;
private String tmpManuallySpecifiedPath; private String tmpManuallySpecifiedPath;
private DataStorageHelper dataStorageHelper; private DataStorageHelper dataStorageHelper;
private boolean calculateTilesBtnPressed; private boolean calculateTilesBtnPressed;
private DataStorageHelper.RefreshMemoryUsedInfo calculateMemoryTask; private RefreshUsedMemoryTask calculateMemoryTask;
private DataStorageHelper.RefreshMemoryUsedInfo calculateTilesMemoryTask; private RefreshUsedMemoryTask calculateTilesMemoryTask;
private OsmandApplication app; private OsmandApplication app;
private OsmandActionBarActivity activity; private OsmandActionBarActivity activity;
@ -95,11 +94,11 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
return; return;
} }
menuItems = dataStorageHelper.getStorageItems(); storageItems = dataStorageHelper.getStorageItems();
memoryItems = dataStorageHelper.getMemoryInfoItems(); memoryItems = dataStorageHelper.getMemoryInfoItems();
dataStorageRadioButtonsGroup = new ArrayList<>(); dataStorageRadioButtonsGroup = new ArrayList<>();
for (DataStorageMenuItem item : menuItems) { for (StorageItem item : storageItems) {
CheckBoxPreference preference = new CheckBoxPreference(activity); CheckBoxPreference preference = new CheckBoxPreference(activity);
preference.setKey(item.getKey()); preference.setKey(item.getKey());
preference.setTitle(item.getTitle()); preference.setTitle(item.getTitle());
@ -136,7 +135,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
Bundle resultData = (Bundle) newValue; Bundle resultData = (Bundle) newValue;
if (resultData.containsKey(ChangeDataStorageBottomSheet.TAG)) { if (resultData.containsKey(ChangeDataStorageBottomSheet.TAG)) {
boolean moveMaps = resultData.getBoolean(MOVE_DATA); boolean moveMaps = resultData.getBoolean(MOVE_DATA);
DataStorageMenuItem newDataStorage = resultData.getParcelable(CHOSEN_DIRECTORY); StorageItem newDataStorage = resultData.getParcelable(CHOSEN_DIRECTORY);
if (newDataStorage != null) { if (newDataStorage != null) {
if (tmpManuallySpecifiedPath != null) { if (tmpManuallySpecifiedPath != null) {
String directory = tmpManuallySpecifiedPath; String directory = tmpManuallySpecifiedPath;
@ -154,9 +153,9 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
if (pathChanged) { if (pathChanged) {
tmpManuallySpecifiedPath = resultData.getString(NEW_PATH); tmpManuallySpecifiedPath = resultData.getString(NEW_PATH);
if (tmpManuallySpecifiedPath != null) { if (tmpManuallySpecifiedPath != null) {
DataStorageMenuItem manuallySpecified = null; StorageItem manuallySpecified = null;
try { try {
manuallySpecified = (DataStorageMenuItem) dataStorageHelper.getManuallySpecified().clone(); manuallySpecified = (StorageItem) dataStorageHelper.getManuallySpecified().clone();
manuallySpecified.setDirectory(tmpManuallySpecifiedPath); manuallySpecified.setDirectory(tmpManuallySpecifiedPath);
} catch (CloneNotSupportedException e) { } catch (CloneNotSupportedException e) {
return false; return false;
@ -170,7 +169,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
//show necessary dialog //show necessary dialog
String key = preference.getKey(); String key = preference.getKey();
if (key != null) { if (key != null) {
DataStorageMenuItem newDataStorage = dataStorageHelper.getStorage(key); StorageItem newDataStorage = dataStorageHelper.getStorage(key);
if (newDataStorage != null) { if (newDataStorage != null) {
if (!currentDataStorage.getKey().equals(newDataStorage.getKey())) { if (!currentDataStorage.getKey().equals(newDataStorage.getKey())) {
if (newDataStorage.getType() == OsmandSettings.EXTERNAL_STORAGE_TYPE_DEFAULT if (newDataStorage.getType() == OsmandSettings.EXTERNAL_STORAGE_TYPE_DEFAULT
@ -212,7 +211,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
final View itemView = holder.itemView; final View itemView = holder.itemView;
if (preference instanceof CheckBoxPreference) { if (preference instanceof CheckBoxPreference) {
DataStorageMenuItem item = dataStorageHelper.getStorage(key); StorageItem item = dataStorageHelper.getStorage(key);
if (item != null) { if (item != null) {
TextView tvTitle = itemView.findViewById(android.R.id.title); TextView tvTitle = itemView.findViewById(android.R.id.title);
TextView tvSummary = itemView.findViewById(R.id.summary); 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); TextView tvSummary = itemView.findViewById(R.id.summary);
tvSummary.setText(DataStorageHelper.getFormattedMemoryInfo(totalUsageBytes, memoryUnitsFormats)); tvSummary.setText(DataStorageHelper.getFormattedMemoryInfo(totalUsageBytes, memoryUnitsFormats));
} else { } else {
for (DataStorageMemoryItem mi : memoryItems) { for (MemoryItem mi : memoryItems) {
if (key.equals(mi.getKey())) { if (key.equals(mi.getKey())) {
TextView tvMemory = itemView.findViewById(R.id.memory); TextView tvMemory = itemView.findViewById(R.id.memory);
String summary = ""; String summary = "";
@ -326,7 +325,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
} }
private void showFolderSelectionDialog() { private void showFolderSelectionDialog() {
DataStorageMenuItem manuallySpecified = dataStorageHelper.getManuallySpecified(); StorageItem manuallySpecified = dataStorageHelper.getManuallySpecified();
if (manuallySpecified != null) { if (manuallySpecified != null) {
SelectFolderBottomSheet.showInstance(getFragmentManager(), manuallySpecified.getKey(), SelectFolderBottomSheet.showInstance(getFragmentManager(), manuallySpecified.getKey(),
manuallySpecified.getDirectory(), DataStorageFragment.this, 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 fromDirectory = new File(currentStorage.getDirectory());
File toDirectory = new File(newStorage.getDirectory()); File toDirectory = new File(newStorage.getDirectory());
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
MoveFilesToDifferentDirectory task = new MoveFilesToDifferentDirectory(activity, fromDirectory, toDirectory) { MoveFilesTask task = new MoveFilesTask(activity, fromDirectory, toDirectory) {
@NonNull @NonNull
@ -405,7 +404,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 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(); String newDirectory = newStorageDirectory.getDirectory();
int type = newStorageDirectory.getType(); int type = newStorageDirectory.getType();
File newDirectoryFile = new File(newDirectory); File newDirectoryFile = new File(newDirectory);
@ -454,7 +453,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
} }
protected void reloadData() { 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 @Override
@ -469,198 +468,4 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto
app.getSettings().OSMAND_USAGE_SPACE.set(dataStorageHelper.getTotalUsedBytes()); app.getSettings().OSMAND_USAGE_SPACE.set(dataStorageHelper.getTotalUsedBytes());
} }
} }
public static class MoveFilesToDifferentDirectory extends AsyncTask<Void, Void, Boolean> {
protected WeakReference<OsmandActionBarActivity> activity;
private WeakReference<Context> 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<Void, Void, Boolean> {
private WeakReference<Context> 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<String>());
return true;
}
}
} }

View file

@ -0,0 +1,318 @@
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 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 OsmandApplication app;
private ArrayList<StorageItem> storageItems = new ArrayList<>();
private StorageItem currentDataStorage;
private StorageItem manuallySpecified;
private ArrayList<MemoryItem> memoryItems = new ArrayList<>();
private MemoryItem mapsMemory;
private MemoryItem srtmAndHillshadeMemory;
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() {
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();
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;
}
initMemoryUsed();
}
private void initMemoryUsed() {
mapsMemory = MemoryItem.builder()
.setKey(MAPS_MEMORY)
.setExtensions(IndexConstants.BINARY_MAP_INDEX_EXT)
.setDirectories(
createDirectory((MAPS_PATH), false, EXTENSIONS, false),
createDirectory((ROADS_INDEX_DIR), true, EXTENSIONS, false),
createDirectory((WIKI_INDEX_DIR), true, EXTENSIONS, false),
createDirectory((WIKIVOYAGE_INDEX_DIR), true, EXTENSIONS, false),
createDirectory((BACKUP_INDEX_DIR), true, EXTENSIONS, false))
.createItem();
memoryItems.add(mapsMemory);
srtmAndHillshadeMemory = MemoryItem.builder()
.setKey(SRTM_AND_HILLSHADE_MEMORY)
.setExtensions(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)
.setDirectories(
createDirectory((SRTM_INDEX_DIR), true, EXTENSIONS, false),
createDirectory((TILES_INDEX_DIR), false, PREFIX, true))
.setPrefixes("Hillshade")
.createItem();
memoryItems.add(srtmAndHillshadeMemory);
tracksMemory = MemoryItem.builder()
.setKey(TRACKS_MEMORY)
// .setExtensions(IndexConstants.GPX_FILE_EXT, ".gpx.bz2")
.setDirectories(
createDirectory((GPX_INDEX_DIR), true, EXTENSIONS, false))
.createItem();
memoryItems.add(tracksMemory);
notesMemory = MemoryItem.builder()
.setKey(NOTES_MEMORY)
// .setExtensions("")
.setDirectories(
createDirectory((AV_INDEX_DIR), true, EXTENSIONS, false))
.createItem();
memoryItems.add(notesMemory);
tilesMemory = MemoryItem.builder()
.setKey(TILES_MEMORY)
// .setExtensions("")
.setDirectories(
createDirectory((TILES_INDEX_DIR), true, EXTENSIONS, false))
.createItem();
memoryItems.add(tilesMemory);
otherMemory = MemoryItem.builder()
.setKey(OTHER_MEMORY)
.createItem();
memoryItems.add(otherMemory);
}
public ArrayList<StorageItem> 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<MemoryItem> getMemoryInfoItems() {
return memoryItems;
}
public RefreshUsedMemoryTask calculateMemoryUsedInfo(UpdateMemoryInfoUIAdapter listener) {
File rootDir = new File(currentStoragePath);
RefreshUsedMemoryTask task = new RefreshUsedMemoryTask(listener, otherMemory, rootDir, null, null, OTHER_MEMORY);
task.execute(mapsMemory, srtmAndHillshadeMemory, 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, srtmAndHillshadeMemory.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 goDeeper,
CheckingType checkingType,
boolean skipOther) {
String path = app.getAppPath(relativePath).getAbsolutePath();
return new DirectoryItem(path, goDeeper, checkingType, skipOther);
}
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);
}
}

View file

@ -0,0 +1,40 @@
package net.osmand.plus.settings.datastorage.item;
public class DirectoryItem {
private final String absolutePath;
private final boolean goDeeper;
private final CheckingType checkingType;
private final boolean skipOther;
public enum CheckingType {
EXTENSIONS,
PREFIX
}
public DirectoryItem(String absolutePath,
boolean goDeeper,
CheckingType 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 CheckingType getCheckingType() {
return checkingType;
}
public boolean isSkipOther() {
return skipOther;
}
}

View file

@ -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 key;
private String[] extensions; private final String[] extensions;
private String[] prefixes; private final String[] prefixes;
private Directory[] directories; private final DirectoryItem[] directories;
private long usedMemoryBytes; 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.key = key;
this.extensions = extensions; this.extensions = extensions;
this.prefixes = prefixes; this.prefixes = prefixes;
@ -42,7 +44,7 @@ public class DataStorageMemoryItem {
return prefixes; return prefixes;
} }
public Directory[] getDirectories() { public DirectoryItem[] getDirectories() {
return directories; return directories;
} }
@ -54,7 +56,7 @@ public class DataStorageMemoryItem {
private String key; private String key;
private String[] extensions; private String[] extensions;
private String[] prefixes; private String[] prefixes;
private Directory[] directories; private DirectoryItem[] directories;
private long usedMemoryBytes; private long usedMemoryBytes;
public DataStorageMemoryItemBuilder setKey(String key) { public DataStorageMemoryItemBuilder setKey(String key) {
@ -72,7 +74,7 @@ public class DataStorageMemoryItem {
return this; return this;
} }
public DataStorageMemoryItemBuilder setDirectories(Directory ... directories) { public DataStorageMemoryItemBuilder setDirectories(DirectoryItem... directories) {
this.directories = directories; this.directories = directories;
return this; return this;
} }
@ -82,38 +84,8 @@ public class DataStorageMemoryItem {
return this; return this;
} }
public DataStorageMemoryItem createItem() { public MemoryItem createItem() {
return new DataStorageMemoryItem(key, extensions, prefixes, usedMemoryBytes, directories); return new MemoryItem(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;
} }
} }
} }

View file

@ -1,11 +1,11 @@
package net.osmand.plus.settings.fragments; package net.osmand.plus.settings.datastorage.item;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.IdRes; import androidx.annotation.IdRes;
public class DataStorageMenuItem implements Parcelable, Cloneable { public class StorageItem implements Parcelable, Cloneable {
private String key; private String key;
private int type; private int type;
@ -15,8 +15,12 @@ public class DataStorageMenuItem implements Parcelable, Cloneable {
@IdRes @IdRes
private int iconResId; private int iconResId;
private DataStorageMenuItem(String key, int type, String title, String description, private StorageItem(String key,
String directory, int iconResId) { int type,
String title,
String description,
String directory,
int iconResId) {
this.key = key; this.key = key;
this.type = type; this.type = type;
this.title = title; this.title = title;
@ -25,7 +29,7 @@ public class DataStorageMenuItem implements Parcelable, Cloneable {
this.iconResId = iconResId; this.iconResId = iconResId;
} }
private DataStorageMenuItem(Parcel in) { private StorageItem(Parcel in) {
key = in.readString(); key = in.readString();
type = in.readInt(); type = in.readInt();
title = in.readString(); title = in.readString();
@ -99,16 +103,16 @@ public class DataStorageMenuItem implements Parcelable, Cloneable {
dest.writeString(directory); dest.writeString(directory);
} }
public static final Parcelable.Creator<DataStorageMenuItem> CREATOR = new Parcelable.Creator<DataStorageMenuItem>() { public static final Parcelable.Creator<StorageItem> CREATOR = new Parcelable.Creator<StorageItem>() {
@Override @Override
public DataStorageMenuItem createFromParcel(Parcel source) { public StorageItem createFromParcel(Parcel source) {
return new DataStorageMenuItem(source); return new StorageItem(source);
} }
@Override @Override
public DataStorageMenuItem[] newArray(int size) { public StorageItem[] newArray(int size) {
return new DataStorageMenuItem[size]; return new StorageItem[size];
} }
}; };
@ -151,14 +155,14 @@ public class DataStorageMenuItem implements Parcelable, Cloneable {
return this; return this;
} }
public DataStorageMenuItem createItem() { public StorageItem createItem() {
return new DataStorageMenuItem(key, type, title, description, directory, iconResId); return new StorageItem(key, type, title, description, directory, iconResId);
} }
} }
@Override @Override
public Object clone() throws CloneNotSupportedException { public Object clone() throws CloneNotSupportedException {
return DataStorageMenuItem.builder() return StorageItem.builder()
.setKey(this.key) .setKey(this.key)
.setTitle(this.title) .setTitle(this.title)
.setDescription(this.description) .setDescription(this.description)

View file

@ -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<Void, Void, Boolean> {
protected WeakReference<OsmandActionBarActivity> activity;
private WeakReference<Context> 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;
}
}

View file

@ -0,0 +1,184 @@
package net.osmand.plus.settings.datastorage.task;
import android.os.AsyncTask;
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;
public class RefreshUsedMemoryTask extends AsyncTask<MemoryItem, Void, Void> {
private UpdateMemoryInfoUIAdapter listener;
private File rootDir;
private MemoryItem otherMemory;
private String[] directoriesToAvoid;
private String[] prefixesToAvoid;
private String taskKey;
private long lastRefreshTime;
public RefreshUsedMemoryTask(UpdateMemoryInfoUIAdapter listener, MemoryItem 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(MemoryItem... items) {
lastRefreshTime = System.currentTimeMillis();
if (rootDir.canRead()) {
calculateMultiTypes(rootDir, items);
}
return null;
}
private void calculateMultiTypes(File rootDir, MemoryItem... 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 (MemoryItem item : items) {
DirectoryItem[] directories = item.getDirectories();
if (directories == null) {
continue;
}
for (DirectoryItem 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 (MemoryItem item : items) {
DirectoryItem[] directories = item.getDirectories();
if (directories == null) {
continue;
}
for (DirectoryItem dir : directories) {
if (rootDir.getAbsolutePath().equals(dir.getAbsolutePath())
|| (rootDir.getAbsolutePath().startsWith(dir.getAbsolutePath()) && dir.isGoDeeper())) {
CheckingType 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();
}
}
}

View file

@ -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<Void, Void, Boolean> {
private WeakReference<Context> 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<String>());
return true;
}
}

View file

@ -73,6 +73,7 @@ import net.osmand.plus.settings.bottomsheets.ChangeGeneralProfilesPrefBottomShee
import net.osmand.plus.settings.bottomsheets.EditTextPreferenceBottomSheet; import net.osmand.plus.settings.bottomsheets.EditTextPreferenceBottomSheet;
import net.osmand.plus.settings.bottomsheets.MultiSelectPreferencesBottomSheet; import net.osmand.plus.settings.bottomsheets.MultiSelectPreferencesBottomSheet;
import net.osmand.plus.settings.bottomsheets.SingleSelectPreferenceBottomSheet; 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.ListPreferenceEx;
import net.osmand.plus.settings.preferences.MultiSelectBooleanPreference; import net.osmand.plus.settings.preferences.MultiSelectBooleanPreference;
import net.osmand.plus.settings.preferences.SwitchPreferenceEx; import net.osmand.plus.settings.preferences.SwitchPreferenceEx;

View file

@ -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<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;
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<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(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<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);
}
}

View file

@ -22,6 +22,8 @@ import net.osmand.plus.profiles.SelectProfileBottomSheet.DialogMode;
import net.osmand.plus.profiles.SelectProfileBottomSheet.OnSelectProfileCallback; import net.osmand.plus.profiles.SelectProfileBottomSheet.OnSelectProfileCallback;
import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.settings.backend.OsmandSettings; 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.ListPreferenceEx;
import net.osmand.plus.settings.preferences.SwitchPreferenceEx; import net.osmand.plus.settings.preferences.SwitchPreferenceEx;
@ -181,7 +183,7 @@ public class GlobalSettingsFragment extends BaseSettingsFragment
externalStorageDir.setIcon(getActiveIcon(R.drawable.ic_action_folder)); externalStorageDir.setIcon(getActiveIcon(R.drawable.ic_action_folder));
DataStorageHelper holder = new DataStorageHelper(app); DataStorageHelper holder = new DataStorageHelper(app);
DataStorageMenuItem currentStorage = holder.getCurrentStorage(); StorageItem currentStorage = holder.getCurrentStorage();
long totalUsed = app.getSettings().OSMAND_USAGE_SPACE.get(); long totalUsed = app.getSettings().OSMAND_USAGE_SPACE.get();
if (totalUsed > 0) { if (totalUsed > 0) {
String[] usedMemoryFormats = new String[] { String[] usedMemoryFormats = new String[] {