Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 052c6202 authored by stefan-niedermann's avatar stefan-niedermann
Browse files

Implement menu for context based formatting without selected range

parent 13016ea7
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package it.niedermann.owncloud.notes.android.fragment;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -31,8 +32,6 @@ import com.yydcdut.markdown.MarkdownEditText;
import com.yydcdut.markdown.MarkdownProcessor;
import com.yydcdut.markdown.syntax.edit.EditFactory;

import java.util.Objects;

import butterknife.BindView;
import butterknife.ButterKnife;
import it.niedermann.owncloud.notes.R;
@@ -139,7 +138,7 @@ public class NoteEditFragment extends SearchableBaseNoteFragment {
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        ButterKnife.bind(this, Objects.requireNonNull(getView()));
        ButterKnife.bind(this, requireView());

        textWatcher = new NotesTextWatcher(editContent) {
            @Override
@@ -177,7 +176,9 @@ public class NoteEditFragment extends SearchableBaseNoteFragment {
            markdownProcessor.live(editContent);

            editContent.setCustomSelectionActionModeCallback(new ContextBasedRangeFormattingCallback(this.editContent));
            editContent.setOnCreateContextMenuListener(new ContextBasedFormattingCallback(this.editContent));
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                editContent.setCustomInsertionActionModeCallback(new ContextBasedFormattingCallback(this.editContent));
            }
            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext().getApplicationContext());
            editContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, getFontSizeFromPreferences(sp));
            if (sp.getBoolean(getString(R.string.pref_key_font), false)) {
+117 −4
Original line number Diff line number Diff line
package it.niedermann.owncloud.notes.util;

import android.view.ContextMenu;
import android.view.View;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Typeface;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;

public class ContextBasedFormattingCallback implements View.OnCreateContextMenuListener {
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Objects;

import it.niedermann.owncloud.notes.R;

import static android.content.Context.CLIPBOARD_SERVICE;

public class ContextBasedFormattingCallback implements ActionMode.Callback {

    private static final String TAG = ContextBasedFormattingCallback.class.getCanonicalName();

@@ -15,7 +32,103 @@ public class ContextBasedFormattingCallback implements View.OnCreateContextMenuL
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        mode.getMenuInflater().inflate(R.menu.context_based_formatting, menu);
        return true;
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        CharSequence text = editText.getText();
        int originalCursorPosition = editText.getSelectionStart();
        int startOfLine = originalCursorPosition;
        int endOfLine = originalCursorPosition;
        while (startOfLine > 0 && text.charAt(startOfLine - 1) != '\n') {
            startOfLine--;
        }
        if (endOfLine != startOfLine) {
            while (endOfLine < text.length() && text.charAt(endOfLine + 1) != '\n') {
                endOfLine--;
            }
        }
        String line = text.subSequence(startOfLine, endOfLine).toString();
        if (NotesTextWatcher.lineStartsWithCheckbox(line, true) || NotesTextWatcher.lineStartsWithCheckbox(line, false)) {
            menu.findItem(R.id.checkbox).setVisible(false);
            Log.i(TAG, "Hide checkbox menu item because line starts already with checkbox");
        }
        return false;
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        switch (item.getItemId()) {
            case R.id.checkbox: {
                // TODO only if line does not already start with a checkbox
                CharSequence text = editText.getText();
                int originalCursorPosition = editText.getSelectionStart();
                int startOfLine = originalCursorPosition;
                int endOfLine = originalCursorPosition;
                while (startOfLine > 0 && text.charAt(startOfLine - 1) != '\n') {
                    startOfLine--;
                }
                if (endOfLine != startOfLine) {
                    while (endOfLine < text.length() && text.charAt(endOfLine + 1) != '\n') {
                        endOfLine--;
                    }
                }
                CharSequence part1 = text.subSequence(0, startOfLine);
                CharSequence part2 = text.subSequence(startOfLine, text.length());
                editText.setText(TextUtils.concat(part1, "- [ ] ", part2));
                editText.setSelection(originalCursorPosition + 6);
                return true;
            }
            case R.id.link: {
                SpannableStringBuilder ssb = new SpannableStringBuilder(editText.getText());
                int start = editText.getText().length();
                int end = start;
                boolean textToFormatIsLink = TextUtils.indexOf(editText.getText().subSequence(start, end), "http") == 0;
                if (textToFormatIsLink) {
                    ssb.insert(end, ")");
                    ssb.insert(start, "[](");
                } else {
                    String clipboardURL = getClipboardURLorNull(editText.getContext());
                    if (clipboardURL != null) {
                        ssb.insert(end, "](" + clipboardURL + ")");
                        end += clipboardURL.length();
                    } else {
                        ssb.insert(end, "]()");
                    }
                    ssb.insert(start, "[");
                }
                end++;
                ssb.setSpan(new StyleSpan(Typeface.NORMAL), start, end, 1);
                editText.setText(ssb);
                if (textToFormatIsLink) {
                    editText.setSelection(start + 1);
                } else {
                    editText.setSelection(end + 2); // after <end>](
                }
                return true;
            }
        }
        return false;
    }

    private static String getClipboardURLorNull(Context context) {
        String clipboardURL = null;
        ClipData clipboardData = Objects.requireNonNull(((ClipboardManager) Objects.requireNonNull(context.getSystemService(CLIPBOARD_SERVICE))).getPrimaryClip());
        if (clipboardData.getItemCount() > 0) {
            try {
                clipboardURL = new URL(clipboardData.getItemAt(0).getText().toString()).toString();
            } catch (MalformedURLException e) {
                Log.d(TAG, "Clipboard does not contain a valid URL: " + clipboardURL);
            }
        }
        return clipboardURL;
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        // Nothing to do here...
    }
}
+3 −5
Original line number Diff line number Diff line
@@ -11,7 +11,6 @@ import android.util.Log;
import android.util.SparseIntArray;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.EditText;

@@ -35,9 +34,7 @@ public class ContextBasedRangeFormattingCallback implements ActionMode.Callback

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.context_based_range_formatting, menu);
        menu.removeItem(android.R.id.selectAll);
        mode.getMenuInflater().inflate(R.menu.context_based_range_formatting, menu);

        SparseIntArray styleFormatMap = new SparseIntArray();
        styleFormatMap.append(R.id.bold, Typeface.BOLD);
@@ -60,6 +57,7 @@ public class ContextBasedRangeFormattingCallback implements ActionMode.Callback

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        // TODO hide actions if not available?
        return false;
    }

@@ -151,7 +149,7 @@ public class ContextBasedRangeFormattingCallback implements ActionMode.Callback

    @Override
    public void onDestroyActionMode(ActionMode mode) {

        // Nothing to do here...
    }

    private boolean hasAlreadyMarkdown(int start, int end, String markdown) {
+1 −1
Original line number Diff line number Diff line
@@ -104,7 +104,7 @@ public abstract class NotesTextWatcher implements TextWatcher {
        }
    }

    private static boolean lineStartsWithCheckbox(String line, boolean starAsLeadingCharacter) {
    static boolean lineStartsWithCheckbox(String line, boolean starAsLeadingCharacter) {
        return starAsLeadingCharacter
                ? line.startsWith(uncheckedStarCheckbox) || line.startsWith(checkedStarCheckbox)
                : line.startsWith(uncheckedMinusCheckbox) || line.startsWith(checkedMinusCheckbox);
+1 −1
Original line number Diff line number Diff line
@@ -155,7 +155,7 @@
    <string name="category_readonly">Read only</string>
    <string name="no_category">No category</string>
    <string name="add_category">Add %1$s</string>
    <string name="simple_checkbox">checkbox</string>
    <string name="simple_checkbox">Checkbox</string>

    <!-- Array: note modes -->
    <string-array name="noteMode_entries">