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