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 d44724726..3a887b78f 100644 --- a/app/src/main/java/net/gsantner/markor/activity/MainActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/MainActivity.java @@ -77,7 +77,6 @@ protected void onCreate(final Bundle savedInstanceState) { } catch (Exception ignored) { } - _cu = new MarkorContextUtils(this); setContentView(R.layout.main__activity); _bottomNav = findViewById(R.id.bottom_navigation_bar); @@ -427,7 +426,6 @@ public void onFsViewerDoUiUpdate(final GsFileBrowserListAdapter adapter) { if (getCurrentPos() == tabIdToPos(R.id.nav_notebook)) { setTitle(getFileBrowserTitle()); } - invalidateOptionsMenu(); } if (toShow != null && adapter != null) { diff --git a/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownActionButtons.java b/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownActionButtons.java index 6bd44dfc4..ee5b3f8f9 100644 --- a/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownActionButtons.java +++ b/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownActionButtons.java @@ -246,14 +246,13 @@ public boolean isValid() { public static Link extract(final CharSequence text, final int pos) { final int[] sel = TextViewUtils.getLineSelection(text, pos); - if (sel != null && sel[0] != -1 && sel[1] != -1) { + if (sel[0] != -1 && sel[1] != -1) { final String line = text.subSequence(sel[0], sel[1]).toString(); final Matcher m = MarkdownSyntaxHighlighter.LINK.matcher(line); - final int po = pos - sel[0]; while (m.find()) { - final int start = m.start(), end = m.end(); - if (start <= po && end >= po) { + final int start = m.start() + sel[0], end = m.end() + sel[0]; + if (start <= pos && end >= pos) { final boolean isImage = m.group(1) != null; final String link = m.group(3); return new Link(m.group(2), link == null ? null : link.trim(), isImage, start, end); diff --git a/app/src/main/java/net/gsantner/markor/format/orgmode/OrgmodeActionButtons.java b/app/src/main/java/net/gsantner/markor/format/orgmode/OrgmodeActionButtons.java index 552e1ee44..8c21a214c 100644 --- a/app/src/main/java/net/gsantner/markor/format/orgmode/OrgmodeActionButtons.java +++ b/app/src/main/java/net/gsantner/markor/format/orgmode/OrgmodeActionButtons.java @@ -7,7 +7,6 @@ import net.gsantner.markor.R; import net.gsantner.markor.format.ActionButtonBase; -import net.gsantner.markor.format.orgmode.OrgmodeReplacePatternGenerator; import net.gsantner.markor.frontend.textview.AutoTextFormatter; import net.gsantner.markor.model.Document; diff --git a/app/src/main/java/net/gsantner/markor/format/orgmode/OrgmodeReplacePatternGenerator.java b/app/src/main/java/net/gsantner/markor/format/orgmode/OrgmodeReplacePatternGenerator.java index 0c65d46d5..a246520eb 100644 --- a/app/src/main/java/net/gsantner/markor/format/orgmode/OrgmodeReplacePatternGenerator.java +++ b/app/src/main/java/net/gsantner/markor/format/orgmode/OrgmodeReplacePatternGenerator.java @@ -7,12 +7,9 @@ #########################################################*/ package net.gsantner.markor.format.orgmode; -import android.util.Log; - import net.gsantner.markor.format.ActionButtonBase; import net.gsantner.markor.frontend.textview.AutoTextFormatter; import net.gsantner.markor.frontend.textview.ReplacePatternGeneratorHelper; -import net.gsantner.opoc.format.GsTextUtils; import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java b/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java index f99bac19d..01601d3b3 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java +++ b/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java @@ -60,8 +60,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; @@ -998,6 +1000,116 @@ public static void showInsertSnippetDialog(final Activity activity, final GsCall GsSearchOrCustomTextDialog.showMultiChoiceDialogWithSearchFilterUI(activity, dopt); } + public static void showFolderSortDialog( + final Activity activity, + final GsFileUtils.SortOrder currentOrder, + final GsFileUtils.SortOrder globalOrder, + final GsCallback.a1 callback + ) { + final DialogOptions dopt = new DialogOptions(); + baseConf(activity, dopt); + + final List data = new ArrayList<>(); + final List icons = new ArrayList<>(); + final List layouts = new ArrayList<>(); + + data.add(activity.getString(R.string.folder_local)); + icons.add(R.drawable.ic_save_black_24dp); + layouts.add(android.R.layout.simple_list_item_multiple_choice); + + data.add(activity.getString(R.string.name)); + icons.add(R.drawable.ic_sort_by_alpha_black_24dp); + layouts.add(android.R.layout.simple_list_item_single_choice); + + data.add(activity.getString(R.string.date)); + icons.add(R.drawable.ic_date_range_black_24dp); + layouts.add(android.R.layout.simple_list_item_single_choice); + + data.add(activity.getString(R.string.size)); + icons.add(R.drawable.ic_sd_card_black_24dp); + layouts.add(android.R.layout.simple_list_item_single_choice); + + data.add(activity.getString(R.string.mime_type)); + icons.add(R.drawable.ic_baseline_plagiarism_24); + layouts.add(android.R.layout.simple_list_item_single_choice); + + data.add(activity.getString(R.string.folder_first)); + icons.add(R.drawable.ic_baseline_rule_folder_24); + layouts.add(android.R.layout.simple_list_item_multiple_choice); + + data.add(activity.getString(R.string.reverse_order)); + icons.add(R.drawable.ic_baseline_arrow_upward_24); + layouts.add(android.R.layout.simple_list_item_multiple_choice); + + data.add(activity.getString(R.string.dotfiles)); + icons.add(R.drawable.ic_regex_black_24dp); + layouts.add(android.R.layout.simple_list_item_multiple_choice); + + dopt.data = data; + dopt.iconsForData = icons; + dopt.listItemLayouts = layouts; + + dopt.preSelected = new HashSet<>(); + if (currentOrder.isFolderLocal) dopt.preSelected.add(0); + if (currentOrder.folderFirst) dopt.preSelected.add(5); + if (currentOrder.reverse) dopt.preSelected.add(6); + if (currentOrder.showDotFiles) dopt.preSelected.add(7); + + final Map typeToPos = new HashMap<>(); + typeToPos.put(GsFileUtils.SORT_BY_NAME, 1); + typeToPos.put(GsFileUtils.SORT_BY_MTIME, 2); + typeToPos.put(GsFileUtils.SORT_BY_FILESIZE, 3); + typeToPos.put(GsFileUtils.SORT_BY_MIMETYPE, 4); + dopt.preSelected.add(GsCollectionUtils.getOrDefault(typeToPos, currentOrder.sortByType, 1)); + + dopt.isMultiSelectEnabled = true; + dopt.isSearchEnabled = false; + dopt.titleText = R.string.sort_by; + dopt.dialogWidthDp = WindowManager.LayoutParams.WRAP_CONTENT; + dopt.showCountInOkButton = false; + dopt.showSelectAllButton = false; + + final Set prevSelection = new HashSet<>(dopt.preSelected); + final boolean[] resetGlobal = {false}; + final Set radioSet = new HashSet<>(Arrays.asList(1, 2, 3, 4)); + dopt.selectionChangedCallback = (selection) -> { + final Set added = GsCollectionUtils.setDiff(selection, prevSelection); + final Set removed = GsCollectionUtils.setDiff(prevSelection, selection); + if (globalOrder != null && currentOrder.isFolderLocal && removed.contains(0)) { + // Reset to global if folder local is unchecked + resetGlobal[0] = true; + selection.clear(); + if (globalOrder.folderFirst) selection.add(5); + if (globalOrder.reverse) selection.add(6); + if (globalOrder.showDotFiles) selection.add(7); + selection.add(GsCollectionUtils.getOrDefault(typeToPos, globalOrder.sortByType, 1)); + } else if (!Collections.disjoint(removed, radioSet)) { + // If a radio button is unchecked add it back + selection.addAll(removed); + } else if (!Collections.disjoint(added, radioSet)) { + // If a radio button is checked, remove all other radio buttons + selection.removeAll(GsCollectionUtils.setDiff(radioSet, added)); + } + prevSelection.clear(); + prevSelection.addAll(selection); + }; + + dopt.positionCallback = (selection) -> { + final GsFileUtils.SortOrder order = new GsFileUtils.SortOrder(); + order.isFolderLocal = selection.contains(0); + order.folderFirst = selection.contains(5); + order.reverse = selection.contains(6); + order.showDotFiles = selection.contains(7); + if (selection.contains(2)) order.sortByType = GsFileUtils.SORT_BY_MTIME; + else if (selection.contains(3)) order.sortByType = GsFileUtils.SORT_BY_FILESIZE; + else if (selection.contains(4)) order.sortByType = GsFileUtils.SORT_BY_MIMETYPE; + else order.sortByType = GsFileUtils.SORT_BY_NAME; + callback.callback(order); + }; + + GsSearchOrCustomTextDialog.showMultiChoiceDialogWithSearchFilterUI(activity, dopt); + } + public static void baseConf(Activity activity, DialogOptions dopt) { dopt.isDarkDialog = GsContextUtils.instance.isDarkModeEnabled(activity); dopt.clearInputIcon = R.drawable.ic_baseline_clear_24; 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 157215771..0bd037779 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 @@ -72,12 +72,14 @@ public static GsFileBrowserOptions.Options prepareFsViewerOpts( opts.folderImage = R.drawable.ic_folder_white_24dp; opts.titleText = R.string.select; opts.mountedStorageFolder = cu.getStorageAccessFolder(context); + opts.sortOrder = appSettings.getFolderSortOrder(null); updateFsViewerOpts(opts, context, appSettings); return opts; } + // We update these because some of these settings can change public static void updateFsViewerOpts( final GsFileBrowserOptions.Options opts, final Context context, @@ -85,10 +87,6 @@ public static void updateFsViewerOpts( ) { 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(); 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 66b86fd2b..939f6e052 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 @@ -36,7 +36,6 @@ import net.gsantner.markor.R; import net.gsantner.markor.activity.MainActivity; import net.gsantner.markor.model.AppSettings; -import net.gsantner.markor.util.TextCasingUtils; import net.gsantner.opoc.format.GsTextUtils; import net.gsantner.opoc.wrapper.GsCallback; import net.gsantner.opoc.wrapper.GsTextWatcherAdapter; 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 1f6a66c2d..7dce19755 100644 --- a/app/src/main/java/net/gsantner/markor/model/AppSettings.java +++ b/app/src/main/java/net/gsantner/markor/model/AppSettings.java @@ -34,9 +34,6 @@ import net.gsantner.opoc.util.GsContextUtils; import net.gsantner.opoc.util.GsFileUtils; -import org.json.JSONArray; -import org.json.JSONObject; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -44,8 +41,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; @@ -85,7 +80,7 @@ public boolean isPreferViewMode() { } public void setNotebookDirectory(final File file) { - setString(R.string.pref_key__notebook_directory, Document.getPath(file)); + setString(R.string.pref_key__notebook_directory, GsFileUtils.getPath(file)); } public File getNotebookDirectory() { @@ -104,7 +99,7 @@ public File getQuickNoteFile() { } public void setQuickNoteFile(final File file) { - setString(R.string.pref_key__quicknote_filepath, Document.getPath(file)); + setString(R.string.pref_key__quicknote_filepath, GsFileUtils.getPath(file)); } public File getDefaultQuickNoteFile() { @@ -116,7 +111,7 @@ public File getTodoFile() { } public void setTodoFile(final File file) { - setString(R.string.pref_key__todo_filepath, Document.getPath(file)); + setString(R.string.pref_key__todo_filepath, GsFileUtils.getPath(file)); } public File getDefaultTodoFile() { @@ -130,7 +125,7 @@ public File getSnippetsDirectory() { } public void setSnippetDirectory(final File folder) { - setString(R.string.pref_key__snippet_directory_path, Document.getPath(folder)); + setString(R.string.pref_key__snippet_directory_path, GsFileUtils.getPath(folder)); } public String getFontFamily() { @@ -259,31 +254,55 @@ public boolean isRecreateMainRequired() { return ret; } - public String setFileBrowserSortByType(String v) { - setString(R.string.pref_key__file_browser__sort_by_type, v); - return v; - } + private final String PREF_PREFIX_FOLDER_SORT_ORDER = "PREF_PREFIX_FOLDER_SORT_ORDER"; - public String getFileBrowserSortByType() { - return getString(R.string.pref_key__file_browser__sort_by_type, GsFileUtils.SORT_BY_NAME); - } + /** + * Set sort order for a folder or globally. + * Passing null as folder will set the global sort order. + * Passing null as sortOrder will remove the sort order for the folder (revert to global). + * + * @param folder Folder to set the sort order for + * @param sortOrder Sort order to set + */ + public void setFolderSortOrder(final @Nullable File folder, final @Nullable GsFileUtils.SortOrder sortOrder) { + if (folder == null && sortOrder == null) { + return; + } - public boolean setFileBrowserSortReverse(boolean value) { - setBool(R.string.pref_key__sort_reverse, value); - return value; - } + // Null folders have empty suffix + final String key = PREF_PREFIX_FOLDER_SORT_ORDER + GsFileUtils.getPath(folder); - public boolean isFileBrowserSortReverse() { - return getBool(R.string.pref_key__sort_reverse, false); - } + if (sortOrder == null) { + remove(key); + return; + } - public boolean setFileBrowserFilterShowDotFiles(boolean v) { - setBool(R.string.pref_key__show_dot_files_v2, v); - return v; + setString(key, sortOrder.toString()); } - public boolean isFileBrowserFilterShowDotFiles() { - return getBool(R.string.pref_key__show_dot_files_v2, true); + /** + * Get sort order for a folder or globally. + * If no sort order is set for the folder, the global sort order is returned. + * If folder is null, the global sort order is returned. + * + * @param folder + * @return + */ + public GsFileUtils.SortOrder getFolderSortOrder(final @Nullable File folder) { + // Null folders have empty suffix + final String path = GsFileUtils.getPath(folder); + final String key = PREF_PREFIX_FOLDER_SORT_ORDER + path; + String json = getString(key, null); + + final boolean isFolderLocal = !GsTextUtils.isNullOrEmpty(path) && json != null; + + if (!isFolderLocal) { + json = getString(PREF_PREFIX_FOLDER_SORT_ORDER, null); + } + + final GsFileUtils.SortOrder order = GsFileUtils.SortOrder.fromString(json); + order.isFolderLocal = isFolderLocal; + return order; } public boolean isShowSettingsOptionInMainToolbar() { @@ -338,12 +357,12 @@ public void addRecentFile(final File file) { if (!listFileInRecents(file)) { return; } - final String path = Document.getPath(file); + final String path = GsFileUtils.getPath(file); if (!file.equals(getTodoFile()) && !file.equals(getQuickNoteFile())) { ArrayList recent = getRecentDocuments(); recent.add(0, path); - recent.remove(Document.getPath(getTodoFile())); - recent.remove(Document.getPath(getQuickNoteFile())); + recent.remove(GsFileUtils.getPath(getTodoFile())); + recent.remove(GsFileUtils.getPath(getQuickNoteFile())); recent.remove(""); recent.remove(null); @@ -357,7 +376,7 @@ public void setFavouriteFiles(final Collection files) { final Set set = new LinkedHashSet<>(); for (final File f : files) { if (f != null && (f.exists() || GsFileBrowserListAdapter.isVirtualFolder(f))) { - set.add(Document.getPath(f)); + set.add(GsFileUtils.getPath(f)); } } setStringList(R.string.pref_key__favourite_files, GsCollectionUtils.map(set, p -> p)); @@ -421,8 +440,8 @@ public void setLastViewPosition(File file, int scrollX, int scrollY) { return; } if (!file.equals(getTodoFile()) && !file.equals(getQuickNoteFile())) { - setInt(PREF_PREFIX_VIEW_SCROLL_X + Document.getPath(file), scrollX, _prefCache); - setInt(PREF_PREFIX_VIEW_SCROLL_Y + Document.getPath(file), scrollY, _prefCache); + setInt(PREF_PREFIX_VIEW_SCROLL_X + GsFileUtils.getPath(file), scrollX, _prefCache); + setInt(PREF_PREFIX_VIEW_SCROLL_Y + GsFileUtils.getPath(file), scrollY, _prefCache); } } @@ -549,14 +568,14 @@ public int getLastViewPositionX(File file) { if (file == null || !file.exists()) { return -1; } - return getInt(PREF_PREFIX_VIEW_SCROLL_X + Document.getPath(file), -3, _prefCache); + return getInt(PREF_PREFIX_VIEW_SCROLL_X + GsFileUtils.getPath(file), -3, _prefCache); } public int getLastViewPositionY(File file) { if (file == null || !file.exists()) { return -1; } - return getInt(PREF_PREFIX_VIEW_SCROLL_Y + Document.getPath(file), -3, _prefCache); + return getInt(PREF_PREFIX_VIEW_SCROLL_Y + GsFileUtils.getPath(file), -3, _prefCache); } private List getPopularDocumentsSorted() { @@ -800,16 +819,16 @@ public int getTabWidth() { } public boolean listFileInRecents(File file) { - return getBool(Document.getPath(file) + "_list_in_recents", true); + return getBool(GsFileUtils.getPath(file) + "_list_in_recents", true); } public void setListFileInRecents(File file, boolean value) { - setBool(Document.getPath(file) + "_list_in_recents", value); + setBool(GsFileUtils.getPath(file) + "_list_in_recents", value); if (!value) { ArrayList recent = getRecentDocuments(); - if (recent.contains(Document.getPath(file))) { - recent.remove(Document.getPath(file)); + if (recent.contains(GsFileUtils.getPath(file))) { + recent.remove(GsFileUtils.getPath(file)); setRecentDocuments(recent); } } @@ -824,11 +843,11 @@ public ArrayList getFilesTaggedWith(String tag) { }*/ public int getRating(File file) { - return getInt(Document.getPath(file) + "_rating", 0); + return getInt(GsFileUtils.getPath(file) + "_rating", 0); } public void setRating(File file, int value) { - setInt(Document.getPath(file) + "_rating", value); + setInt(GsFileUtils.getPath(file) + "_rating", value); } public boolean isEditorLineBreakingEnabled() { @@ -926,7 +945,7 @@ public void setNewFileDialogLastUsedType(final int format) { } public void setFileBrowserLastBrowsedFolder(File f) { - setString(R.string.pref_key__file_browser_last_browsed_folder, Document.getPath(f)); + setString(R.string.pref_key__file_browser_last_browsed_folder, GsFileUtils.getPath(f)); } public File getFileBrowserLastBrowsedFolder() { @@ -1014,33 +1033,33 @@ public List> getSnippetFiles() { public void setTypeTemplate(final @StringRes int format, final String template) { final String js = getString(R.string.pref_key__filetype_template_map, "{}"); - final Map map = jsonStringToMap(js); + final Map map = GsTextUtils.jsonStringToMap(js); map.put(_context.getString(format), template); - setString(R.string.pref_key__filetype_template_map, mapToJsonString(map)); + setString(R.string.pref_key__filetype_template_map, GsTextUtils.mapToJsonString(map)); } public @Nullable String getTypeTemplate(final @StringRes int format) { final String js = getString(R.string.pref_key__filetype_template_map, "{}"); - final Map map = jsonStringToMap(js); + final Map map = GsTextUtils.jsonStringToMap(js); return map.get(format == 0 ? "" : _context.getString(format)); } public void setTemplateTitleFormat(final String templateName, final String titleFormat) { final String js = getString(R.string.pref_key__template_title_format_map, "{}"); - final Map map = jsonStringToMap(js); + final Map map = GsTextUtils.jsonStringToMap(js); map.put(templateName, titleFormat); - setString(R.string.pref_key__template_title_format_map, mapToJsonString(map)); + setString(R.string.pref_key__template_title_format_map, GsTextUtils.mapToJsonString(map)); } public @Nullable String getTemplateTitleFormat(final String templateName) { final String js = getString(R.string.pref_key__template_title_format_map, "{}"); - final Map map = jsonStringToMap(js); + final Map map = GsTextUtils.jsonStringToMap(js); return map.get(templateName); } public Set getTitleFormats() { final String js = getString(R.string.pref_key__title_format_list, "[]"); - final Set formats = new LinkedHashSet<>(jsonStringToList(js)); + final Set formats = new LinkedHashSet<>(GsTextUtils.jsonStringToList(js)); formats.addAll(Arrays.asList( "`yyyy-MM-dd`-{{title}}", "{{date}}_{{title}}", @@ -1060,7 +1079,7 @@ public void saveTitleFormat(final String format, final int maxCount) { break; } } - setString(R.string.pref_key__title_format_list, toJsonString(updated)); + setString(R.string.pref_key__title_format_list, GsTextUtils.listToJsonString(updated)); } public void setFormatShareAsLink(final boolean asLink) { @@ -1070,43 +1089,4 @@ public void setFormatShareAsLink(final boolean asLink) { public boolean getFormatShareAsLink() { return getBool(R.string.pref_key__format_share_as_link, true); } - - private static String mapToJsonString(final Map map) { - return new JSONObject(map).toString(); - } - - private static Map jsonStringToMap(final String jsonString) { - final Map map = new LinkedHashMap<>(); - try { - final JSONObject jsonObject = new JSONObject(jsonString); - final Iterator keys = jsonObject.keys(); - - while (keys.hasNext()) { - String key = keys.next(); - String value = jsonObject.getString(key); - map.put(key, value); - } - } catch (Exception e) { - e.printStackTrace(); - } - return map; - } - - public String toJsonString(final Collection list) { - final JSONArray jsonArray = new JSONArray(list); - return jsonArray.toString(); - } - - public List jsonStringToList(final String jsonString) { - final List list = new ArrayList<>(); - try { - final JSONArray jsonArray = new JSONArray(jsonString); - for (int i = 0; i < jsonArray.length(); i++) { - list.add(jsonArray.getString(i)); - } - } catch (Exception e) { - e.printStackTrace(); - } - return list; - } } diff --git a/app/src/main/java/net/gsantner/markor/model/Document.java b/app/src/main/java/net/gsantner/markor/model/Document.java index c3f86d4b9..fbb31ae75 100644 --- a/app/src/main/java/net/gsantner/markor/model/Document.java +++ b/app/src/main/java/net/gsantner/markor/model/Document.java @@ -72,7 +72,7 @@ public class Document implements Serializable { private int _lastLength = -1; public Document(@NonNull final File f) { - path = getPath(f); + path = GsFileUtils.getPath(f); file = new File(path); title = GsFileUtils.getFilenameWithoutExtension(file); extension = GsFileUtils.getFilenameExtension(file); @@ -86,16 +86,6 @@ public Document(@NonNull final File f) { } } - public static String getPath(final File file) { - try { - return file.getCanonicalPath(); - } catch (IOException e) { - return file.getAbsolutePath(); - } catch (NullPointerException e) { - return ""; - } - } - // Get a default file public static Document getDefault(final Context context) { final File notebook = ApplicationObject.settings().getNotebookDirectory(); diff --git a/app/src/main/java/net/gsantner/opoc/format/GsTextUtils.java b/app/src/main/java/net/gsantner/opoc/format/GsTextUtils.java index 554402b70..587317f35 100644 --- a/app/src/main/java/net/gsantner/opoc/format/GsTextUtils.java +++ b/app/src/main/java/net/gsantner/opoc/format/GsTextUtils.java @@ -20,9 +20,13 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -446,4 +450,48 @@ public static boolean inRange(final int min, final int max, final int... values) } return true; } + + public static String mapToJsonString(final Map map) { + return new JSONObject(map).toString(); + } + + public static Map jsonStringToMap(final String jsonString) { + final Map map = new LinkedHashMap<>(); + + if (isNullOrEmpty(jsonString)) { + return map; + } + + try { + final JSONObject jsonObject = new JSONObject(jsonString); + final Iterator keys = jsonObject.keys(); + + while (keys.hasNext()) { + String key = keys.next(); + String value = jsonObject.getString(key); + map.put(key, value); + } + } catch (Exception e) { + e.printStackTrace(); + } + return map; + } + + public static String listToJsonString(final Collection list) { + final JSONArray jsonArray = new JSONArray(list); + return jsonArray.toString(); + } + + public static List jsonStringToList(final String jsonString) { + final List list = new ArrayList<>(); + try { + final JSONArray jsonArray = new JSONArray(jsonString); + for (int i = 0; i < jsonArray.length(); i++) { + list.add(jsonArray.getString(i)); + } + } catch (Exception e) { + e.printStackTrace(); + } + return list; + } } diff --git a/app/src/main/java/net/gsantner/opoc/frontend/GsSearchOrCustomTextDialog.java b/app/src/main/java/net/gsantner/opoc/frontend/GsSearchOrCustomTextDialog.java index 1929df7db..d8d964c62 100644 --- a/app/src/main/java/net/gsantner/opoc/frontend/GsSearchOrCustomTextDialog.java +++ b/app/src/main/java/net/gsantner/opoc/frontend/GsSearchOrCustomTextDialog.java @@ -43,7 +43,6 @@ import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; -import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -84,9 +83,16 @@ public static class DialogOptions { @Nullable public GsCallback.a1> positionCallback = null; + // For multi select public boolean isMultiSelectEnabled = false; + public Collection preSelected = null; // Indices of pre-selected items + public boolean showSelectAllButton = true; + public boolean showCountInOkButton = true; + public GsCallback.a1> selectionChangedCallback = null; + public List data = null; public List highlightData = null; + public List listItemLayouts = null; public List iconsForData; public CharSequence messageText = ""; public String defaultText = ""; @@ -99,7 +105,6 @@ public static class DialogOptions { public int dialogHeightDp = WindowManager.LayoutParams.WRAP_CONTENT; public int searchInputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; public String extraFilter = null; - public Collection preSelected = null; public GsCallback.a1 highlighter = null; public GsCallback.a1 neutralButtonCallback = null; public GsCallback.a1 dismissCallback = null; @@ -135,8 +140,6 @@ public static class DialogState { } public static class Adapter extends BaseAdapter { - @LayoutRes - private final int _layout; private final int _layoutHeight; private final LayoutInflater _inflater; private final DialogOptions _dopt; @@ -164,7 +167,6 @@ public long getItemId(int position) { private Adapter(final Context context, final DialogOptions dopt) { super(); _filteredItems = new ArrayList<>(); - _layout = dopt.isMultiSelectEnabled ? android.R.layout.simple_list_item_multiple_choice : android.R.layout.simple_list_item_1; _inflater = LayoutInflater.from(context); _dopt = dopt; _extraPattern = (_dopt.extraFilter == null ? null : Pattern.compile(_dopt.extraFilter).matcher("")); @@ -172,6 +174,16 @@ private Adapter(final Context context, final DialogOptions dopt) { _layoutHeight = GsContextUtils.instance.convertDpToPx(context, 36); } + private int chooseLayout(final int pos) { + if (_dopt.listItemLayouts != null && pos < _dopt.listItemLayouts.size()) { + return _dopt.listItemLayouts.get(pos); + } else if (_dopt.isMultiSelectEnabled) { + return android.R.layout.simple_list_item_multiple_choice; + } else { + return android.R.layout.simple_list_item_1; + } + } + @NonNull @Override public View getView(int pos, @Nullable View convertView, @NonNull ViewGroup parent) { @@ -179,7 +191,7 @@ public View getView(int pos, @Nullable View convertView, @NonNull ViewGroup pare final TextView textView; if (convertView == null) { - textView = (TextView) _inflater.inflate(_layout, parent, false); + textView = (TextView) _inflater.inflate(chooseLayout(pos), parent, false); textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); textView.setMinHeight(_layoutHeight); } else { @@ -279,7 +291,6 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi // SearchView is currently constructed even if it isn't needed final View searchView = makeSearchView(activity, dopt); final EditText searchEditText = searchView.findViewWithTag("EDIT"); - searchEditText.addTextChangedListener(GsTextWatcherAdapter.after(listAdapter::filter)); if (dopt.isSearchEnabled) { mainLayout.addView(searchView); @@ -325,6 +336,18 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi // ========================================================================================= + final GsCallback.a0 setSelectAllButtonState = () -> { + if (selectAll != null) { + final boolean allVisibleSelected = listAdapter._selectedItems.containsAll(listAdapter._filteredItems); + ((Checkable) selectAll).setChecked(allVisibleSelected); + } + }; + + searchEditText.addTextChangedListener(GsTextWatcherAdapter.after((constraint) -> { + listAdapter.filter(constraint); + setSelectAllButtonState.callback(); + })); + // Ok button only present under these circumstances final boolean isSearchOk = dopt.callback != null && dopt.isSearchEnabled; final boolean isMultiSelOk = dopt.positionCallback != null && dopt.isMultiSelectEnabled; @@ -332,10 +355,10 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi if (isSearchOk || isMultiSelOk || isPlainDialog) { dialogBuilder.setPositiveButton(dopt.okButtonText, (dialogInterface, i) -> { final String searchText = dopt.isSearchEnabled ? searchEditText.getText().toString() : null; - if (dopt.positionCallback != null && !GsCollectionUtils.setEquals(dopt.preSelected, listAdapter._selectedItems)) { - // Position callback triggered when selection changed + final boolean selectionChanged = !GsCollectionUtils.setEquals(dopt.preSelected, listAdapter._selectedItems); + if (dopt.positionCallback != null && (selectionChanged || dopt.callback == null)) { dopt.positionCallback.callback(new ArrayList<>(listAdapter._selectedItems)); - } else if (dopt.callback != null && (!dopt.isSearchEnabled || !TextUtils.isEmpty(searchText))) { + } else if (dopt.callback != null && !TextUtils.isEmpty(searchText)) { dopt.callback.callback(searchText); } }); @@ -408,7 +431,7 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi final Button okButton = dialog.getButton(Dialog.BUTTON_POSITIVE); final GsCallback.a0 setOkButtonState = () -> { if (okButton != null) { - if (dopt.isMultiSelectEnabled) { + if (dopt.isMultiSelectEnabled && dopt.showCountInOkButton) { okButton.setText(okString + String.format(" (%d)", listAdapter._selectedItems.size())); } else { okButton.setText(okString); @@ -418,21 +441,17 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi // Set ok button text initially setOkButtonState.callback(); - final GsCallback.a0 setSelectAllButtonState = () -> { - if (selectAll != null) { - ((Checkable) selectAll).setChecked(listAdapter._selectedItems.size() >= dopt.data.size()); - } - }; // Set select all button state initially setSelectAllButtonState.callback(); if (selectAll != null && dopt.isMultiSelectEnabled) { selectAll.setOnClickListener((v) -> { - if (listAdapter._selectedItems.size() < dopt.data.size()) { - listAdapter._selectedItems.addAll(GsCollectionUtils.range(dopt.data.size())); + final boolean allVisibleSelected = listAdapter._selectedItems.containsAll(listAdapter._filteredItems); + if (!allVisibleSelected) { + listAdapter._selectedItems.addAll(listAdapter._filteredItems); } else { - listAdapter._selectedItems.clear(); + listAdapter._selectedItems.removeAll(listAdapter._filteredItems); } listAdapter.notifyDataSetChanged(); setOkButtonState.callback(); @@ -451,9 +470,10 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi } else { listAdapter._selectedItems.add(index); } - if (textView instanceof Checkable) { - ((Checkable) textView).setChecked(listAdapter._selectedItems.contains(index)); + if (dopt.selectionChangedCallback != null) { + dopt.selectionChangedCallback.callback(listAdapter._selectedItems); } + listAdapter.notifyDataSetChanged(); setOkButtonState.callback(); setSelectAllButtonState.callback(); } else { @@ -512,7 +532,8 @@ private static View makeTitleView(final Context context, final DialogOptions dop LinearLayout.LayoutParams.WRAP_CONTENT)); } - if (dopt.isMultiSelectEnabled) { + // Add select all button + if (dopt.isMultiSelectEnabled && dopt.showSelectAllButton) { // Using a multiple choice text view as a selectable checkbox button // Requires no styling to match the existing check boxes final LayoutInflater inflater = LayoutInflater.from(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 083b3adba..3eb5c29cd 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 @@ -310,6 +310,13 @@ public void onFsViewerItemLongPressed(File file, boolean doSelectMultiple) { } } + @Override + public void onFsViewerFolderChange(File newFolder) { + if (_callback != null) { + _callback.onFsViewerFolderChange(newFolder); + } + } + @Override public void onStart() { super.onStart(); 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 c732b69e5..3ef8547f6 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 @@ -125,7 +125,6 @@ public void onViewCreated(@NonNull View root, @Nullable Bundle savedInstanceStat _filesystemViewerAdapter = new GsFileBrowserListAdapter(_dopt, context); _recyclerList.setAdapter(_filesystemViewerAdapter); setReloadRequiredOnResume(false); // setAdapter will trigger a load - onFsViewerDoUiUpdate(_filesystemViewerAdapter); _swipe.setOnRefreshListener(() -> { _filesystemViewerAdapter.reloadCurrentFolder(); @@ -180,6 +179,14 @@ private void checkOptions() { } } + @Override + public void onFsViewerFolderChange(final File newFolder) { + if (_callback != null) { + _callback.onFsViewerFolderChange(newFolder); + } + + _dopt.sortOrder = _appSettings.getFolderSortOrder(newFolder); + } @Override public void onFsViewerSelected(String request, File file, final Integer lineNumber) { @@ -223,12 +230,10 @@ public void onFsViewerDoUiUpdate(GsFileBrowserListAdapter adapter) { } updateMenuItems(); - _emptyHint.postDelayed(() -> _emptyHint.setVisibility(adapter.isCurrentFolderEmpty() ? View.VISIBLE : View.GONE), 200); - _recyclerList.postDelayed(this::updateMenuItems, 1000); + _emptyHint.setVisibility(adapter.isCurrentFolderEmpty() ? View.VISIBLE : View.GONE); } private void updateMenuItems() { - final String curFilepath = (getCurrentFolder() != null ? getCurrentFolder() : new File("/")).getAbsolutePath(); final Set selFiles = _filesystemViewerAdapter.getCurrentSelection(); final int selCount = selFiles.size(); final int totalCount = _filesystemViewerAdapter.getItemCount() - 1; // Account for ".." @@ -260,7 +265,7 @@ private void updateMenuItems() { _fragmentMenu.findItem(R.id.action_copy_selected_items).setVisible((selMulti1 || selMultiMore) && selWritable && !_cu.isUnderStorageAccessFolder(getContext(), getCurrentFolder(), true)); _fragmentMenu.findItem(R.id.action_share_files).setVisible(selFilesOnly && (selMulti1 || selMultiMore) && !_cu.isUnderStorageAccessFolder(getContext(), getCurrentFolder(), true)); _fragmentMenu.findItem(R.id.action_go_to).setVisible(!_filesystemViewerAdapter.areItemsSelected()); - _fragmentMenu.findItem(R.id.action_sort).setVisible(!_filesystemViewerAdapter.areItemsSelected()); + _fragmentMenu.findItem(R.id.action_sort).setVisible(_filesystemViewerAdapter.isCurrentFolderSortable() && !_filesystemViewerAdapter.areItemsSelected()); _fragmentMenu.findItem(R.id.action_import).setVisible(!_filesystemViewerAdapter.areItemsSelected() && !_filesystemViewerAdapter.isCurrentFolderVirtual()); _fragmentMenu.findItem(R.id.action_settings).setVisible(!_filesystemViewerAdapter.areItemsSelected()); _fragmentMenu.findItem(R.id.action_favourite).setVisible(selMultiAny && !allSelectedFav); @@ -269,6 +274,11 @@ private void updateMenuItems() { _fragmentMenu.findItem(R.id.action_create_shortcut).setVisible(selMulti1 && (selFilesOnly || selDirectoriesOnly)); _fragmentMenu.findItem(R.id.action_check_all).setVisible(_filesystemViewerAdapter.areItemsSelected() && selCount < totalCount); _fragmentMenu.findItem(R.id.action_clear_selection).setVisible(_filesystemViewerAdapter.areItemsSelected()); + + final MenuItem sortItem = _fragmentMenu.findItem(R.id.action_sort); + if (sortItem != null) { + _cu.tintDrawable(sortItem.getIcon(), _dopt.sortOrder.isFolderLocal ? GsFileBrowserListAdapter.FAVOURITE_COLOR : Color.WHITE); + } } // Update subtitle with count @@ -298,7 +308,6 @@ public boolean onBackPressed() { public void reloadCurrentFolder() { _filesystemViewerAdapter.reloadCurrentFolder(); - onFsViewerDoUiUpdate(_filesystemViewerAdapter); } public File getCurrentFolder() { @@ -340,31 +349,10 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { _cu.tintMenuItems(menu, true, Color.WHITE); _cu.setSubMenuIconsVisibility(menu, true); - MenuItem item; - if ((item = menu.findItem(R.id.action_folder_first)) != null) { - item.setChecked(_dopt.sortFolderFirst); - } - if ((item = menu.findItem(R.id.action_sort_reverse)) != null) { - item.setChecked(_dopt.sortReverse); - } - if ((item = menu.findItem(R.id.action_show_dotfiles)) != null) { - item.setChecked(_dopt.filterShowDotFiles); - } - - if ((item = menu.findItem(R.id.action_sort_by_name)) != null && GsFileUtils.SORT_BY_NAME.equals(_dopt.sortByType)) { - item.setChecked(true); - } else if ((item = menu.findItem(R.id.action_sort_by_date)) != null && GsFileUtils.SORT_BY_MTIME.equals(_dopt.sortByType)) { - item.setChecked(true); - } else if ((item = menu.findItem(R.id.action_sort_by_filesize)) != null && GsFileUtils.SORT_BY_FILESIZE.equals(_dopt.sortByType)) { - item.setChecked(true); - } else if ((item = menu.findItem(R.id.action_sort_by_mimetype)) != null && GsFileUtils.SORT_BY_MIMETYPE.equals(_dopt.sortByType)) { - item.setChecked(true); - } - List> sdcardFolders = _cu.getAppDataPublicDirs(getContext(), false, true, true); int[] sdcardResIds = {}; for (int i = 0; i < sdcardResIds.length && i < sdcardFolders.size(); i++) { - item = menu.findItem(sdcardResIds[i]); + final MenuItem item = menu.findItem(sdcardResIds[i]); item.setTitle(item.getTitle().toString().replaceFirst("[)]\\s*$", " " + sdcardFolders.get(i).second) + ")"); item.setVisible(true); } @@ -388,40 +376,8 @@ public boolean onOptionsItemSelected(final MenuItem item) { _cu.createLauncherDesktopShortcut(getContext(), file); return true; } - case R.id.action_sort_by_name: { - item.setChecked(true); - _dopt.sortByType = _appSettings.setFileBrowserSortByType(GsFileUtils.SORT_BY_NAME); - reloadCurrentFolder(); - return true; - } - case R.id.action_sort_by_date: { - item.setChecked(true); - _dopt.sortByType = _appSettings.setFileBrowserSortByType(GsFileUtils.SORT_BY_MTIME); - reloadCurrentFolder(); - return true; - } - case R.id.action_sort_by_filesize: { - item.setChecked(true); - _dopt.sortByType = _appSettings.setFileBrowserSortByType(GsFileUtils.SORT_BY_FILESIZE); - reloadCurrentFolder(); - return true; - } - case R.id.action_sort_by_mimetype: { - item.setChecked(true); - _dopt.sortByType = _appSettings.setFileBrowserSortByType(GsFileUtils.SORT_BY_MIMETYPE); - reloadCurrentFolder(); - return true; - } - case R.id.action_sort_reverse: { - item.setChecked(!item.isChecked()); - _dopt.sortReverse = _appSettings.setFileBrowserSortReverse(item.isChecked()); - reloadCurrentFolder(); - return true; - } - case R.id.action_show_dotfiles: { - item.setChecked(!item.isChecked()); - _dopt.filterShowDotFiles = _appSettings.setFileBrowserFilterShowDotFiles(item.isChecked()); - reloadCurrentFolder(); + case R.id.action_sort: { + updateSortSettings(); return true; } case R.id.action_import: { @@ -432,12 +388,6 @@ public boolean onOptionsItemSelected(final MenuItem item) { executeSearchAction(); return true; } - case R.id.action_folder_first: { - item.setChecked(!item.isChecked()); - _dopt.sortFolderFirst = _appSettings.setFileBrowserSortFolderFirst(item.isChecked()); - reloadCurrentFolder(); - return true; - } case R.id.action_go_to: { final File folder = new File("/storage"); _filesystemViewerAdapter.setCurrentFolder(folder); @@ -640,4 +590,23 @@ private void importFileToCurrentDirectory(Context context, File sourceFile) { public GsFileBrowserOptions.Options getOptions() { return _dopt; } + + private void updateSortSettings() { + final GsFileUtils.SortOrder globalOrder = _appSettings.getFolderSortOrder(null); + MarkorDialogFactory.showFolderSortDialog(getActivity(), _dopt.sortOrder, globalOrder, + (order) -> { + // Erase local sort order if local is unset + final File currentFolder = getCurrentFolder(); + if (_dopt.sortOrder.isFolderLocal && !order.isFolderLocal) { + _appSettings.setFolderSortOrder(currentFolder, null); + } + + // Set new sort order to folder or global as needed + _dopt.sortOrder = order; + _appSettings.setFolderSortOrder(order.isFolderLocal ? currentFolder : null, _dopt.sortOrder); + + // Ui will be updated by onFsViewerDoUiUpdate after the load + reloadCurrentFolder(); + }); + } } 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 49246f829..10ffd1561 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 @@ -49,7 +49,6 @@ import java.io.FilenameFilter; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -79,6 +78,7 @@ public class GsFileBrowserListAdapter extends RecyclerView.Adapter fileOverallFilter = (context, file) -> true; @StringRes @@ -166,5 +166,10 @@ public void onFsViewerDoUiUpdate(GsFileBrowserListAdapter adapter) { public void onFsViewerItemLongPressed(File file, boolean doSelectMultiple) { } + + @Override + public void onFsViewerFolderChange(File newFolder) { + + } } } diff --git a/app/src/main/java/net/gsantner/opoc/model/GsSharedPreferencesPropertyBackend.java b/app/src/main/java/net/gsantner/opoc/model/GsSharedPreferencesPropertyBackend.java index e65af3e99..29a110143 100644 --- a/app/src/main/java/net/gsantner/opoc/model/GsSharedPreferencesPropertyBackend.java +++ b/app/src/main/java/net/gsantner/opoc/model/GsSharedPreferencesPropertyBackend.java @@ -566,6 +566,14 @@ public boolean contains(String key, final SharedPreferences... pref) { return gp(pref).contains(key); } + public void remove(@StringRes int keyResourceId, final SharedPreferences... pref) { + gp(pref).edit().remove(rstr(keyResourceId)).apply(); + } + + public void remove(final String key, final SharedPreferences... pref) { + gp(pref).edit().remove(key).apply(); + } + /** * Substract current datetime by given amount of days */ 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 9a53ad41f..cfdd4342b 100644 --- a/app/src/main/java/net/gsantner/opoc/util/GsCollectionUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/GsCollectionUtils.java @@ -19,7 +19,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -128,14 +127,8 @@ public static Set setDiff(final Collection a, final Collection b) { /** * Check if 2 collections have the same elements */ - public static boolean setEquals(Collection a, Collection b) { - a = a != null ? a : Collections.emptySet(); - b = b != null ? b : Collections.emptySet(); - - a = a instanceof Set ? a : new HashSet<>(a); - b = b instanceof Set ? b : new HashSet<>(b); - - return a.equals(b); + public static boolean setEquals(final Collection a, final Collection b) { + return (a == b) || (a != null && b != null && a.size() == b.size() && a.containsAll(b)); } /** diff --git a/app/src/main/java/net/gsantner/opoc/util/GsContextUtils.java b/app/src/main/java/net/gsantner/opoc/util/GsContextUtils.java index f20225eb9..b78dea927 100644 --- a/app/src/main/java/net/gsantner/opoc/util/GsContextUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/GsContextUtils.java @@ -869,7 +869,11 @@ public Bitmap drawTextOnDrawable(final Context context, @DrawableRes final int d /** * Try to tint all {@link Menu}s {@link MenuItem}s with given color */ - public void tintMenuItems(final Menu menu, final boolean recurse, @ColorInt final int iconColor) { + public void tintMenuItems(final @Nullable Menu menu, final boolean recurse, @ColorInt final int iconColor) { + if (menu == null) { + return; + } + for (int i = 0; i < menu.size(); i++) { MenuItem item = menu.getItem(i); try { diff --git a/app/src/main/java/net/gsantner/opoc/util/GsFileUtils.java b/app/src/main/java/net/gsantner/opoc/util/GsFileUtils.java index b5c5d6399..339189428 100644 --- a/app/src/main/java/net/gsantner/opoc/util/GsFileUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/GsFileUtils.java @@ -17,6 +17,7 @@ import android.util.Log; import android.util.Pair; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -55,6 +56,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -772,22 +774,51 @@ private static String makeSortKey(final String sortBy, final File file) { } } - public static void sortFiles( - final Collection filesToSort, - final String sortBy, - final boolean folderFirst, - final boolean reverse - ) { + public static class SortOrder { + public String sortByType = SORT_BY_NAME; + public boolean reverse = false; + public boolean showDotFiles = false; + public boolean folderFirst = true; + public boolean isFolderLocal = false; + + private final static String SORT_BY_KEY = "SORT_BY"; + private final static String REVERSE_KEY = "REVERSE"; + private final static String SHOW_DOT_FILES_KEY = "SHOW_DOT_FILES"; + private final static String FOLDER_FIRST_KEY = "FOLDER_FIRST"; + + @NonNull + @Override + public String toString() { + final Map map = new HashMap<>(); + map.put(SORT_BY_KEY, sortByType); + map.put(REVERSE_KEY, String.valueOf(reverse)); + map.put(SHOW_DOT_FILES_KEY, String.valueOf(showDotFiles)); + map.put(FOLDER_FIRST_KEY, String.valueOf(folderFirst)); + return GsTextUtils.mapToJsonString(map); + } + + public static SortOrder fromString(final String json) { + final SortOrder fso = new SortOrder(); + final Map map = GsTextUtils.jsonStringToMap(json); + fso.sortByType = GsCollectionUtils.getOrDefault(map, SORT_BY_KEY, SORT_BY_NAME); + fso.reverse = Boolean.parseBoolean(GsCollectionUtils.getOrDefault(map, REVERSE_KEY, "false")); + fso.showDotFiles = Boolean.parseBoolean(GsCollectionUtils.getOrDefault(map, SHOW_DOT_FILES_KEY, "false")); + fso.folderFirst = Boolean.parseBoolean(GsCollectionUtils.getOrDefault(map, FOLDER_FIRST_KEY, "true")); + return fso; + } + } + + public static void sortFiles(final Collection filesToSort, final SortOrder order) { if (filesToSort != null && !filesToSort.isEmpty()) { try { final boolean copy = !(filesToSort instanceof List); final List sortable = copy ? new ArrayList<>(filesToSort) : (List) filesToSort; - GsCollectionUtils.keySort(sortable, (f) -> makeSortKey(sortBy, f)); - if (reverse) { + GsCollectionUtils.keySort(sortable, (f) -> makeSortKey(order.sortByType, f)); + if (order.reverse) { Collections.reverse(sortable); } - if (folderFirst) { + if (order.folderFirst) { GsCollectionUtils.keySort(sortable, (f) -> !f.isDirectory()); } @@ -929,4 +960,14 @@ public FileVisitResult visitFile(final Path path, final BasicFileAttributes attr } return null; } + + public static String getPath(final File file) { + try { + return file.getCanonicalPath(); + } catch (IOException e) { + return file.getAbsolutePath(); + } catch (NullPointerException e) { + return ""; + } + } } diff --git a/app/src/main/res/menu/filesystem__menu.xml b/app/src/main/res/menu/filesystem__menu.xml index 02886a57c..b9639cdde 100644 --- a/app/src/main/res/menu/filesystem__menu.xml +++ b/app/src/main/res/menu/filesystem__menu.xml @@ -85,58 +85,7 @@ android:layout_width="match_parent" android:icon="@drawable/ic_sort_black_24dp" android:title="@string/sort_by" - app:showAsAction="always"> - - - - - - - - - - - - - - - - + app:showAsAction="always" /> . Capitalize Words (Ex: a note->A Note) Capitalize Sentences (Ex: case->Case) Indent lines with TAB key + Folder local diff --git a/app/thirdparty/java/other/writeily/widget/WrFilesWidgetFactory.java b/app/thirdparty/java/other/writeily/widget/WrFilesWidgetFactory.java index 69af563f1..39f4f4c85 100644 --- a/app/thirdparty/java/other/writeily/widget/WrFilesWidgetFactory.java +++ b/app/thirdparty/java/other/writeily/widget/WrFilesWidgetFactory.java @@ -51,6 +51,7 @@ public void onDataSetChanged() { _widgetFilesList.clear(); final File dir = WrWidgetConfigure.getWidgetDirectory(_context, _appWidgetId); final AppSettings as = ApplicationObject.settings(); + final GsFileUtils.SortOrder order = as.getFolderSortOrder(dir); if (dir.equals(GsFileBrowserListAdapter.VIRTUAL_STORAGE_RECENTS)) { _widgetFilesList.addAll(ApplicationObject.settings().getRecentFiles()); @@ -59,11 +60,11 @@ public void onDataSetChanged() { } else if (dir.equals(GsFileBrowserListAdapter.VIRTUAL_STORAGE_FAVOURITE)) { _widgetFilesList.addAll(ApplicationObject.settings().getFavouriteFiles()); } else if (dir.exists() && dir.canRead()) { - final boolean showDot = as.isFileBrowserFilterShowDotFiles(); - final File[] all = dir.listFiles(file -> showDot || !file.getName().startsWith(".")); + final File[] all = dir.listFiles(file -> order.showDotFiles || !file.getName().startsWith(".")); _widgetFilesList.addAll(all != null ? Arrays.asList(all) : Collections.emptyList()); } - GsFileUtils.sortFiles(_widgetFilesList, as.getFileBrowserSortByType(), as.isFileBrowserSortFolderFirst(), as.isFileBrowserSortReverse()); + + GsFileUtils.sortFiles(_widgetFilesList, order); } @Override