From cc9518be465357f453f93fd7e400f55d17b4b871 Mon Sep 17 00:00:00 2001 From: Harshad Vedartham Date: Mon, 23 Dec 2024 00:13:59 +0530 Subject: [PATCH 1/3] Multi user file browsing, closes #2479 (PR #2482) * Many fixes, setting current folder outside the async func * Moved case functions to TextViewUtils * notebook now special too * Cleaner approach to icons * Start at root folder for widget dialog * Changes to order of operations --- .../activity/DocumentEditAndViewFragment.java | 16 +- .../markor/activity/MainActivity.java | 3 +- .../filebrowser/MarkorFileBrowserFactory.java | 56 +++- .../frontend/textview/HighlightingEditor.java | 56 ---- .../frontend/textview/TextViewUtils.java | 50 ++- .../gsantner/markor/model/AppSettings.java | 2 - .../filebrowser/GsFileBrowserDialog.java | 3 +- .../filebrowser/GsFileBrowserFragment.java | 13 +- .../filebrowser/GsFileBrowserListAdapter.java | 304 +++++++++--------- .../filebrowser/GsFileBrowserOptions.java | 13 +- .../gsantner/opoc/util/GsCollectionUtils.java | 9 + .../res/drawable/baseline_download_24.xml | 5 + .../writeily/widget/WrWidgetConfigure.java | 3 +- 13 files changed, 270 insertions(+), 263 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_download_24.xml diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java b/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java index 5cec9c9409..a4819371d5 100644 --- a/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java +++ b/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java @@ -653,24 +653,16 @@ public void onFsViewerConfig(GsFileBrowserOptions.Options dopt) { return true; } case R.id.action_toggle_case: - if (_hlEditor != null) { - _hlEditor.toggleCase(); - } + TextViewUtils.toggleSelectionCase(_hlEditor.getText()); return true; case R.id.action_switch_case: - if (_hlEditor != null) { - _hlEditor.switchCase(); - } + TextViewUtils.switchSelectionCase(_hlEditor.getText()); return true; case R.id.action_capitalize_words: - if (_hlEditor != null) { - _hlEditor.capitalizeWords(); - } + TextViewUtils.capitalizeSelectionWords(_hlEditor.getText()); return true; case R.id.action_capitalize_sentences: - if (_hlEditor != null) { - _hlEditor.capitalizeSentences(); - } + TextViewUtils.capitalizeSelectionSentences(_hlEditor.getText()); return true; default: { return super.onOptionsItemSelected(item); diff --git a/app/src/main/java/net/gsantner/markor/activity/MainActivity.java b/app/src/main/java/net/gsantner/markor/activity/MainActivity.java index beb962f3c6..d15b39fc60 100644 --- a/app/src/main/java/net/gsantner/markor/activity/MainActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/MainActivity.java @@ -124,7 +124,6 @@ public void onActivityFirstTimeVisible() { } } - @Override public void onSaveInstanceState(@NonNull final Bundle outState) { super.onSaveInstanceState(outState); @@ -334,7 +333,7 @@ public void onBackPressed() { } public String getFileBrowserTitle() { - final File file = _notebook.getCurrentFolder(); + final File file = _notebook != null ? _notebook.getCurrentFolder() : null; if (file != null && !_appSettings.getNotebookDirectory().equals(file)) { return "> " + file.getName(); } else { diff --git a/app/src/main/java/net/gsantner/markor/frontend/filebrowser/MarkorFileBrowserFactory.java b/app/src/main/java/net/gsantner/markor/frontend/filebrowser/MarkorFileBrowserFactory.java index 1217ec039e..72b0b7e557 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/filebrowser/MarkorFileBrowserFactory.java +++ b/app/src/main/java/net/gsantner/markor/frontend/filebrowser/MarkorFileBrowserFactory.java @@ -8,6 +8,7 @@ package net.gsantner.markor.frontend.filebrowser; import android.content.Context; +import android.os.Environment; import androidx.fragment.app.FragmentManager; @@ -16,6 +17,7 @@ import net.gsantner.markor.model.AppSettings; import net.gsantner.markor.util.MarkorContextUtils; import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserDialog; +import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserListAdapter; import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserOptions; import net.gsantner.opoc.util.GsContextUtils; import net.gsantner.opoc.wrapper.GsCallback; @@ -41,6 +43,7 @@ public static GsFileBrowserOptions.Options prepareFsViewerOpts( if (listener != null) { opts.listener = listener; } + opts.doSelectFolder = doSelectFolder; opts.doSelectFile = !doSelectFolder; @@ -67,29 +70,50 @@ public static GsFileBrowserOptions.Options prepareFsViewerOpts( opts.folderColor = R.color.folder; opts.fileImage = R.drawable.ic_file_white_24dp; opts.folderImage = R.drawable.ic_folder_white_24dp; - opts.descriptionFormat = appSettings.getString(R.string.pref_key__file_description_format, ""); - opts.titleText = R.string.select; - opts.mountedStorageFolder = cu.getStorageAccessFolder(context); - opts.refresh = () -> { - opts.sortFolderFirst = appSettings.isFileBrowserSortFolderFirst(); - opts.sortByType = appSettings.getFileBrowserSortByType(); - opts.sortReverse = appSettings.isFileBrowserSortReverse(); - opts.filterShowDotFiles = appSettings.isFileBrowserFilterShowDotFiles(); - opts.favouriteFiles = appSettings.getFavouriteFiles(); - opts.recentFiles = appSettings.getRecentFiles(); - opts.popularFiles = appSettings.getPopularFiles(); - opts.storageMaps.clear(); - opts.storageMaps.put(new File("/storage", cu.rstr(context, R.string.notebook)), appSettings.getNotebookDirectory()); - opts.storageMaps.put(new File("/storage/Download"), new File("/storage/emulated/0/Download")); - }; - opts.refresh.callback(); + updateFsViewerOpts(opts, context, appSettings); return opts; } + public static void updateFsViewerOpts( + final GsFileBrowserOptions.Options opts, + final Context context, + AppSettings appSettings + ) { + appSettings = appSettings != null ? appSettings : ApplicationObject.settings(); + + opts.sortFolderFirst = appSettings.isFileBrowserSortFolderFirst(); + opts.sortByType = appSettings.getFileBrowserSortByType(); + opts.sortReverse = appSettings.isFileBrowserSortReverse(); + opts.filterShowDotFiles = appSettings.isFileBrowserFilterShowDotFiles(); + opts.favouriteFiles = appSettings.getFavouriteFiles(); + opts.recentFiles = appSettings.getRecentFiles(); + opts.popularFiles = appSettings.getPopularFiles(); + + opts.descriptionFormat = appSettings.getString(R.string.pref_key__file_description_format, ""); + + opts.storageMaps.clear(); + opts.iconMaps.clear(); + + final File downloads = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + opts.addVirtualFile("Download", downloads, R.drawable.baseline_download_24); + + final File notebook = appSettings.getNotebookDirectory(); + opts.addVirtualFile(context.getString(R.string.notebook), notebook, R.drawable.ic_home_black_24dp); + + opts.iconMaps.put(GsFileBrowserListAdapter.VIRTUAL_STORAGE_FAVOURITE, R.drawable.ic_star_black_24dp); + opts.iconMaps.put(GsFileBrowserListAdapter.VIRTUAL_STORAGE_RECENTS, R.drawable.ic_history_black_24dp); + opts.iconMaps.put(GsFileBrowserListAdapter.VIRTUAL_STORAGE_POPULAR, R.drawable.ic_favorite_black_24dp); + opts.iconMaps.put(notebook, R.drawable.ic_home_black_24dp); + opts.iconMaps.put(downloads, R.drawable.baseline_download_24); + opts.iconMaps.put(appSettings.getQuickNoteFile(), R.drawable.ic_lightning_black_24dp); + opts.iconMaps.put(appSettings.getTodoFile(), R.drawable.ic_assignment_turned_in_black_24dp); + } + + public static File[] strlistToArray(List strlist) { File[] files = new File[strlist.size()]; for (int i = 0; i < files.length; i++) { diff --git a/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java b/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java index 1202fc5e1a..72bb9f5ee4 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java +++ b/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java @@ -39,9 +39,7 @@ import net.gsantner.opoc.format.GsTextUtils; import net.gsantner.opoc.wrapper.GsCallback; import net.gsantner.opoc.wrapper.GsTextWatcherAdapter; -import net.gsantner.markor.util.TextCasingUtils; -import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.SynchronousQueue; @@ -320,60 +318,6 @@ private int rowEnd(final int y) { return layout == null ? 0 : layout.getLineEnd(layout.getLineForVertical(y)); } - // Text-Casing - // --------------------------------------------------------------------------------------------- - public void toggleCase() { - String text = getSelectedText(); - if (text.isEmpty()) { - text = Objects.requireNonNull(getText()).toString(); - } - String newText = TextCasingUtils.toggleCase(text); - replaceSelection(newText); - } - - public void switchCase() { - String text = getSelectedText(); - if (text.isEmpty()) { - text = Objects.requireNonNull(getText()).toString(); - } - String newText = TextCasingUtils.switchCase(text); - replaceSelection(newText); - } - - public void capitalizeWords() { - String text = getSelectedText(); - if (text.isEmpty()) { - text = Objects.requireNonNull(getText()).toString(); - } - String newText = TextCasingUtils.capitalizeWords(text); - replaceSelection(newText); - } - - public void capitalizeSentences() { - String text = getSelectedText(); - if (text.isEmpty()) { - text = Objects.requireNonNull(getText()).toString(); - } - String newText = TextCasingUtils.capitalizeSentences(text); - replaceSelection(newText); - } - - private String getSelectedText() { - int start = Math.max(0, getSelectionStart()); - int end = Math.max(0, getSelectionEnd()); - return Objects.requireNonNull(getText()).toString().substring(start, end); - } - - private void replaceSelection(String replacement) { - int start = Math.max(0, getSelectionStart()); - int end = Math.max(0, getSelectionEnd()); - if (start == end) { // If no selection is made, replace all the text in the document - setText(replacement); - } else { // Replace only the selected text - Objects.requireNonNull(getText()).replace(start, end, replacement); - } - } - // Various overrides // --------------------------------------------------------------------------------------------- public void setSaveInstanceState(final boolean save) { diff --git a/app/src/main/java/net/gsantner/markor/frontend/textview/TextViewUtils.java b/app/src/main/java/net/gsantner/markor/frontend/textview/TextViewUtils.java index 725ff3ba7a..6384eecebe 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/textview/TextViewUtils.java +++ b/app/src/main/java/net/gsantner/markor/frontend/textview/TextViewUtils.java @@ -28,6 +28,7 @@ import androidx.annotation.NonNull; +import net.gsantner.markor.util.TextCasingUtils; import net.gsantner.opoc.format.GsTextUtils; import net.gsantner.opoc.util.GsContextUtils; @@ -40,7 +41,7 @@ import java.util.TreeSet; import java.util.UUID; -@SuppressWarnings({"CharsetObjectCanBeUsed", "WeakerAccess", "unused"}) +@SuppressWarnings({"WeakerAccess", "unused"}) public final class TextViewUtils { // Suppress default constructor for noninstantiability @@ -128,15 +129,24 @@ public static int[] getSelection(final CharSequence text) { } } - public static String getSelectedText(final CharSequence text) { + public static CharSequence getSelectedText(final CharSequence text) { final int[] sel = getSelection(text); - return (sel[0] >= 0 && sel[1] >= 0) ? text.subSequence(sel[0], sel[1]).toString() : ""; + return (sel[0] >= 0 && sel[1] >= 0) ? text.subSequence(sel[0], sel[1]) : ""; } - public static String getSelectedText(final TextView text) { + public static CharSequence getSelectedText(final TextView text) { return getSelectedText(text.getText()); } + public static void replaceSelection(final Editable text, final CharSequence replace) { + if (text != null && replace != null) { + final int[] sel = getSelection(text); + if (sel[0] >= 0 && sel[1] >= 0) { + text.replace(sel[0], sel[1], replace); + } + } + } + public static int[] getLineSelection(final CharSequence text, final int[] sel) { return sel != null && sel.length >= 2 ? new int[]{getLineStart(text, sel[0]), getLineEnd(text, sel[1])} : new int[]{-1, -1}; } @@ -380,7 +390,7 @@ public static void setSelectionAndShow(final EditText edit, final int... sel) { * @param title Title of note (for {{title}}) * @param selectedText Currently selected text */ - public static String interpolateSnippet(String text, final String title, final String selectedText) { + public static String interpolateSnippet(String text, final CharSequence title, final CharSequence selectedText) { final long current = System.currentTimeMillis(); final String time = GsContextUtils.instance.formatDateTime((Locale) null, "HH:mm", current); final String date = GsContextUtils.instance.formatDateTime((Locale) null, "yyyy-MM-dd", current); @@ -753,4 +763,34 @@ public static Boolean isImeOpen(final View view) { } return null; // Uncertain } + + // Text-Casing + // --------------------------------------------------------------------------------------------- + public static void toggleSelectionCase(final Editable edit) { + final String text = getSelectedText(edit).toString(); + if (!text.isEmpty()) { + replaceSelection(edit, TextCasingUtils.toggleCase(text)); + } + } + + public static void switchSelectionCase(final Editable edit) { + final String text = getSelectedText(edit).toString(); + if (!text.isEmpty()) { + replaceSelection(edit, TextCasingUtils.switchCase(text)); + } + } + + public static void capitalizeSelectionWords(final Editable edit) { + final String text = getSelectedText(edit).toString(); + if (!text.isEmpty()) { + replaceSelection(edit, TextCasingUtils.capitalizeWords(text)); + } + } + + public static void capitalizeSelectionSentences(final Editable edit) { + final String text = getSelectedText(edit).toString(); + if (!text.isEmpty()) { + replaceSelection(edit, TextCasingUtils.capitalizeSentences(text)); + } + } } diff --git a/app/src/main/java/net/gsantner/markor/model/AppSettings.java b/app/src/main/java/net/gsantner/markor/model/AppSettings.java index f354681012..844ec7cb86 100644 --- a/app/src/main/java/net/gsantner/markor/model/AppSettings.java +++ b/app/src/main/java/net/gsantner/markor/model/AppSettings.java @@ -61,8 +61,6 @@ public class AppSettings extends GsSharedPreferencesPropertyBackend { public static Boolean _isDeviceGoodHardware = null; private MarkorContextUtils _cu; - private static final File LOCAL_TESTFOLDER_FILEPATH = new File("/storage/emulated/0/00_sync/documents/special"); - @Override public AppSettings init(final Context context) { super.init(context); diff --git a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserDialog.java b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserDialog.java index 6ba6c1cd1e..c58f2311fe 100644 --- a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserDialog.java +++ b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserDialog.java @@ -173,11 +173,12 @@ public void onViewCreated(final View root, final @Nullable Bundle savedInstanceS _filesystemViewerAdapter = new GsFileBrowserListAdapter(_dopt, activity); _recyclerList.setAdapter(_filesystemViewerAdapter); - onFsViewerDoUiUpdate(_filesystemViewerAdapter); // Setup callbacks _dopt.setSubtitle = _toolBar::setSubtitle; _dopt.setTitle = _toolBar::setTitle; + + _recyclerList.post(() -> onFsViewerDoUiUpdate(_filesystemViewerAdapter)); } private int rcolor(@ColorRes int colorRes) { diff --git a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserFragment.java b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserFragment.java index f0bb0a4f8c..c9a6f46ebc 100644 --- a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserFragment.java +++ b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserFragment.java @@ -91,7 +91,6 @@ public static GsFileBrowserFragment newInstance() { private Menu _fragmentMenu; private MarkorContextUtils _cu; private Toolbar _toolbar; - private File _lastSelectedFile; //######################## //## Methods @@ -207,6 +206,11 @@ public void onFsViewerConfig(GsFileBrowserOptions.Options dopt) { if (_callback != null) { _callback.onFsViewerConfig(dopt); } + + final Context context = getContext(); + if (context != null) { + MarkorFileBrowserFactory.updateFsViewerOpts(dopt, context, _appSettings); + } } @Override @@ -235,7 +239,7 @@ private void updateMenuItems() { // Check if is a favourite boolean selTextFilesOnly = true; boolean selDirectoriesOnly = true; - boolean selWritable = (!curFilepath.equals("/storage") && !curFilepath.equals("/storage/emulated")); + boolean selWritable = true; boolean allSelectedFav = true; final Collection favFiles = _dopt.favouriteFiles != null ? _dopt.favouriteFiles : Collections.emptySet(); for (final File f : selFiles) { @@ -314,10 +318,7 @@ public void onViewStateRestored(final Bundle savedInstanceState) { @Override public void onResume() { super.onResume(); - if (_dopt.refresh != null) { - _dopt.refresh.callback(); - } - + _dopt.listener.onFsViewerConfig(_dopt); final File folder = getCurrentFolder(); final Activity activity = getActivity(); if (isVisible() && folder != null && activity != null) { diff --git a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserListAdapter.java b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserListAdapter.java index 1dee2e5ae8..8cb5b9bc8f 100644 --- a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserListAdapter.java +++ b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserListAdapter.java @@ -14,12 +14,14 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Environment; import android.os.Parcelable; import android.text.Spannable; import android.text.Spanned; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.style.StrikethroughSpan; +import android.util.Log; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; @@ -47,9 +49,10 @@ import java.io.FilenameFilter; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -72,13 +75,14 @@ public class GsFileBrowserListAdapter extends RecyclerView.Adapter _virtualMapping; - private final Map _reverseVirtualMapping; + private final Map _virtualMapping = new LinkedHashMap<>(); private final Map _fileIdMap = new HashMap<>(); private final Map _folderScrollMap = new HashMap<>(); private final Stack _backStack = new Stack<>(); + private final int _userId = getUserId(); private long _prevModSum = 0; //######################## @@ -136,35 +140,37 @@ public GsFileBrowserListAdapter(GsFileBrowserOptions.Options options, Context co _dopt.folderColor = cu.getResId(context, GsContextUtils.ResType.COLOR, "folder"); } - _virtualMapping = Collections.unmodifiableMap(getVirtualFolders()); - _reverseVirtualMapping = Collections.unmodifiableMap(GsCollectionUtils.reverse(_virtualMapping)); - loadFolder(_dopt.startFolder != null ? _dopt.startFolder : _dopt.rootFolder, null); + updateVirtualFolders(); _filter = new StringFilter(this); } - public Map getVirtualFolders() { + public void updateVirtualFolders() { final GsContextUtils cu = GsContextUtils.instance; - final Map map = new HashMap<>(); + _virtualMapping.clear(); + _virtualMapping.put(VIRTUAL_STORAGE_EMULATED, VIRTUAL_STORAGE_EMULATED); final File appDataFolder = _context.getFilesDir(); if (appDataFolder.exists() || appDataFolder.mkdir()) { - map.put(VIRTUAL_STORAGE_APP_DATA_PRIVATE, appDataFolder); + _virtualMapping.put(VIRTUAL_STORAGE_APP_DATA_PRIVATE, appDataFolder); } for (final File file : ContextCompat.getExternalFilesDirs(_context, null)) { - if (file == null || (file != null && file.getParentFile() == null)) { - continue; + if (file != null) { + final File parent = file.getParentFile(); + if (parent != null) { + final String name = parent.toString().replace("/", "-").substring(1); + final File remap = new File(VIRTUAL_STORAGE_ROOT, "AppData (" + name + ")"); + _virtualMapping.put(remap, file); + } } - final File remap = new File(VIRTUAL_STORAGE_ROOT, "AppData (" + file.getParentFile().toString().replace("/", "-").substring(1) + ")"); - map.put(remap, file); } - map.put(VIRTUAL_STORAGE_RECENTS, VIRTUAL_STORAGE_RECENTS); - map.put(VIRTUAL_STORAGE_POPULAR, VIRTUAL_STORAGE_POPULAR); - map.put(VIRTUAL_STORAGE_FAVOURITE, VIRTUAL_STORAGE_FAVOURITE); + _virtualMapping.putAll(_dopt.storageMaps); - return map; + _virtualMapping.put(VIRTUAL_STORAGE_RECENTS, VIRTUAL_STORAGE_RECENTS); + _virtualMapping.put(VIRTUAL_STORAGE_POPULAR, VIRTUAL_STORAGE_POPULAR); + _virtualMapping.put(VIRTUAL_STORAGE_FAVOURITE, VIRTUAL_STORAGE_FAVOURITE); } @NonNull @@ -186,69 +192,78 @@ public boolean isFileWriteable(File file, boolean isGoUp) { @SuppressWarnings("ConstantConditions") public void onBindViewHolder(@NonNull FilesystemViewerViewHolder holder, int position) { final File displayFile = _adapterDataFiltered.get(position); - final File file; + if (displayFile == null) { holder.title.setText("????"); return; - } else if (_virtualMapping.containsKey(displayFile)) { - file = _virtualMapping.get(displayFile); - } else { - file = displayFile; } - final String filename = displayFile.getName(); - final String currentFolderName = _currentFolder != null ? _currentFolder.getName() : ""; - final File currentFolderParent = _currentFolder != null ? _currentFolder.getParentFile() : null; + final File file = GsCollectionUtils.getOrDefault(_virtualMapping, displayFile, displayFile); - final boolean isGoUp = VIRTUAL_STORAGE_ROOT.equals(displayFile) || file.equals(currentFolderParent); + final boolean isGoUp = displayFile instanceof GoUpFile; + final boolean isVirtual = _virtualMapping.containsKey(displayFile); final boolean isSelected = _currentSelection.contains(displayFile); final boolean isFavourite = _dopt.favouriteFiles != null && _dopt.favouriteFiles.contains(displayFile); final boolean isPopular = _dopt.popularFiles != null && _dopt.popularFiles.contains(displayFile); - final int descriptionRes = isSelected ? _dopt.contentDescriptionSelected : (displayFile.isDirectory() ? _dopt.contentDescriptionFolder : _dopt.contentDescriptionFile); + final boolean isFile = displayFile.isFile(); - String titleText = filename; - if (isCurrentFolderVirtual() && "index.html".equals(filename)) { + String titleText = displayFile.getName(); + if (isCurrentFolderVirtual() && "index.html".equals(titleText)) { + final String currentFolderName = _currentFolder != null ? _currentFolder.getName() : ""; titleText += " [" + currentFolderName + "]"; } - if (currentFolderName.equals("storage") && _dopt.storageMaps.containsValue(displayFile)) { - titleText = GsCollectionUtils.reverse(_dopt.storageMaps).get(displayFile).getName(); - } + // Set title holder.title.setText(isGoUp ? ".." : titleText, TextView.BufferType.SPANNABLE); holder.title.setTextColor(ContextCompat.getColor(_context, _dopt.primaryTextColor)); - if (!isFileWriteable(displayFile, isGoUp) && !isVirtualFolder(displayFile) && holder.title.length() > 0) { + if (!isFileWriteable(displayFile, isGoUp) && !isVirtual && holder.title.length() > 0) { try { ((Spannable) holder.title.getText()).setSpan(STRIKE_THROUGH_SPAN, 0, holder.title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } catch (Exception ignored) { } } - final boolean isFile = displayFile.isFile(); - - holder.description.setText(!_dopt.descModtimeInsteadOfParent || isGoUp - ? file.getAbsolutePath() : formatFileDescription(file, _dopt.descriptionFormat)); + // Set description + if (!_dopt.descModtimeInsteadOfParent || isGoUp) { + holder.description.setText(file.getAbsolutePath()); + } else { + holder.description.setText(formatFileDescription(file, _dopt.descriptionFormat)); + } holder.description.setTextColor(ContextCompat.getColor(_context, _dopt.secondaryTextColor)); - holder.image.setImageResource(isSelected ? _dopt.selectedItemImage : isFile ? _dopt.fileImage : _dopt.folderImage); - holder.image.setColorFilter(ContextCompat.getColor(_context, - isSelected ? _dopt.accentColor : isFile ? _dopt.fileColor : _dopt.folderColor), - android.graphics.PorterDuff.Mode.SRC_ATOP); + // Set icon + if (isSelected) { + holder.image.setImageResource(_dopt.selectedItemImage); + } else if (_dopt.iconMaps != null && _dopt.iconMaps.containsKey(displayFile)) { + holder.image.setImageResource(_dopt.iconMaps.get(displayFile)); + } else { + holder.image.setImageResource(isFile ? _dopt.fileImage : _dopt.folderImage); + } + + holder.image.setColorFilter(ContextCompat.getColor( + _context, + isSelected ? _dopt.accentColor : isFile ? _dopt.fileColor : _dopt.folderColor), + android.graphics.PorterDuff.Mode.SRC_ATOP + ); if (!isSelected && isFavourite) { holder.image.setColorFilter(0xFFE3B51B); } + // Some extras if (_dopt.itemSidePadding > 0) { int dp = (int) (_dopt.itemSidePadding * _context.getResources().getDisplayMetrics().density); holder.itemRoot.setPadding(dp, holder.itemRoot.getPaddingTop(), dp, holder.itemRoot.getPaddingBottom()); } - holder.itemRoot.setContentDescription((descriptionRes != 0 ? (_context.getString(descriptionRes) + " ") : "") + holder.title.getText().toString() + " " + holder.description.getText().toString()); + final int descriptionRes = isSelected ? _dopt.contentDescriptionSelected : (displayFile.isDirectory() ? _dopt.contentDescriptionFolder : _dopt.contentDescriptionFile); + holder.itemRoot.setContentDescription((descriptionRes != 0 ? (_context.getString(descriptionRes) + " ") : "") + titleText + " " + holder.description.getText().toString()); holder.image.setOnLongClickListener(view -> { Toast.makeText(_context, displayFile.getAbsolutePath(), Toast.LENGTH_SHORT).show(); return true; }); + holder.itemRoot.setTag(new TagContainer(displayFile, position)); holder.itemRoot.setOnClickListener(this); holder.itemRoot.setOnLongClickListener(this); @@ -264,6 +279,7 @@ public void onAttachedToRecyclerView(@NonNull final RecyclerView view) { super.onAttachedToRecyclerView(view); _recyclerView = view; _layoutManager = (LinearLayoutManager) view.getLayoutManager(); + loadFolder(_dopt.startFolder != null ? _dopt.startFolder : _dopt.rootFolder, null); } public String formatFileDescription(final File file, String format) { @@ -292,15 +308,16 @@ public void restoreSavedInstanceState(final Bundle savedInstanceState) { return; } + if (_dopt != null && _dopt.listener != null) { + _dopt.listener.onFsViewerConfig(_dopt); + } + if (savedInstanceState.containsKey(EXTRA_CURRENT_FOLDER)) { final String path = savedInstanceState.getString(EXTRA_CURRENT_FOLDER); if (path != null) { final File f = new File(path); - final boolean isVirtualDirectory = _virtualMapping.containsKey(f) || isVirtualFolder(f); + final boolean isVirtualDirectory = _virtualMapping.containsKey(f); - if (isVirtualDirectory && _dopt != null && _dopt.listener != null) { - _dopt.listener.onFsViewerConfig(_dopt); - } if (f.isDirectory() || isVirtualDirectory) { loadFolder(f, null); } @@ -313,7 +330,7 @@ public void restoreSavedInstanceState(final Bundle savedInstanceState) { } public void reloadCurrentFolder() { - loadFolder(_currentFolder, null); + loadFolder(_currentFolder != null ? _currentFolder : _dopt.rootFolder, null); } public void setCurrentFolder(final File folder) { @@ -322,13 +339,6 @@ public void setCurrentFolder(final File folder) { } } - public void reconfigure() { - if (_dopt.listener != null) { - _dopt.listener.onFsViewerConfig(_dopt); - reloadCurrentFolder(); - } - } - public boolean isCurrentFolderVirtual() { return isVirtualFolder(_currentFolder); } @@ -400,19 +410,17 @@ public void onClick(View view) { case R.id.opoc_filesystem_item__root: { // A own item was clicked if (data.file != null) { - final File file = GsCollectionUtils.getOrDefault(_virtualMapping, data.file, data.file); - if (areItemsSelected()) { // There are 1 or more items selected yet - if (!toggleSelection(data) && file != null && file.isDirectory()) { - loadFolder(file, null); + if (!toggleSelection(data) && data.file.isDirectory()) { + loadFolder(data.file, null); } - } else if (file != null) { + } else { // No pre-selection - if (file.isDirectory() || isVirtualFolder(file)) { - loadFolder(file, isParent(file, _currentFolder) ? _currentFolder : null); - } else if (file.isFile()) { - _dopt.listener.onFsViewerSelected(_dopt.requestId, file, null); + if (data.file.isDirectory() || _virtualMapping.containsKey(data.file)) { + loadFolder(data.file, _currentFolder); + } else if (data.file.isFile()) { + _dopt.listener.onFsViewerSelected(_dopt.requestId, data.file, null); } } } @@ -488,7 +496,7 @@ public boolean toggleSelection(final TagContainer data) { } boolean clickHandled = false; - if (data.file != null && _currentFolder != null && !isParent(data.file, _currentFolder)) { + if (data.file != null && _currentFolder != null && !(data.file instanceof GoUpFile)) { if (_currentSelection.contains(data.file)) { // Single selection _currentSelection.remove(data.file); @@ -515,45 +523,32 @@ public boolean toggleSelection(final TagContainer data) { } public boolean goBack() { - if (canGoBack()) { - final File show = GsCollectionUtils.getOrDefault(_reverseVirtualMapping, _currentFolder, _currentFolder); + if (!_backStack.isEmpty()) { + File show = _currentFolder; + if (VIRTUAL_STORAGE_ROOT.equals(_backStack.peek())) { + show = GsCollectionUtils.reverseSearch(_virtualMapping, _currentFolder); + } loadFolder(GO_BACK_SIGNIFIER, show); return true; } return false; } - public boolean canGoBack() { - return !_backStack.isEmpty(); - } - - public boolean goUp() { - if (_currentFolder != null && canGoUp()) { - if (_reverseVirtualMapping.containsKey(_currentFolder)) { - loadFolder(VIRTUAL_STORAGE_ROOT, _reverseVirtualMapping.get(_currentFolder)); - return true; - } else { - final File parent = _currentFolder.getParentFile(); - if (parent != null) { - loadFolder(_currentFolder.getParentFile(), _currentFolder); - return true; - } - } + private @Nullable File getCurrentParent() { + if (_currentFolder == null) { + return null; } - return false; - } - public boolean canGoUp() { - return canGoUp(_currentFolder); - } + final File parent = _currentFolder.getParentFile(); + if ((parent != null && parent.canWrite()) || GsFileUtils.isChild(VIRTUAL_STORAGE_ROOT, parent)) { + return parent; + } - public boolean canGoUp(final File folder) { - try { - final File parent = folder != null ? folder.getParentFile() : null; - return (parent != null && parent.canWrite()) || GsFileUtils.isChild(VIRTUAL_STORAGE_ROOT, folder); - } catch (SecurityException ignored) { - return false; + if (VIRTUAL_STORAGE_ROOT.equals(parent) || _virtualMapping.containsValue(_currentFolder)) { + return VIRTUAL_STORAGE_ROOT; } + + return null; } @Override @@ -575,7 +570,7 @@ public File createDirectoryHere(final CharSequence name) { final String trimmed = name.toString().trim(); - if (trimmed.length() == 0) { + if (trimmed.isEmpty()) { return null; } @@ -648,6 +643,9 @@ public boolean scrollToAndFlash(final File file) { private static final ExecutorService executorService = new ThreadPoolExecutor(0, 3, 60, TimeUnit.SECONDS, new SynchronousQueue<>()); private void loadFolder(final File folder, final File show) { + if (folder == null || _recyclerView == null) { + return; + } final boolean folderChanged = !folder.equals(_currentFolder); @@ -655,51 +653,37 @@ private void loadFolder(final File folder, final File show) { _folderScrollMap.put(_currentFolder, _layoutManager.onSaveInstanceState()); } - final File toLoad; if (GO_BACK_SIGNIFIER == folder) { - toLoad = _backStack.pop(); + _currentFolder = _backStack.pop(); } else { if (folderChanged && _currentFolder != null) { _backStack.push(_currentFolder); } - toLoad = folder; + _currentFolder = GsCollectionUtils.getOrDefault(_virtualMapping, folder, folder); } - if (_dopt.refresh != null) { - _dopt.refresh.callback(); - } - - if (_fileToShowAfterNextLoad != null) { - _recyclerView.post(() -> { - scrollToAndFlash(_fileToShowAfterNextLoad); - _fileToShowAfterNextLoad = null; - }); - } - - final File toShow = show == null ? _fileToShowAfterNextLoad : show; - _fileToShowAfterNextLoad = null; + if (_currentFolder != null) { + final File toShow = show == null ? _fileToShowAfterNextLoad : show; + _fileToShowAfterNextLoad = null; - try { - executorService.execute(() -> _loadFolder(toLoad, toShow)); - } catch (RejectedExecutionException ignored) { // during exit + try { + executorService.execute(() -> _loadFolder(folderChanged, toShow)); + } catch (RejectedExecutionException err) { // during exit + Log.d(GsFileBrowserListAdapter.class.getName(), err.toString()); + } } } - // This function is not called on the main thread, so post to the UI thread - private synchronized void _loadFolder(final @NonNull File folder, final @Nullable File toShow) { + // This function is not called on the main thread + private synchronized void _loadFolder(final boolean folderChanged, final @Nullable File toShow) { - if (_recyclerView == null) { - return; - } - - final boolean folderChanged = !folder.equals(_currentFolder); final List newData = new ArrayList<>(); // Make sure /storage/emulated/0 is browsable, even though filesystem says it's not accessible - if (folder.equals(new File("/"))) { + if (_currentFolder.equals(new File("/"))) { newData.add(VIRTUAL_STORAGE_ROOT); - } else if (folder.equals(VIRTUAL_STORAGE_ROOT)) { - newData.add(VIRTUAL_STORAGE_EMULATED); + } else if (_currentFolder.equals(VIRTUAL_STORAGE_ROOT)) { + newData.addAll(_virtualMapping.keySet()); // SD Card and other external storage directories that are also not listable for (final Pair p : GsContextUtils.instance.getAppDataPublicDirs(_context, false, true, false)) { @@ -709,33 +693,24 @@ private synchronized void _loadFolder(final @NonNull File folder, final @Nullabl } newData.add(f); } - } else if (folder.equals(VIRTUAL_STORAGE_EMULATED)) { - newData.add(new File(folder, "0")); - } - - if (folder.equals(VIRTUAL_STORAGE_RECENTS)) { + } else if (_currentFolder.equals(VIRTUAL_STORAGE_EMULATED)) { + newData.add(new File(_currentFolder, "" + _userId)); + } else if (_currentFolder.equals(VIRTUAL_STORAGE_RECENTS)) { newData.addAll(_dopt.recentFiles); - } else if (folder.equals(VIRTUAL_STORAGE_POPULAR)) { + } else if (_currentFolder.equals(VIRTUAL_STORAGE_POPULAR)) { newData.addAll(_dopt.popularFiles); - } else if (folder.equals(VIRTUAL_STORAGE_FAVOURITE)) { + } else if (_currentFolder.equals(VIRTUAL_STORAGE_FAVOURITE)) { newData.addAll(_dopt.favouriteFiles); - } else if (folder.isDirectory()) { - GsCollectionUtils.addAll(newData, folder.listFiles(GsFileBrowserListAdapter.this)); } - if (folder.equals(VIRTUAL_STORAGE_ROOT)) { - newData.addAll(_dopt.storageMaps.values()); - newData.addAll(_virtualMapping.keySet()); - } - - if (folder.getAbsolutePath().equals("/")) { - newData.add(new File(folder, VIRTUAL_STORAGE_ROOT.getName())); + if (_currentFolder.isDirectory() && _currentFolder.canRead()) { + GsCollectionUtils.addAll(newData, _currentFolder.listFiles(GsFileBrowserListAdapter.this)); } GsCollectionUtils.deduplicate(newData); - // Don't sort recent items - use the default order - if (!folder.equals(VIRTUAL_STORAGE_RECENTS)) { + // Don't sort recent or virtual root items - use the default order + if (!Arrays.asList(VIRTUAL_STORAGE_RECENTS, VIRTUAL_STORAGE_ROOT).contains(_currentFolder)) { GsFileUtils.sortFiles(newData, _dopt.sortByType, _dopt.sortFolderFirst, _dopt.sortReverse); } @@ -743,12 +718,9 @@ private synchronized void _loadFolder(final @NonNull File folder, final @Nullabl final long modSum = GsCollectionUtils.accumulate(newData, (f, s) -> s + f.lastModified(), 0L); final boolean modSumChanged = modSum != _prevModSum; - if (canGoUp(folder)) { - if (isVirtualFolder(folder) || _virtualMapping.containsValue(folder) || !GsFileUtils.isChild(VIRTUAL_STORAGE_ROOT, folder)) { - newData.add(0, VIRTUAL_STORAGE_ROOT); - } else { - newData.add(0, folder.getParentFile()); - } + final File parent = getCurrentParent(); + if (parent != null) { + newData.add(0, new GoUpFile(parent)); } if (folderChanged || modSumChanged || !newData.equals(_adapterData)) { @@ -762,7 +734,6 @@ private synchronized void _loadFolder(final @NonNull File folder, final @Nullabl _adapterDataFiltered.clear(); _adapterDataFiltered.addAll(filteredData); _currentSelection.retainAll(_adapterData); - _currentFolder = folder; _prevModSum = modSum; if (folderChanged) { @@ -879,15 +850,15 @@ public static class FilesystemViewerViewHolder extends RecyclerView.ViewHolder { //######################## //## UI Binding //######################## - LinearLayout itemRoot; - ImageView image; - TextView title; - TextView description; + final LinearLayout itemRoot; + final ImageView image; + final TextView title; + final TextView description; //######################## //## Methods //######################## - FilesystemViewerViewHolder(View row) { + FilesystemViewerViewHolder(final View row) { super(row); itemRoot = row.findViewById(R.id.opoc_filesystem_item__root); image = row.findViewById(R.id.opoc_filesystem_item__image); @@ -901,14 +872,27 @@ public static boolean isVirtualFolder(final File file) { VIRTUAL_STORAGE_FAVOURITE.equals(file) || VIRTUAL_STORAGE_POPULAR.equals(file) || VIRTUAL_STORAGE_APP_DATA_PRIVATE.equals(file) || - VIRTUAL_STORAGE_EMULATED.equals(file); - } - - private boolean isParent(File parent, File child) { - return (VIRTUAL_STORAGE_ROOT.equals(parent) && _virtualMapping.containsKey(child)) || GsFileUtils.isChild(parent, child); + VIRTUAL_STORAGE_EMULATED.equals(file) || + (file != null && VIRTUAL_STORAGE_ROOT.equals(file.getParentFile()) && !file.exists()); } public void showFileAfterNextLoad(final File file) { _fileToShowAfterNextLoad = file; } + + private int getUserId() { + try { + final String path = Environment.getExternalStorageDirectory().getAbsolutePath(); + final String[] parts = path.split("/"); + return Integer.parseInt(parts[parts.length - 1]); + } catch (Exception ignored) { + return 0; + } + } + + private static class GoUpFile extends File { + GoUpFile(final File f) { + super(f.getPath()); + } + } } diff --git a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserOptions.java b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserOptions.java index 53e9be30bb..4f1df3cb4a 100644 --- a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserOptions.java +++ b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserOptions.java @@ -23,6 +23,8 @@ import java.io.Serializable; import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; @SuppressWarnings({"unused", "WeakerAccess"}) public class GsFileBrowserOptions { @@ -106,6 +108,7 @@ public static class Options { public int selectedItemImage = android.R.drawable.checkbox_on_background; @DrawableRes public int fileImage = android.R.drawable.ic_menu_edit; + @ColorRes public int backgroundColor = android.R.color.background_light; @ColorRes @@ -123,13 +126,19 @@ public static class Options { @ColorRes public int folderColor = 0; - public final HashMap storageMaps = new HashMap<>(); + public final Map storageMaps = new LinkedHashMap<>(); + public final Map iconMaps = new HashMap<>(); public Collection favouriteFiles, recentFiles, popularFiles = null; public GsCallback.a1 setTitle = null, setSubtitle = null; - public GsCallback.a0 refresh = null; + public void addVirtualFile(final String name, final File target, final int icon) { + final File file = new File(GsFileBrowserListAdapter.VIRTUAL_STORAGE_ROOT, name); + storageMaps.put(file, target); + iconMaps.put(file, icon); + } } + public static class SelectionListenerAdapter implements SelectionListener, Serializable { @Override public void onFsViewerSelected(String request, File file, final Integer lineNumber) { diff --git a/app/src/main/java/net/gsantner/opoc/util/GsCollectionUtils.java b/app/src/main/java/net/gsantner/opoc/util/GsCollectionUtils.java index eb0fe9f511..9a53ad41f5 100644 --- a/app/src/main/java/net/gsantner/opoc/util/GsCollectionUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/GsCollectionUtils.java @@ -272,6 +272,15 @@ public static V getOrDefault(final Map map, final K key, final V de return map.containsKey(key) ? map.get(key) : defaultValue; } + public static K reverseSearch(final Map map, final V value) { + for (final Map.Entry entry : map.entrySet()) { + if (entry.getValue().equals(value)) { + return entry.getKey(); + } + } + return null; + } + public static void deduplicate(final Collection data) { if (!(data instanceof Set)) { final LinkedHashSet deduped = new LinkedHashSet<>(data); diff --git a/app/src/main/res/drawable/baseline_download_24.xml b/app/src/main/res/drawable/baseline_download_24.xml new file mode 100644 index 0000000000..818e0a0176 --- /dev/null +++ b/app/src/main/res/drawable/baseline_download_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/thirdparty/java/other/writeily/widget/WrWidgetConfigure.java b/app/thirdparty/java/other/writeily/widget/WrWidgetConfigure.java index 18d9f76541..46750d59c4 100644 --- a/app/thirdparty/java/other/writeily/widget/WrWidgetConfigure.java +++ b/app/thirdparty/java/other/writeily/widget/WrWidgetConfigure.java @@ -20,6 +20,7 @@ import net.gsantner.markor.R; import net.gsantner.markor.activity.MarkorBaseActivity; import net.gsantner.markor.frontend.filebrowser.MarkorFileBrowserFactory; +import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserListAdapter; import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserOptions; import java.io.File; @@ -65,7 +66,7 @@ public void onFsViewerSelected(String request, File file, final Integer lineNumb @Override public void onFsViewerConfig(GsFileBrowserOptions.Options dopt) { dopt.titleText = R.string.select_folder; - dopt.rootFolder = ApplicationObject.settings().getNotebookDirectory(); + dopt.rootFolder = GsFileBrowserListAdapter.VIRTUAL_STORAGE_ROOT; } @Override From 9f4ace51ccd9688fd33513f50aaccc1d70c5fd8a Mon Sep 17 00:00:00 2001 From: Harshad Vedartham Date: Mon, 23 Dec 2024 00:15:04 +0530 Subject: [PATCH 2/3] Restore audio action (PR #2481) --- .../markor/frontend/AttachLinkOrFileDialog.java | 10 +++++++++- app/src/main/res/layout/select_path_dialog.xml | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/net/gsantner/markor/frontend/AttachLinkOrFileDialog.java b/app/src/main/java/net/gsantner/markor/frontend/AttachLinkOrFileDialog.java index a4c7a7078e..6d6ecf388f 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/AttachLinkOrFileDialog.java +++ b/app/src/main/java/net/gsantner/markor/frontend/AttachLinkOrFileDialog.java @@ -17,6 +17,7 @@ import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; +import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -98,6 +99,7 @@ public static void showInsertImageOrLinkDialog( final Button buttonPictureGallery = view.findViewById(R.id.ui__select_path_dialog__gallery_picture); final Button buttonPictureCamera = view.findViewById(R.id.ui__select_path_dialog__camera_picture); final Button buttonPictureEdit = view.findViewById(R.id.ui__select_path_dialog__edit_picture); + final Button buttonAudioRecord = view.findViewById(R.id.ui__select_path_dialog__record_audio); // Extract filepath if using Markdown if (textFormatId == FormatRegistry.FORMAT_MARKDOWN) { @@ -133,6 +135,11 @@ public static void showInsertImageOrLinkDialog( dialog.setTitle(R.string.insert_image); browseType = InsertType.IMAGE_BROWSE; okType = InsertType.IMAGE_DIALOG; + } else if (action == AUDIO_ACTION) { + dialog.setTitle(R.string.audio); + buttonAudioRecord.setVisibility(View.VISIBLE); + browseType = InsertType.AUDIO_BROWSE; + okType = InsertType.AUDIO_DIALOG; } else { dialog.setTitle(R.string.insert_link); buttonSelectSpecial.setVisibility(View.VISIBLE); @@ -148,6 +155,7 @@ public static void showInsertImageOrLinkDialog( buttonSearch.setOnClickListener(v -> _insertItem.callback(InsertType.LINK_SEARCH)); buttonPictureCamera.setOnClickListener(b -> _insertItem.callback(InsertType.IMAGE_CAMERA)); buttonPictureGallery.setOnClickListener(v -> _insertItem.callback(InsertType.IMAGE_GALLERY)); + buttonAudioRecord.setOnClickListener(v -> _insertItem.callback(InsertType.AUDIO_RECORDING)); buttonPictureEdit.setOnClickListener(v -> _insertItem.callback(InsertType.IMAGE_EDIT)); dialog.show(); @@ -400,7 +408,7 @@ private static void insertItem( } case AUDIO_RECORDING: { if (!cu.requestAudioRecording(activity, insertFileLink)) { - // noop, OM library is outdated and so voice recording feature removed + Toast.makeText(activity, "❌", Toast.LENGTH_SHORT).show(); } break; } diff --git a/app/src/main/res/layout/select_path_dialog.xml b/app/src/main/res/layout/select_path_dialog.xml index ff66f295b1..70830d0b9b 100644 --- a/app/src/main/res/layout/select_path_dialog.xml +++ b/app/src/main/res/layout/select_path_dialog.xml @@ -93,6 +93,15 @@ android:drawableLeft="@drawable/ic_crop_black_24dp" android:visibility="gone" android:text="@string/edit_picture" /> + +