diff --git a/app/src/main/java/eu/faircode/email/ApplicationEx.java b/app/src/main/java/eu/faircode/email/ApplicationEx.java index 3d26042d95..9911567e48 100644 --- a/app/src/main/java/eu/faircode/email/ApplicationEx.java +++ b/app/src/main/java/eu/faircode/email/ApplicationEx.java @@ -662,6 +662,12 @@ public class ApplicationEx extends Application else if (version < 1994) { // 2022-10-28 Spamcop blocks Google's addresses editor.putBoolean("blocklist.Spamcop", false); + } else if (version < 2013) { + if (prefs.contains("compose_block")) { + if (prefs.getBoolean("experiments", false)) + editor.putBoolean("compose_style", prefs.getBoolean("compose_block", false)); + editor.remove("compose_block"); + } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !BuildConfig.DEBUG) diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index 2655e67bcb..026b2bfeb2 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -287,6 +287,7 @@ public class FragmentCompose extends FragmentBase { private String display_font; private boolean dsn = true; private Integer encrypt = null; + private boolean style = false; private boolean media = true; private boolean compact = false; private int zoom = 0; @@ -349,6 +350,7 @@ public class FragmentCompose extends FragmentBase { compose_font = prefs.getString("compose_font", ""); display_font = prefs.getString("display_font", ""); + style = prefs.getBoolean("compose_style", false); media = prefs.getBoolean("compose_media", true); compact = prefs.getBoolean("compose_compact", false); zoom = prefs.getInt("compose_zoom", compact ? 0 : 1); @@ -626,8 +628,8 @@ public class FragmentCompose extends FragmentBase { return; if (styling != selection) { styling = selection; - media_bar.setVisibility(styling ? View.GONE : View.VISIBLE); - style_bar.setVisibility(styling ? View.VISIBLE : View.GONE); + style_bar.setVisibility(style || styling ? View.VISIBLE : View.GONE); + media_bar.setVisibility(!style && styling ? View.GONE : View.VISIBLE); invalidateOptionsMenu(); } } @@ -1921,6 +1923,7 @@ public class FragmentCompose extends FragmentBase { menu.findItem(R.id.menu_translate).setEnabled(state == State.LOADED); menu.findItem(R.id.menu_translate).setVisible(DeepL.isAvailable(context)); menu.findItem(R.id.menu_zoom).setEnabled(state == State.LOADED); + menu.findItem(R.id.menu_style).setEnabled(state == State.LOADED); menu.findItem(R.id.menu_media).setEnabled(state == State.LOADED); menu.findItem(R.id.menu_compact).setEnabled(state == State.LOADED); menu.findItem(R.id.menu_contact_group).setEnabled(state == State.LOADED); @@ -1971,7 +1974,6 @@ public class FragmentCompose extends FragmentBase { ibZoom.setEnabled(state == State.LOADED); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - boolean experiments = prefs.getBoolean("experiments", false); boolean save_drafts = prefs.getBoolean("save_drafts", true); boolean send_chips = prefs.getBoolean("send_chips", true); boolean send_dialog = prefs.getBoolean("send_dialog", true); @@ -1981,6 +1983,7 @@ public class FragmentCompose extends FragmentBase { menu.findItem(R.id.menu_send_chips).setChecked(send_chips); menu.findItem(R.id.menu_send_dialog).setChecked(send_dialog); menu.findItem(R.id.menu_image_dialog).setChecked(image_dialog); + menu.findItem(R.id.menu_style).setChecked(style); menu.findItem(R.id.menu_media).setChecked(media); menu.findItem(R.id.menu_compact).setChecked(compact); @@ -2053,6 +2056,9 @@ public class FragmentCompose extends FragmentBase { } else if (itemId == R.id.menu_image_dialog) { onMenuImageDialog(); return true; + } else if (itemId == R.id.menu_style) { + onMenuStyleBar(); + return true; } else if (itemId == R.id.menu_media) { onMenuMediaBar(); return true; @@ -2209,15 +2215,22 @@ public class FragmentCompose extends FragmentBase { prefs.edit().putBoolean("image_dialog", !image_dialog).apply(); } + private void onMenuStyleBar() { + style = !style; + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + prefs.edit().putBoolean("compose_style", style).apply(); + style_bar.setVisibility(style ? View.VISIBLE : View.GONE); + media_bar.setVisibility(media ? View.VISIBLE : View.GONE); + invalidateOptionsMenu(); + } + private void onMenuMediaBar() { media = !media; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); prefs.edit().putBoolean("compose_media", media).apply(); etBody.setSelection(etBody.getSelectionStart()); - media_bar.getMenu().clear(); - media_bar.inflateMenu(R.menu.action_compose_media); + style_bar.setVisibility(style ? View.VISIBLE : View.GONE); media_bar.setVisibility(media ? View.VISIBLE : View.GONE); - style_bar.setVisibility(View.GONE); invalidateOptionsMenu(); } @@ -6905,6 +6918,7 @@ public class FragmentCompose extends FragmentBase { @Override protected void onPostExecute(Bundle args) { pbWait.setVisibility(View.GONE); + style_bar.setVisibility(style ? View.VISIBLE : View.GONE); media_bar.setVisibility(media ? View.VISIBLE : View.GONE); bottom_navigation.getMenu().findItem(R.id.action_undo).setVisible(draft.revision > 1); bottom_navigation.getMenu().findItem(R.id.action_redo).setVisible(draft.revision < draft.revisions); diff --git a/app/src/main/java/eu/faircode/email/StyleHelper.java b/app/src/main/java/eu/faircode/email/StyleHelper.java index c3bc3cd7e7..c9f40e58b7 100644 --- a/app/src/main/java/eu/faircode/email/StyleHelper.java +++ b/app/src/main/java/eu/faircode/email/StyleHelper.java @@ -150,6 +150,18 @@ public class StyleHelper { _end = tmp; } + if (_start == _end && + itemId != R.id.menu_style_align && groupId != R.id.group_style_align && + itemId != R.id.menu_style_list && groupId != R.id.group_style_list && + itemId != R.id.menu_style_indentation && groupId != R.id.group_style_indentation && + itemId != R.id.menu_style_blockquote && groupId != R.id.group_style_blockquote) { + Pair word = getWord(etBody); + if (word == null) + return false; + _start = word.first; + _end = word.second; + } + final Editable edit = etBody.getText(); final int start = _start; final int end = _end; @@ -350,7 +362,6 @@ public class StyleHelper { } else if (groupId == R.id.group_style_font_standard || groupId == R.id.group_style_font_custom) { - Log.breadcrumb("style", "action", "font"); return setFont(etBody, start, end, (String) args[0]); } else if (itemId == R.id.menu_style_align) { @@ -358,6 +369,12 @@ public class StyleHelper { PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, owner, anchor); popupMenu.inflate(R.menu.popup_style_alignment); + if (start == end) { + Pair block = StyleHelper.getParagraph(etBody, true); + if (block == null) + return false; + } + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { @@ -370,15 +387,31 @@ public class StyleHelper { popupMenu.show(); } else if (groupId == R.id.group_style_align) { - return setAlignment(itemId, etBody, start, end, true); + if (start == end) { + Pair block = StyleHelper.getParagraph(etBody, true); + if (block == null) + return false; + return setAlignment(itemId, etBody, block.first, block.second, false); + } else + return setAlignment(itemId, etBody, start, end, true); } else if (itemId == R.id.menu_style_list) { Context context = anchor.getContext(); PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, owner, anchor); popupMenu.inflate(R.menu.popup_style_list); - Integer maxLevel = getMaxListLevel(edit, start, end); - IndentSpan[] indents = edit.getSpans(start, end, IndentSpan.class); + int s = start; + int e = end; + if (s == e) { + Pair p = StyleHelper.getParagraph(etBody, false); + if (p == null) + return false; + s = p.first; + e = p.second; + } + + Integer maxLevel = getMaxListLevel(edit, s, e); + IndentSpan[] indents = edit.getSpans(s, e, IndentSpan.class); popupMenu.getMenu().findItem(R.id.menu_style_list_bullets).setEnabled(indents.length == 0); popupMenu.getMenu().findItem(R.id.menu_style_list_numbered).setEnabled(indents.length == 0); @@ -397,19 +430,40 @@ public class StyleHelper { popupMenu.show(); } else if (groupId == R.id.group_style_list) { - if (itemId == R.id.menu_style_list_increase || - itemId == R.id.menu_style_list_decrease) - return setListLevel(itemId, etBody, start, end, true); - else - return setList(itemId, etBody, start, end, true); + boolean level = (itemId == R.id.menu_style_list_decrease || itemId == R.id.menu_style_list_increase); + if (start == end) { + Pair p = StyleHelper.getParagraph(etBody, false); + if (p == null) + return false; + if (level) + StyleHelper.setListLevel(itemId, etBody, p.first, p.second, false); + else + StyleHelper.setList(itemId, etBody, p.first, p.second, false); + return true; + } else { + if (level) + return setListLevel(itemId, etBody, start, end, true); + else + return setList(itemId, etBody, start, end, true); + } } else if (itemId == R.id.menu_style_indentation) { Context context = anchor.getContext(); PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, owner, anchor); popupMenu.inflate(R.menu.popup_style_indentation); - Integer maxLevel = getMaxListLevel(edit, start, end); - IndentSpan[] indents = edit.getSpans(start, end, IndentSpan.class); + int s = start; + int e = end; + if (s == e) { + Pair block = StyleHelper.getParagraph(etBody, true); + if (block == null) + return false; + s = block.first; + e = block.second; + } + + Integer maxLevel = getMaxListLevel(edit, s, e); + IndentSpan[] indents = edit.getSpans(s, e, IndentSpan.class); popupMenu.getMenu().findItem(R.id.menu_style_indentation_increase).setEnabled(maxLevel == null); popupMenu.getMenu().findItem(R.id.menu_style_indentation_decrease).setEnabled(indents.length > 0); @@ -426,10 +480,23 @@ public class StyleHelper { popupMenu.show(); } else if (groupId == R.id.group_style_indentation) { - return setIndentation(itemId, etBody, start, end, true); + if (start == end) { + Pair block = StyleHelper.getParagraph(etBody, true); + if (block == null) + return false; + StyleHelper.setIndentation(itemId, etBody, block.first, block.second, false); + } else + return setIndentation(itemId, etBody, start, end, true); } else if (itemId == R.id.menu_style_blockquote || groupId == R.id.group_style_blockquote) { - return setBlockQuote(etBody, start, end, true); + if (start == end) { + Pair block = StyleHelper.getParagraph(etBody, true); + if (block == null) + return false; + StyleHelper.setBlockQuote(etBody, block.first, block.second, false); + return true; + } else + return setBlockQuote(etBody, start, end, true); } else if (itemId == R.id.menu_style_mark || groupId == R.id.group_style_mark) { return setMark(etBody, start, end, itemId == R.id.menu_style_mark); @@ -1137,6 +1204,34 @@ public class StyleHelper { return new Pair<>(start, end); } + static private Pair getWord(TextView tvBody) { + int start = tvBody.getSelectionStart(); + int end = tvBody.getSelectionEnd(); + Spannable edit = (Spannable) tvBody.getText(); + + if (start < 0 || end < 0) + return null; + + if (start > end) { + int tmp = start; + start = end; + end = tmp; + } + + // Expand selection at start + while (start > 0 && edit.charAt(start - 1) != ' ' && edit.charAt(start - 1) != '\n') + start--; + + // Expand selection at end + while (end < edit.length() && edit.charAt(end) != ' ' && edit.charAt(end) != '\n') + end++; + + if (start == end) + return null; + + return new Pair<>(start, end); + } + public static Pair getParagraph(TextView tvBody) { return getParagraph(tvBody, false); } diff --git a/app/src/main/res/menu/menu_compose.xml b/app/src/main/res/menu/menu_compose.xml index ef925ccdf6..ff7cf95a6e 100644 --- a/app/src/main/res/menu/menu_compose.xml +++ b/app/src/main/res/menu/menu_compose.xml @@ -51,6 +51,14 @@ android:title="@string/title_image_dialog" app:showAsAction="never" /> + + Save drafts on the server Show send options Show image options + Style toolbar Media toolbar Manage local contacts Insert contact group