Add RenameFileBottomSheet

This commit is contained in:
Vitaliy 2021-01-11 07:21:16 +02:00
parent 6188343341
commit 643eaf3293
6 changed files with 262 additions and 105 deletions

View file

@ -1,27 +1,22 @@
package net.osmand;
import android.app.Activity;
import android.content.DialogInterface;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import net.osmand.plus.GpxSelectionHelper;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.SQLiteTileSource;
import net.osmand.plus.dialogs.RenameFileBottomSheet;
import net.osmand.util.Algorithms;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.regex.Pattern;
public class FileUtils {
@ -29,70 +24,11 @@ public class FileUtils {
public static final Pattern ILLEGAL_FILE_NAME_CHARACTERS = Pattern.compile("[?:\"*|/<>]");
public static final Pattern ILLEGAL_PATH_NAME_CHARACTERS = Pattern.compile("[?:\"*|<>]");
public static void renameFile(Activity a, final File f, final RenameCallback callback) {
final WeakReference<Activity> weakActivity = new WeakReference<>(a);
AlertDialog.Builder b = new AlertDialog.Builder(a);
if (f.exists()) {
int xt = f.getName().lastIndexOf('.');
final String ext = xt == -1 ? "" : f.getName().substring(xt);
final String originalName = xt == -1 ? f.getName() : f.getName().substring(0, xt);
final EditText editText = new EditText(a);
editText.setText(originalName);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
Editable text = editText.getText();
if (text.length() >= 1) {
Activity activity = weakActivity.get();
if (ILLEGAL_FILE_NAME_CHARACTERS.matcher(text).find() && activity != null) {
editText.setError(activity.getString(R.string.file_name_containes_illegal_char));
}
}
}
});
b.setTitle(R.string.shared_string_rename);
int leftPadding = AndroidUtils.dpToPx(a, 24f);
int topPadding = AndroidUtils.dpToPx(a, 4f);
b.setView(editText, leftPadding, topPadding, leftPadding, topPadding);
// Behaviour will be overwritten later;
b.setPositiveButton(R.string.shared_string_save, null);
b.setNegativeButton(R.string.shared_string_cancel, null);
final AlertDialog alertDialog = b.create();
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
Activity activity = weakActivity.get();
if (activity != null) {
OsmandApplication app = (OsmandApplication) activity.getApplication();
if (ext.equals(SQLiteTileSource.EXT)) {
if (renameSQLiteFile(app, f, editText.getText().toString() + ext,
callback) != null) {
alertDialog.dismiss();
}
} else {
if (renameGpxFile(app, f, editText.getText().toString() + ext,
false, callback) != null) {
alertDialog.dismiss();
}
}
}
}
});
}
});
alertDialog.show();
public static void renameFile(@NonNull FragmentActivity activity, @NonNull File file,
@Nullable Fragment target, boolean usedOnMap) {
if (file.exists()) {
FragmentManager fragmentManager = activity.getSupportFragmentManager();
RenameFileBottomSheet.showInstance(fragmentManager, target, file, usedOnMap);
}
}

View file

@ -5,6 +5,7 @@ import android.content.Intent;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MenuItem;
@ -45,6 +46,7 @@ import net.osmand.plus.settings.backend.OsmAndAppCustomization;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.track.TrackDisplayHelper;
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;
import net.osmand.util.Algorithms;
import java.io.File;
import java.lang.ref.WeakReference;
@ -83,10 +85,35 @@ public class TrackActivity extends TabActivity {
return;
}
displayHelper = new TrackDisplayHelper(app);
if (intent.hasExtra(TRACK_FILE_NAME)) {
if (savedInstanceState != null) {
String path = savedInstanceState.getString(TRACK_FILE_NAME);
if (!Algorithms.isEmpty(path)) {
displayHelper.setFile(new File(path));
}
} else if (intent.hasExtra(TRACK_FILE_NAME)) {
displayHelper.setFile(new File(intent.getStringExtra(TRACK_FILE_NAME)));
}
setupActionBar();
if (intent.hasExtra(OPEN_POINTS_TAB)
|| (savedInstanceState != null && savedInstanceState.getBoolean(OPEN_POINTS_TAB, false))) {
openPointsTab = true;
}
if (intent.hasExtra(OPEN_TRACKS_LIST)) {
openTracksList = true;
}
setContentView(R.layout.track_content);
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
File file = getFile();
outState.putString(TRACK_FILE_NAME, file != null ? file.getAbsolutePath() : null);
outState.putBoolean(CURRENT_RECORDING, file == null);
super.onSaveInstanceState(outState, outPersistentState);
}
public void setupActionBar() {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
if (getFile() != null) {
@ -97,14 +124,6 @@ public class TrackActivity extends TabActivity {
}
actionBar.setElevation(AndroidUtils.dpToPx(app, 4f));
}
if (intent.hasExtra(OPEN_POINTS_TAB)
|| (savedInstanceState != null && savedInstanceState.getBoolean(OPEN_POINTS_TAB, false))) {
openPointsTab = true;
}
if (intent.hasExtra(OPEN_TRACKS_LIST)) {
openTracksList = true;
}
setContentView(R.layout.track_content);
}
public TrackDisplayHelper getDisplayHelper() {

View file

@ -0,0 +1,164 @@
package net.osmand.plus.dialogs;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import net.osmand.AndroidUtils;
import net.osmand.FileUtils.RenameCallback;
import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.SQLiteTileSource;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.base.MenuBottomSheetDialogFragment;
import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
import java.io.File;
import static net.osmand.FileUtils.ILLEGAL_FILE_NAME_CHARACTERS;
import static net.osmand.FileUtils.renameGpxFile;
import static net.osmand.FileUtils.renameSQLiteFile;
public class RenameFileBottomSheet extends MenuBottomSheetDialogFragment {
private static final Log LOG = PlatformUtil.getLog(RenameFileBottomSheet.class);
private static final String TAG = RenameFileBottomSheet.class.getName();
private static final String SOURCE_FILE_NAME_KEY = "source_file_name_key";
private static final String SELECTED_FILE_NAME_KEY = "selected_file_name_key";
private OsmandApplication app;
private TextInputEditText editText;
private TextInputLayout nameTextBox;
private File file;
private String selectedFileName;
@Override
public void createMenuItems(Bundle savedInstanceState) {
app = requiredMyApplication();
if (savedInstanceState != null) {
String path = savedInstanceState.getString(SOURCE_FILE_NAME_KEY);
if (!Algorithms.isEmpty(path)) {
file = new File(path);
}
selectedFileName = savedInstanceState.getString(SELECTED_FILE_NAME_KEY);
}
items.add(new TitleItem(getString(R.string.shared_string_rename)));
View view = UiUtilities.getInflater(app, nightMode).inflate(R.layout.track_name_edit_text, null);
nameTextBox = view.findViewById(R.id.name_text_box);
nameTextBox.setBoxBackgroundColorResource(nightMode ? R.color.list_background_color_dark : R.color.activity_background_color_light);
nameTextBox.setHint(AndroidUtils.addColon(app, R.string.shared_string_name));
ColorStateList colorStateList = ColorStateList.valueOf(ContextCompat
.getColor(app, nightMode ? R.color.text_color_secondary_dark : R.color.text_color_secondary_light));
nameTextBox.setDefaultHintTextColor(colorStateList);
editText = view.findViewById(R.id.name_edit_text);
editText.setText(selectedFileName != null ? selectedFileName : Algorithms.getFileNameWithoutExtension(file));
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
updateFileName(s.toString());
}
});
BaseBottomSheetItem editFolderName = new BaseBottomSheetItem.Builder()
.setCustomView(view)
.create();
items.add(editFolderName);
}
private void updateFileName(String name) {
if (!Algorithms.isEmpty(name) && ILLEGAL_FILE_NAME_CHARACTERS.matcher(name).find()) {
nameTextBox.setError(getString(R.string.file_name_containes_illegal_char));
} else {
selectedFileName = name;
nameTextBox.setError(null);
}
updateBottomButtons();
}
@Override
protected boolean isRightBottomButtonEnabled() {
return nameTextBox.getError() == null;
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(SOURCE_FILE_NAME_KEY, file.getAbsolutePath());
outState.putString(SELECTED_FILE_NAME_KEY, selectedFileName);
super.onSaveInstanceState(outState);
}
@Override
protected void onRightBottomButtonClick() {
FragmentActivity activity = getActivity();
if (activity != null) {
AndroidUtils.hideSoftKeyboard(activity, editText);
}
File dest;
int index = file.getName().lastIndexOf('.');
String ext = index == -1 ? "" : file.getName().substring(index);
if (SQLiteTileSource.EXT.equals(ext)) {
dest = renameSQLiteFile(app, file, selectedFileName + ext, null);
} else {
dest = renameGpxFile(app, file, selectedFileName + ext, false, null);
}
if (dest != null) {
Fragment fragment = getTargetFragment();
if (fragment instanceof RenameCallback) {
RenameCallback listener = (RenameCallback) fragment;
listener.renamedTo(dest);
}
dismiss();
}
}
@Override
protected int getDismissButtonTextId() {
return R.string.shared_string_cancel;
}
@Override
protected int getRightBottomButtonTextId() {
return R.string.shared_string_save;
}
public static void showInstance(@NonNull FragmentManager fragmentManager, @Nullable Fragment target,
@NonNull File file, boolean usedOnMap) {
if (file.exists() && !fragmentManager.isStateSaved()
&& fragmentManager.findFragmentByTag(RenameFileBottomSheet.TAG) == null) {
RenameFileBottomSheet fragment = new RenameFileBottomSheet();
fragment.file = file;
fragment.setUsedOnMap(usedOnMap);
fragment.setTargetFragment(target, 0);
fragment.show(fragmentManager, RenameFileBottomSheet.TAG);
}
}
}

View file

@ -29,6 +29,7 @@ import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.PopupMenu;
import androidx.core.content.ContextCompat;
import androidx.core.view.MenuItemCompat;
import androidx.fragment.app.FragmentActivity;
import net.osmand.AndroidUtils;
import net.osmand.Collator;
@ -74,7 +75,7 @@ import java.util.Map;
import java.util.Set;
public class LocalIndexesFragment extends OsmandExpandableListFragment implements DownloadEvents,
OnMapSourceUpdateListener {
OnMapSourceUpdateListener, RenameCallback {
private LoadLocalIndexTask asyncLoader;
private Map<String, IndexItem> filesToUpdate = new HashMap<>();
private LocalIndexesAdapter listAdapter;
@ -141,13 +142,11 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement
private boolean performBasicOperation(int resId, final LocalIndexInfo info) {
if (resId == R.string.shared_string_rename) {
FileUtils.renameFile(getActivity(), new File(info.getPathToData()), new RenameCallback() {
@Override
public void renamedTo(File file) {
getDownloadActivity().reloadLocalIndexes();
FragmentActivity activity = getActivity();
if (activity != null) {
File file = new File(info.getPathToData());
FileUtils.renameFile(activity, file, this, false);
}
});
} else if (resId == R.string.clear_tile_data) {
AlertDialog.Builder confirm = new AlertDialog.Builder(getActivity());
confirm.setPositiveButton(R.string.shared_string_yes, new DialogInterface.OnClickListener() {
@ -188,7 +187,19 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement
@Override
public void onMapSourceUpdated() {
getDownloadActivity().reloadLocalIndexes();
reloadLocalIndexes();
}
@Override
public void renamedTo(File file) {
reloadLocalIndexes();
}
private void reloadLocalIndexes() {
DownloadActivity activity = getDownloadActivity();
if (activity != null) {
activity.reloadLocalIndexes();
}
}
public class LoadLocalIndexTask extends AsyncTask<Void, LocalIndexInfo, List<LocalIndexInfo>>
@ -666,7 +677,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement
public void localOptionsMenu(final int itemId) {
if (itemId == R.string.shared_string_refresh) {
getDownloadActivity().reloadLocalIndexes();
reloadLocalIndexes();
} else if (itemId == R.string.shared_string_delete) {
openSelectionMode(itemId, R.drawable.ic_action_delete_dark,
new DialogInterface.OnClickListener() {

View file

@ -42,6 +42,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.SearchView;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import net.osmand.AndroidUtils;
import net.osmand.Collator;
@ -114,7 +115,7 @@ import static net.osmand.util.Algorithms.objectEquals;
import static net.osmand.util.Algorithms.removeAllFiles;
public class AvailableGPXFragment extends OsmandExpandableListFragment implements
FavoritesFragmentStateHolder, OsmAuthorizationListener {
FavoritesFragmentStateHolder, OsmAuthorizationListener, RenameCallback {
public static final Pattern ILLEGAL_PATH_NAME_CHARACTERS = Pattern.compile("[?:\"*|<>]");
public static final int SEARCH_ID = -1;
@ -884,8 +885,7 @@ public class AvailableGPXFragment extends OsmandExpandableListFragment implement
File dest = new File(destFolder, info.fileName);
if (info.file.renameTo(dest)) {
app.getGpxDbHelper().rename(info.file, dest);
asyncLoader = new LoadGpxTask();
asyncLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getActivity());
reloadTracks();
} else {
Toast.makeText(app, R.string.file_can_not_be_moved, Toast.LENGTH_LONG).show();
}
@ -907,8 +907,7 @@ public class AvailableGPXFragment extends OsmandExpandableListFragment implement
} else {
if (info.file.renameTo(dest)) {
app.getGpxDbHelper().rename(info.file, dest);
asyncLoader = new LoadGpxTask();
asyncLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getActivity());
reloadTracks();
} else {
Toast.makeText(app, R.string.file_can_not_be_moved, Toast.LENGTH_LONG).show();
}
@ -943,6 +942,11 @@ public class AvailableGPXFragment extends OsmandExpandableListFragment implement
app.startActivity(intent);
}
@Override
public void renamedTo(File file) {
reloadTracks();
}
public class LoadGpxTask extends AsyncTask<Activity, GpxInfo, List<GpxInfo>> {
private List<GpxInfo> result;
@ -1548,13 +1552,10 @@ public class AvailableGPXFragment extends OsmandExpandableListFragment implement
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FileUtils.renameFile(getActivity(), gpxInfo.file, new RenameCallback() {
@Override
public void renamedTo(File file) {
asyncLoader = new LoadGpxTask();
asyncLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getActivity());
FragmentActivity activity = getActivity();
if (activity != null) {
FileUtils.renameFile(activity, gpxInfo.file, AvailableGPXFragment.this, false);
}
});
}
})
.create()

View file

@ -26,6 +26,7 @@ import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import net.osmand.AndroidUtils;
import net.osmand.FileUtils;
import net.osmand.FileUtils.RenameCallback;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.Track;
import net.osmand.GPXUtilities.TrkSegment;
@ -56,7 +57,8 @@ import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class TrackSegmentFragment extends OsmAndListFragment implements TrackBitmapDrawerListener, SegmentActionsListener {
public class TrackSegmentFragment extends OsmAndListFragment implements TrackBitmapDrawerListener,
SegmentActionsListener, RenameCallback {
public static final String TRACK_DELETED_KEY = "track_deleted_key";
@ -139,6 +141,20 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
}
});
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
MenuItem renameItem = menu.add(R.string.shared_string_rename)
.setIcon(app.getUIUtilities().getIcon((R.drawable.ic_action_edit_dark)))
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
GPXFile gpx = displayHelper.getGpx();
FragmentActivity activity = getActivity();
if (activity != null && gpx != null) {
FileUtils.renameFile(activity, new File(gpx.path), TrackSegmentFragment.this, false);
}
return true;
}
});
renameItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
MenuItem deleteItem = menu.add(R.string.shared_string_delete)
.setIcon(app.getUIUtilities().getIcon((R.drawable.ic_action_delete_dark)))
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@ -438,4 +454,14 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public void renamedTo(File file) {
displayHelper.setFile(file);
TrackActivity activity = getTrackActivity();
if (activity != null) {
activity.setupActionBar();
activity.loadGpx();
}
}
}