diff --git a/OsmAnd/res/layout/bottom_sheet_change_data_storage.xml b/OsmAnd/res/layout/bottom_sheet_change_data_storage.xml
new file mode 100644
index 0000000000..8702a1cb65
--- /dev/null
+++ b/OsmAnd/res/layout/bottom_sheet_change_data_storage.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd/res/layout/bottom_sheet_dialog_button_big.xml b/OsmAnd/res/layout/bottom_sheet_dialog_button_big.xml
new file mode 100644
index 0000000000..7a8d1ee9a9
--- /dev/null
+++ b/OsmAnd/res/layout/bottom_sheet_dialog_button_big.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd/res/layout/bottom_sheet_item_btn_with_icon_and_text.xml b/OsmAnd/res/layout/bottom_sheet_item_btn_with_icon_and_text.xml
new file mode 100644
index 0000000000..6adcae70ac
--- /dev/null
+++ b/OsmAnd/res/layout/bottom_sheet_item_btn_with_icon_and_text.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd/res/layout/bottom_sheet_select_folder.xml b/OsmAnd/res/layout/bottom_sheet_select_folder.xml
new file mode 100644
index 0000000000..4693c7d881
--- /dev/null
+++ b/OsmAnd/res/layout/bottom_sheet_select_folder.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd/res/layout/data_storage_list_item.xml b/OsmAnd/res/layout/data_storage_list_item.xml
new file mode 100644
index 0000000000..4e8a231449
--- /dev/null
+++ b/OsmAnd/res/layout/data_storage_list_item.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml
index 5a481e052d..7889f194c2 100644
--- a/OsmAnd/res/values/strings.xml
+++ b/OsmAnd/res/values/strings.xml
@@ -11,6 +11,16 @@
Thx - Hardy
-->
+ Move OsmAnd data files to the new destination?\n%1$s > %2$s
+ %1$s • Used %2$s GB
+ Free %1$s • %2$s / %3$s GB
+ Enter path to the folder
+ Select folder
+ Paste path to the folder with OsmAnd data
+ Change OsmAnd data folder?
+ Move to the new destination
+ Internal storage, hiden from user and other apps, so no one except OsmAnd can’t get access to your data
+ Change data storage folder
Download detailed %s map, to view this area.
By default
Plugin settings
diff --git a/OsmAnd/res/xml/data_storage.xml b/OsmAnd/res/xml/data_storage.xml
new file mode 100644
index 0000000000..527ab8e1a9
--- /dev/null
+++ b/OsmAnd/res/xml/data_storage.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd/res/xml/global_settings.xml b/OsmAnd/res/xml/global_settings.xml
index 5194192ab7..2e95524c79 100644
--- a/OsmAnd/res/xml/global_settings.xml
+++ b/OsmAnd/res/xml/global_settings.xml
@@ -21,6 +21,7 @@
android:layout="@layout/preference_with_descr"
android:persistent="false"
android:title="@string/application_dir"
+ app:fragment="net.osmand.plus.settings.DataStorageFragment"
tools:icon="@drawable/ic_action_folder" />
= Build.VERSION_CODES.LOLLIPOP;
View buttonContainer = buttonView.findViewById(R.id.button_container);
+ int textAndIconColorResId = INVALID_ID;
switch (buttonType) {
case PRIMARY:
if (v21) {
AndroidUtils.setBackground(ctx, buttonContainer, nightMode, R.drawable.ripple_solid_light, R.drawable.ripple_solid_dark);
}
AndroidUtils.setBackground(ctx, buttonView, nightMode, R.drawable.dlg_btn_primary_light, R.drawable.dlg_btn_primary_dark);
- buttonTextView.setTextColor(ContextCompat.getColorStateList(ctx, nightMode ? R.color.dlg_btn_primary_text_dark : R.color.dlg_btn_primary_text_light));
+ textAndIconColorResId = nightMode ? R.color.dlg_btn_primary_text_dark : R.color.dlg_btn_primary_text_light;
break;
case SECONDARY:
if (v21) {
AndroidUtils.setBackground(ctx, buttonContainer, nightMode, R.drawable.ripple_solid_light, R.drawable.ripple_solid_dark);
}
AndroidUtils.setBackground(ctx, buttonView, nightMode, R.drawable.dlg_btn_secondary_light, R.drawable.dlg_btn_secondary_dark);
- buttonTextView.setTextColor(ContextCompat.getColorStateList(ctx, nightMode ? R.color.dlg_btn_secondary_text_dark : R.color.dlg_btn_secondary_text_light));
+ textAndIconColorResId = nightMode ? R.color.dlg_btn_secondary_text_dark : R.color.dlg_btn_secondary_text_light;
break;
case STROKED:
if (v21) {
AndroidUtils.setBackground(ctx, buttonContainer, nightMode, R.drawable.ripple_light, R.drawable.ripple_dark);
}
AndroidUtils.setBackground(ctx, buttonView, nightMode, R.drawable.dlg_btn_stroked_light, R.drawable.dlg_btn_stroked_dark);
- buttonTextView.setTextColor(ContextCompat.getColorStateList(ctx, nightMode ? R.color.dlg_btn_secondary_text_dark : R.color.dlg_btn_secondary_text_light));
+ textAndIconColorResId = nightMode ? R.color.dlg_btn_secondary_text_dark : R.color.dlg_btn_secondary_text_light;
break;
}
- buttonTextView.setText(buttonText);
- buttonTextView.setEnabled(buttonView.isEnabled());
+ if (textAndIconColorResId != INVALID_ID) {
+ ColorStateList colorStateList = ContextCompat.getColorStateList(ctx, textAndIconColorResId);
+ buttonTextView.setText(buttonText);
+ buttonTextView.setTextColor(colorStateList);
+ buttonTextView.setEnabled(buttonView.isEnabled());
+ if (iconResId != INVALID_ID) {
+ Drawable icon = tintDrawable(ContextCompat.getDrawable(ctx, iconResId), ContextCompat.getColor(ctx, textAndIconColorResId));
+ buttonTextView.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
+ buttonTextView.setCompoundDrawablePadding(AndroidUtils.dpToPx(ctx, ctx.getResources().getDimension(R.dimen.content_padding_half)));
+ }
+ }
}
public static Context getThemedContext(Context context, boolean nightMode) {
diff --git a/OsmAnd/src/net/osmand/plus/settings/BaseSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/BaseSettingsFragment.java
index 2163069af1..6f3cf45df2 100644
--- a/OsmAnd/src/net/osmand/plus/settings/BaseSettingsFragment.java
+++ b/OsmAnd/src/net/osmand/plus/settings/BaseSettingsFragment.java
@@ -93,7 +93,8 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl
VOICE_ANNOUNCES(VoiceAnnouncesFragment.class.getName(), true, R.xml.voice_announces, R.layout.profile_preference_toolbar_with_switch),
VEHICLE_PARAMETERS(VehicleParametersFragment.class.getName(), true, R.xml.vehicle_parameters, R.layout.profile_preference_toolbar),
MAP_DURING_NAVIGATION(MapDuringNavigationFragment.class.getName(), true, R.xml.map_during_navigation, R.layout.profile_preference_toolbar),
- TURN_SCREEN_ON(TurnScreenOnFragment.class.getName(), true, R.xml.turn_screen_on, R.layout.profile_preference_toolbar_with_switch);
+ TURN_SCREEN_ON(TurnScreenOnFragment.class.getName(), true, R.xml.turn_screen_on, R.layout.profile_preference_toolbar_with_switch),
+ DATA_STORAGE(DataStorageFragment.class.getName(), false, R.xml.data_storage, R.layout.global_preference_toolbar);
public final String fragmentName;
public final boolean profileDependent;
diff --git a/OsmAnd/src/net/osmand/plus/settings/DataStorageFragment.java b/OsmAnd/src/net/osmand/plus/settings/DataStorageFragment.java
new file mode 100644
index 0000000000..f4013e536c
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/settings/DataStorageFragment.java
@@ -0,0 +1,566 @@
+package net.osmand.plus.settings;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import net.osmand.AndroidUtils;
+import net.osmand.plus.OsmandApplication;
+import net.osmand.plus.OsmandSettings;
+import net.osmand.plus.ProgressImplementation;
+import net.osmand.plus.R;
+import net.osmand.plus.UiUtilities;
+import net.osmand.plus.activities.OsmandActionBarActivity;
+import net.osmand.plus.download.DownloadActivity;
+import net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet;
+import net.osmand.plus.settings.bottomsheets.SelectFolderBottomSheet;
+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;
+import java.text.DecimalFormat;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import static net.osmand.plus.settings.DataStorageItemsHolder.INTERNAL_STORAGE;
+import static net.osmand.plus.settings.DataStorageItemsHolder.MANUALLY_SPECIFIED;
+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.SelectFolderBottomSheet.PATH_CHANGED;
+import static net.osmand.plus.settings.bottomsheets.SelectFolderBottomSheet.NEW_PATH;
+
+public class DataStorageFragment extends BaseSettingsFragment {
+
+ private final static String CHANGE_DIRECTORY_BUTTON = "change_directory";
+
+ private ArrayList menuItems;
+ private ArrayList dataStorageRadioButtonsGroup;
+ private Preference changeButton;
+ private DataStorageMenuItem currentDataStorage;
+ private String tmpManuallySpecifiedPath;
+ private DataStorageItemsHolder itemsHolder;
+
+ private OsmandApplication app;
+ private OsmandActionBarActivity activity;
+ private OsmandSettings settings;
+
+ @Override
+ protected void setupPreferences() {
+ app = getMyApplication();
+ activity = getMyActivity();
+ PreferenceScreen screen = getPreferenceScreen();
+
+ if (screen == null || app == null || activity == null) {
+ return;
+ }
+ settings = app.getSettings();
+
+ itemsHolder = DataStorageItemsHolder.refreshInfo(app);
+ menuItems = itemsHolder.getStorageItems();
+ dataStorageRadioButtonsGroup = new ArrayList<>();
+
+ for (DataStorageMenuItem item : menuItems) {
+ CheckBoxPreference preference = new CheckBoxPreference(activity);
+ preference.setKey(item.getKey());
+ preference.setTitle(item.getTitle());
+ preference.setLayoutResource(R.layout.data_storage_list_item);
+ screen.addPreference(preference);
+ dataStorageRadioButtonsGroup.add(preference);
+ }
+
+ currentDataStorage = itemsHolder.getCurrentStorage();
+
+ changeButton = new Preference(app);
+ changeButton.setKey(CHANGE_DIRECTORY_BUTTON);
+ changeButton.setLayoutResource(R.layout.bottom_sheet_item_btn_with_icon_and_text);
+ screen.addPreference(changeButton);
+
+ updateView(currentDataStorage.getKey());
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (CHANGE_DIRECTORY_BUTTON.equals(preference.getKey())) {
+ showFolderSelectionDialog();
+ return false;
+ }
+ return super.onPreferenceClick(preference);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ super.onPreferenceChange(preference, newValue);
+
+ if (newValue instanceof Bundle) {
+ //results from BottomSheets
+ Bundle resultData = (Bundle) newValue;
+ if (resultData.containsKey(ChangeDataStorageBottomSheet.TAG)) {
+ boolean moveMaps = resultData.getBoolean(MOVE_DATA);
+ DataStorageMenuItem newDataStorage = resultData.getParcelable(CHOSEN_DIRECTORY);
+ if (newDataStorage != null) {
+ if (tmpManuallySpecifiedPath != null) {
+ String directory = tmpManuallySpecifiedPath;
+ tmpManuallySpecifiedPath = null;
+ newDataStorage.setDirectory(directory);
+ }
+ if (moveMaps) {
+ moveData(currentDataStorage, newDataStorage);
+ } else {
+ confirm(app, activity, newDataStorage, false);
+ }
+ }
+ } else if (resultData.containsKey(SelectFolderBottomSheet.TAG)) {
+ boolean pathChanged = resultData.getBoolean(PATH_CHANGED);
+ if (pathChanged) {
+ tmpManuallySpecifiedPath = resultData.getString(NEW_PATH);
+ if (tmpManuallySpecifiedPath != null) {
+ DataStorageMenuItem manuallySpecified = null;
+ try {
+ manuallySpecified = (DataStorageMenuItem) itemsHolder.getManuallySpecified().clone();
+ manuallySpecified.setDirectory(tmpManuallySpecifiedPath);
+ } catch (CloneNotSupportedException e) {
+ return false;
+ }
+ ChangeDataStorageBottomSheet.showInstance(getFragmentManager(), MANUALLY_SPECIFIED,
+ currentDataStorage, manuallySpecified, this, false);
+ }
+ }
+ }
+ } else {
+ //show necessary dialog
+ String key = preference.getKey();
+ if (key != null) {
+ DataStorageMenuItem newDataStorage = itemsHolder.getStorage(key);
+ if (newDataStorage != null) {
+ if (!currentDataStorage.getKey().equals(newDataStorage.getKey())) {
+ if (newDataStorage.getType() == OsmandSettings.EXTERNAL_STORAGE_TYPE_DEFAULT
+ && !DownloadActivity.hasPermissionToWriteExternalStorage(activity)) {
+ ActivityCompat.requestPermissions(activity,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ DownloadActivity.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
+ } else if (key.equals(MANUALLY_SPECIFIED)) {
+ showFolderSelectionDialog();
+ } else {
+ ChangeDataStorageBottomSheet.showInstance(getFragmentManager(), key,
+ currentDataStorage, newDataStorage, DataStorageFragment.this, false);
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void onBindPreferenceViewHolder(Preference preference, PreferenceViewHolder holder) {
+ super.onBindPreferenceViewHolder(preference, holder);
+ String key = preference.getKey();
+ if (key == null) {
+ return;
+ }
+ View itemView = holder.itemView;
+ if (preference instanceof CheckBoxPreference) {
+ DataStorageMenuItem item = itemsHolder.getStorage(key);
+ if (item != null) {
+ TextView tvTitle = itemView.findViewById(android.R.id.title);
+ TextView tvSummary = itemView.findViewById(R.id.summary);
+ TextView tvAdditionalDescription = itemView.findViewById(R.id.additionalDescription);
+ ImageView ivIcon = itemView.findViewById(R.id.icon);
+ View divider = itemView.findViewById(R.id.divider);
+ View secondPart = itemView.findViewById(R.id.secondPart);
+
+ tvTitle.setText(item.getTitle());
+ String currentKey = item.getKey();
+ boolean isCurrent = currentDataStorage.getKey().equals(currentKey);
+
+ int defaultIconColor = isNightMode() ? R.color.icon_color_default_dark : R.color.icon_color_default_light;
+ int chosenIconColor = isNightMode() ? R.color.icon_color_osmand_dark : R.color.icon_color_osmand_light;
+ Drawable icon = app.getUIUtilities().getIcon(item.getIconResId(),
+ isCurrent ? chosenIconColor : defaultIconColor);
+ ivIcon.setImageDrawable(icon);
+
+ if (currentKey.equals(MANUALLY_SPECIFIED)) {
+ tvSummary.setText(item.getDirectory());
+ secondPart.setVisibility(View.GONE);
+ tvAdditionalDescription.setVisibility(View.GONE);
+ divider.setVisibility(View.GONE);
+ } else {
+ tvAdditionalDescription.setVisibility(View.VISIBLE);
+ divider.setVisibility(View.VISIBLE);
+ secondPart.setVisibility(View.VISIBLE);
+ String space = getSpaceDescription(item.getDirectory());
+ tvSummary.setText(space);
+ if (currentKey.equals(INTERNAL_STORAGE)) {
+ tvAdditionalDescription.setText(item.getDescription());
+ } else {
+ tvAdditionalDescription.setText(item.getDirectory());
+ }
+ }
+ }
+ } else if (key.equals(CHANGE_DIRECTORY_BUTTON)) {
+ ImageView icon = itemView.findViewById(R.id.button_icon);
+ TextView title = itemView.findViewById(R.id.button_text);
+ int colorResId = isNightMode() ? R.color.active_color_primary_dark : R.color.active_color_primary_light;
+ int color = ContextCompat.getColor(app, colorResId);
+ Drawable drawable = UiUtilities.getColoredSelectableDrawable(app, color, 0.3f);
+ AndroidUtils.setBackground(itemView, drawable);
+ icon.setVisibility(View.INVISIBLE);
+ title.setText(R.string.shared_string_change);
+ }
+ }
+
+ private void updateView(String key) {
+ //selection set up
+ for (CheckBoxPreference preference : dataStorageRadioButtonsGroup) {
+ String preferenceKey = preference.getKey();
+ boolean checked = preferenceKey != null && preferenceKey.equals(key);
+ preference.setChecked(checked);
+ }
+ boolean visible = key.equals(MANUALLY_SPECIFIED);
+ changeButton.setVisible(visible);
+ }
+
+ private void showFolderSelectionDialog() {
+ DataStorageMenuItem manuallySpecified = itemsHolder.getManuallySpecified();
+ if (manuallySpecified != null) {
+ SelectFolderBottomSheet.showInstance(getFragmentManager(), manuallySpecified.getKey(),
+ manuallySpecified.getDirectory(), DataStorageFragment.this,
+ getString(R.string.storage_directory_manual), getString(R.string.paste_Osmand_data_folder_path),
+ getString(R.string.shared_string_select_folder), false);
+ }
+ }
+
+ private void moveData(final DataStorageMenuItem currentStorage, final DataStorageMenuItem newStorage) {
+ File fromDirectory = new File(currentStorage.getDirectory());
+ File toDirectory = new File(newStorage.getDirectory());
+ @SuppressLint("StaticFieldLeak")
+ MoveFilesToDifferentDirectory task = new MoveFilesToDifferentDirectory(activity, fromDirectory, toDirectory) {
+
+ private MessageFormat formatMb = new MessageFormat("{0, number,##.#} MB", Locale.US);
+
+ @NonNull
+ private String getFormattedSize(long sizeBytes) {
+ int size = (int) ((sizeBytes + 512) >> 10);
+ if (size >= 0) {
+ if (size > 100) {
+ return formatMb.format(new Object[]{(float) size / (1 << 10)});
+ } else {
+ return size + " kB";
+ }
+ }
+ return "";
+ }
+
+ private void showResultsDialog() {
+ StringBuilder sb = new StringBuilder();
+ Context ctx = activity.get();
+ if (ctx == null) {
+ return;
+ }
+ int moved = getMovedCount();
+ int copied = getCopiedCount();
+ int failed = getFailedCount();
+ sb.append(ctx.getString(R.string.files_moved, moved, getFormattedSize(getMovedSize()))).append("\n");
+ if (copied > 0) {
+ sb.append(ctx.getString(R.string.files_copied, copied, getFormattedSize(getCopiedSize()))).append("\n");
+ }
+ if (failed > 0) {
+ sb.append(ctx.getString(R.string.files_failed, failed, getFormattedSize(getFailedSize()))).append("\n");
+ }
+ if (copied > 0 || failed > 0) {
+ int count = copied + failed;
+ sb.append(ctx.getString(R.string.files_present, count, getFormattedSize(getCopiedSize() + getFailedSize()), newStorage.getDirectory()));
+ }
+ AlertDialog.Builder bld = new AlertDialog.Builder(ctx);
+ bld.setMessage(sb.toString());
+ bld.setPositiveButton(R.string.shared_string_restart, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ confirm(app, activity.get(), newStorage, true);
+ }
+ });
+ bld.show();
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ super.onPostExecute(result);
+ OsmandActionBarActivity a = this.activity.get();
+ if (a == null) {
+ return;
+ }
+ OsmandApplication app = a.getMyApplication();
+ if (result) {
+ app.getResourceManager().resetStoreDirectory();
+ // immediately proceed with change (to not loose where maps are currently located)
+ if (getCopiedCount() > 0 || getFailedCount() > 0) {
+ showResultsDialog();
+ } else {
+ confirm(app, a, newStorage, false);
+ }
+ } else {
+ showResultsDialog();
+ Toast.makeText(a, R.string.copying_osmand_file_failed,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ }
+ };
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ private void confirm(OsmandApplication app, OsmandActionBarActivity activity, DataStorageMenuItem newStorageDirectory, boolean silentRestart) {
+ String newDirectory = newStorageDirectory.getDirectory();
+ int type = newStorageDirectory.getType();
+ File newDirectoryFile = new File(newDirectory);
+ boolean wr = OsmandSettings.isWritable(newDirectoryFile);
+ if (wr) {
+ app.setExternalStorageDirectory(type, newDirectory);
+ reloadData();
+ if (silentRestart) {
+ android.os.Process.killProcess(android.os.Process.myPid());
+ } else {
+ app.restartApp(activity);
+ }
+ } else {
+ Toast.makeText(activity, R.string.specified_directiory_not_writeable,
+ Toast.LENGTH_LONG).show();
+ }
+ updateAllSettings();
+ }
+
+ private String getSpaceDescription(String path) {
+ File dir = new File(path);
+ File dirParent = dir.getParentFile();
+ while (!dir.exists() && dirParent != null) {
+ dir = dir.getParentFile();
+ dirParent = dir.getParentFile();
+ }
+ if (dir.exists()) {
+ DecimalFormat formatter = new DecimalFormat("#.##");
+ return String.format(getString(R.string.data_storage_space_description),
+ formatter.format(AndroidUtils.getFreeSpaceGb(dir)),
+ formatter.format(AndroidUtils.getUsedSpaceGb(dir)),
+ formatter.format(AndroidUtils.getTotalSpaceGb(dir)));
+ }
+ return "";
+ }
+
+ protected void reloadData() {
+ new ReloadData(activity, getMyApplication()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+ }
+
+ public static class MoveFilesToDifferentDirectory extends AsyncTask {
+
+ protected WeakReference activity;
+ private WeakReference context;
+ private File from;
+ private File to;
+ protected ProgressImplementation progress;
+ private Runnable runOnSuccess;
+ private int movedCount;
+ private long movedSize;
+ private int copiedCount;
+ private long copiedSize;
+ private int failedCount;
+ private long failedSize;
+
+ 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, R.string.shared_string_io_error, 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) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ public static class ReloadData extends AsyncTask {
+ private WeakReference ctx;
+ protected ProgressImplementation progress;
+ private OsmandApplication app;
+
+ public ReloadData(Context ctx, OsmandApplication app) {
+ this.ctx = new WeakReference<>(ctx);
+ this.app = app;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ Context c = ctx.get();
+ if (c == null) {
+ return;
+ }
+ progress = ProgressImplementation.createProgressDialog(c, c.getString(R.string.loading_data),
+ c.getString(R.string.loading_data), ProgressDialog.STYLE_HORIZONTAL);
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ try {
+ if (progress.getDialog().isShowing()) {
+ progress.getDialog().dismiss();
+ }
+ } catch (Exception e) {
+ //ignored
+ }
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ app.getResourceManager().reloadIndexes(progress, new ArrayList());
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/settings/DataStorageItemsHolder.java b/OsmAnd/src/net/osmand/plus/settings/DataStorageItemsHolder.java
new file mode 100644
index 0000000000..089bf5c448
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/settings/DataStorageItemsHolder.java
@@ -0,0 +1,200 @@
+package net.osmand.plus.settings;
+
+import android.os.Build;
+
+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.util.ArrayList;
+
+public class DataStorageItemsHolder {
+ 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";
+
+ private ArrayList menuItems;
+ private DataStorageMenuItem currentDataStorage;
+ private DataStorageMenuItem manuallySpecified;
+
+ private int currentStorageType;
+ private String currentStoragePath;
+
+ private OsmandApplication app;
+ private OsmandSettings settings;
+
+ private DataStorageItemsHolder(OsmandApplication app) {
+ this.app = app;
+ this.settings = app.getSettings();
+ prepareData();
+ }
+
+ public static DataStorageItemsHolder refreshInfo(OsmandApplication app) {
+ return new DataStorageItemsHolder(app);
+ }
+
+ private void prepareData() {
+
+ if (app == null) {
+ return;
+ }
+
+ if (settings.getExternalStorageDirectoryTypeV19() >= 0) {
+ currentStorageType = settings.getExternalStorageDirectoryTypeV19();
+ } else {
+ ValueHolder vh = new ValueHolder();
+ if (vh.value != null && vh.value >= 0) {
+ currentStorageType = vh.value;
+ } else {
+ currentStorageType = 0;
+ }
+ }
+ currentStoragePath = settings.getExternalStorageDirectory().getAbsolutePath();
+
+ menuItems = new ArrayList<>();
+
+ 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()
+ .buildKey(INTERNAL_STORAGE)
+ .buildTitle(getString(R.string.storage_directory_internal_app))
+ .buildDirectory(path)
+ .buildDescription(getString(R.string.internal_app_storage_description))
+ .buildType(OsmandSettings.EXTERNAL_STORAGE_TYPE_INTERNAL_FILE)
+ .buildIconResId(iconId)
+ .build();
+ addItem(internalStorageItem);
+
+ //shared_storage
+ dir = settings.getDefaultInternalStorage();
+ path = dir.getAbsolutePath();
+ iconId = R.drawable.ic_action_phone;
+
+ DataStorageMenuItem sharedStorageItem = DataStorageMenuItem.builder()
+ .buildKey(SHARED_STORAGE)
+ .buildTitle(getString(R.string.storage_directory_shared))
+ .buildDirectory(path)
+ .buildType(OsmandSettings.EXTERNAL_STORAGE_TYPE_DEFAULT)
+ .buildIconResId(iconId)
+ .build();
+ 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()
+ .buildKey(EXTERNAL_STORAGE + i)
+ .buildTitle(getString(R.string.storage_directory_external) + " " + i)
+ .buildDirectory(path)
+ .buildType(OsmandSettings.EXTERNAL_STORAGE_TYPE_EXTERNAL_FILE)
+ .buildIconResId(iconId)
+ .build();
+ 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()
+ .buildKey(MULTIUSER_STORAGE + i)
+ .buildTitle(getString(R.string.storage_directory_multiuser) + " " + i)
+ .buildDirectory(path)
+ .buildType(OsmandSettings.EXTERNAL_STORAGE_TYPE_OBB)
+ .buildIconResId(iconId)
+ .build();
+ addItem(multiuserStorageItem);
+ }
+ }
+ }
+ }
+
+ //manually specified storage
+ manuallySpecified = DataStorageMenuItem.builder()
+ .buildKey(MANUALLY_SPECIFIED)
+ .buildTitle(getString(R.string.storage_directory_manual))
+ .buildDirectory(currentStoragePath)
+ .buildType(OsmandSettings.EXTERNAL_STORAGE_TYPE_SPECIFIED)
+ .buildIconResId(R.drawable.ic_action_folder)
+ .build();
+ menuItems.add(manuallySpecified);
+
+ if (currentDataStorage == null) {
+ currentDataStorage = manuallySpecified;
+ }
+ }
+
+ private String getString(int resId) {
+ return app.getString(resId);
+ }
+
+ public ArrayList getStorageItems() {
+ return menuItems;
+ }
+
+ private int getIconForStorageType(File dir) {
+ return R.drawable.ic_action_folder;
+ }
+
+ public DataStorageMenuItem getCurrentStorage() {
+ return currentDataStorage;
+ }
+
+ private void addItem(DataStorageMenuItem item) {
+ if (currentStorageType == item.getType() && currentStoragePath.equals(item.getDirectory())) {
+ currentDataStorage = item;
+ }
+ menuItems.add(item);
+ }
+
+ public DataStorageMenuItem getManuallySpecified() {
+ return manuallySpecified;
+ }
+
+ public DataStorageMenuItem getStorage(String key) {
+ if (menuItems != null && key != null) {
+ for (DataStorageMenuItem menuItem : menuItems) {
+ if (key.equals(menuItem.getKey())) {
+ return menuItem;
+ }
+ }
+ }
+ return null;
+ }
+
+ public int getCurrentType() {
+ return currentStorageType;
+ }
+
+ public String getCurrentPath() {
+ return currentStoragePath;
+ }
+}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/settings/DataStorageMenuItem.java b/OsmAnd/src/net/osmand/plus/settings/DataStorageMenuItem.java
new file mode 100644
index 0000000000..548150089d
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/settings/DataStorageMenuItem.java
@@ -0,0 +1,169 @@
+package net.osmand.plus.settings;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.IdRes;
+
+public class DataStorageMenuItem implements Parcelable, Cloneable {
+
+ private String key;
+ private int type;
+ private String title;
+ private String description;
+ private String directory;
+ @IdRes
+ private int iconResId;
+
+ private DataStorageMenuItem(String key, int type, String title, String description,
+ String directory, int iconResId) {
+ this.key = key;
+ this.type = type;
+ this.title = title;
+ this.description = description;
+ this.directory = directory;
+ this.iconResId = iconResId;
+ }
+
+ private DataStorageMenuItem(Parcel in) {
+ key = in.readString();
+ type = in.readInt();
+ title = in.readString();
+ description = in.readString();
+ directory = in.readString();
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getDirectory() {
+ return directory;
+ }
+
+ public int getIconResId() {
+ return iconResId;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void setDirectory(String directory) {
+ this.directory = directory;
+ }
+
+ public void setIconResId(int iconResId) {
+ this.iconResId = iconResId;
+ }
+
+ public static DataStorageMenuItemBuilder builder() {
+ return new DataStorageMenuItemBuilder();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(key);
+ dest.writeInt(type);
+ dest.writeString(title);
+ dest.writeString(description);
+ dest.writeString(directory);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+
+ @Override
+ public DataStorageMenuItem createFromParcel(Parcel source) {
+ return new DataStorageMenuItem(source);
+ }
+
+ @Override
+ public DataStorageMenuItem[] newArray(int size) {
+ return new DataStorageMenuItem[size];
+ }
+ };
+
+ public static class DataStorageMenuItemBuilder {
+ private String key;
+ private int type;
+ private String title;
+ private String description;
+ private String directory;
+ @IdRes
+ private int iconResId;
+
+ public DataStorageMenuItemBuilder buildKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public DataStorageMenuItemBuilder buildType(int type) {
+ this.type = type;
+ return this;
+ }
+
+ public DataStorageMenuItemBuilder buildTitle(String title) {
+ this.title = title;
+ return this;
+ }
+
+ public DataStorageMenuItemBuilder buildDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public DataStorageMenuItemBuilder buildDirectory(String directory) {
+ this.directory = directory;
+ return this;
+ }
+
+ public DataStorageMenuItemBuilder buildIconResId(int iconResId) {
+ this.iconResId = iconResId;
+ return this;
+ }
+
+ public DataStorageMenuItem build() {
+ return new DataStorageMenuItem(key, type, title, description, directory, iconResId);
+ }
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return DataStorageMenuItem.builder()
+ .buildKey(this.key)
+ .buildTitle(this.title)
+ .buildDescription(this.description)
+ .buildDirectory(this.directory)
+ .buildType(this.type)
+ .buildIconResId(this.iconResId)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/settings/GlobalSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/GlobalSettingsFragment.java
index d3709fbc3c..d39263ff02 100644
--- a/OsmAnd/src/net/osmand/plus/settings/GlobalSettingsFragment.java
+++ b/OsmAnd/src/net/osmand/plus/settings/GlobalSettingsFragment.java
@@ -7,6 +7,7 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.SwitchPreferenceCompat;
import android.util.Pair;
+import net.osmand.AndroidUtils;
import net.osmand.plus.ApplicationMode;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings;
@@ -16,6 +17,9 @@ import net.osmand.plus.dialogs.SendAnalyticsBottomSheetDialogFragment;
import net.osmand.plus.settings.preferences.ListPreferenceEx;
import net.osmand.plus.settings.preferences.SwitchPreferenceEx;
+import java.io.File;
+import java.text.DecimalFormat;
+
public class GlobalSettingsFragment extends BaseSettingsFragment implements SendAnalyticsBottomSheetDialogFragment.OnSendAnalyticsPrefsUpdate, OnPreferenceChanged {
@@ -137,6 +141,14 @@ public class GlobalSettingsFragment extends BaseSettingsFragment implements Send
Preference externalStorageDir = (Preference) findPreference(OsmandSettings.EXTERNAL_STORAGE_DIR);
externalStorageDir.setIcon(getContentIcon(R.drawable.ic_action_folder));
+ DataStorageItemsHolder holder = DataStorageItemsHolder.refreshInfo(app);
+ DataStorageMenuItem currentStorage = holder.getCurrentStorage();
+ File dir = new File(currentStorage.getDirectory());
+ DecimalFormat formatter = new DecimalFormat("#.##");
+ String summary = String.format(getString(R.string.data_storage_preference_summary),
+ currentStorage.getTitle(),
+ formatter.format(AndroidUtils.getUsedSpaceGb(dir)));
+ externalStorageDir.setSummary(summary);
}
private void setupSendAnonymousDataPref() {
diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ChangeDataStorageBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ChangeDataStorageBottomSheet.java
new file mode 100644
index 0000000000..27016b7eac
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ChangeDataStorageBottomSheet.java
@@ -0,0 +1,180 @@
+package net.osmand.plus.settings.bottomsheets;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.content.ContextCompat;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.view.View;
+
+import net.osmand.PlatformUtil;
+import net.osmand.plus.OsmandSettings;
+import net.osmand.plus.R;
+import net.osmand.plus.UiUtilities;
+import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem;
+import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription;
+import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
+import net.osmand.plus.settings.BaseSettingsFragment;
+import net.osmand.plus.settings.DataStorageMenuItem;
+
+import org.apache.commons.logging.Log;
+
+import java.io.File;
+
+import static net.osmand.plus.settings.DataStorageItemsHolder.MANUALLY_SPECIFIED;
+
+public class ChangeDataStorageBottomSheet extends BasePreferenceBottomSheet {
+
+ public static final String TAG = "ChangeDataStorageBottomSheet";
+
+ private static final Log LOG = PlatformUtil.getLog(ChangeDataStorageBottomSheet.class);
+
+ private final static String CURRENT_DIRECTORY = "current_directory";
+ private final static String NEW_DIRECTORY = "new_directory";
+
+ public final static String MOVE_DATA = "move_data";
+ public final static String CHOSEN_DIRECTORY = "chosen_storage";
+
+ private DataStorageMenuItem currentDirectory;
+ private DataStorageMenuItem newDirectory;
+
+ @Override
+ public void createMenuItems(Bundle savedInstanceState) {
+
+ Context ctx = getContext();
+
+ if (savedInstanceState != null) {
+ currentDirectory = savedInstanceState.getParcelable(CURRENT_DIRECTORY);
+ newDirectory = savedInstanceState.getParcelable(NEW_DIRECTORY);
+ }
+
+ if (ctx == null || currentDirectory == null || newDirectory == null) {
+ return;
+ }
+
+ items.add(new TitleItem(getString(R.string.change_osmand_data_folder_question)));
+
+ int textColorPrimary = nightMode ? R.color.text_color_primary_dark : R.color.text_color_primary_light;
+ int activeColor = nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light;
+ CharSequence desc = null;
+
+ File currentStorageFile = new File(currentDirectory.getDirectory());
+ if ((!OsmandSettings.isWritable(currentStorageFile))) {
+ desc = String.format(getString(R.string.android_19_location_disabled), currentStorageFile.getAbsoluteFile());
+ } else {
+ String from = currentDirectory.getKey().equals(MANUALLY_SPECIFIED) ? currentDirectory.getDirectory() : currentDirectory.getTitle();
+ String to = newDirectory.getKey().equals(MANUALLY_SPECIFIED) ? newDirectory.getDirectory() : newDirectory.getTitle();
+ String fullDescription = String.format(getString(R.string.change_data_storage_full_description), from, to);
+ SpannableStringBuilder coloredDescription = new SpannableStringBuilder(
+ fullDescription);
+ int startIndexFrom = fullDescription.indexOf(from);
+ int endIndexFrom = startIndexFrom + from.length();
+ int startIndexTo = fullDescription.indexOf(to);
+ int endIndexTo = startIndexTo + to.length();
+ coloredDescription.setSpan(new ForegroundColorSpan(
+ ContextCompat.getColor(ctx, activeColor)),
+ startIndexFrom, endIndexFrom, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ coloredDescription.setSpan(new ForegroundColorSpan(
+ ContextCompat.getColor(ctx, activeColor)),
+ startIndexTo, endIndexTo, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ desc = coloredDescription;
+ }
+ BottomSheetItemWithDescription description = (BottomSheetItemWithDescription) new BottomSheetItemWithDescription.Builder()
+ .setDescription(desc)
+ .setDescriptionColorId(textColorPrimary)
+ .setLayoutId(R.layout.bottom_sheet_item_description_long)
+ .create();
+ items.add(description);
+
+ //buttons
+ View mainView = View.inflate(ctx, R.layout.bottom_sheet_change_data_storage, null);
+
+ View btnDontMoveView = mainView.findViewById(R.id.btnDontMove);
+ btnDontMoveView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ positiveButtonsClick(false);
+ }
+ });
+ UiUtilities.setupDialogButton(nightMode, btnDontMoveView, UiUtilities.DialogButtonType.SECONDARY, getString(R.string.dont_move_maps), currentDirectory.getIconResId());
+
+ View btnMoveView = mainView.findViewById(R.id.btnMove);
+ btnMoveView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ positiveButtonsClick(true);
+ }
+ });
+ UiUtilities.setupDialogButton(nightMode, btnMoveView, UiUtilities.DialogButtonType.PRIMARY, getString(R.string.move_maps_to_new_destination), R.drawable.ic_action_folder_move);
+
+ View btnCloseView = mainView.findViewById(R.id.btnClose);
+ btnCloseView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ }
+ });
+ UiUtilities.setupDialogButton(nightMode, btnCloseView, UiUtilities.DialogButtonType.SECONDARY, getString(R.string.shared_string_cancel), R.drawable.ic_action_undo_dark);
+
+ BaseBottomSheetItem baseItem = new BaseBottomSheetItem.Builder()
+ .setCustomView(mainView)
+ .create();
+ items.add(baseItem);
+ }
+
+ public void setCurrentDirectory(DataStorageMenuItem currentDirectory) {
+ this.currentDirectory = currentDirectory;
+ }
+
+ public void setNewDirectory(DataStorageMenuItem newDirectory) {
+ this.newDirectory = newDirectory;
+ }
+
+ private void positiveButtonsClick(boolean moveData) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(TAG, true);
+ bundle.putParcelable(CHOSEN_DIRECTORY, newDirectory);
+ bundle.putBoolean(MOVE_DATA, moveData);
+ Fragment fragment = getTargetFragment();
+ if (fragment instanceof BaseSettingsFragment) {
+ ((BaseSettingsFragment) fragment).onPreferenceChange(getPreference(), bundle);
+ }
+ dismiss();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(CURRENT_DIRECTORY, currentDirectory);
+ outState.putParcelable(NEW_DIRECTORY, newDirectory);
+ }
+
+ @Override
+ protected boolean hideButtonsContainer() {
+ return true;
+ }
+
+ public static boolean showInstance(FragmentManager fm, String prefId, DataStorageMenuItem currentDirectory,
+ DataStorageMenuItem newDirectory, Fragment target, boolean usedOnMap) {
+ try {
+ if (fm.findFragmentByTag(TAG) == null) {
+ Bundle args = new Bundle();
+ args.putString(PREFERENCE_ID, prefId);
+
+ ChangeDataStorageBottomSheet fragment = new ChangeDataStorageBottomSheet();
+ fragment.setCurrentDirectory(currentDirectory);
+ fragment.setNewDirectory(newDirectory);
+ fragment.setTargetFragment(target, 0);
+ fragment.setUsedOnMap(usedOnMap);
+ fragment.show(fm, TAG);
+ return true;
+ }
+ } catch (RuntimeException e) {
+ LOG.error(e.getMessage());
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/SelectFolderBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/SelectFolderBottomSheet.java
new file mode 100644
index 0000000000..c9efa0d21c
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/SelectFolderBottomSheet.java
@@ -0,0 +1,198 @@
+package net.osmand.plus.settings.bottomsheets;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import net.osmand.PlatformUtil;
+import net.osmand.plus.R;
+import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem;
+import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
+import net.osmand.plus.settings.BaseSettingsFragment;
+
+import org.apache.commons.logging.Log;
+
+import static android.view.View.GONE;
+
+public class SelectFolderBottomSheet extends BasePreferenceBottomSheet {
+
+ public static final String TAG = "SelectFolderBottomSheet";
+ private static final Log LOG = PlatformUtil.getLog(SelectFolderBottomSheet.class);
+ private static final int CHOOSE_FOLDER_REQUEST_CODE = 0;
+
+ private static final String EDIT_TEXT_PREFERENCE_KEY = "edit_text_preference_key";
+ private static final String DIALOG_TITLE = "dialog_title";
+ private static final String DESCRIPTION = "description";
+ private static final String BTN_TITLE = "btn_title";
+ private static final String ET_WAS_FOCUSED = "edit_text_was_focused";
+ public static final String NEW_PATH = "path";
+ public static final String PATH_CHANGED = "changed";
+
+ private EditText editText;
+
+ private String currentPath;
+ private String dialogTitle;
+ private String btnTitle;
+ private String description;
+ private boolean etWasFocused;
+
+ @Override
+ public void createMenuItems(Bundle savedInstanceState) {
+ final Context ctx = getContext();
+
+ String text = null;
+ if (savedInstanceState != null) {
+ String folderPath = savedInstanceState.getString(NEW_PATH);
+ if (folderPath != null) {
+ currentPath = folderPath;
+ }
+ text = savedInstanceState.getString(EDIT_TEXT_PREFERENCE_KEY);
+ dialogTitle = savedInstanceState.getString(DIALOG_TITLE);
+ description = savedInstanceState.getString(DESCRIPTION);
+ btnTitle = savedInstanceState.getString(BTN_TITLE);
+ etWasFocused = savedInstanceState.getBoolean(ET_WAS_FOCUSED);
+ }
+
+ if (ctx == null || currentPath == null) {
+ return;
+ }
+
+ if (dialogTitle != null) {
+ items.add(new TitleItem(dialogTitle));
+ }
+
+ View mainView = View.inflate(ctx, R.layout.bottom_sheet_select_folder, null);
+
+ TextView tvDescription = mainView.findViewById(R.id.description);
+ TextView tvBtnTitle = mainView.findViewById(R.id.title);
+ editText = mainView.findViewById(R.id.text);
+
+ View divider = mainView.findViewById(R.id.divider);
+ View btnOpenChoseDialog = mainView.findViewById(R.id.button);
+
+// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+// if (btnTitle != null) {
+// tvBtnTitle.setText(btnTitle);
+// int colorResId = nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light;
+// int color = ContextCompat.getColor(ctx, colorResId);
+// Drawable drawable = UiUtilities.getColoredSelectableDrawable(ctx, color, 0.3f);
+// AndroidUtils.setBackground(btnOpenChoseDialog, drawable);
+// btnOpenChoseDialog.setOnClickListener(new View.OnClickListener() {
+// @Override
+// public void onClick(View v) {
+// openDocumentTree();
+// }
+// });
+// }
+// } else {
+ divider.setVisibility(GONE);
+ btnOpenChoseDialog.setVisibility(GONE);
+// }
+
+ if (text != null) {
+ editText.setText(text);
+ }
+
+ if (description != null) {
+ tvDescription.setText(description);
+ }
+
+ editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus && !etWasFocused) {
+ etWasFocused = true;
+ editText.setText(currentPath);
+ }
+ }
+ });
+
+ BaseBottomSheetItem baseItem = new BaseBottomSheetItem.Builder()
+ .setCustomView(mainView)
+ .create();
+ items.add(baseItem);
+
+ }
+
+ public static boolean showInstance(FragmentManager fm, String prefId, String currentPath, Fragment target,
+ String dialogTitle, String description, String btnTitle, boolean usedOnMap) {
+ try {
+ if (fm.findFragmentByTag(TAG) == null) {
+ Bundle args = new Bundle();
+ args.putString(PREFERENCE_ID, prefId);
+
+ SelectFolderBottomSheet fragment = new SelectFolderBottomSheet();
+ fragment.setCurrentPath(currentPath);
+ fragment.setTargetFragment(target, 0);
+ fragment.setDialogTitle(dialogTitle);
+ fragment.setDescription(description);
+ fragment.setBtnTitle(btnTitle);
+ fragment.setUsedOnMap(usedOnMap);
+ fragment.show(fm, TAG);
+ }
+ return true;
+ } catch (RuntimeException e) {
+ return false;
+ }
+ }
+
+ @Override
+ protected int getDismissButtonTextId() {
+ return R.string.shared_string_close;
+ }
+
+ @Override
+ protected int getRightBottomButtonTextId() {
+ return R.string.shared_string_apply;
+ }
+
+ @Override
+ protected void onRightBottomButtonClick() {
+ Fragment fragment = getTargetFragment();
+ if (fragment instanceof BaseSettingsFragment) {
+ String newPath = editText.getText().toString();
+ if (!newPath.equals("")) {
+ boolean pathChanged = !newPath.equals(currentPath);
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(TAG, true);
+ bundle.putString(NEW_PATH, newPath);
+ bundle.putBoolean(PATH_CHANGED, pathChanged);
+ ((BaseSettingsFragment) fragment).onPreferenceChange(getPreference(), bundle);
+ }
+ }
+ dismiss();
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(EDIT_TEXT_PREFERENCE_KEY, editText.getText().toString());
+ outState.putString(NEW_PATH, currentPath);
+ outState.putString(DIALOG_TITLE, dialogTitle);
+ outState.putString(DESCRIPTION, description);
+ outState.putString(BTN_TITLE, btnTitle);
+ outState.putBoolean(ET_WAS_FOCUSED, etWasFocused);
+ }
+
+ public void setCurrentPath(String currentPath) {
+ this.currentPath = currentPath;
+ }
+
+ public void setDialogTitle(String dialogTitle) {
+ this.dialogTitle = dialogTitle;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void setBtnTitle(String btnTitle) {
+ this.btnTitle = btnTitle;
+ }
+
+}
\ No newline at end of file