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

Commit c20f54af authored by James Cook's avatar James Cook Committed by Android (Google) Code Review
Browse files

Merge "Add basic support for Ctrl-Z to editable TextViews"

parents 0a7269bb 9201e797
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1711,6 +1711,7 @@ package android {
    field public static final int paste = 16908322; // 0x1020022
    field public static final int primary = 16908300; // 0x102000c
    field public static final int progress = 16908301; // 0x102000d
    field public static final int redo = 16908338; // 0x1020032
    field public static final int secondaryProgress = 16908303; // 0x102000f
    field public static final int selectAll = 16908319; // 0x102001f
    field public static final int selectTextMode = 16908333; // 0x102002d
@@ -1727,6 +1728,7 @@ package android {
    field public static final int text2 = 16908309; // 0x1020015
    field public static final int title = 16908310; // 0x1020016
    field public static final int toggle = 16908311; // 0x1020017
    field public static final int undo = 16908337; // 0x1020031
    field public static final int widget_frame = 16908312; // 0x1020018
  }
+2 −0
Original line number Diff line number Diff line
@@ -1787,6 +1787,7 @@ package android {
    field public static final int paste = 16908322; // 0x1020022
    field public static final int primary = 16908300; // 0x102000c
    field public static final int progress = 16908301; // 0x102000d
    field public static final int redo = 16908338; // 0x1020032
    field public static final int secondaryProgress = 16908303; // 0x102000f
    field public static final int selectAll = 16908319; // 0x102001f
    field public static final int selectTextMode = 16908333; // 0x102002d
@@ -1803,6 +1804,7 @@ package android {
    field public static final int text2 = 16908309; // 0x1020015
    field public static final int title = 16908310; // 0x1020016
    field public static final int toggle = 16908311; // 0x1020017
    field public static final int undo = 16908337; // 0x1020031
    field public static final int widget_frame = 16908312; // 0x1020018
  }
+58 −9
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.graphics.drawable.Drawable;
import android.inputmethodservice.ExtractEditText;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelableParcel;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.DynamicLayout;
@@ -118,15 +119,18 @@ import java.util.HashMap;
 */
public class Editor {
    private static final String TAG = "Editor";
    static final boolean DEBUG_UNDO = false;
    private static final boolean DEBUG_UNDO = false;

    static final int BLINK = 500;
    private static final float[] TEMP_POSITION = new float[2];
    private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
    // Tag used when the Editor maintains its own separate UndoManager.
    private static final String UNDO_OWNER_TAG = "Editor";

    UndoManager mUndoManager;
    UndoOwner mUndoOwner;
    InputFilter mUndoInputFilter;
    // Each Editor manages its own undo stack.
    private final UndoManager mUndoManager = new UndoManager();
    private UndoOwner mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
    final InputFilter mUndoInputFilter = new UndoInputFilter(this);

    // Cursor Controllers.
    InsertionPointCursorController mInsertionPointCursorController;
@@ -222,6 +226,39 @@ public class Editor {

    Editor(TextView textView) {
        mTextView = textView;
        // Synchronize the filter list, which places the undo input filter at the end.
        mTextView.setFilters(mTextView.getFilters());
    }

    ParcelableParcel saveInstanceState() {
        // For now there is only undo state.
        return (ParcelableParcel) mUndoManager.saveInstanceState();
    }

    void restoreInstanceState(ParcelableParcel state) {
        mUndoManager.restoreInstanceState(state);
        // Re-associate this object as the owner of undo state.
        mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
    }

    boolean canUndo() {
        UndoOwner[] owners = { mUndoOwner };
        return mUndoManager.countUndos(owners) > 0;
    }

    boolean canRedo() {
        UndoOwner[] owners = { mUndoOwner };
        return mUndoManager.countRedos(owners) > 0;
    }

    void undo() {
        UndoOwner[] owners = { mUndoOwner };
        mUndoManager.undo(owners, 1);  // Undo 1 action.
    }

    void redo() {
        UndoOwner[] owners = { mUndoOwner };
        mUndoManager.redo(owners, 1);  // Redo 1 action.
    }

    void onAttachedToWindow() {
@@ -1706,7 +1743,7 @@ public class Editor {

    /**
     * Called by the framework in response to a text auto-correction (such as fixing a typo using a
     * a dictionnary) from the current input method, provided by it calling
     * a dictionary) from the current input method, provided by it calling
     * {@link InputConnection#commitCorrection} InputConnection.commitCorrection()}. The default
     * implementation flashes the background of the corrected word to provide feedback to the user.
     *
@@ -4161,8 +4198,12 @@ public class Editor {
        int mChangedStart, mChangedEnd, mChangedDelta;
    }

    /**
     * An InputFilter that monitors text input to maintain undo history. It does not modify the
     * text being typed (and hence always returns null from the filter() method).
     */
    public static class UndoInputFilter implements InputFilter {
        final Editor mEditor;
        private final Editor mEditor;

        public UndoInputFilter(Editor editor) {
            mEditor = editor;
@@ -4192,6 +4233,8 @@ public class Editor {
                    // The current operation is an add...  are we adding more?  We are adding
                    // more if we are either appending new text to the end of the last edit or
                    // completely replacing some or all of the last edit.
                    // TODO: This sequence doesn't work right: a, left-arrow, b, undo, undo.
                    // The two edits are incorrectly merged, so there is only one undo available.
                    if (start < end && ((dstart >= op.mRangeStart && dend <= op.mRangeEnd)
                            || (dstart == op.mRangeEnd && dend == op.mRangeEnd))) {
                        op.mRangeEnd = dstart + (end-start);
@@ -4245,7 +4288,10 @@ public class Editor {
        }
    }

    public static class TextModifyOperation extends UndoOperation<TextView> {
    /**
     * An operation to undo a single "edit" to a text view.
     */
    public static class TextModifyOperation extends UndoOperation<Editor> {
        int mRangeStart, mRangeEnd;
        CharSequence mOldText;

@@ -4277,8 +4323,8 @@ public class Editor {
        private void swapText() {
            // Both undo and redo involves swapping the contents of the range
            // in the text view with our local text.
            TextView tv = getOwnerData();
            Editable editable = (Editable)tv.getText();
            Editor editor = getOwnerData();
            Editable editable = (Editable)editor.mTextView.getText();
            CharSequence curText;
            if (mRangeStart >= mRangeEnd) {
                curText = null;
@@ -4309,14 +4355,17 @@ public class Editor {

        public static final Parcelable.ClassLoaderCreator<TextModifyOperation> CREATOR
                = new Parcelable.ClassLoaderCreator<TextModifyOperation>() {
            @Override
            public TextModifyOperation createFromParcel(Parcel in) {
                return new TextModifyOperation(in, null);
            }

            @Override
            public TextModifyOperation createFromParcel(Parcel in, ClassLoader loader) {
                return new TextModifyOperation(in, loader);
            }

            @Override
            public TextModifyOperation[] newArray(int size) {
                return new TextModifyOperation[size];
            }
+70 −20
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ParcelableParcel;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -1612,7 +1613,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     * @hide
     */
    public final UndoManager getUndoManager() {
        return mEditor == null ? null : mEditor.mUndoManager;
        // TODO: Consider supporting a global undo manager.
        throw new UnsupportedOperationException("not implemented");
    }

    /**
@@ -1630,22 +1632,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     * @hide
     */
    public final void setUndoManager(UndoManager undoManager, String tag) {
        if (undoManager != null) {
            createEditorIfNeeded();
            mEditor.mUndoManager = undoManager;
            mEditor.mUndoOwner = undoManager.getOwner(tag, this);
            mEditor.mUndoInputFilter = new Editor.UndoInputFilter(mEditor);
            if (!(mText instanceof Editable)) {
                setText(mText, BufferType.EDITABLE);
            }

            setFilters((Editable) mText, mFilters);
        } else if (mEditor != null) {
            // XXX need to destroy all associated state.
            mEditor.mUndoManager = null;
            mEditor.mUndoOwner = null;
            mEditor.mUndoInputFilter = null;
        }
        // TODO: Consider supporting a global undo manager. An implementation will need to:
        // * createEditorIfNeeded()
        // * Promote to BufferType.EDITABLE if needed.
        // * Update the UndoManager and UndoOwner.
        // Likewise it will need to be able to restore the default UndoManager.
        throw new UnsupportedOperationException("not implemented");
    }

    /**
@@ -3899,6 +3891,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

            ss.error = getError();

            if (mEditor != null) {
                ss.editorState = mEditor.saveInstanceState();
            }
            return ss;
        }

@@ -3968,6 +3963,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                }
            });
        }

        if (ss.editorState != null) {
            createEditorIfNeeded();
            mEditor.restoreInstanceState(ss.editorState);
        }
    }

    /**
@@ -8375,14 +8375,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

    @Override
    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
        final int filteredMetaState = event.getMetaState() & ~KeyEvent.META_CTRL_MASK;
        if (KeyEvent.metaStateHasNoModifiers(filteredMetaState)) {
        if (event.hasModifiers(KeyEvent.META_CTRL_ON)) {
            // Handle Ctrl-only shortcuts.
            switch (keyCode) {
            case KeyEvent.KEYCODE_A:
                if (canSelectText()) {
                    return onTextContextMenuItem(ID_SELECT_ALL);
                }
                break;
            case KeyEvent.KEYCODE_Z:
                if (canUndo()) {
                    return onTextContextMenuItem(ID_UNDO);
                }
                break;
            case KeyEvent.KEYCODE_X:
                if (canCut()) {
                    return onTextContextMenuItem(ID_CUT);
@@ -8399,6 +8404,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                }
                break;
            }
        } else if (event.hasModifiers(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)) {
            // Handle Ctrl-Shift shortcuts.
            switch (keyCode) {
                case KeyEvent.KEYCODE_Z:
                    if (canRedo()) {
                        return onTextContextMenuItem(ID_REDO);
                    }
                    break;
            }
        }
        return super.onKeyShortcut(keyCode, event);
    }
@@ -8775,6 +8789,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    }

    static final int ID_SELECT_ALL = android.R.id.selectAll;
    static final int ID_UNDO = android.R.id.undo;
    static final int ID_REDO = android.R.id.redo;
    static final int ID_CUT = android.R.id.cut;
    static final int ID_COPY = android.R.id.copy;
    static final int ID_PASTE = android.R.id.paste;
@@ -8805,6 +8821,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                selectAllText();
                return true;

            case ID_UNDO:
                if (mEditor != null) {
                    mEditor.undo();
                }
                return true;  // Returns true even if nothing was undone.

            case ID_REDO:
                if (mEditor != null) {
                    mEditor.redo();
                }
                return true;  // Returns true even if nothing was undone.

            case ID_PASTE:
                paste(min, max);
                return true;
@@ -8934,8 +8962,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     * @hide
     */
    protected void stopSelectionActionMode() {
        if (mEditor != null) {
            mEditor.stopSelectionActionMode();
        }
    }

    boolean canUndo() {
        return mEditor != null && mEditor.canUndo();
    }

    boolean canRedo() {
        return mEditor != null && mEditor.canRedo();
    }

    boolean canCut() {
        if (hasPasswordTransformationMethod()) {
@@ -9304,6 +9342,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        CharSequence text;
        boolean frozenWithFocus;
        CharSequence error;
        ParcelableParcel editorState;  // Optional state from Editor.

        SavedState(Parcelable superState) {
            super(superState);
@@ -9323,6 +9362,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                out.writeInt(1);
                TextUtils.writeToParcel(error, out, flags);
            }

            if (editorState == null) {
                out.writeInt(0);
            } else {
                out.writeInt(1);
                editorState.writeToParcel(out, flags);
            }
        }

        @Override
@@ -9358,6 +9404,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            if (in.readInt() != 0) {
                error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
            }

            if (in.readInt() != 0) {
                editorState = ParcelableParcel.CREATOR.createFromParcel(in);
            }
        }
    }

+2 −0
Original line number Diff line number Diff line
@@ -89,4 +89,6 @@
  <item type="id" name="parentMatrix" />
  <item type="id" name="statusBarBackground" />
  <item type="id" name="navigationBarBackground" />
  <item type="id" name="undo" />
  <item type="id" name="redo" />
</resources>
Loading