diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1bd5ac72fd..00b6c054ae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,7 +63,7 @@ android:name=".activity.MainActivity" android:exported="true" android:label="@string/app_name" - android:launchMode="singleTop" + android:launchMode="singleInstance" android:taskAffinity=".activity.MainActivity" android:windowSoftInputMode="stateUnchanged|adjustResize"> diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentActivity.java b/app/src/main/java/net/gsantner/markor/activity/DocumentActivity.java index 5c3f0d3eb5..c7db8591aa 100644 --- a/app/src/main/java/net/gsantner/markor/activity/DocumentActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/DocumentActivity.java @@ -16,6 +16,7 @@ import android.os.Build; import android.os.Bundle; import android.text.Html; +import android.view.KeyEvent; import android.view.MotionEvent; import android.widget.TextView; @@ -55,9 +56,9 @@ public static void launch(Activity activity, File path, Boolean doPreview, Inten intent = new Intent(activity, DocumentActivity.class); } if (path != null) { - intent.putExtra(Document.EXTRA_PATH, path); + intent.putExtra(Document.EXTRA_FILE, path); } else { - path = intent.hasExtra(Document.EXTRA_PATH) ? ((File) intent.getSerializableExtra(Document.EXTRA_PATH)) : null; + path = intent.hasExtra(Document.EXTRA_FILE) ? ((File) intent.getSerializableExtra(Document.EXTRA_FILE)) : null; } if (lineNumber != null && lineNumber >= 0) { intent.putExtra(Document.EXTRA_FILE_LINE_NUMBER, lineNumber); @@ -71,7 +72,7 @@ public static void launch(Activity activity, File path, Boolean doPreview, Inten intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); } if (path != null && path.isDirectory()) { - intent = new Intent(activity, MainActivity.class).putExtra(Document.EXTRA_PATH, path); + intent = new Intent(activity, MainActivity.class).putExtra(Document.EXTRA_FILE, path); } if (path != null && path.isFile() && as.isPreferViewMode()) { as.setDocumentPreviewState(path.getAbsolutePath(), true); @@ -87,7 +88,7 @@ public static void handleFileClick(Activity activity, File file, Integer lineNum if (file.isDirectory()) { if (file.canRead()) { - launch(activity, file, null, null, lineNumber); + launch(activity, file, null, null, null); } } else if (FormatRegistry.isFileSupported(file) && GsFileUtils.canCreate(file)) { launch(activity, file, null, null, lineNumber); @@ -113,12 +114,12 @@ public static Object[] checkIfLikelyTextfileAndGetExt(File file) { } public static void askUserIfWantsToOpenFileInThisApp(final Activity activity, final File file) { - Object[] fret = checkIfLikelyTextfileAndGetExt(file); - boolean isLikelyTextfile = (boolean) fret[0]; - String ext = (String) fret[1]; - boolean isYes = ApplicationObject.settings().isExtOpenWithThisApp(ext); + final Object[] fret = checkIfLikelyTextfileAndGetExt(file); + final boolean isLikelyTextfile = (boolean) fret[0]; + final String ext = (String) fret[1]; + final boolean isYes = ApplicationObject.settings().isExtOpenWithThisApp(ext); - GsCallback.a1 openFile = (openInThisApp) -> { + final GsCallback.a1 openFile = (openInThisApp) -> { if (openInThisApp) { DocumentActivity.launch(activity, file, null, null, null); } else { @@ -178,7 +179,7 @@ private void handleLaunchingIntent(final Intent intent) { // Pull the file from the intent // ----------------------------------------------------------------------- - File file = (File) intent.getSerializableExtra(Document.EXTRA_PATH); + File file = (File) intent.getSerializableExtra(Document.EXTRA_FILE); final boolean intentIsView = Intent.ACTION_VIEW.equals(intentAction); final boolean intentIsSend = Intent.ACTION_SEND.equals(intentAction); @@ -288,7 +289,7 @@ public void setDocumentTitle(final String title) { } public void showTextEditor(final Document document, final Integer lineNumber, final Boolean startPreview) { - GsFragmentBase currentFragment = getCurrentVisibleFragment(); + final GsFragmentBase currentFragment = getCurrentVisibleFragment(); final boolean sameDocumentRequested = ( currentFragment instanceof DocumentEditAndViewFragment && @@ -339,10 +340,13 @@ public void onBackPressed() { } } - public GsFragmentBase showFragment(GsFragmentBase fragment) { + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return super.onReceiveKeyPress(getCurrentVisibleFragment(), keyCode, event) ? true : super.onKeyDown(keyCode, event); + } + public GsFragmentBase showFragment(GsFragmentBase fragment) { if (fragment != getCurrentVisibleFragment()) { - _fragManager.beginTransaction() .replace(R.id.document__placeholder_fragment, fragment, fragment.getFragmentTag()) .commit(); @@ -353,9 +357,7 @@ public GsFragmentBase showFragment(GsFragmentBase fragment) { } public synchronized GsFragmentBase getExistingFragment(final String fragmentTag) { - FragmentManager fmgr = getSupportFragmentManager(); - GsFragmentBase fragment = (GsFragmentBase) fmgr.findFragmentByTag(fragmentTag); - return fragment; + return (GsFragmentBase) getSupportFragmentManager().findFragmentByTag(fragmentTag); } private GsFragmentBase getCurrentVisibleFragment() { 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 8500a09793..8993fe55fa 100644 --- a/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java +++ b/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java @@ -12,6 +12,7 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Typeface; @@ -21,6 +22,7 @@ import android.util.Log; import android.util.TypedValue; import android.view.Gravity; +import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -87,20 +89,21 @@ public static DocumentEditAndViewFragment newInstance(final @NonNull Document do } private HighlightingEditor _hlEditor; - private ViewGroup _textActionsBar; private WebView _webView; - private DraggableScrollbarScrollView _primaryScrollView; + private MarkorWebViewClient _webViewClient; + private ViewGroup _textActionsBar; + private DraggableScrollbarScrollView _primaryScrollView; private HorizontalScrollView _hsView; private SearchView _menuSearchViewForViewMode; private Document _document; private FormatRegistry _format; private MarkorContextUtils _cu; private TextViewUndoRedo _editTextUndoRedoHelper; + private MenuItem _saveMenuItem, _undoMenuItem, _redoMenuItem; private boolean _isPreviewVisible; - private MarkorWebViewClient _webViewClient; private boolean _nextConvertToPrintMode = false; - private MenuItem _saveMenuItem, _undoMenuItem, _redoMenuItem; + public DocumentEditAndViewFragment() { super(); @@ -357,6 +360,33 @@ public boolean onQueryTextChange(String text) { updateUndoRedoIconStates(); } + @Override + public boolean onReceiveKeyPress(int keyCode, KeyEvent event) { + if (event.isCtrlPressed()) { + if (event.isShiftPressed() && keyCode == KeyEvent.KEYCODE_Z) { + if (_editTextUndoRedoHelper != null && _editTextUndoRedoHelper.getCanRedo()) { + _hlEditor.withAutoFormatDisabled(_editTextUndoRedoHelper::redo); + updateUndoRedoIconStates(); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_S) { + saveDocument(true); + return true; + } else if (keyCode == KeyEvent.KEYCODE_Z) { + if (_editTextUndoRedoHelper != null && _editTextUndoRedoHelper.getCanUndo()) { + _hlEditor.withAutoFormatDisabled(_editTextUndoRedoHelper::undo); + updateUndoRedoIconStates(); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_SLASH) { + setViewModeVisibility(!_isPreviewVisible); + return true; + } + } + + return false; + } + private void updateUndoRedoIconStates() { final boolean canUndo = _editTextUndoRedoHelper != null && _editTextUndoRedoHelper.getCanUndo(); if (_undoMenuItem != null && _undoMenuItem.isEnabled() != canUndo) { @@ -462,15 +492,19 @@ public boolean onOptionsItemSelected(@NonNull final MenuItem item) { setViewModeVisibility(!_isPreviewVisible); return true; } + case R.id.action_share_path: { + _cu.shareText(getActivity(), _document.getFile().getAbsolutePath(), GsContextUtils.MIME_TEXT_PLAIN); + return true; + } case R.id.action_share_text: { if (saveDocument(false)) { - _cu.shareText(getActivity(), getTextString(), "text/plain"); + _cu.shareText(getActivity(), getTextString(), GsContextUtils.MIME_TEXT_PLAIN); } return true; } case R.id.action_share_file: { if (saveDocument(false)) { - _cu.shareStream(getActivity(), _document.getFile(), "text/plain"); + _cu.shareStream(getActivity(), _document.getFile(), GsContextUtils.MIME_TEXT_PLAIN); } return true; } @@ -597,6 +631,12 @@ public void onFsViewerConfig(GsFileBrowserOptions.Options dopt) { _hlEditor.setTextSize(TypedValue.COMPLEX_UNIT_SP, (float) newSize); _appSettings.setDocumentFontSize(_document.getPath(), newSize); }); + return true; + } + case R.id.action_show_file_browser: { + final Intent intent = new Intent(activity, MainActivity.class).putExtra(Document.EXTRA_FILE, _document.getFile()); + GsContextUtils.instance.animateToActivity(activity, intent, false, null); + return true; } default: { return super.onOptionsItemSelected(item); @@ -819,6 +859,13 @@ public void onAnimationEnd(Animator animation) { return true; } + @Override + protected void onToolbarClicked(View v) { + if (!_isPreviewVisible && _format != null) { + _format.getActions().runTitleClick(); + } + } + @Override protected boolean onToolbarLongClicked(View v) { if (isVisible() && isResumed()) { @@ -828,21 +875,6 @@ protected boolean onToolbarLongClicked(View v) { return false; } - public Document getDocument() { - return _document; - } - - public WebView getWebview() { - return _webView; - } - - @Override - protected void onToolbarClicked(View v) { - if (!_isPreviewVisible && _format != null) { - _format.getActions().runTitleClick(); - } - } - @Override public void onDestroy() { try { @@ -853,6 +885,14 @@ public void onDestroy() { super.onDestroy(); } + public Document getDocument() { + return _document; + } + + public WebView getWebview() { + return _webView; + } + public String getTextString() { final CharSequence text = _hlEditor != null ? _hlEditor.getText() : null; return text != null ? text.toString() : ""; diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java b/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java index 3c8a9454df..cfe4bbaa8d 100644 --- a/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java +++ b/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java @@ -59,7 +59,7 @@ public static DocumentShareIntoFragment newInstance(Intent intent) { final String sharedText = formatLink(intent.getStringExtra(Intent.EXTRA_SUBJECT), intent.getStringExtra(Intent.EXTRA_TEXT)); - final Object intentFile = intent.getSerializableExtra(Document.EXTRA_PATH); + final Object intentFile = intent.getSerializableExtra(Document.EXTRA_FILE); if (intentFile instanceof File && ((File) intentFile).isDirectory()) { f.workingDir = (File) intentFile; } 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 2e2878b32a..76434048b4 100644 --- a/app/src/main/java/net/gsantner/markor/activity/MainActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/MainActivity.java @@ -17,6 +17,7 @@ import android.os.Handler; import android.text.TextUtils; import android.util.Log; +import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -108,7 +109,7 @@ public void onActivityFirstTimeVisible() { super.onActivityFirstTimeVisible(); // Switch to tab if specific folder _not_ requested, and not recreating from saved instance final int startTab = _appSettings.getAppStartupTab(); - if (startTab != R.id.nav_notebook && MarkorContextUtils.getValidIntentDir(getIntent(), null) == null) { + if (startTab != R.id.nav_notebook && MarkorContextUtils.getValidIntentFile(getIntent(), null) == null) { _viewPager.postDelayed(() -> _viewPager.setCurrentItem(tabIdToPos(startTab)), 100); } } @@ -173,9 +174,13 @@ private void reduceViewpagerSwipeSensitivity() { @Override protected void onNewIntent(final Intent intent) { super.onNewIntent(intent); - final File dir = MarkorContextUtils.getValidIntentDir(intent, null); - if (_notebook != null && dir != null) { - _notebook.post(() -> _notebook.setCurrentFolder(dir)); + final File file = MarkorContextUtils.getValidIntentFile(intent, null); + if (_notebook != null && file != null) { + if (file.isDirectory()) { + _notebook.post(() -> _notebook.setCurrentFolder(file)); + } else { + _notebook.post(() -> _notebook.getAdapter().showFile(file)); + } _bottomNav.postDelayed(() -> _bottomNav.setSelectedItemId(R.id.nav_notebook), 10); } } @@ -192,7 +197,7 @@ private void optShowRate() { } @Override - public boolean onOptionsItemSelected(MenuItem item) { + public boolean onOptionsItemSelected(@NonNull MenuItem item) { super.onOptionsItemSelected(item); if (item.getItemId() == R.id.action_settings) { _cu.animateToActivity(this, SettingsActivity.class, false, null); @@ -314,12 +319,12 @@ public void onBackPressed() { } // Check if fragment handled back press - final GsFragmentBase frag = getPosFrament(getCurrentPos()); + final GsFragmentBase frag = getPosFragment(getCurrentPos()); if (frag != null && frag.onBackPressed()) { return; } - // Confirm exit with back / snackbar + // Confirm exit with back / snack bar _doubleBackToExitPressedOnce = true; _cu.showSnackBar(this, R.string.press_back_again_to_exit, false, R.string.exit, view -> finish()); new Handler().postDelayed(() -> _doubleBackToExitPressedOnce = false, 2000); @@ -364,7 +369,7 @@ public String getPosTitle(final int pos) { return ""; } - public GsFragmentBase getPosFrament(final int pos) { + public GsFragmentBase getPosFragment(final int pos) { if (pos == 0) return _notebook; if (pos == 1) return _todo; if (pos == 2) return _quicknote; @@ -372,6 +377,17 @@ public String getPosTitle(final int pos) { return null; } + /** + * Restores the default toolbar. Used when changing the tab or moving to another activity + * while {@link GsFileBrowserFragment} action mode is active (e.g. when renaming a file) + */ + private void restoreDefaultToolbar() { + GsFileBrowserFragment wrFragment = getNotebook(); + if (wrFragment != null) { + wrFragment.clearSelection(); + } + } + public void onViewPagerPageSelected(final int pos) { _bottomNav.getMenu().getItem(pos).setChecked(true); @@ -392,24 +408,39 @@ public void onViewPagerPageSelected(final int pos) { public GsFileBrowserOptions.Options getFilesystemFragmentOptions(GsFileBrowserOptions.Options existingOptions) { if (_filesystemDialogOptions == null) { _filesystemDialogOptions = MarkorFileBrowserFactory.prepareFsViewerOpts(this, false, new GsFileBrowserOptions.SelectionListenerAdapter() { + File toShow = null; + @Override public void onFsViewerConfig(GsFileBrowserOptions.Options dopt) { dopt.descModtimeInsteadOfParent = true; dopt.rootFolder = _appSettings.getNotebookDirectory(); - dopt.startFolder = MarkorContextUtils.getValidIntentDir(getIntent(), _appSettings.getFolderToLoadByMenuId(_appSettings.getAppStartupFolderMenuId())); + final File fallback = _appSettings.getFolderToLoadByMenuId(_appSettings.getAppStartupFolderMenuId()); + final File file = MarkorContextUtils.getValidIntentFile(getIntent(), fallback); + if (!GsFileBrowserListAdapter.isVirtualFolder(file) && file.isFile()) { + dopt.startFolder = file.getParentFile(); + toShow = file; + } else { + dopt.startFolder = file; + } + toShow = file.isFile() ? file : null; dopt.doSelectMultiple = dopt.doSelectFolder = dopt.doSelectFile = true; dopt.mountedStorageFolder = _cu.getStorageAccessFolder(MainActivity.this); } @Override - public void onFsViewerDoUiUpdate(GsFileBrowserListAdapter adapter) { + public void onFsViewerDoUiUpdate(final GsFileBrowserListAdapter adapter) { if (adapter != null && adapter.getCurrentFolder() != null && !TextUtils.isEmpty(adapter.getCurrentFolder().getName())) { _appSettings.setFileBrowserLastBrowsedFolder(adapter.getCurrentFolder()); if (getCurrentPos() == tabIdToPos(R.id.nav_notebook)) { - setTitle(adapter.areItemsSelected() ? "" : getFileBrowserTitle()); + setTitle(getFileBrowserTitle()); } invalidateOptionsMenu(); } + + if (toShow != null && adapter != null) { + adapter.showFile(toShow); + toShow = null; + } } @Override @@ -473,14 +504,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { _cu.extractResultFromActivityResult(this, requestCode, resultCode, data); } - /** - * Restores the default toolbar. Used when changing the tab or moving to another activity - * while {@link GsFileBrowserFragment} action mode is active (e.g. when renaming a file) - */ - private void restoreDefaultToolbar() { - GsFileBrowserFragment wrFragment = getNotebook(); - if (wrFragment != null) { - wrFragment.clearSelection(); - } + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return super.onReceiveKeyPress(getPosFragment(getCurrentPos()), keyCode, event) ? true : super.onKeyDown(keyCode, event); } } diff --git a/app/src/main/java/net/gsantner/markor/activity/MarkorBaseActivity.java b/app/src/main/java/net/gsantner/markor/activity/MarkorBaseActivity.java index 0f4e2d33fe..69bcfb6b46 100644 --- a/app/src/main/java/net/gsantner/markor/activity/MarkorBaseActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/MarkorBaseActivity.java @@ -4,6 +4,7 @@ import android.graphics.Color; import android.os.Build; import android.os.Bundle; +import android.view.KeyEvent; import android.view.WindowManager; import androidx.annotation.Nullable; @@ -13,6 +14,7 @@ import net.gsantner.markor.model.AppSettings; import net.gsantner.markor.util.MarkorContextUtils; import net.gsantner.opoc.frontend.base.GsActivityBase; +import net.gsantner.opoc.frontend.base.GsFragmentBase; public abstract class MarkorBaseActivity extends GsActivityBase { @@ -31,6 +33,10 @@ protected void onPreCreate(@Nullable Bundle savedInstanceState) { } } + protected boolean onReceiveKeyPress(GsFragmentBase fragment, int keyCode, KeyEvent event) { + return fragment.onReceiveKeyPress(keyCode, event); + } + @Override public Integer getNewNavigationBarColor() { return _cu.parseHexColorString(_appSettings.getNavigationBarColor()); diff --git a/app/src/main/java/net/gsantner/markor/activity/SettingsActivity.java b/app/src/main/java/net/gsantner/markor/activity/SettingsActivity.java index ee729fdaf6..994849a4ce 100644 --- a/app/src/main/java/net/gsantner/markor/activity/SettingsActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/SettingsActivity.java @@ -166,6 +166,8 @@ public void doUpdatePreferences() { ); updateSummary(R.string.pref_key__exts_to_always_open_in_this_app, _appSettings.getString(R.string.pref_key__exts_to_always_open_in_this_app, "")); + updateSummary(R.string.pref_key__snippet_directory_path, _appSettings.getSnippetsDirectory().getAbsolutePath()); + final String fileDescFormat = _appSettings.getString(R.string.pref_key__file_description_format, ""); if (fileDescFormat.equals("")) { updateSummary(R.string.pref_key__file_description_format, getString(R.string.default_)); diff --git a/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java b/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java index fc4d9d4dfd..bb75d85460 100644 --- a/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java @@ -29,7 +29,7 @@ protected void onCreate(@Nullable final Bundle savedInstanceState) { protected void openEditorForFile(final File file, final Integer line) { final Intent openIntent = new Intent(getApplicationContext(), OpenFromShortcutOrWidgetActivity.class) .setAction(Intent.ACTION_EDIT) - .putExtra(Document.EXTRA_PATH, file); + .putExtra(Document.EXTRA_FILE, file); if (line != null) { openIntent.putExtra(Document.EXTRA_FILE_LINE_NUMBER, line); diff --git a/app/src/main/java/net/gsantner/markor/format/ActionButtonBase.java b/app/src/main/java/net/gsantner/markor/format/ActionButtonBase.java index d7d3c6d78b..4331ae1d58 100644 --- a/app/src/main/java/net/gsantner/markor/format/ActionButtonBase.java +++ b/app/src/main/java/net/gsantner/markor/format/ActionButtonBase.java @@ -12,11 +12,14 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; +import android.os.Build; +import android.os.Handler; import android.text.Editable; import android.text.Selection; import android.text.TextUtils; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; @@ -27,6 +30,7 @@ import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; import androidx.appcompat.widget.TooltipCompat; @@ -47,6 +51,7 @@ import net.gsantner.markor.util.MarkorContextUtils; import net.gsantner.opoc.format.GsTextUtils; import net.gsantner.opoc.util.GsCollectionUtils; +import net.gsantner.opoc.util.GsContextUtils; import net.gsantner.opoc.util.GsFileUtils; import net.gsantner.opoc.wrapper.GsCallback; @@ -80,10 +85,12 @@ public abstract class ActionButtonBase { private static final String ORDER_SUFFIX = "_order"; private static final String DISABLED_SUFFIX = "_disabled"; + private static final Pattern UNTRIMMED_TEXT = Pattern.compile("(\\s*)(.*?)(\\s*)", Pattern.DOTALL); + public ActionButtonBase(@NonNull final Context context, final Document document) { _document = document; _appSettings = ApplicationObject.settings(); - _buttonHorizontalMargin = (int) (_appSettings.getEditorActionButtonItemPadding() * context.getResources().getDisplayMetrics().density); + _buttonHorizontalMargin = GsContextUtils.instance.convertDpToPx(context, _appSettings.getEditorActionButtonItemPadding()); _indent = _appSettings.getDocumentIndentSize(_document != null ? _document.getPath() : null); } @@ -122,7 +129,7 @@ public boolean runTitleClick() { * * @return List of ActionItems */ - protected abstract List getActiveActionList(); + protected abstract List getFormatActionList(); /** * These will not be added to the actions list. @@ -139,7 +146,7 @@ public List getDisabledActions() { * @return Map of String key -> Action */ public Map getActiveActionMap() { - final List actionList = getActiveActionList(); + final List actionList = getActionList(); final List keyList = getActiveActionKeys(); final Map map = new HashMap(); @@ -150,13 +157,47 @@ public Map getActiveActionMap() { return map; } + /** + * Get a combined action list - from derived format and the base actions + */ + private List getActionList() { + final List commonActions = Arrays.asList( + new ActionItem(R.string.abid_common_delete_lines, R.drawable.ic_delete_black_24dp, R.string.delete_lines), + new ActionItem(R.string.abid_common_new_line_below, R.drawable.ic_baseline_keyboard_return_24, R.string.start_new_line_below), + new ActionItem(R.string.abid_common_move_text_one_line_up, R.drawable.ic_baseline_arrow_upward_24, R.string.move_text_one_line_up).setRepeatable(true), + new ActionItem(R.string.abid_common_move_text_one_line_down, R.drawable.ic_baseline_arrow_downward_24, R.string.move_text_one_line_down).setRepeatable(true), + new ActionItem(R.string.abid_common_insert_snippet, R.drawable.ic_baseline_file_copy_24, R.string.insert_snippet), + new ActionItem(R.string.abid_common_special_key, R.drawable.ic_keyboard_black_24dp, R.string.special_key), + new ActionItem(R.string.abid_common_time, R.drawable.ic_access_time_black_24dp, R.string.date_and_time), + new ActionItem(R.string.abid_common_open_link_browser, R.drawable.ic_open_in_browser_black_24dp, R.string.open_link), + + new ActionItem(R.string.abid_common_web_jump_to_very_top_or_bottom, R.drawable.ic_vertical_align_center_black_24dp, R.string.jump_to_bottom).setDisplayMode(ActionItem.DisplayMode.VIEW), + new ActionItem(R.string.abid_common_view_file_in_other_app, R.drawable.ic_baseline_open_in_new_24, R.string.open_with).setDisplayMode(ActionItem.DisplayMode.VIEW), + new ActionItem(R.string.abid_common_rotate_screen, R.drawable.ic_rotate_left_black_24dp, R.string.rotate).setDisplayMode(ActionItem.DisplayMode.ANY) + ); + + // Order is enforced separately + final Map unique = new HashMap<>(); + + for (final ActionItem item : commonActions) { + unique.put(item.keyId, item); + } + + // Actions in the derived class override common actions if they share the same keyId + for (final ActionItem item : getFormatActionList()) { + unique.put(item.keyId, item); + } + + return new ArrayList<>(unique.values()); + } + /** * Get string for every ActionItem.keyId defined by getActiveActionList * * @return List or resource strings */ public List getActiveActionKeys() { - return GsCollectionUtils.map(getActiveActionList(), item -> rstr(item.keyId)); + return GsCollectionUtils.map(getActionList(), item -> rstr(item.keyId)); } /** @@ -226,10 +267,11 @@ public List getActionOrder() { final Set added = GsCollectionUtils.setDiff(defined, existing); final Set removed = GsCollectionUtils.setDiff(existing, defined); + // NOTE: suppressing this for increased discoverability // Disable any new actions unless none existing (i.e. first run) - if (!existing.isEmpty()) { - disabled.addAll(added); - } + // if (!existing.isEmpty()) { + // disabled.addAll(added); + // } // Add new ones to order order.addAll(added); @@ -261,31 +303,78 @@ public void recreateActionButtons(ViewGroup barLayout, ActionItem.DisplayMode di } } - protected void appendActionButtonToBar(ViewGroup barLayout, @NonNull ActionItem action) { + @RequiresApi(api = Build.VERSION_CODES.P) + private void setupRepeat(final View btn, final ActionItem action) { + // Velocity and acceleration parameters + final int initialDelay = 400, deltaDelay = 50, minDelay = 100; + final Integer token = action.keyId; + + btn.setOnTouchListener(new View.OnTouchListener() { + Handler handler = null; + int delay = initialDelay; + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(final View v, final MotionEvent event) { + if (handler == null) { + handler = v.getHandler(); + } + + final int action = event.getAction(); + if (action == MotionEvent.ACTION_DOWN) { + delay = initialDelay; + onClick(v); + return true; + } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + handler.removeCallbacksAndMessages(token); + return true; + } + return false; + } + + private void onClick(final View v) { + try { + v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + onActionClick(action.keyId); + handler.postDelayed(() -> onClick(v), token, delay); + delay = Math.max(minDelay, delay - deltaDelay); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + } + + @SuppressLint("ClickableViewAccessibility") + protected void appendActionButtonToBar(final ViewGroup barLayout, final @NonNull ActionItem action) { final ImageView btn = (ImageView) _activity.getLayoutInflater().inflate(R.layout.quick_keyboard_button, null); btn.setImageResource(action.iconId); final String desc = rstr(action.stringId); btn.setContentDescription(desc); TooltipCompat.setTooltipText(btn, desc); - btn.setOnClickListener(v -> { - try { - // run action - v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); - onActionClick(action.keyId); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - btn.setOnLongClickListener(v -> { - try { - v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); - return onActionLongClick(action.keyId); - } catch (Exception ex) { - ex.printStackTrace(); - } - return false; - }); + if (action.isRepeatable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + setupRepeat(btn, action); + } else { + btn.setOnClickListener(v -> { + try { + // run action + v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + onActionClick(action.keyId); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + btn.setOnLongClickListener(v -> { + try { + v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + return onActionLongClick(action.keyId); + } catch (Exception ex) { + ex.printStackTrace(); + } + return false; + }); + } final int sidePadding = _buttonHorizontalMargin + btn.getPaddingLeft(); // Left and right are symmetrical btn.setPadding(sidePadding, btn.getPaddingTop(), sidePadding, btn.getPaddingBottom()); barLayout.addView(btn); @@ -443,59 +532,69 @@ private static void runRegexReplaceAction(final Editable editable, final List= 0 && (text.substring(selectionStart, selectionEnd) - .matches("(\\*\\*|~~|_|`)[a-zA-Z0-9\\s]*(\\*\\*|~~|_|`)"))) { + protected void runSurroundAction(final String delim) { + runSurroundAction(delim, delim, true); + } - text = text.substring(selectionStart + _action.length(), - selectionEnd - _action.length()); - _hlEditor.getText() - .replace(selectionStart, selectionEnd, text); + /** + * Surrounds the current selection with the given startDelimiter and end strings. + * If the region is already surrounded by the given strings, they are removed instead. + * + * @param open The string to insert at the start of the selection + * @param close The string to insert at the end of the selection + * @param trim Whether to trim spaces from the start and end of the selection + */ + protected void runSurroundAction(final String open, final String close, final boolean trim) { + final Editable text = _hlEditor.getText(); + if (text == null) { + return; + } + // Detect if delims within or around selection + // If so, remove it + // ------------------------------------------------------------------------- + final int[] sel = TextViewUtils.getSelection(_hlEditor); + final int ss = sel[0], se = sel[1]; + final int ol = open.length(), cl = close.length(), sl = se - ss; + // Left as a CharSequence to help maintain spans + final CharSequence selection = text.subSequence(ss, se); + + // Case delims around selection + if ((ss > ol) && ((se + cl) <= text.length())) { + final String before = text.subSequence(ss - ol, ss).toString(); + final String after = text.subSequence(se, se + cl).toString(); + if (before.equals(open) && after.equals(close)) { + text.replace(ss - ol, se + cl, selection); + _hlEditor.setSelection(ss - ol, se - ol); + return; } - //Check if Selection is Preceded and succeeded by shortcut characters - else if (((selectionEnd <= (_hlEditor.length() - _action.length())) && - (selectionStart >= _action.length())) && - (text.substring(selectionStart - _action.length(), - selectionEnd + _action.length()) - .matches("(\\*\\*|~~|_|`)[a-zA-Z0-9\\s]*(\\*\\*|~~|_|`)"))) { - - text = text.substring(selectionStart, selectionEnd); - _hlEditor.getText() - .replace(selectionStart - _action.length(), - selectionEnd + _action.length(), text); + } + // Case delims within selection + if ((se - ss) >= (ol + cl)) { + final String within = text.subSequence(ss, se).toString(); + if (within.startsWith(open) && within.endsWith(close)) { + text.replace(ss, se, within.substring(ol, within.length() - cl)); + _hlEditor.setSelection(ss, se - ol - cl); + return; } - //Condition to insert shortcut preceding and succeeding the selection - else { - _hlEditor.getText().insert(selectionStart, _action); - _hlEditor.getText().insert(_hlEditor.getSelectionEnd(), _action); - } + } + + final String replace; + if (trim && selection.length() > 0) { + final int f = TextViewUtils.getFirstNonWhitespace(selection); + final int l = TextViewUtils.getLastNonWhitespace(selection) + 1; + replace = selection.subSequence(0, f) + open + + selection.subSequence(f, l) + close + + selection.subSequence(l, sl); } else { - //Condition for Empty Selection - /*if (false) { - // Condition for things that should only be placed at the start of the line even if no text is selected - } else */ - if ("----\n".equals(_action)) { - _hlEditor.getText().insert(_hlEditor.getSelectionStart(), _action); - } else { - // Condition for formatting which is inserted on either side of the cursor - _hlEditor.getText().insert(_hlEditor.getSelectionStart(), _action) - .insert(_hlEditor.getSelectionEnd(), _action); - _hlEditor.setSelection(_hlEditor.getSelectionStart() - _action.length()); - } + replace = open + selection + close; } - } + text.replace(ss, se, replace); + _hlEditor.setSelection(ss + ol, se + ol); + } public ActionButtonBase setUiReferences(@Nullable final Activity activity, @Nullable final HighlightingEditor hlEditor, @Nullable final WebView webview) { _activity = activity; @@ -718,15 +817,26 @@ public static class ActionItem { public int iconId; @StringRes public int stringId; - public DisplayMode displayMode; + public DisplayMode displayMode = DisplayMode.EDIT; + + public boolean isRepeatable = false; public enum DisplayMode {EDIT, VIEW, ANY} - public ActionItem(@StringRes int key, @DrawableRes int icon, @StringRes int string, final DisplayMode... a_displayMode) { + public ActionItem(@StringRes int key, @DrawableRes int icon, @StringRes int string) { keyId = key; iconId = icon; stringId = string; - displayMode = a_displayMode != null && a_displayMode.length > 0 ? a_displayMode[0] : DisplayMode.EDIT; + } + + public ActionItem setDisplayMode(DisplayMode mode) { + displayMode = mode; + return this; + } + + public ActionItem setRepeatable(boolean repeatable) { + isRepeatable = repeatable; + return this; } } diff --git a/app/src/main/java/net/gsantner/markor/format/asciidoc/AsciidocActionButtons.java b/app/src/main/java/net/gsantner/markor/format/asciidoc/AsciidocActionButtons.java index 5d12fee47f..342aa92503 100644 --- a/app/src/main/java/net/gsantner/markor/format/asciidoc/AsciidocActionButtons.java +++ b/app/src/main/java/net/gsantner/markor/format/asciidoc/AsciidocActionButtons.java @@ -29,8 +29,8 @@ public AsciidocActionButtons(@NonNull Context context, Document document) { } @Override - public List getActiveActionList() { - final ActionItem[] TMA_ACTIONS = { + public List getFormatActionList() { + return Arrays.asList( new ActionItem(R.string.abid_asciidoc_checkbox_list, R.drawable.ic_check_box_black_24dp, R.string.check_list), new ActionItem(R.string.abid_asciidoc_unordered_list_char, R.drawable.ic_list_black_24dp, R.string.unordered_list), new ActionItem(R.string.abid_asciidoc_ordered_list_char, R.drawable.ic_format_list_numbered_black_24dp, R.string.ordered_list), @@ -39,10 +39,8 @@ public List getActiveActionList() { new ActionItem(R.string.abid_common_indent, R.drawable.ic_format_indent_increase_black_24dp, R.string.indent), new ActionItem(R.string.abid_common_deindent, R.drawable.ic_format_indent_decrease_black_24dp, R.string.deindent), new ActionItem(R.string.abid_asciidoc_squarebrackets, R.drawable.ic_baseline_data_array_24, R.string.squarebrackets), - new ActionItem(R.string.abid_common_special_key, R.drawable.ic_keyboard_black_24dp, R.string.special_key), // similar to abid_common_special_key, but separate menu new ActionItem(R.string.abid_asciidoc_special_key, R.drawable.asciidoc_icon_black_24dp, R.string.asciidoc_special_key), - new ActionItem(R.string.abid_common_insert_snippet, R.drawable.ic_baseline_file_copy_24, R.string.insert_snippet), new ActionItem(R.string.abid_asciidoc_h1, R.drawable.format_header_1, R.string.heading_1), new ActionItem(R.string.abid_asciidoc_h2, R.drawable.format_header_2, R.string.heading_2), new ActionItem(R.string.abid_asciidoc_h3, R.drawable.format_header_3, R.string.heading_3), @@ -67,20 +65,10 @@ public List getActiveActionList() { // TODO: Implement Table Generator later // new ActionItem(R.string.abid_asciidoc_table_insert_columns, R.drawable.ic_view_module_black_24dp, R.string.table), // similar to other formats: - new ActionItem(R.string.abid_common_delete_lines, R.drawable.ic_delete_black_24dp, R.string.delete_lines), - new ActionItem(R.string.abid_common_open_link_browser, R.drawable.ic_open_in_browser_black_24dp, R.string.open_link), // a different icon was used than in other formats, so that you can distinguish // it from opening in the browser - new ActionItem(R.string.abid_common_view_file_in_other_app, R.drawable.ic_baseline_open_in_new_24, R.string.open_with, ActionItem.DisplayMode.ANY), - new ActionItem(R.string.abid_common_time, R.drawable.ic_access_time_black_24dp, R.string.date_and_time), - new ActionItem(R.string.abid_common_new_line_below, R.drawable.ic_baseline_keyboard_return_24, R.string.start_new_line_below), - new ActionItem(R.string.abid_common_move_text_one_line_up, R.drawable.ic_baseline_arrow_upward_24, R.string.move_text_one_line_up), - new ActionItem(R.string.abid_common_move_text_one_line_down, R.drawable.ic_baseline_arrow_downward_24, R.string.move_text_one_line_down), - new ActionItem(R.string.abid_common_web_jump_to_very_top_or_bottom, R.drawable.ic_vertical_align_center_black_24dp, R.string.jump_to_bottom, ActionItem.DisplayMode.VIEW), - new ActionItem(R.string.abid_common_rotate_screen, R.drawable.ic_rotate_left_black_24dp, R.string.rotate, ActionItem.DisplayMode.ANY), - }; - - return Arrays.asList(TMA_ACTIONS); + new ActionItem(R.string.abid_common_view_file_in_other_app, R.drawable.ic_baseline_open_in_new_24, R.string.open_with).setDisplayMode(ActionItem.DisplayMode.ANY) + ); } // indent deindent: 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 0473ab2bc5..ede2fdd247 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 @@ -41,6 +41,21 @@ public class MarkdownActionButtons extends ActionButtonBase { private final Set _disabledHeadings = new HashSet<>(); + public static final String LINE_PREFIX = "^(>\\s|#{1,6}\\s|\\s*[-*+](?:\\s\\[[ xX]\\])?\\s|\\s*\\d+[.)]\\s)?"; + + // Patterns used for surrounding entire lines + // ---------------------------------------------------------------------------- + // TODO - make these more intelligent. Should work with combined delimiters. + // Goup 1: Prefix, Group 2: Pre-space, Group 3: Open delim, Group 4: Text, Group 5: Close delim, Group 6: Post-space + public static final Pattern LINE_BOLD = Pattern.compile(LINE_PREFIX + "(\\s*)(\\*\\*)(\\S.*\\S)(\\3)(\\s*)$"); + public static final Pattern LINE_ITALIC = Pattern.compile(LINE_PREFIX + "(\\s*)(_)(\\S.*\\S)(\\3)(\\s*)$"); + public static final Pattern LINE_STRIKEOUT = Pattern.compile(LINE_PREFIX + "(\\s*)(~~)(\\S.*\\S)(\\3)(\\s*)$"); + // Group 1: Prefix, Group 2: Pre-space, Group 3: Text, Group 4: Post-space + public static final Pattern LINE_NONE = Pattern.compile(LINE_PREFIX + "(\\s*)(.*?)(\\s*)$"); + // ---------------------------------------------------------------------------- + + public static final Pattern CHECKED_LIST_LINE = Pattern.compile("^(\\s*)(([-*+])\\s\\[([xX ])\\]\\s)"); + public MarkdownActionButtons(@NonNull Context context, Document document) { super(context, document); } @@ -52,43 +67,28 @@ int getFormatActionsKey() { } @Override - public List getActiveActionList() { - - final ActionItem[] TMA_ACTIONS = { + public List getFormatActionList() { + return Arrays.asList( new ActionItem(R.string.abid_common_checkbox_list, R.drawable.ic_check_box_black_24dp, R.string.check_list), new ActionItem(R.string.abid_common_unordered_list_char, R.drawable.ic_list_black_24dp, R.string.unordered_list), + new ActionItem(R.string.abid_common_ordered_list_number, R.drawable.ic_format_list_numbered_black_24dp, R.string.ordered_list), new ActionItem(R.string.abid_markdown_bold, R.drawable.ic_format_bold_black_24dp, R.string.bold), new ActionItem(R.string.abid_markdown_italic, R.drawable.ic_format_italic_black_24dp, R.string.italic), - new ActionItem(R.string.abid_common_delete_lines, R.drawable.ic_delete_black_24dp, R.string.delete_lines), - new ActionItem(R.string.abid_common_open_link_browser, R.drawable.ic_open_in_browser_black_24dp, R.string.open_link), + new ActionItem(R.string.abid_markdown_strikeout, R.drawable.ic_format_strikethrough_black_24dp, R.string.strikeout), new ActionItem(R.string.abid_common_insert_link, R.drawable.ic_link_black_24dp, R.string.insert_link), new ActionItem(R.string.abid_common_insert_image, R.drawable.ic_image_black_24dp, R.string.insert_image), new ActionItem(R.string.abid_common_insert_audio, R.drawable.ic_keyboard_voice_black_24dp, R.string.audio), - new ActionItem(R.string.abid_common_special_key, R.drawable.ic_keyboard_black_24dp, R.string.special_key), - new ActionItem(R.string.abid_common_time, R.drawable.ic_access_time_black_24dp, R.string.date_and_time), new ActionItem(R.string.abid_markdown_code_inline, R.drawable.ic_code_black_24dp, R.string.inline_code), - new ActionItem(R.string.abid_common_ordered_list_number, R.drawable.ic_format_list_numbered_black_24dp, R.string.ordered_list), new ActionItem(R.string.abid_markdown_table_insert_columns, R.drawable.ic_view_module_black_24dp, R.string.table), new ActionItem(R.string.abid_markdown_quote, R.drawable.ic_format_quote_black_24dp, R.string.quote), new ActionItem(R.string.abid_markdown_h1, R.drawable.format_header_1, R.string.heading_1), new ActionItem(R.string.abid_markdown_h2, R.drawable.format_header_2, R.string.heading_2), new ActionItem(R.string.abid_markdown_h3, R.drawable.format_header_3, R.string.heading_3), new ActionItem(R.string.abid_markdown_horizontal_line, R.drawable.ic_more_horiz_black_24dp, R.string.horizontal_line), - new ActionItem(R.string.abid_markdown_strikeout, R.drawable.ic_format_strikethrough_black_24dp, R.string.strikeout), - new ActionItem(R.string.abid_common_accordion, R.drawable.ic_arrow_drop_down_black_24dp, R.string.accordion), new ActionItem(R.string.abid_common_indent, R.drawable.ic_format_indent_increase_black_24dp, R.string.indent), new ActionItem(R.string.abid_common_deindent, R.drawable.ic_format_indent_decrease_black_24dp, R.string.deindent), - new ActionItem(R.string.abid_common_new_line_below, R.drawable.ic_baseline_keyboard_return_24, R.string.start_new_line_below), - new ActionItem(R.string.abid_common_move_text_one_line_up, R.drawable.ic_baseline_arrow_upward_24, R.string.move_text_one_line_up), - new ActionItem(R.string.abid_common_move_text_one_line_down, R.drawable.ic_baseline_arrow_downward_24, R.string.move_text_one_line_down), - new ActionItem(R.string.abid_common_insert_snippet, R.drawable.ic_baseline_file_copy_24, R.string.insert_snippet), - - new ActionItem(R.string.abid_common_web_jump_to_very_top_or_bottom, R.drawable.ic_vertical_align_center_black_24dp, R.string.jump_to_bottom, ActionItem.DisplayMode.VIEW), - new ActionItem(R.string.abid_common_web_jump_to_table_of_contents, R.drawable.ic_list_black_24dp, R.string.table_of_contents, ActionItem.DisplayMode.VIEW), - new ActionItem(R.string.abid_common_rotate_screen, R.drawable.ic_rotate_left_black_24dp, R.string.rotate, ActionItem.DisplayMode.ANY), - }; - - return Arrays.asList(TMA_ACTIONS); + new ActionItem(R.string.abid_common_accordion, R.drawable.ic_arrow_drop_down_black_24dp, R.string.accordion) + ); } @Override @@ -126,23 +126,23 @@ public boolean onActionClick(final @StringRes int action) { return true; } case R.string.abid_markdown_bold: { - runInlineAction("**"); + runSurroundAction("**"); return true; } case R.string.abid_markdown_italic: { - runInlineAction("_"); + runSurroundAction("_"); return true; } case R.string.abid_markdown_strikeout: { - runInlineAction("~~"); + runSurroundAction("~~"); return true; } case R.string.abid_markdown_code_inline: { - runInlineAction("`"); + runSurroundAction("`"); return true; } case R.string.abid_markdown_horizontal_line: { - runInlineAction("----\n"); + _hlEditor.insertOrReplaceTextOnCursor("----\n"); return true; } case R.string.abid_markdown_table_insert_columns: { @@ -160,6 +160,22 @@ public boolean onActionClick(final @StringRes int action) { } } + /** + * Used to surround selected text with a given delimiter (and remove it if present) + * + * Not super intelligent about how patterns can be combined. + * Current regexes just look for the litera delimiters. + * + * @param pattern - Pattern to match if delimiter is present + * @param delim - Delimiter to surround text with + */ + private void runLineSurroundAction(final Pattern pattern, final String delim) { + runRegexReplaceAction( + new ReplacePattern(pattern, "$1$2$4$6"), + new ReplacePattern(LINE_NONE, "$1$2" + delim + "$3" + delim + "$4") + ); + } + @SuppressLint("NonConstantResourceId") @Override public boolean onActionLongClick(final @StringRes int action) { @@ -177,6 +193,24 @@ public boolean onActionLongClick(final @StringRes int action) { }); return true; } + case R.string.abid_markdown_bold: { + runLineSurroundAction(LINE_BOLD, "**"); + return true; + } + case R.string.abid_markdown_italic: { + runLineSurroundAction(LINE_ITALIC, "_"); + return true; + } + case R.string.abid_markdown_strikeout: { + runLineSurroundAction(LINE_STRIKEOUT, "~~"); + return true; + } + case R.string.abid_common_checkbox_list: { + MarkorDialogFactory.showDocumentChecklistDialog( + getActivity(), _hlEditor.getText(), CHECKED_LIST_LINE, 4, "xX", " ", + pos -> TextViewUtils.setSelectionAndShow(_hlEditor, pos)); + return true; + } default: { return runCommonLongPressAction(action); } 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 8fd0032d63..779e397bd8 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 @@ -21,29 +21,15 @@ public OrgmodeActionButtons(@NonNull Context context, Document document) { } @Override - public List getActiveActionList() { - final ActionItem[] TMA_ACTIONS = { + public List getFormatActionList() { + return Arrays.asList( new ActionItem(R.string.abid_common_checkbox_list, R.drawable.ic_check_box_black_24dp, R.string.check_list), new ActionItem(R.string.abid_common_unordered_list_char, R.drawable.ic_list_black_24dp, R.string.unordered_list), new ActionItem(R.string.abid_common_ordered_list_number, R.drawable.ic_format_list_numbered_black_24dp, R.string.ordered_list), - new ActionItem(R.string.abid_common_delete_lines, R.drawable.ic_delete_black_24dp, R.string.delete_lines), - new ActionItem(R.string.abid_common_open_link_browser, R.drawable.ic_open_in_browser_black_24dp, R.string.open_link), new ActionItem(R.string.abid_common_attach_something, R.drawable.ic_attach_file_black_24dp, R.string.attach), - new ActionItem(R.string.abid_common_special_key, R.drawable.ic_keyboard_black_24dp, R.string.special_key), - new ActionItem(R.string.abid_common_time, R.drawable.ic_access_time_black_24dp, R.string.date_and_time), new ActionItem(R.string.abid_common_indent, R.drawable.ic_format_indent_increase_black_24dp, R.string.indent), - new ActionItem(R.string.abid_common_deindent, R.drawable.ic_format_indent_decrease_black_24dp, R.string.deindent), - new ActionItem(R.string.abid_common_new_line_below, R.drawable.ic_baseline_keyboard_return_24, R.string.start_new_line_below), - new ActionItem(R.string.abid_common_move_text_one_line_up, R.drawable.ic_baseline_arrow_upward_24, R.string.move_text_one_line_up), - new ActionItem(R.string.abid_common_move_text_one_line_down, R.drawable.ic_baseline_arrow_downward_24, R.string.move_text_one_line_down), - new ActionItem(R.string.abid_common_insert_snippet, R.drawable.ic_baseline_file_copy_24, R.string.insert_snippet), - - new ActionItem(R.string.abid_common_web_jump_to_very_top_or_bottom, R.drawable.ic_vertical_align_center_black_24dp, R.string.jump_to_bottom, ActionItem.DisplayMode.VIEW), - new ActionItem(R.string.abid_common_view_file_in_other_app, R.drawable.ic_open_in_browser_black_24dp, R.string.open_with, ActionItem.DisplayMode.ANY), - new ActionItem(R.string.abid_common_rotate_screen, R.drawable.ic_rotate_left_black_24dp, R.string.rotate, ActionItem.DisplayMode.ANY), - }; - - return Arrays.asList(TMA_ACTIONS); + new ActionItem(R.string.abid_common_deindent, R.drawable.ic_format_indent_decrease_black_24dp, R.string.deindent) + ); } @Override diff --git a/app/src/main/java/net/gsantner/markor/format/plaintext/PlaintextActionButtons.java b/app/src/main/java/net/gsantner/markor/format/plaintext/PlaintextActionButtons.java index 2d50965674..5fdb191f65 100644 --- a/app/src/main/java/net/gsantner/markor/format/plaintext/PlaintextActionButtons.java +++ b/app/src/main/java/net/gsantner/markor/format/plaintext/PlaintextActionButtons.java @@ -28,29 +28,16 @@ public PlaintextActionButtons(@NonNull Context context, Document document) { } @Override - public List getActiveActionList() { - final ActionItem[] TMA_ACTIONS = { + public List getFormatActionList() { + return Arrays.asList( new ActionItem(R.string.abid_common_checkbox_list, R.drawable.ic_check_box_black_24dp, R.string.check_list), new ActionItem(R.string.abid_common_unordered_list_char, R.drawable.ic_list_black_24dp, R.string.unordered_list), new ActionItem(R.string.abid_common_ordered_list_number, R.drawable.ic_format_list_numbered_black_24dp, R.string.ordered_list), - new ActionItem(R.string.abid_common_delete_lines, R.drawable.ic_delete_black_24dp, R.string.delete_lines), - new ActionItem(R.string.abid_common_open_link_browser, R.drawable.ic_open_in_browser_black_24dp, R.string.open_link), new ActionItem(R.string.abid_common_attach_something, R.drawable.ic_attach_file_black_24dp, R.string.attach), new ActionItem(R.string.abid_common_special_key, R.drawable.ic_keyboard_black_24dp, R.string.special_key), - new ActionItem(R.string.abid_common_time, R.drawable.ic_access_time_black_24dp, R.string.date_and_time), new ActionItem(R.string.abid_common_indent, R.drawable.ic_format_indent_increase_black_24dp, R.string.indent), - new ActionItem(R.string.abid_common_deindent, R.drawable.ic_format_indent_decrease_black_24dp, R.string.deindent), - new ActionItem(R.string.abid_common_new_line_below, R.drawable.ic_baseline_keyboard_return_24, R.string.start_new_line_below), - new ActionItem(R.string.abid_common_move_text_one_line_up, R.drawable.ic_baseline_arrow_upward_24, R.string.move_text_one_line_up), - new ActionItem(R.string.abid_common_move_text_one_line_down, R.drawable.ic_baseline_arrow_downward_24, R.string.move_text_one_line_down), - new ActionItem(R.string.abid_common_insert_snippet, R.drawable.ic_baseline_file_copy_24, R.string.insert_snippet), - - new ActionItem(R.string.abid_common_web_jump_to_very_top_or_bottom, R.drawable.ic_vertical_align_center_black_24dp, R.string.jump_to_bottom, ActionItem.DisplayMode.VIEW), - new ActionItem(R.string.abid_common_view_file_in_other_app, R.drawable.ic_open_in_browser_black_24dp, R.string.open_with, ActionItem.DisplayMode.ANY), - new ActionItem(R.string.abid_common_rotate_screen, R.drawable.ic_rotate_left_black_24dp, R.string.rotate, ActionItem.DisplayMode.ANY), - }; - - return Arrays.asList(TMA_ACTIONS); + new ActionItem(R.string.abid_common_deindent, R.drawable.ic_format_indent_decrease_black_24dp, R.string.deindent) + ); } @Override diff --git a/app/src/main/java/net/gsantner/markor/format/todotxt/TodoTxtActionButtons.java b/app/src/main/java/net/gsantner/markor/format/todotxt/TodoTxtActionButtons.java index 26432f0299..eb5372eec5 100644 --- a/app/src/main/java/net/gsantner/markor/format/todotxt/TodoTxtActionButtons.java +++ b/app/src/main/java/net/gsantner/markor/format/todotxt/TodoTxtActionButtons.java @@ -50,30 +50,17 @@ public TodoTxtActionButtons(@NonNull Context context, Document document) { } @Override - public List getActiveActionList() { - - final ActionItem[] TMA_ACTIONS = { + public List getFormatActionList() { + return Arrays.asList( new ActionItem(R.string.abid_todotxt_toggle_done, R.drawable.ic_check_box_black_24dp, R.string.toggle_done), new ActionItem(R.string.abid_todotxt_add_context, R.drawable.gs_email_sign_black_24dp, R.string.add_context), new ActionItem(R.string.abid_todotxt_add_project, R.drawable.ic_new_label_black_24dp, R.string.add_project), new ActionItem(R.string.abid_todotxt_priority, R.drawable.ic_star_border_black_24dp, R.string.priority), - new ActionItem(R.string.abid_common_delete_lines, R.drawable.ic_delete_black_24dp, R.string.delete_lines), - new ActionItem(R.string.abid_common_open_link_browser, R.drawable.ic_open_in_browser_black_24dp, R.string.open_link), - new ActionItem(R.string.abid_common_attach_something, R.drawable.ic_attach_file_black_24dp, R.string.attach), - new ActionItem(R.string.abid_common_special_key, R.drawable.ic_keyboard_black_24dp, R.string.special_key), new ActionItem(R.string.abid_todotxt_archive_done_tasks, R.drawable.ic_archive_black_24dp, R.string.archive_completed_tasks), - new ActionItem(R.string.abid_todotxt_sort_todo, R.drawable.ic_sort_by_alpha_black_24dp, R.string.sort_by), new ActionItem(R.string.abid_todotxt_current_date, R.drawable.ic_date_range_black_24dp, R.string.current_date), - new ActionItem(R.string.abid_common_new_line_below, R.drawable.ic_baseline_keyboard_return_24, R.string.start_new_line_below), - new ActionItem(R.string.abid_common_move_text_one_line_up, R.drawable.ic_baseline_arrow_upward_24, R.string.move_text_one_line_up), - new ActionItem(R.string.abid_common_move_text_one_line_down, R.drawable.ic_baseline_arrow_downward_24, R.string.move_text_one_line_down), - new ActionItem(R.string.abid_common_insert_snippet, R.drawable.ic_baseline_file_copy_24, R.string.insert_snippet), - - new ActionItem(R.string.abid_common_web_jump_to_very_top_or_bottom, R.drawable.ic_vertical_align_center_black_24dp, R.string.jump_to_bottom, ActionItem.DisplayMode.VIEW), - new ActionItem(R.string.abid_common_rotate_screen, R.drawable.ic_rotate_left_black_24dp, R.string.rotate, ActionItem.DisplayMode.ANY), - }; - - return Arrays.asList(TMA_ACTIONS); + new ActionItem(R.string.abid_todotxt_sort_todo, R.drawable.ic_sort_by_alpha_black_24dp, R.string.sort_by), + new ActionItem(R.string.abid_common_attach_something, R.drawable.ic_attach_file_black_24dp, R.string.attach) + ); } @Override diff --git a/app/src/main/java/net/gsantner/markor/format/wikitext/WikitextActionButtons.java b/app/src/main/java/net/gsantner/markor/format/wikitext/WikitextActionButtons.java index 05cba2ff47..3def72f9e3 100644 --- a/app/src/main/java/net/gsantner/markor/format/wikitext/WikitextActionButtons.java +++ b/app/src/main/java/net/gsantner/markor/format/wikitext/WikitextActionButtons.java @@ -46,9 +46,8 @@ int getFormatActionsKey() { } @Override - public List getActiveActionList() { - - final ActionItem[] ACTION_ITEMS = { + public List getFormatActionList() { + return Arrays.asList( new ActionItem(R.string.abid_common_checkbox_list, R.drawable.ic_check_box_black_24dp, R.string.check_list), new ActionItem(R.string.abid_common_unordered_list_char, R.drawable.ic_list_black_24dp, R.string.unordered_list), new ActionItem(R.string.abid_wikitext_bold, R.drawable.ic_format_bold_black_24dp, R.string.bold), @@ -59,10 +58,6 @@ public List getActiveActionList() { new ActionItem(R.string.abid_wikitext_h1, R.drawable.format_header_1, R.string.heading_1), new ActionItem(R.string.abid_wikitext_h2, R.drawable.format_header_2, R.string.heading_2), new ActionItem(R.string.abid_wikitext_h3, R.drawable.format_header_3, R.string.heading_3), - new ActionItem(R.string.abid_common_delete_lines, R.drawable.ic_delete_black_24dp, R.string.delete_lines), - new ActionItem(R.string.abid_common_open_link_browser, R.drawable.ic_open_in_browser_black_24dp, R.string.open_link), - new ActionItem(R.string.abid_common_special_key, R.drawable.ic_keyboard_black_24dp, R.string.special_key), - new ActionItem(R.string.abid_common_time, R.drawable.ic_access_time_black_24dp, R.string.date_and_time), new ActionItem(R.string.abid_common_ordered_list_number, R.drawable.ic_format_list_numbered_black_24dp, R.string.ordered_list), new ActionItem(R.string.abid_common_accordion, R.drawable.ic_arrow_drop_down_black_24dp, R.string.accordion), new ActionItem(R.string.abid_common_indent, R.drawable.ic_format_indent_increase_black_24dp, R.string.indent), @@ -70,18 +65,8 @@ public List getActiveActionList() { new ActionItem(R.string.abid_wikitext_h4, R.drawable.format_header_4, R.string.heading_4), new ActionItem(R.string.abid_wikitext_h5, R.drawable.format_header_5, R.string.heading_5), new ActionItem(R.string.abid_common_insert_image, R.drawable.ic_image_black_24dp, R.string.insert_image), - new ActionItem(R.string.abid_common_insert_link, R.drawable.ic_link_black_24dp, R.string.insert_link), - new ActionItem(R.string.abid_common_new_line_below, R.drawable.ic_baseline_keyboard_return_24, R.string.start_new_line_below), - new ActionItem(R.string.abid_common_move_text_one_line_up, R.drawable.ic_baseline_arrow_upward_24, R.string.move_text_one_line_up), - new ActionItem(R.string.abid_common_move_text_one_line_down, R.drawable.ic_baseline_arrow_downward_24, R.string.move_text_one_line_down), - new ActionItem(R.string.abid_common_insert_snippet, R.drawable.ic_baseline_file_copy_24, R.string.insert_snippet), - - new ActionItem(R.string.abid_common_web_jump_to_very_top_or_bottom, R.drawable.ic_vertical_align_center_black_24dp, R.string.jump_to_bottom, ActionItem.DisplayMode.VIEW), - new ActionItem(R.string.abid_common_web_jump_to_table_of_contents, R.drawable.ic_list_black_24dp, R.string.table_of_contents, ActionItem.DisplayMode.VIEW), - new ActionItem(R.string.abid_common_rotate_screen, R.drawable.ic_rotate_left_black_24dp, R.string.rotate, ActionItem.DisplayMode.ANY), - }; - - return Arrays.asList(ACTION_ITEMS); + new ActionItem(R.string.abid_common_insert_link, R.drawable.ic_link_black_24dp, R.string.insert_link) + ); } @Override @@ -122,29 +107,25 @@ public boolean onActionClick(final @StringRes int action) { return true; } case R.string.abid_wikitext_bold: { - runInlineAction("**"); + runSurroundAction("**"); return true; } case R.string.abid_wikitext_italic: { - runInlineAction("//"); + runSurroundAction("//"); return true; } case R.string.abid_wikitext_highlight: { - runInlineAction("__"); + runSurroundAction("__"); return true; } case R.string.abid_wikitext_strikeout: { - runInlineAction("~~"); + runSurroundAction("~~"); return true; } case R.string.abid_wikitext_code_inline: { - runInlineAction("''"); + runSurroundAction("''"); return true; } - // case R.string.abid_wikitext_horizontal_line: { - // runInlineAction("----\n"); - // return true; - // } case R.string.abid_common_indent: runRegexReplaceAction(WikitextReplacePatternGenerator.indentOneTab()); runRenumberOrderedListIfRequired(); 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 c783423ac1..4abbac0126 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java +++ b/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java @@ -31,6 +31,7 @@ import android.widget.EditText; import android.widget.Toast; +import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; @@ -59,11 +60,15 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class MarkorDialogFactory { public static AppSettings as() { @@ -506,6 +511,79 @@ public static void showColorSelectionModeDialog(Activity activity, GsCallback.a1 GsSearchOrCustomTextDialog.showMultiChoiceDialogWithSearchFilterUI(activity, dopt); } + /** + * Shows all checkboxes in the file in a muti select dialog. + * The multi select can be used to check or uncheck them. + * + * Long pressing a line will jump to the line in the file. + */ + public static void showDocumentChecklistDialog( + final Activity activity, + final Editable text, + final Pattern checkPattern, + final int checkGroup, + final String checkedChars, + final String uncheckedChars, + final @Nullable GsCallback.a1 showCallback + ) { + final List lines = new ArrayList<>(); // String of each line + final Set checked = new HashSet<>(); // Whether the line is checked + final List indices = new ArrayList<>(); // Indices of the check char in the line + final Matcher matcher = checkPattern.matcher(""); + GsTextUtils.forEachline(text, (index, start, end) -> { + final String line = text.subSequence(start, end).toString(); + matcher.reset(line); + if (matcher.find()) { + final int cs = matcher.start(checkGroup); + final char c = line.charAt(cs); + if (checkedChars.indexOf(c) >= 0) { + checked.add(lines.size()); + } + lines.add(line); + indices.add(cs + start); + } + }); + + final DialogOptions dopt = new DialogOptions(); + baseConf(activity, dopt); + dopt.isMultiSelectEnabled = true; + dopt.data = lines; + dopt.preSelected = checked; + dopt.titleText = R.string.check_list; + + final String check = Character.toString(checkedChars.charAt(0)); + final String uncheck = Character.toString(uncheckedChars.charAt(0)); + final TextViewUtils.ChunkedEditable chunked = TextViewUtils.ChunkedEditable.wrap(text); + + dopt.positionCallback = (result) -> { + // Result has the indices of the checker lines which are selected + for (final int i: GsCollectionUtils.setDiff(checked, result)) { + final int cs = indices.get(i); + chunked.replace(cs, cs + 1, uncheck); + } + + for (final int i: GsCollectionUtils.setDiff(result, checked)) { + final int cs = indices.get(i); + chunked.replace(cs, cs + 1, check); + } + + chunked.applyChanges(); + }; + + if (showCallback != null) { + dopt.callback = (line) -> { + final int index = lines.indexOf(line); + if (index >= 0) { + final int cs = indices.get(index); + final int end = TextViewUtils.getLineEnd(text, cs); + showCallback.callback(end); + } + }; + } + + GsSearchOrCustomTextDialog.showMultiChoiceDialogWithSearchFilterUI(activity, dopt); + } + // Insert items public static void showInsertItemsDialog( final Activity activity, @@ -543,7 +621,6 @@ public static void showUpdateItemsDialog( dopt.titleText = title; dopt.searchHintText = R.string.search_or_custom; dopt.isMultiSelectEnabled = true; - dopt.isLongPressSelectEnabled = false; // Don't want to remove all others by selecting just one dopt.callback = (str) -> callback.callback(GsCollectionUtils.union(currentKeys, Collections.singleton(str))); dopt.positionCallback = (newSel) -> callback.callback( GsCollectionUtils.map(newSel, pi -> dopt.data.get(pi).toString())); @@ -609,7 +686,6 @@ public static void showHeadlineDialog( final List filtered = GsCollectionUtils.indices(headings, h -> !disabled.contains(h.level)); final List data = GsCollectionUtils.map(filtered, i -> headings.get(i).str); - final DialogOptions dopt = new DialogOptions(); baseConf(activity, dopt); dopt.data = data; diff --git a/app/src/main/java/net/gsantner/markor/frontend/filesearch/FileSearchResultSelectorDialog.java b/app/src/main/java/net/gsantner/markor/frontend/filesearch/FileSearchResultSelectorDialog.java index 125c816114..0704191f70 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/filesearch/FileSearchResultSelectorDialog.java +++ b/app/src/main/java/net/gsantner/markor/frontend/filesearch/FileSearchResultSelectorDialog.java @@ -116,6 +116,18 @@ public void beforeTextChanged(final CharSequence arg0, final int arg1, final int return false; }); + final GsCallback.b5 onChildClick = (parent, view, groupPos, childPos, id) -> { + final ExpandableListAdapter _adapter = parent.getExpandableListAdapter(); + final FitFile groupItem = (FitFile) _adapter.getGroup(groupPos); + final Pair childItem = (Pair) _adapter.getChild(groupPos, childPos); + if (childItem != null && childItem.second != null && childItem.second >= 0) { + dialogCallback.callback(groupItem.path, childItem.second, false); + } + return false; + }; + + expandableListView.setOnChildClickListener(onChildClick::callback); + // Long click on file name takes us to the file's location expandableListView.setOnItemLongClickListener((parent, view, position, id) -> { try { @@ -125,21 +137,16 @@ public void beforeTextChanged(final CharSequence arg0, final int arg1, final int final String path = ((FitFile) expandableListView.getExpandableListAdapter().getGroup(group)).path; dismiss.callback(); dialogCallback.callback(path, null, true); + } else { + final int groupPos = ExpandableListView.getPackedPositionGroup(packed); + final int childPos = ExpandableListView.getPackedPositionChild(packed); + onChildClick.callback(expandableListView, view, groupPos, childPos, id); } } catch (ClassCastException | NullPointerException ignored) { } return true; }); - expandableListView.setOnChildClickListener((parent, v, groupPosition, childPosition, id) -> { - final FitFile groupItem = (FitFile) parent.getExpandableListAdapter().getGroup(groupPosition); - Pair childItem = (Pair) parent.getExpandableListAdapter().getChild(groupPosition, childPosition); - if (childItem != null && childItem.second != null && childItem.second >= 0) { - dialogCallback.callback(groupItem.path, childItem.second, false); - } - return false; - }); - dialogLayout.addView(expandableListView); // Configure dialog diff --git a/app/src/main/java/net/gsantner/markor/frontend/textview/AutoTextFormatter.java b/app/src/main/java/net/gsantner/markor/frontend/textview/AutoTextFormatter.java index 5759105c82..7c71e453f4 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/textview/AutoTextFormatter.java +++ b/app/src/main/java/net/gsantner/markor/frontend/textview/AutoTextFormatter.java @@ -231,7 +231,7 @@ public UnOrderedOrCheckListLine(CharSequence text, int position, FormatPatterns if (isUnorderedOrCheckList) { groupStart = lineStart + matcher.start(FULL_ITEM_PREFIX_GROUP); groupEnd = lineStart + matcher.end(FULL_ITEM_PREFIX_GROUP); - String emptyCheckboxContent = " "; + final String emptyCheckboxContent = " "; newItemPrefix = isChecklist ? matcher.group(CHECKBOX_PREFIX_LEFT_GROUP) + emptyCheckboxContent + matcher.group(CHECKBOX_PREFIX_RIGHT_GROUP) : matcher.group(FULL_ITEM_PREFIX_GROUP); } else { 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 540dc4cd5b..6bc15d3cc4 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 @@ -94,8 +94,12 @@ public static int getLineEnd(CharSequence s, int start, int maxRange) { return i; } - public static int getLastNonWhitespace(final CharSequence s, final int start) { - for (int i = Math.min(s.length() - 1, start); i >= 0; i--) { + public static int getLastNonWhitespace(final CharSequence s) { + return getLastNonWhitespace(s, s.length() - 1); + } + + public static int getLastNonWhitespace(final CharSequence s, final int end) { + for (int i = Math.min(s.length() - 1, end); i >= 0; i--) { char c = s.charAt(i); if (c != ' ' && c != '\t') { return i; @@ -104,6 +108,11 @@ public static int getLastNonWhitespace(final CharSequence s, final int start) { return -1; } + + public static int getFirstNonWhitespace(final CharSequence s) { + return getNextNonWhitespace(s, 0); + } + public static int getNextNonWhitespace(final CharSequence s, final int start) { final int length = s.length(); for (int i = Math.max(0, start); i < length; i++) { @@ -419,12 +428,14 @@ public static String interpolateSnippet(String text, final String title, final S 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); + final String weekday = GsContextUtils.instance.formatDateTime((Locale) null, "EEEE", current); // Replace placeholders text = text .replace("{{time}}", time) .replace("{{date}}", date) .replace("{{title}}", title) + .replace("{{weekday}}", weekday) .replace("{{sel}}", selectedText) .replace("{{cursor}}", HighlightingEditor.PLACE_CURSOR_HERE_TOKEN); 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 ed0282c918..23c0da0f72 100644 --- a/app/src/main/java/net/gsantner/markor/model/AppSettings.java +++ b/app/src/main/java/net/gsantner/markor/model/AppSettings.java @@ -28,15 +28,19 @@ import net.gsantner.opoc.format.GsTextUtils; import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserListAdapter; import net.gsantner.opoc.model.GsSharedPreferencesPropertyBackend; +import net.gsantner.opoc.util.GsCollectionUtils; import net.gsantner.opoc.util.GsContextUtils; import net.gsantner.opoc.util.GsFileUtils; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Set; import other.de.stanetz.jpencconverter.PasswordStore; @@ -315,7 +319,7 @@ public boolean isEditorStartEditingInCenter() { } public int getEditorActionButtonItemPadding() { - return getInt(R.string.pref_key__editor_textaction_bar_item_padding, 8); + return getInt(R.string.pref_key__editor_textaction_bar_item_padding, 6); } public boolean isDisableSpellingRedUnderline() { @@ -340,21 +344,25 @@ public void addRecentDocument(File file) { ShortcutUtils.setShortcuts(_context); } - public void toggleFavouriteFile(File file) { - List list = new ArrayList<>(); - List favourites = getFavouriteFiles(); - for (File f : favourites) { + public void setFavouriteFiles(final Collection files) { + final Set set = new LinkedHashSet<>(); + for (final File f : files) { if (f != null && (f.exists() || GsFileBrowserListAdapter.isVirtualStorage(f))) { - list.add(f.getAbsolutePath()); + set.add(f.getAbsolutePath()); } } - String abs = file.getAbsolutePath(); - if (list.contains(abs)) { - list.remove(abs); + setStringList(R.string.pref_key__favourite_files, GsCollectionUtils.map(set, p -> p)); + } + + public void toggleFavouriteFile(File file) { + final List list = new ArrayList<>(); + final Set favourites = getFavouriteFiles(); + if (favourites.contains(file)) { + favourites.remove(file); } else { - list.add(abs); + favourites.add(file); } - setStringList(R.string.pref_key__favourite_files, list); + setFavouriteFiles(favourites); } private static final String PREF_PREFIX_EDIT_POS_CHAR = "PREF_PREFIX_EDIT_POS_CHAR"; @@ -579,15 +587,15 @@ public ArrayList getAsFileList(List list) { return r; } - public ArrayList getFavouriteFiles() { - ArrayList list = new ArrayList<>(); - for (String fp : getStringList(R.string.pref_key__favourite_files)) { - File f = new File(fp); + public Set getFavouriteFiles() { + final Set set = new LinkedHashSet<>(); + for (final String fp : getStringList(R.string.pref_key__favourite_files)) { + final File f = new File(fp); if (f.exists() || GsFileBrowserListAdapter.isVirtualStorage(f)) { - list.add(f); + set.add(f); } } - return list; + return set; } public String getInjectedHeader() { 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 5c19d87aa1..6fe2b73bad 100644 --- a/app/src/main/java/net/gsantner/markor/model/Document.java +++ b/app/src/main/java/net/gsantner/markor/model/Document.java @@ -49,7 +49,7 @@ public class Document implements Serializable { private static final String MOD_PREF_NAME = "DOCUMENT_MOD_TIMES"; public static final String EXTRA_DOCUMENT = "EXTRA_DOCUMENT"; // Document - public static final String EXTRA_PATH = "EXTRA_PATH"; // java.io.File + public static final String EXTRA_FILE = "EXTRA_FILE"; // java.io.File public static final String EXTRA_FILE_LINE_NUMBER = "EXTRA_FILE_LINE_NUMBER"; // int public static final int EXTRA_FILE_LINE_NUMBER_LAST = -919385553; // Flag for last line diff --git a/app/src/main/java/net/gsantner/markor/util/MarkorContextUtils.java b/app/src/main/java/net/gsantner/markor/util/MarkorContextUtils.java index c9fd835824..272717e2d7 100644 --- a/app/src/main/java/net/gsantner/markor/util/MarkorContextUtils.java +++ b/app/src/main/java/net/gsantner/markor/util/MarkorContextUtils.java @@ -78,7 +78,7 @@ public static File getIntentFile(final Intent intent, final File fallback) { } // By extra path - final File file = (File) intent.getSerializableExtra(Document.EXTRA_PATH); + final File file = (File) intent.getSerializableExtra(Document.EXTRA_FILE); if (file != null) { return file; } @@ -92,8 +92,8 @@ public static File getIntentFile(final Intent intent, final File fallback) { return fallback; } - public static File getValidIntentDir(final Intent intent, final File fallback) { + public static File getValidIntentFile(final Intent intent, final File fallback) { final File f = getIntentFile(intent, null); - return (f != null && f.isDirectory() && f.exists()) ? f : fallback; + return f != null && f.exists() ? f : fallback; } } 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 4f3754efe6..3ca26d524c 100644 --- a/app/src/main/java/net/gsantner/opoc/frontend/GsSearchOrCustomTextDialog.java +++ b/app/src/main/java/net/gsantner/opoc/frontend/GsSearchOrCustomTextDialog.java @@ -50,12 +50,14 @@ import androidx.appcompat.widget.TooltipCompat; import androidx.core.widget.TextViewCompat; +import net.gsantner.markor.R; import net.gsantner.opoc.util.GsCollectionUtils; import net.gsantner.opoc.util.GsContextUtils; import net.gsantner.opoc.wrapper.GsCallback; import net.gsantner.opoc.wrapper.GsTextWatcherAdapter; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -81,7 +83,6 @@ public static class DialogOptions { public GsCallback.a1> positionCallback = null; public boolean isMultiSelectEnabled = false; - public boolean isLongPressSelectEnabled = true; public List data = null; public List highlightData = null; public List iconsForData; @@ -95,7 +96,7 @@ public static class DialogOptions { public int searchInputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; public GsCallback.a1 highlighter = null; public String extraFilter = null; - public List preSelected = null; + public Collection preSelected = null; public GsCallback.a1 neutralButtonCallback = null; public GsCallback.b2 searchFunction = GsSearchOrCustomTextDialog::standardSearch; public GsCallback.a1 dismissCallback = null; @@ -242,13 +243,18 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi // Constructing the dialog // ========================================================================================= - if (dopt.titleText != 0 || !TextUtils.isEmpty(dopt.messageText)) { + final View selectAll; + if (dopt.titleText != 0 || !TextUtils.isEmpty(dopt.messageText) || dopt.isMultiSelectEnabled) { // Using a custom view for title and message. // This is needed because: // 1. https://stackoverflow.com/questions/61339887/alertdialog-doesnt-fit-long-list-view-buttons-if-used-together // 2. In order to control spacing // And is much less hacky than the other approaches - dialogBuilder.setCustomTitle(makeTitleView(activity, dopt)); + final View title = makeTitleView(activity, dopt); + dialogBuilder.setCustomTitle(title); + selectAll = title.findViewWithTag("SELECT_ALL"); + } else { + selectAll = null; } final LinearLayout mainLayout = new LinearLayout(activity); @@ -259,6 +265,7 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi final EditText searchEditText = searchView.findViewWithTag("EDIT"); searchEditText.addTextChangedListener(GsTextWatcherAdapter.after(listAdapter::filter)); + if (dopt.isSearchEnabled) { mainLayout.addView(searchView); } @@ -374,6 +381,34 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi // Set ok button text initially setOkButtonState.callback(); + final GsCallback.a0 setSelectAllButtonState = () -> { + if (selectAll != null) { + if (listAdapter._selectedItems.size() < dopt.data.size()) { + selectAll.setContentDescription(activity.getString(R.string.select_all)); + ((Checkable) selectAll).setChecked(false); + } else { + selectAll.setContentDescription(activity.getString(R.string.clear_selection)); + ((Checkable) selectAll).setChecked(true); + } + } + }; + + // 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())); + } else { + listAdapter._selectedItems.clear(); + } + listAdapter.notifyDataSetChanged(); + setOkButtonState.callback(); + setSelectAllButtonState.callback(); + }); + } + // Item click action listView.setOnItemClickListener((parent, textView, pos, id) -> { if (dopt.isMultiSelectEnabled) { @@ -387,25 +422,37 @@ public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activi ((Checkable) textView).setChecked(listAdapter._selectedItems.contains(index)); } setOkButtonState.callback(); + setSelectAllButtonState.callback(); } else { directActivate.callback(pos); } }); - if (dopt.isLongPressSelectEnabled) { - listView.setOnItemLongClickListener((parent, view, pos, id) -> directActivate.callback(pos)); - } + listView.setOnItemLongClickListener((parent, view, pos, id) -> directActivate.callback(pos)); } private static View makeTitleView(final Context context, final DialogOptions dopt) { - final int paddingSide = GsContextUtils.instance.convertDpToPx(context, 16); - final int paddingBetween = GsContextUtils.instance.convertDpToPx(context, 4); + // We nest the title layout within a horizontal layout so we can add a select all button + // We return the horizontal layout so that the select all button can be found by tag + final int padding = GsContextUtils.instance.convertDpToPx(context, 4); final LinearLayout titleLayout = new LinearLayout(context); - titleLayout.setOrientation(LinearLayout.VERTICAL); - titleLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + titleLayout.setOrientation(LinearLayout.HORIZONTAL); + titleLayout.setPadding(4 * padding, 2 * padding, 4 * padding, padding); + titleLayout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT) + ); titleLayout.setGravity(Gravity.CENTER_VERTICAL | Gravity.START); - titleLayout.setPadding(paddingSide, 2 * paddingBetween, paddingSide, paddingBetween); + + final LinearLayout titleTextLayout = new LinearLayout(context); + titleTextLayout.setOrientation(LinearLayout.VERTICAL); + titleTextLayout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT, 5) + ); + titleLayout.addView(titleTextLayout); + if (dopt.titleText != 0) { final TextView title = new TextView(context, null, android.R.attr.windowTitleStyle); @@ -413,16 +460,39 @@ private static View makeTitleView(final Context context, final DialogOptions dop title.setEllipsize(TextUtils.TruncateAt.END); title.setText(dopt.titleText); title.setPadding(0, 0, 0, 0); - titleLayout.addView(title, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + titleTextLayout.addView(title, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); } if (!TextUtils.isEmpty(dopt.messageText)) { final TextView subTitle = new TextView(context, null, android.R.attr.textAppearanceMedium); - subTitle.setPadding(0, dopt.titleText == 0 ? 0 : paddingBetween, 0, 0); + subTitle.setPadding(0, dopt.titleText == 0 ? 0 : padding, 0, 0); subTitle.setText(dopt.messageText); subTitle.setTextIsSelectable(true); subTitle.setMovementMethod(LinkMovementMethod.getInstance()); // Allow links to be shown and followed - titleLayout.addView(subTitle, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + titleTextLayout.addView(subTitle, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + } + + if (dopt.isMultiSelectEnabled) { + // 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); + final TextView selectAll = (TextView) inflater.inflate(android.R.layout.simple_list_item_multiple_choice, titleLayout, false); + selectAll.setTag("SELECT_ALL"); + selectAll.setText(""); + TooltipCompat.setTooltipText(selectAll, context.getString(R.string.select_all)); + // Remove padding to right to help align it + titleLayout.setPadding(titleLayout.getPaddingLeft(), titleLayout.getPaddingTop(), 0, titleLayout.getPaddingBottom()); + + final LinearLayout.LayoutParams selLp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.MATCH_PARENT, 0); + selLp.gravity = Gravity.END | Gravity.CENTER_VERTICAL; + + titleLayout.addView(selectAll, selLp); } return titleLayout; diff --git a/app/src/main/java/net/gsantner/opoc/frontend/base/GsFragmentBase.java b/app/src/main/java/net/gsantner/opoc/frontend/base/GsFragmentBase.java index f1e0561f80..51267101be 100644 --- a/app/src/main/java/net/gsantner/opoc/frontend/base/GsFragmentBase.java +++ b/app/src/main/java/net/gsantner/opoc/frontend/base/GsFragmentBase.java @@ -13,6 +13,7 @@ import android.content.Context; import android.os.Bundle; import android.util.Log; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -205,6 +206,10 @@ protected Toolbar getToolbar() { } } + public boolean onReceiveKeyPress(int keyCode, KeyEvent event) { + return false; + } + /** * Called when on @{{@link Toolbar#setOnClickListener(View.OnClickListener)}} click * 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 275af85d14..054c2c4388 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 @@ -55,6 +55,8 @@ import java.io.File; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -231,28 +233,24 @@ public void onFsViewerDoUiUpdate(GsFileBrowserListAdapter adapter) { private void updateMenuItems() { final String curFilepath = (getCurrentFolder() != null ? getCurrentFolder() : new File("/")).getAbsolutePath(); - final boolean selMulti1 = _dopt.doSelectMultiple && _filesystemViewerAdapter.getCurrentSelection().size() == 1; - final boolean selMultiMore = _dopt.doSelectMultiple && _filesystemViewerAdapter.getCurrentSelection().size() > 1; - final boolean selFilesOnly = _filesystemViewerAdapter.isFilesOnlySelected(); final Set selFiles = _filesystemViewerAdapter.getCurrentSelection(); + final int selCount = selFiles.size(); + final boolean selMulti1 = _dopt.doSelectMultiple && selCount == 1; + final boolean selMultiMore = _dopt.doSelectMultiple && selCount > 1; + final boolean selMultiAny = selMultiMore || selMulti1; + final boolean selFilesOnly = _filesystemViewerAdapter.isFilesOnlySelected(); // Check if is a favourite - boolean isFavourite = false; boolean selTextFilesOnly = true; boolean selDirectoriesOnly = true; boolean selWritable = (!curFilepath.equals("/storage") && !curFilepath.equals("/storage/emulated")); - if (selMulti1) { - for (File favourite : _dopt.favouriteFiles == null ? new ArrayList() : _dopt.favouriteFiles) { - if (selFiles.contains(favourite)) { - isFavourite = true; - break; - } - } - } - for (final File f : _filesystemViewerAdapter.getCurrentSelection()) { + boolean allSelectedFav = true; + final Collection favFiles = _dopt.favouriteFiles != null ? _dopt.favouriteFiles : Collections.emptySet(); + for (final File f : selFiles) { selTextFilesOnly &= FormatRegistry.isFileSupported(f, true); selWritable &= f.canWrite(); selDirectoriesOnly &= f.isDirectory(); + allSelectedFav &= favFiles.contains(f); } if (_fragmentMenu != null && _fragmentMenu.findItem(R.id.action_delete_selected_items) != null) { @@ -267,8 +265,8 @@ private void updateMenuItems() { _fragmentMenu.findItem(R.id.action_sort).setVisible(!_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(selMulti1 && !isFavourite); - _fragmentMenu.findItem(R.id.action_favourite_remove).setVisible(selMulti1 && isFavourite); + _fragmentMenu.findItem(R.id.action_favourite).setVisible(selMultiAny && !allSelectedFav); + _fragmentMenu.findItem(R.id.action_favourite_remove).setVisible(selMultiAny && allSelectedFav); _fragmentMenu.findItem(R.id.action_fs_copy_to_clipboard).setVisible(selMulti1 && selTextFilesOnly); _fragmentMenu.findItem(R.id.action_create_shortcut).setVisible(selMulti1 && (selFilesOnly || selDirectoriesOnly)); } @@ -291,7 +289,6 @@ public boolean onBackPressed() { } public void reloadCurrentFolder() { - _filesystemViewerAdapter.unselectAll(); _filesystemViewerAdapter.reloadCurrentFolder(); onFsViewerDoUiUpdate(_filesystemViewerAdapter); } @@ -385,12 +382,13 @@ public GsFileBrowserListAdapter getAdapter() { } @Override - public boolean onOptionsItemSelected(MenuItem item) { + public boolean onOptionsItemSelected(final MenuItem item) { final int _id = item.getItemId(); + final Set currentSelection = _filesystemViewerAdapter.getCurrentSelection(); switch (_id) { case R.id.action_create_shortcut: { - final File file = _filesystemViewerAdapter.getCurrentSelection().iterator().next(); + final File file = currentSelection.iterator().next(); _cu.createLauncherDesktopShortcut(getContext(), file); return true; } @@ -458,11 +456,22 @@ public boolean onOptionsItemSelected(MenuItem item) { Toast.makeText(getContext(), folder.getAbsolutePath(), Toast.LENGTH_SHORT).show(); return true; } - case R.id.action_favourite: + case R.id.action_favourite: { + if (_filesystemViewerAdapter.areItemsSelected()) { + final Set favs = _appSettings.getFavouriteFiles(); + favs.addAll(currentSelection); + _appSettings.setFavouriteFiles(favs); + _dopt.favouriteFiles = favs; + updateMenuItems(); + } + return true; + } case R.id.action_favourite_remove: { if (_filesystemViewerAdapter.areItemsSelected()) { - _appSettings.toggleFavouriteFile(new ArrayList<>(_filesystemViewerAdapter.getCurrentSelection()).get(0)); - _dopt.favouriteFiles = _appSettings.getFavouriteFiles(); + final Set favs = _appSettings.getFavouriteFiles(); + favs.removeAll(currentSelection); + _appSettings.setFavouriteFiles(favs); + _dopt.favouriteFiles = favs; updateMenuItems(); } return true; @@ -471,9 +480,8 @@ public boolean onOptionsItemSelected(MenuItem item) { askForDeletingFilesRecursive((confirmed, data) -> { if (confirmed) { Runnable deleter = () -> { - WrMarkorSingleton.getInstance().deleteSelectedItems(_filesystemViewerAdapter.getCurrentSelection(), getContext()); + WrMarkorSingleton.getInstance().deleteSelectedItems(currentSelection, getContext()); _recyclerList.post(() -> { - _filesystemViewerAdapter.unselectAll(); _filesystemViewerAdapter.reloadCurrentFolder(); }); }; @@ -496,15 +504,14 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.action_share_files: { MarkorContextUtils s = new MarkorContextUtils(getContext()); - s.shareStreamMultiple(getContext(), _filesystemViewerAdapter.getCurrentSelection(), "*/*"); - _filesystemViewerAdapter.unselectAll(); + s.shareStreamMultiple(getContext(), currentSelection, "*/*"); _filesystemViewerAdapter.reloadCurrentFolder(); return true; } case R.id.action_info_selected_item: { if (_filesystemViewerAdapter.areItemsSelected()) { - File file = new ArrayList<>(_filesystemViewerAdapter.getCurrentSelection()).get(0); + File file = new ArrayList<>(currentSelection).get(0); FileInfoDialog.show(file, getChildFragmentManager()); } return true; @@ -512,8 +519,8 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.action_rename_selected_item: { if (_filesystemViewerAdapter.areItemsSelected()) { - File file = new ArrayList<>(_filesystemViewerAdapter.getCurrentSelection()).get(0); - WrRenameDialog renameDialog = WrRenameDialog.newInstance(file, renamedFile -> reloadCurrentFolder()); + final File file = currentSelection.iterator().next(); + final WrRenameDialog renameDialog = WrRenameDialog.newInstance(file, renamedFile -> reloadCurrentFolder()); renameDialog.show(getChildFragmentManager(), WrRenameDialog.FRAGMENT_TAG); } return true; @@ -521,11 +528,10 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.action_fs_copy_to_clipboard: { if (_filesystemViewerAdapter.areItemsSelected()) { - File file = new ArrayList<>(_filesystemViewerAdapter.getCurrentSelection()).get(0); + final File file = new ArrayList<>(currentSelection).get(0); if (FormatRegistry.isFileSupported(file, true)) { _cu.setClipboard(getContext(), GsFileUtils.readTextFileFast(file).first); Toast.makeText(getContext(), R.string.clipboard, Toast.LENGTH_SHORT).show(); - _filesystemViewerAdapter.unselectAll(); } } return true; 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 23b660f1a7..c5af2bcafb 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 @@ -90,7 +90,6 @@ public GsFileBrowserListAdapter(GsFileBrowserOptions.Options options, Context co _adapterDataFiltered = new ArrayList<>(); _currentSelection = new HashSet<>(); _context = context; - loadFolder((options.startFolder != null) ? options.startFolder : options.rootFolder); _prefApp = _context.getSharedPreferences("app", Context.MODE_PRIVATE); // Prevents view flicker - https://stackoverflow.com/a/32488059 @@ -267,16 +266,7 @@ public void reconfigure() { } public boolean isCurrentFolderVirtual() { - return _currentFolder != null && ( - _currentFolder.equals(VIRTUAL_STORAGE_APP_DATA_PRIVATE) - || _currentFolder.equals(VIRTUAL_STORAGE_FAVOURITE) - || _currentFolder.equals(VIRTUAL_STORAGE_POPULAR) - || _currentFolder.equals(VIRTUAL_STORAGE_RECENTS) - || _currentFolder.equals(new File("/")) - || _currentFolder.equals(new File("/storage")) - || _currentFolder.equals(new File("/storage/self")) - || _currentFolder.equals(new File("/storage/emulated")) - ); + return isVirtualFolder(_currentFolder); } public static class TagContainer { @@ -348,7 +338,6 @@ public void onClick(View view) { return; } case R.id.ui__filesystem_dialog__home: { - _currentSelection.clear(); loadFolder(_dopt.rootFolder); return; } @@ -386,7 +375,7 @@ public void selectAll() { public void unselectAll() { for (int i = 0; i < _adapterDataFiltered.size(); i++) { - TagContainer data = new TagContainer(_adapterDataFiltered.get(i), i); + final TagContainer data = new TagContainer(_adapterDataFiltered.get(i), i); if (_currentSelection.contains(data.file)) { _currentSelection.remove(data.file); notifyItemChanged(data.position); @@ -412,7 +401,7 @@ public boolean isFilesOnlySelected() { return true; } - public boolean toggleSelection(TagContainer data) { + public boolean toggleSelection(final TagContainer data) { boolean clickHandled = false; if (data != null && data.file != null && _currentFolder != null) { if (data.file.isDirectory() && _currentFolder.getParentFile() != null && _currentFolder.getParentFile().equals(data.file)) { @@ -464,10 +453,10 @@ public boolean canGoUp(File currentFolder) { } @Override - public boolean onLongClick(View view) { + public boolean onLongClick(final View view) { switch (view.getId()) { case R.id.opoc_filesystem_item__root: { - TagContainer data = (TagContainer) view.getTag(); + final TagContainer data = (TagContainer) view.getTag(); toggleSelection(data); _dopt.listener.onFsViewerItemLongPressed(data.file, _dopt.doSelectMultiple); return true; @@ -570,112 +559,101 @@ public int getFilePosition(final File file) { private void loadFolder(final File folder) { - final Handler handler = new Handler(); - _currentSelection.clear(); - - new Thread() { - @Override - public void run() { - synchronized (LOAD_FOLDER_SYNC_OBJECT) { - - _currentFolder = folder; - _virtualMapping.clear(); - - final List newData = new ArrayList<>(); - if (_currentFolder.isDirectory()) { - GsCollectionUtils.addAll(newData, _currentFolder.listFiles(GsFileBrowserListAdapter.this)); - } else if (_currentFolder.equals(VIRTUAL_STORAGE_RECENTS)) { - newData.addAll(_dopt.recentFiles); - } else if (_currentFolder.equals(VIRTUAL_STORAGE_POPULAR)) { - newData.addAll(_dopt.popularFiles); - } else if (_currentFolder.equals(VIRTUAL_STORAGE_FAVOURITE)) { - GsCollectionUtils.addAll(newData, _dopt.favouriteFiles); + final Handler handler = _recyclerView != null ? _recyclerView.getHandler() : new Handler(); + + new Thread(() -> { + synchronized (LOAD_FOLDER_SYNC_OBJECT) { + + _currentFolder = folder; + _virtualMapping.clear(); + final List newData = new ArrayList<>(); + + if (_currentFolder.isDirectory()) { + GsCollectionUtils.addAll(newData, _currentFolder.listFiles(GsFileBrowserListAdapter.this)); + } else if (_currentFolder.equals(VIRTUAL_STORAGE_RECENTS)) { + newData.addAll(_dopt.recentFiles); + } else if (_currentFolder.equals(VIRTUAL_STORAGE_POPULAR)) { + newData.addAll(_dopt.popularFiles); + } else if (_currentFolder.equals(VIRTUAL_STORAGE_FAVOURITE)) { + GsCollectionUtils.addAll(newData, _dopt.favouriteFiles); + } else if (folder.getAbsolutePath().equals("/storage/emulated")) { + newData.add(new File(folder, "0")); + } else if (folder.getAbsolutePath().equals("/")) { + newData.add(new File(folder, "storage")); + } else if (folder.equals(_context.getFilesDir().getParentFile())) { + // Private AppStorage: Allow to access to files directory only + // (don't allow access to internals like shared_preferences & databases) + newData.add(new File(folder, "files")); + } else if (folder.getAbsolutePath().equals("/storage")) { + // Scan for /storage/emulated/{0,1,2,..} + for (int i = 0; i < 10; i++) { + final File file = new File("/storage/emulated/" + i); + if (canWrite(file)) { + File remap = new File(folder, "emulated-" + i); + _virtualMapping.put(remap, file); + newData.add(remap); + } else { + break; + } } - if (folder.getAbsolutePath().equals("/storage/emulated")) { - newData.add(new File(folder, "0")); + if (_dopt.recentFiles != null) { + _virtualMapping.put(VIRTUAL_STORAGE_RECENTS, VIRTUAL_STORAGE_RECENTS); + newData.add(VIRTUAL_STORAGE_RECENTS); } - - if (folder.getAbsolutePath().equals("/")) { - newData.add(new File(folder, "storage")); + if (_dopt.popularFiles != null) { + _virtualMapping.put(VIRTUAL_STORAGE_POPULAR, VIRTUAL_STORAGE_POPULAR); + newData.add(VIRTUAL_STORAGE_POPULAR); } - - // Private AppStorage: Allow to access to files directory only (don't allow access to internals like shared_preferences & databases) - if (folder.equals(_context.getFilesDir().getParentFile())) { - newData.clear(); - newData.add(new File(folder, "files")); + if (_dopt.favouriteFiles != null) { + _virtualMapping.put(VIRTUAL_STORAGE_FAVOURITE, VIRTUAL_STORAGE_FAVOURITE); + newData.add(VIRTUAL_STORAGE_FAVOURITE); } - - if (folder.getAbsolutePath().equals("/storage")) { - // Scan for /storage/emulated/{0,1,2,..} - for (int i = 0; i < 10; i++) { - final File file = new File("/storage/emulated/" + i); - if (canWrite(file)) { - File remap = new File(folder, "emulated-" + i); - _virtualMapping.put(remap, file); - newData.add(remap); - } else { - break; - } - } - - if (_dopt.recentFiles != null) { - _virtualMapping.put(VIRTUAL_STORAGE_RECENTS, VIRTUAL_STORAGE_RECENTS); - newData.add(VIRTUAL_STORAGE_RECENTS); - } - if (_dopt.popularFiles != null) { - _virtualMapping.put(VIRTUAL_STORAGE_POPULAR, VIRTUAL_STORAGE_POPULAR); - newData.add(VIRTUAL_STORAGE_POPULAR); - } - if (_dopt.favouriteFiles != null) { - _virtualMapping.put(VIRTUAL_STORAGE_FAVOURITE, VIRTUAL_STORAGE_FAVOURITE); - newData.add(VIRTUAL_STORAGE_FAVOURITE); - } - File appDataFolder = _context.getFilesDir(); - if (appDataFolder.exists() || (!appDataFolder.exists() && appDataFolder.mkdir())) { - _virtualMapping.put(VIRTUAL_STORAGE_APP_DATA_PRIVATE, appDataFolder); - newData.add(VIRTUAL_STORAGE_APP_DATA_PRIVATE); - } + File appDataFolder = _context.getFilesDir(); + if (appDataFolder.exists() || (!appDataFolder.exists() && appDataFolder.mkdir())) { + _virtualMapping.put(VIRTUAL_STORAGE_APP_DATA_PRIVATE, appDataFolder); + newData.add(VIRTUAL_STORAGE_APP_DATA_PRIVATE); } + } - for (final File externalFileDir : ContextCompat.getExternalFilesDirs(_context, null)) { - for (int i = 0; i < newData.size(); i++) { - final File file = newData.get(i); - if (!canWrite(file) && !file.getAbsolutePath().equals("/") && externalFileDir != null && externalFileDir.getAbsolutePath().startsWith(file.getAbsolutePath())) { - final int depth = TextViewUtils.countChars(file.getAbsolutePath(), '/')[0]; - if (depth < 3) { - final File remap = new File(file.getParentFile().getAbsolutePath(), "appdata-public (" + file.getName() + ")"); - _virtualMapping.put(remap, new File(externalFileDir.getAbsolutePath())); - newData.add(remap); - } + for (final File externalFileDir : ContextCompat.getExternalFilesDirs(_context, null)) { + for (int i = 0; i < newData.size(); i++) { + final File file = newData.get(i); + if (!canWrite(file) && !file.getAbsolutePath().equals("/") && externalFileDir != null && externalFileDir.getAbsolutePath().startsWith(file.getAbsolutePath())) { + final int depth = TextViewUtils.countChars(file.getAbsolutePath(), '/')[0]; + if (depth < 3) { + final File remap = new File(file.getParentFile().getAbsolutePath(), "appdata-public (" + file.getName() + ")"); + _virtualMapping.put(remap, new File(externalFileDir.getAbsolutePath())); + newData.add(remap); } } } + } - // Don't sort recents - use the default order - if (!_currentFolder.equals(VIRTUAL_STORAGE_RECENTS)) { - GsFileUtils.sortFiles(newData, _dopt.sortByType, _dopt.sortFolderFirst, _dopt.sortReverse); - } + // Don't sort recents - use the default order + if (!_currentFolder.equals(VIRTUAL_STORAGE_RECENTS)) { + GsFileUtils.sortFiles(newData, _dopt.sortByType, _dopt.sortFolderFirst, _dopt.sortReverse); + } - if (canGoUp(_currentFolder)) { - newData.add(0, _currentFolder.equals(new File("/storage/emulated/0")) ? new File("/storage/emulated") : _currentFolder.getParentFile()); - } + if (canGoUp(_currentFolder)) { + newData.add(0, _currentFolder.equals(new File("/storage/emulated/0")) ? new File("/storage/emulated") : _currentFolder.getParentFile()); + } - if (!newData.equals(_adapterData)) { - _adapterData.clear(); - _adapterData.addAll(newData); - handler.post(() -> { - _filter.filter(_filter._lastFilter); - // TODO - add logic to notify the changed bits - notifyDataSetChanged(); - if (_dopt.listener != null) { - _dopt.listener.onFsViewerDoUiUpdate(GsFileBrowserListAdapter.this); - } - }); - } + if (!newData.equals(_adapterData)) { + _adapterData.clear(); + _adapterData.addAll(newData); + _currentSelection.retainAll(_adapterData); + handler.post(() -> { + _filter.filter(_filter._lastFilter); + // TODO - add logic to notify the changed bits + notifyDataSetChanged(); + if (_dopt.listener != null) { + _dopt.listener.onFsViewerDoUiUpdate(GsFileBrowserListAdapter.this); + } + }); } } - }.start(); + }).start(); } private boolean canWrite(File file) { @@ -786,4 +764,17 @@ static class FilesystemViewerViewHolder extends RecyclerView.ViewHolder { description = row.findViewById(R.id.opoc_filesystem_item__description); } } + + public static boolean isVirtualFolder(final File file) { + return file != null && ( + file.equals(VIRTUAL_STORAGE_APP_DATA_PRIVATE) || + file.equals(VIRTUAL_STORAGE_FAVOURITE) || + file.equals(VIRTUAL_STORAGE_POPULAR) || + file.equals(VIRTUAL_STORAGE_RECENTS) || + file.equals(new File("/")) || + file.equals(new File("/storage")) || + file.equals(new File("/storage/self")) || + file.equals(new File("/storage/emulated")) + ); + } } 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 518ed98577..e7d6feb25c 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 @@ -21,6 +21,7 @@ import java.io.File; import java.io.Serializable; +import java.util.Collection; import java.util.List; @SuppressWarnings({"unused", "WeakerAccess"}) @@ -118,7 +119,7 @@ public static class Options { @ColorRes public int titleTextColor = 0; - public List favouriteFiles, recentFiles, popularFiles = null; + public Collection favouriteFiles, recentFiles, popularFiles = null; } public static class SelectionListenerAdapter implements SelectionListener, Serializable { 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 c201ee59fb..a106a953b2 100644 --- a/app/src/main/java/net/gsantner/opoc/util/GsContextUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/GsContextUtils.java @@ -175,8 +175,8 @@ protected T thisp() { //######################## @SuppressLint("ConstantLocale") public final static Locale INITIAL_LOCALE = Locale.getDefault(); - public final static String EXTRA_FILEPATH = "real_file_path_2"; - public final static String EXTRA_URI = "real_uri_0"; + public final static String EXTRA_FILEPATH = "EXTRA_FILEPATH"; + public final static String EXTRA_URI = "EXTRA_URI"; public final static SimpleDateFormat DATEFORMAT_RFC3339ISH = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", INITIAL_LOCALE); public final static String MIME_TEXT_PLAIN = "text/plain"; public final static String PREF_KEY__SAF_TREE_URI = "pref_key__saf_tree_uri"; @@ -1337,7 +1337,7 @@ public boolean viewFileInOtherApp(final Context context, final File file, @Nulla intent.putExtra(EXTRA_FILEPATH, file.getAbsolutePath()); intent.putExtra(Intent.EXTRA_TITLE, file.getName()); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - showChooser(context, intent, null); + startActivity(context, intent); return true; } return false; 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 1353f5b33a..1cab913585 100644 --- a/app/src/main/java/net/gsantner/opoc/util/GsFileUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/GsFileUtils.java @@ -710,7 +710,7 @@ private static String makeSortKey(final String sortBy, final File file) { return file.lastModified() + name; } case SORT_BY_FILESIZE: { - return file.length() + name; + return String.format("%015d", file.length()) + name; } case SORT_BY_MIMETYPE: { return getMimeType(file).toLowerCase() + name; diff --git a/app/src/main/res/menu/document__edit__menu.xml b/app/src/main/res/menu/document__edit__menu.xml index 0a82222a71..2f360d8e96 100644 --- a/app/src/main/res/menu/document__edit__menu.xml +++ b/app/src/main/res/menu/document__edit__menu.xml @@ -161,6 +161,10 @@ android:title="@string/share" app:showAsAction="never"> + + . إنشاء مجلد إنشاء ملف جديد نقل - نسخ معلومات الملف/المجلد موجود مسبقاً! الرجاء استخدام اسم مختلف استيراد من الجهاز diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index e4c9eb646c..2de1eb5e68 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -27,7 +27,6 @@ work. If not, see . ফোল্ডার তৈরি করুন নতুন ডকুমেন্ট তৈরি করুন সরান - Ftt তথ্য ফাইল/ফোল্ডার আগেই আছে! অনুগ্রহ করে অন্য নাম ব্যবহার করো ডিভাইস থেকে আমদানি করো diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index f7e556e5e6..8b0008b0d4 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Kreiraj direktorij Kreiraj novi dokument Premjesti - Kopiraj Informacija Datoteka/direktorij već postoji! Molimo odaberite drugi naziv Prenesi sa uređaja diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 3cb0ad924c..c4a1573401 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Crea una carpeta Crea un nou document Mou - Copia Informació El fitxer/carpeta ja existeix. Utilitzeu un nom diferent Importa des d\'un dispositiu @@ -60,6 +59,7 @@ work. If not, see . Última modificació Selecciona aquesta carpeta Selecciona + Selecciona-ho tot Crea Selecciona les entrades Un element seleccionat diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml index 0846e83610..1b5e6bd8a8 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -27,7 +27,6 @@ work. If not, see . دروستکردنی فۆڵدەر دروستکردنی بەڵگەنامەی نوێ گواستنەوە - لەبەرگرتنەوە زانیاری فایل/فۆڵدەر بوونی هەیە! تکایە ناوێکی جیاواز بەکاربێنە هێنان لە ئامێرەوە diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 74272f52d4..d1d6d23618 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Vytvořit složku Vytvořit nový dokument Přesunout - Kopírovat Informace Soubor/složka již existuje, prosím, použijte jiné jméno Import ze zařízení diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index a54a102fc5..cc12b787fb 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Opret mappe Opret nyt dokument Flyt - Kopiér Info Fil/mappe findes allerede! Brug venligst et andet navn Importér fra enhed diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e3c8cbf8e9..212e380970 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Ordner erstellen Neues Dokument erstellen Verschieben - Kopieren Info Datei/Ordner existiert bereits! Verwende bitte einen anderen Namen Von Gerät importieren diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index b6a7cc5030..74691d5e4d 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Δημιουργία Φακέλου Δημιουργία νέου εγγράφου Μετακίνηση - Αντιγραφή Πληροφορίες Το αρχείο/φάκελος υπάρχει ήδη! Παρακαλούμε χρησιμοποιήστε ένα διαφορετικό όνομα Εισαγωγή από τη συσκευή diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 07877a1ef5..8fa89f6113 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -25,7 +25,6 @@ work. If not, see . Krei Dosierujon Krei novan dokumenton Movi - Kopii Informo Dosiero/dosierujo jam ekzistas! Bonvolu uzi malsaman nomon Enporti el aparato diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 9522a7d33b..41164e8622 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Crear carpeta Crear nuevo documento Mover - Copiar Información ¡El archivo/carpeta ya existe! Por favor, usa un nombre diferente Importar desde dispositivo @@ -60,6 +59,7 @@ work. If not, see . Última modificación Seleccionar esta carpeta Seleccionar + Seleccionar todo Crear Seleccionar entradas Un elemento seleccionado @@ -442,8 +442,11 @@ work. If not, see . Para seguir los enlaces de la wiki, busque dinámicamente el archivo notebook.zim para identificar el directorio raíz del cuaderno actual. Marque esta opción si utiliza múltiples cuadernos zim o el directorio de cuadernos establecido en la aplicación no corresponde a su cuaderno zim. Formato/prefijo para el texto compartido en Markor. Sigue las reglas de SimpleDateFormat.\n\nUtilice el marcador de posición \'{{text}}\' (rodeado por comillas simples) para colocar el texto compartido. Si no se especifica, el texto compartido es añadido y el formato utilizado es el de prefijo. Compartir en - Formato + Nombre de la carpeta adjunta + Subcarpeta debajo de la carpeta actual donde se guardarán los accesorios. Si se deja en blanco, los accesorios se guardarán en la carpeta actual. Auto-formato Insertar fragmento + Directorio de fragmentos/plantillas Error: No se pudo abrir el archivo. Error encontrado: Texto copiado al portapapeles. Archivos con menos de %d caracteres no guardados automáticamente, para prevenir la perdida de datos. diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 3abc439b6f..a5c91f9891 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Loo kaust Loo uus kirje Teisalda - Kopeeri Teave Kirje/kaust juba leidub! Palun pane erinev nimi Impordi seadmest @@ -60,6 +59,7 @@ work. If not, see . Viimati muudeti Vali see kaust Vali + Vali kõik Loo Vali sissekanded Üks asi valitud diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 8670f6f4fd..f04db389b9 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -27,7 +27,6 @@ work. If not, see . ایجاد پوشه ایجاد پرونده جدید بریدن - کپی اطلاعات پوشه وجود دارد! لطفا از نام دیگری استفاده کنید درون‌ریزی از دستگاه diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 11a6cdaca3..7bbd299a82 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Luo kansio Luo uusi asiakirja Siirrä - Kopioi Tiedot Tiedosto / kansio on jo olemassa! Käytä toista nimeä Tuo laitteelta diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml index ba5d5d81d2..ba267929bc 100644 --- a/app/src/main/res/values-fil/strings.xml +++ b/app/src/main/res/values-fil/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Gumawa ng Folder Gumawa ng bagong dokumento Ilipat - Kopyahin Impormasyon Mayroong nang ganitong file/folder! Mangyaring gumamit ng ibang pangalan Kumuha mula sa device diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 70696e1ea0..61d39c6300 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Nouveau dossier Créer un nouveau document Déplacer - Copie Info Le fichier/dossier existe déjà ! Veuillez utiliser un nom différent Importer depuis cet appareil diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index b8ecd5983c..23f567ae69 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Crear cartafol Crear un novo Documento Mover - Copiar Información Importar dun dispositivo Importar diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index f0fe7a3ca8..fb5faedd93 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -27,7 +27,6 @@ work. If not, see . फोल्डर बनाएं नया दस्तावेज़ बनाएं स्थान-परिवर्तन - प्रतिलिपि बनाएँ जानकारी फाइल या फोल्डर पहले से उप्ल्व्द उप्ल्व्द है! दूसरा नाम सोचे डिवाइस से आयात करें diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 09c49c148c..576614a453 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Mappa létrehozása Dokumentum létrehozása Mozgatás - Másolás Infó A fájl már létezik! Változtasd meg a nevet Importálás az eszközről diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index d734835086..45e6973caa 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Buat Folder Buat dokumen baru Pindahkan - Salin Informasi File/folder sudah ada! Silahkan menggunakan nama lain Impor dari perangkat diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 042385ce0d..75c0e0e6bf 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Crea una nuova cartella Crea un nuovo documento Sposta - Copia Informazioni File/cartella già esistente! Si prega di usare un nome diverso Importa dal dispositivo @@ -60,6 +59,7 @@ work. If not, see . Ultima modifica Seleziona questa cartella Seleziona + Seleziona tutto Crea Seleziona elementi Un elemento selezionato diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index f7d32d94d5..9275bb3f04 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -27,7 +27,6 @@ work. If not, see . צור תיקייה צור מסמך חדש הזז - להעתיק מידע הקובץ/תיקיה כבר קיים! נא לבחור שם אחר ייבא מהמכשיר diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index e2733f84bc..aa1936f9e1 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -27,7 +27,6 @@ work. If not, see . フォルダーを作成 新しいドキュメントを作成 移動 - コピー 情報 ファイルまたはフォルダ名はすでに存在します。別の名前を使用してください。 デバイスからインポート diff --git a/app/src/main/res/values-kmr/strings.xml b/app/src/main/res/values-kmr/strings.xml index 0861dcd704..3db2bfd2f8 100644 --- a/app/src/main/res/values-kmr/strings.xml +++ b/app/src/main/res/values-kmr/strings.xml @@ -19,7 +19,6 @@ work. If not, see . Jêbirinê piştrast bike Sernav Barkirin - Kopî Agahî Têxistin Peldankê hilbijêre diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index e48f89f9fc..d8d6e77fe6 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Создај директориум Создај нов документ Премести - Копирај Инфо Фајл/директориум веќе постои! Одберете друго име Увезете од други уреди diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 96e640a0e2..af189d546c 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Opprett mappe Opprett nytt dokument Flytt - Kopier Info Filen/mappen finnes allerede! Vennligst velg et annet navn Importer fra enhet diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 34680b1f18..741c21dbb3 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Maak folder Maak een nieuw document Verplaatsen - Kopiëren Info Bestand/map bestaat al! Gebruik een andere naam Importeer van apparaat diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 3e5db3868f..137b1d22dd 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Opprett mappe Opprett nytt dokument Flytt - Kopier Informasjon Filen/mappen finnes allerede! Bruk et annet navn Importer fra enhet diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index 6df49270b0..528b493645 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -27,7 +27,6 @@ work. If not, see . ଫୋଲ୍ଡର୍ ସୃଷ୍ଟି କରନ୍ତୁ ନୂତନ ଡକ୍ୟୁମେଣ୍ଟ୍ ସୃଷ୍ଟି କରନ୍ତୁ ଘୁଞ୍ଚାନ୍ତୁ - କପି କରନ୍ତୁ ସୂଚନା ଫାଇଲ୍/ଫୋଲ୍ଡର୍ ପୂର୍ବରୁ ବିଦ୍ୟମାନ ଅଛି! ଦୟାକରି ଏକ ଭିନ୍ନ ନାମ ବ୍ୟବହାର କରନ୍ତୁ ଡିଭାଇସରୁ ଆମଦାନୀ କରନ୍ତୁ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 73516d39bc..575340fb44 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Utwórz folder Utwórz nowy dokument Przenieś - Kopiuj Informacje Plik/katalog już istnieje! Proszę użyć innej nazwy Importuj z urządzenia diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 7bd57fad1f..19a3f31c14 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Criar pasta Criar novo documento Mover - Copiar Informações Arquivo/pasta já existe! Por favor use um nome diferente Importar do dispositivo diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 4b1fb15bce..185554c2fd 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Criar pasta Criar novo documento Mover - copiar Info Ficheiro/pasta já existe! Utilize um nome diferente. Importar do dispositivo @@ -60,6 +59,7 @@ work. If not, see . Última modificação Selecionar esta pasta Selecionar + Selecionar tudo Criar Selecionar entradas Um item selecionado diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index fadec1eeed..e1f455ec00 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Creați Folder Creați un document nou Mutați - Copiază Info Fișierul/directorul există deja! Te rugăm să folosești un nume diferit Importă de pe dispozitiv diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index dfe2bc6776..51815b750f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Создать папку Создать документ Переместить - Копировать Инфо Файл или папка уже существует! Используйте другое имя Импортировать с устройства @@ -60,6 +59,7 @@ work. If not, see . Был изменён Выбрать текущую папку Выбрать + Выбрать всё Создать Выбрать элементы Один элемент выбран @@ -199,7 +199,7 @@ work. If not, see . Файл или директория не существует и не может быть создан Разрешения Вернуть пути по умолчанию? - Разрешено не выдано + Разрешение не выдано Примечание: приложения синхронизации могут размещать файлы в пользовательских папках. Откройте их, соответственно изменяя папку документов Маркора, или в другом менеджере файлов. Выравнивание по вертикали Выравнивание по горизонтали @@ -209,8 +209,8 @@ work. If not, see . По верхнему краю Снизу Центр - Интервал действия кнопки - Горизонтальное пространство между кнопками действий + Интервал между кнопками действий + Горизонтальный интервал между кнопками действий Центрировать текст Начинать редактирование текста по центру Буфер обмена @@ -445,8 +445,8 @@ work. If not, see . Название папки с вложением Подпапка в текущей папке, где будут храниться вложения. Если оставить пустым, то вложения будут храниться в текущей папке. Автоформат - Insert snippet - Директория сниппета / шаблона + Вставить фрагмент + Директория с фрагментами / шаблонами Ошибка: Не удалось открыть файл. Произошла ошибка: текст скопирован в буфер обмена. Файлы содержащие менее %d символов не сохраняются автоматически во избежание случайного удаления данных. diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 819ba1573e..ae7ff98dbe 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Crea una cartella Crea unu documentu nou Move - Còpia Informatziones Su documentu/sa cartella esistit giai! Pro praghere imprea unu nùmene diferente Importa dae su dispositivu @@ -60,6 +59,7 @@ work. If not, see . Ùrtima modìfica Ischerta custa cartella Ischerta + Seletziona totu Crea Ischerta sas intradas Un\'elementu ischertadu diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 4a4ce68498..cf934d0e78 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -27,7 +27,6 @@ work. If not, see . ෆෝල්ඩරය නිර්මාණය නව ලේඛනයක් සාදන්න චලනය කරන්න - පිටපත් කරන්න තොරතුරු ගොනුව/ෆෝල්ඩරය දැනටමත් පවතී! කරුණාකර වෙනත් නමක් භාවිතා කරන්න උපාංගයෙන් ආයාත කරන්න diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index e441b6acd2..4fd6846886 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Vytvoriť priečinok Vytvoriť nový dokument Presunúť - Kopírovať Informácie Súbor/priečinok už existuje. Prosím, použite iný názov Importovať zo zariadenia diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 8034182d3c..7de92a65c7 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Skapa mapp Skapa nytt dokument Flytta - Kopiera Information Fil/mapp finns redan! Använd ett annat namn Importera från enhet @@ -60,6 +59,7 @@ work. If not, see . Senast ändrad Välj den här mappen Välj + Välj alla Skapa Välj poster Ett objekt valt diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index bff5d5c96a..c6454de61c 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Klasör Oluştur Yeni belge oluştur Taşı - Kopyala Bilgi Dosya/dizin zaten var! Lütfen farklı bir ad kullanın Cihazdan içe aktar @@ -60,6 +59,7 @@ work. If not, see . Son düzenleme Bu klasörü seç Seç + Tümünü seç Oluştur Öğeleri seç Bir öğe seçildi diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index eb384ba242..4fc48ed87b 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Створити директорію Створити документ Перемістити - Копіювати Інформація Файл або директорія з таким іменем вже існує! Будь ласка, оберіть інше Імпортувати з пристрою diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 77b5d16c1f..c60f499b81 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -27,7 +27,6 @@ work. If not, see . Tạo thư mục Tạo tài liệu mới Di chuyển - Sao Chép Xem thông tin Trùng tên! Vui lòng sử dụng tên khác Nhập từ thiết bị diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 3f9d765198..318da60930 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -27,7 +27,6 @@ work. If not, see . 新建文件夹 新建文档 移动 - 复制 信息 已存在同名文件/文件夹!请换一个名字试试。 从本设备导入 @@ -60,6 +59,7 @@ work. If not, see . 最后修改 选择这个文件夹 选择 + 全选 新建 选择 输入文字 已选中一项 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index a45138959a..aa487260a2 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -27,7 +27,6 @@ work. If not, see . 建立資料夾 建立新的文件 移動 - 複製 資訊 文件/資料夾名稱已存在,請重新命名 從裝置匯入 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6657587164..a284216e81 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,8 +26,8 @@ work. If not, see . Create Folder Create new document Move - Copy Info + Show File/folder already exists! Please use a different name Import from device @@ -385,6 +385,7 @@ work. If not, see . Create a new file or folder Choose format + Clear selection Selected Back Expand / Collapse diff --git a/app/thirdparty/java/other/writeily/widget/WrFilesWidgetFactory.java b/app/thirdparty/java/other/writeily/widget/WrFilesWidgetFactory.java index c454fd36fa..3d04bd4c9b 100644 --- a/app/thirdparty/java/other/writeily/widget/WrFilesWidgetFactory.java +++ b/app/thirdparty/java/other/writeily/widget/WrFilesWidgetFactory.java @@ -83,7 +83,7 @@ public RemoteViews getViewAt(final int position) { rowView.setTextViewText(R.id.widget_note_title, "???"); if (position < _widgetFilesList.size()) { final File file = _widgetFilesList.get(position); - final Intent fillInIntent = new Intent().putExtra(Document.EXTRA_PATH, file); + final Intent fillInIntent = new Intent().putExtra(Document.EXTRA_FILE, file); rowView.setTextViewText(R.id.widget_note_title, file.getName()); rowView.setOnClickFillInIntent(R.id.widget_note_title, fillInIntent); final int icon = file.isDirectory() ? R.drawable.ic_folder_gray_24dp : R.drawable.ic_file_gray_24dp; diff --git a/app/thirdparty/java/other/writeily/widget/WrMarkorWidgetProvider.java b/app/thirdparty/java/other/writeily/widget/WrMarkorWidgetProvider.java index ef38250203..0d32779cb1 100644 --- a/app/thirdparty/java/other/writeily/widget/WrMarkorWidgetProvider.java +++ b/app/thirdparty/java/other/writeily/widget/WrMarkorWidgetProvider.java @@ -61,33 +61,33 @@ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] a // Open Folder final Intent goToFolder = new Intent(context, MainActivity.class) .setAction(Intent.ACTION_VIEW) - .putExtra(Document.EXTRA_PATH, directoryF); + .putExtra(Document.EXTRA_FILE, directoryF); views.setOnClickPendingIntent(R.id.widget_header, PendingIntent.getActivity(context, requestCode++, goToFolder, staticFlags)); // Open To-do final Intent openTodo = new Intent(context, OpenFromShortcutOrWidgetActivity.class) .setAction(Intent.ACTION_EDIT) - .putExtra(Document.EXTRA_PATH, appSettings.getTodoFile()) + .putExtra(Document.EXTRA_FILE, appSettings.getTodoFile()) .putExtra(Document.EXTRA_FILE_LINE_NUMBER, Document.EXTRA_FILE_LINE_NUMBER_LAST); views.setOnClickPendingIntent(R.id.widget_todo, PendingIntent.getActivity(context, requestCode++, openTodo, staticFlags)); // Open QuickNote final Intent openQuickNote = new Intent(context, OpenFromShortcutOrWidgetActivity.class) .setAction(Intent.ACTION_EDIT) - .putExtra(Document.EXTRA_PATH, appSettings.getQuickNoteFile()) + .putExtra(Document.EXTRA_FILE, appSettings.getQuickNoteFile()) .putExtra(Document.EXTRA_FILE_LINE_NUMBER, Document.EXTRA_FILE_LINE_NUMBER_LAST); views.setOnClickPendingIntent(R.id.widget_quicknote, PendingIntent.getActivity(context, requestCode++, openQuickNote, staticFlags)); // Open Notebook final Intent goHome = new Intent(context, MainActivity.class) .setAction(Intent.ACTION_VIEW) - .putExtra(Document.EXTRA_PATH, appSettings.getNotebookDirectory()); + .putExtra(Document.EXTRA_FILE, appSettings.getNotebookDirectory()); views.setOnClickPendingIntent(R.id.widget_main, PendingIntent.getActivity(context, requestCode++, goHome, staticFlags)); // ListView final Intent notesListIntent = new Intent(context, WrFilesWidgetService.class) .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) - .putExtra(Document.EXTRA_PATH, directoryF); + .putExtra(Document.EXTRA_FILE, directoryF); notesListIntent.setData(Uri.parse(notesListIntent.toUri(Intent.URI_INTENT_SCHEME))); views.setEmptyView(R.id.widget_list_container, R.id.widget_empty_hint);