diff --git a/app/src/main/java/eu/faircode/email/EditTextCompose.java b/app/src/main/java/eu/faircode/email/EditTextCompose.java index 799eab4971..61a1837fb8 100644 --- a/app/src/main/java/eu/faircode/email/EditTextCompose.java +++ b/app/src/main/java/eu/faircode/email/EditTextCompose.java @@ -36,7 +36,8 @@ import androidx.core.view.inputmethod.InputConnectionCompat; import androidx.core.view.inputmethod.InputContentInfoCompat; public class EditTextCompose extends AppCompatEditText { - private IInputContentListener listener = null; + private ISelection selectionListener = null; + private IInputContentListener inputContentListener = null; public EditTextCompose(Context context) { super(context); @@ -50,6 +51,13 @@ public class EditTextCompose extends AppCompatEditText { super(context, attrs, defStyleAttr); } + @Override + protected void onSelectionChanged(int selStart, int selEnd) { + super.onSelectionChanged(selStart, selEnd); + if (selectionListener != null) + selectionListener.onSelected(hasSelection()); + } + @Override public boolean onTextContextMenuItem(int id) { try { @@ -117,14 +125,14 @@ public class EditTextCompose extends AppCompatEditText { public boolean onCommitContent(InputContentInfoCompat info, int flags, Bundle opts) { Log.i("Uri=" + info.getContentUri()); try { - if (listener == null) + if (inputContentListener == null) throw new IllegalArgumentException("InputContent listener not set"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && (flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) info.requestPermission(); - listener.onInputContent(info.getContentUri()); + inputContentListener.onInputContent(info.getContentUri()); return true; } catch (Throwable ex) { Log.w(ex); @@ -135,10 +143,18 @@ public class EditTextCompose extends AppCompatEditText { } void setInputContentListener(IInputContentListener listener) { - this.listener = listener; + this.inputContentListener = listener; } interface IInputContentListener { void onInputContent(Uri uri); } + + void setSelectionListener(ISelection listener) { + this.selectionListener = listener; + } + + interface ISelection { + void onSelected(boolean selection); + } } diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index 5bfd2073b5..5873c1ffa6 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -74,7 +74,6 @@ import android.text.style.URLSpan; import android.text.style.UnderlineSpan; import android.util.DisplayMetrics; import android.util.TypedValue; -import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -183,6 +182,7 @@ public class FragmentCompose extends FragmentBase { private ImageButton ibReferenceDelete; private ImageButton ibReferenceEdit; private ImageButton ibReferenceImages; + private BottomNavigationView style_bar; private BottomNavigationView media_bar; private BottomNavigationView bottom_navigation; private ContentLoadingProgressBar pbWait; @@ -278,6 +278,7 @@ public class FragmentCompose extends FragmentBase { ibReferenceDelete = view.findViewById(R.id.ibReferenceDelete); ibReferenceEdit = view.findViewById(R.id.ibReferenceEdit); ibReferenceImages = view.findViewById(R.id.ibReferenceImages); + style_bar = view.findViewById(R.id.style_bar); media_bar = view.findViewById(R.id.media_bar); bottom_navigation = view.findViewById(R.id.bottom_navigation); @@ -346,8 +347,6 @@ public class FragmentCompose extends FragmentBase { setZoom(); - etBody.setCustomSelectionActionModeCallback(actionCallback); - etBody.setInputContentListener(new EditTextCompose.IInputContentListener() { @Override public void onInputContent(Uri uri) { @@ -355,6 +354,13 @@ public class FragmentCompose extends FragmentBase { } }); + etBody.setSelectionListener(new EditTextCompose.ISelection() { + @Override + public void onSelected(boolean selection) { + style_bar.setVisibility(selection ? View.VISIBLE : View.GONE); + } + }); + cbSignature.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { @@ -422,6 +428,13 @@ public class FragmentCompose extends FragmentBase { etBody.setTypeface(monospaced ? Typeface.MONOSPACE : Typeface.DEFAULT); tvReference.setTypeface(monospaced ? Typeface.MONOSPACE : Typeface.DEFAULT); + style_bar.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { + @Override + public boolean onNavigationItemSelected(@NonNull MenuItem item) { + return onActionStyle(item.getItemId()); + } + }); + PackageManager pm = getContext().getPackageManager(); Intent take_photo = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Intent record_audio = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); @@ -497,6 +510,7 @@ public class FragmentCompose extends FragmentBase { ibReferenceEdit.setVisibility(View.GONE); ibReferenceImages.setVisibility(View.GONE); tvReference.setVisibility(View.GONE); + style_bar.setVisibility(View.GONE); media_bar.setVisibility(View.GONE); bottom_navigation.setVisibility(View.GONE); pbWait.setVisibility(View.VISIBLE); @@ -1048,6 +1062,106 @@ public class FragmentCompose extends FragmentBase { fragment.show(getFragmentManager(), "compose:answer"); } + private boolean onActionStyle(int action) { + Log.i("Style action=" + action); + + int start = etBody.getSelectionStart(); + int end = etBody.getSelectionEnd(); + + if (start < 0) + start = 0; + if (end < 0) + end = 0; + + if (start > end) { + int tmp = start; + start = end; + end = tmp; + } + + SpannableString ss = new SpannableString(etBody.getText()); + + switch (action) { + case R.id.menu_bold: + case R.id.menu_italic: { + int style = (action == R.id.menu_bold ? Typeface.BOLD : Typeface.ITALIC); + boolean has = false; + for (StyleSpan span : ss.getSpans(start, end, StyleSpan.class)) + if (span.getStyle() == style) { + has = true; + ss.removeSpan(span); + } + + if (!has) + ss.setSpan(new StyleSpan(style), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + etBody.setText(ss); + etBody.setSelection(start, end); + + return true; + } + + case R.id.menu_underline: { + boolean has = false; + for (UnderlineSpan span : ss.getSpans(start, end, UnderlineSpan.class)) { + has = true; + ss.removeSpan(span); + } + + if (!has) + ss.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + etBody.setText(ss); + etBody.setSelection(start, end); + + return true; + } + + case R.id.menu_size: { + RelativeSizeSpan[] spans = ss.getSpans(start, end, RelativeSizeSpan.class); + float size = (spans.length > 0 ? spans[0].getSizeChange() : 1.0f); + + // Match small/big + if (size == 0.8f) + size = 1.0f; + else if (size == 1.0) + size = 1.25f; + else + size = 0.8f; + + for (RelativeSizeSpan span : spans) + ss.removeSpan(span); + + if (size != 1.0f) + ss.setSpan(new RelativeSizeSpan(size), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + etBody.setText(ss); + etBody.setSelection(start, end); + + return true; + } + + case R.id.menu_color: { + Bundle args = new Bundle(); + args.putInt("start", start); + args.putInt("end", end); + + ForegroundColorSpan[] spans = ss.getSpans(start, end, ForegroundColorSpan.class); + int color = (spans.length > 0 ? spans[0].getForegroundColor() : Color.TRANSPARENT); + + FragmentDialogColor fragment = new FragmentDialogColor(); + fragment.initialize(R.string.title_style_color, color, args, getContext()); + fragment.setTargetFragment(FragmentCompose.this, REQUEST_COLOR); + fragment.show(getFragmentManager(), "account:color"); + + return true; + } + + default: + return false; + } + } + private void onActionRecordAudio() { Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); startActivityForResult(intent, REQUEST_RECORD_AUDIO); @@ -1607,8 +1721,9 @@ public class FragmentCompose extends FragmentBase { for (ForegroundColorSpan span : ss.getSpans(start, end, ForegroundColorSpan.class)) ss.removeSpan(span); ss.setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + etBody.setText(ss); - etBody.setSelection(end, end); + etBody.setSelection(start, end); } private void onContactGroupSelected(Bundle args) { @@ -3349,129 +3464,6 @@ public class FragmentCompose extends FragmentBase { } }; - private ActionMode.Callback actionCallback = new ActionMode.Callback() { - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - menu.add(1, R.string.title_style_bold, 1, R.string.title_style_bold).setIcon(R.drawable.baseline_format_bold_24); - menu.add(1, R.string.title_style_italic, 2, R.string.title_style_italic).setIcon(R.drawable.baseline_format_italic_24); - menu.add(1, R.string.title_style_underline, 3, R.string.title_style_underline).setIcon(R.drawable.baseline_format_underlined_24); - menu.add(1, R.string.title_style_size, 4, R.string.title_style_size).setIcon(R.drawable.baseline_format_size_24); - menu.add(1, R.string.title_style_color, 5, R.string.title_style_color).setIcon(R.drawable.baseline_format_color_text_24); - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - Log.i("Action=" + item.getGroupId() + ":" + item.getItemId()); - - if (item.getGroupId() != 1) - return false; - - int start = etBody.getSelectionStart(); - int end = etBody.getSelectionEnd(); - - if (start < 0) - start = 0; - if (end < 0) - end = 0; - - if (start > end) { - int tmp = start; - start = end; - end = tmp; - } - - SpannableString ss = new SpannableString(etBody.getText()); - - switch (item.getItemId()) { - case R.string.title_style_bold: - case R.string.title_style_italic: { - int style = (item.getItemId() == R.string.title_style_bold ? Typeface.BOLD : Typeface.ITALIC); - boolean has = false; - for (StyleSpan span : ss.getSpans(start, end, StyleSpan.class)) - if (span.getStyle() == style) { - has = true; - ss.removeSpan(span); - } - - if (!has) - ss.setSpan(new StyleSpan(style), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - etBody.setText(ss); - etBody.setSelection(end, end); - return true; - } - - case R.string.title_style_underline: { - boolean has = false; - for (UnderlineSpan span : ss.getSpans(start, end, UnderlineSpan.class)) { - has = true; - ss.removeSpan(span); - } - - if (!has) - ss.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - etBody.setText(ss); - etBody.setSelection(end, end); - return true; - } - - case R.string.title_style_size: { - RelativeSizeSpan[] spans = ss.getSpans(start, end, RelativeSizeSpan.class); - float size = (spans.length > 0 ? spans[0].getSizeChange() : 1.0f); - - // Match small/big - if (size == 0.8f) - size = 1.0f; - else if (size == 1.0) - size = 1.25f; - else - size = 0.8f; - - for (RelativeSizeSpan span : spans) - ss.removeSpan(span); - - if (size != 1.0f) - ss.setSpan(new RelativeSizeSpan(size), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - etBody.setText(ss); - etBody.setSelection(end, end); - return true; - } - - case R.string.title_style_color: { - Bundle args = new Bundle(); - args.putInt("start", start); - args.putInt("end", end); - - ForegroundColorSpan[] spans = ss.getSpans(start, end, ForegroundColorSpan.class); - int color = (spans.length > 0 ? spans[0].getForegroundColor() : Color.TRANSPARENT); - - FragmentDialogColor fragment = new FragmentDialogColor(); - fragment.initialize(R.string.title_style_color, color, args, getContext()); - fragment.setTargetFragment(FragmentCompose.this, REQUEST_COLOR); - fragment.show(getFragmentManager(), "account:color"); - - etBody.setSelection(end, end); - return true; - } - - default: - return false; - } - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - } - }; - private ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { diff --git a/app/src/main/res/layout/fragment_compose.xml b/app/src/main/res/layout/fragment_compose.xml index 49fa6f4bc0..f0c78bad2f 100644 --- a/app/src/main/res/layout/fragment_compose.xml +++ b/app/src/main/res/layout/fragment_compose.xml @@ -14,7 +14,7 @@ @@ -417,6 +417,19 @@ + + + app:menu="@menu/action_compose_media" /> + + + + + + + + + + +