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

Commit 3aa49b6f authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

New UndoManager.

Basic implementation of an undo manager.  Supports
multi-level undo/redo, building on the top undo state
as edits occur, managing multiple distinct entities in
the undo state (such as embedded objects in a document),
and saving/restoring the full undo state.  Still some
work remaining on correctly dealing with dependencies
between undo states that hold multiple owners.

Also do a simple implementation of undo state in TextView
to see how things actually work.  The implementation here
is very primitive: it needs a lot more work to correctly
identify when to merge undo ops, is not trying to do
anything smart with style spans, etc.

Change-Id: Ie30f4e133351e2f569ffb48c6c44a2b19cadee27
parent 2ae118d1
Loading
Loading
Loading
Loading
+55 −0
Original line number Diff line number Diff line
@@ -6610,6 +6610,59 @@ package android.content {
    method public abstract void onStatusChanged(int);
  }
  public class UndoManager {
    ctor public UndoManager();
    method public void addOperation(android.content.UndoOperation<?>, int);
    method public void beginUpdate(java.lang.CharSequence);
    method public int commitState(android.content.UndoOwner);
    method public int countRedos(android.content.UndoOwner[]);
    method public int countUndos(android.content.UndoOwner[]);
    method public void endUpdate();
    method public int forgetRedos(android.content.UndoOwner[], int);
    method public int forgetUndos(android.content.UndoOwner[], int);
    method public int getHistorySize();
    method public android.content.UndoOperation<?> getLastOperation(int);
    method public android.content.UndoOperation<?> getLastOperation(android.content.UndoOwner, int);
    method public T getLastOperation(java.lang.Class<T>, android.content.UndoOwner, int);
    method public android.content.UndoOwner getOwner(java.lang.String, java.lang.Object);
    method public java.lang.CharSequence getRedoLabel(android.content.UndoOwner[]);
    method public java.lang.CharSequence getUndoLabel(android.content.UndoOwner[]);
    method public int getUpdateNestingLevel();
    method public boolean hasOperation(android.content.UndoOwner);
    method public boolean isInUndo();
    method public boolean isInUpdate();
    method public int redo(android.content.UndoOwner[], int);
    method public void restoreInstanceState(android.os.Parcelable);
    method public android.os.Parcelable saveInstanceState();
    method public void setHistorySize(int);
    method public void setUndoLabel(java.lang.CharSequence);
    method public void suggestUndoLabel(java.lang.CharSequence);
    method public boolean uncommitState(int, android.content.UndoOwner);
    method public int undo(android.content.UndoOwner[], int);
    field public static final int MERGE_MODE_ANY = 2; // 0x2
    field public static final int MERGE_MODE_NONE = 0; // 0x0
    field public static final int MERGE_MODE_UNIQUE = 1; // 0x1
  }
  public abstract class UndoOperation implements android.os.Parcelable {
    ctor public UndoOperation(android.content.UndoOwner);
    ctor protected UndoOperation(android.os.Parcel, java.lang.ClassLoader);
    method public boolean allowMerge();
    method public abstract void commit();
    method public int describeContents();
    method public android.content.UndoOwner getOwner();
    method public DATA getOwnerData();
    method public boolean hasData();
    method public boolean matchOwner(android.content.UndoOwner);
    method public abstract void redo();
    method public abstract void undo();
  }
  public class UndoOwner {
    method public java.lang.Object getData();
    method public java.lang.String getTag();
  }
  public class UriMatcher {
    ctor public UriMatcher(int);
    method public void addURI(java.lang.String, java.lang.String, int);
@@ -30873,6 +30926,7 @@ package android.widget {
    method public int getTotalPaddingTop();
    method public final android.text.method.TransformationMethod getTransformationMethod();
    method public android.graphics.Typeface getTypeface();
    method public final android.content.UndoManager getUndoManager();
    method public android.text.style.URLSpan[] getUrls();
    method public boolean hasSelection();
    method public boolean isCursorVisible();
@@ -30971,6 +31025,7 @@ package android.widget {
    method public final void setTransformationMethod(android.text.method.TransformationMethod);
    method public void setTypeface(android.graphics.Typeface, int);
    method public void setTypeface(android.graphics.Typeface);
    method public final void setUndoManager(android.content.UndoManager, java.lang.String);
    method public void setWidth(int);
  }
+932 −0

File added.

Preview size limit exceeded, changes collapsed.

+110 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * A single undoable operation.  You must subclass this to implement the state
 * and behavior for your operation.  Instances of this class are placed and
 * managed in an {@link UndoManager}.
 */
public abstract class UndoOperation<DATA> implements Parcelable {
    UndoOwner mOwner;

    /**
     * Create a new instance of the operation.
     * @param owner Who owns the data being modified by this undo state; must be
     * returned by {@link UndoManager#getOwner(String, Object) UndoManager.getOwner}.
     */
    public UndoOperation(UndoOwner owner) {
        mOwner = owner;
    }

    /**
     * Construct from a Parcel.
     */
    protected UndoOperation(Parcel src, ClassLoader loader) {
    }

    /**
     * Owning object as given to {@link #UndoOperation(UndoOwner)}.
     */
    public UndoOwner getOwner() {
        return mOwner;
    }

    /**
     * Synonym for {@link #getOwner()}.{@link android.content.UndoOwner#getData()}.
     */
    public DATA getOwnerData() {
        return (DATA)mOwner.getData();
    }

    /**
     * Return true if this undo operation is a member of the given owner.
     * The default implementation is <code>owner == getOwner()</code>.  You
     * can override this to provide more sophisticated dependencies between
     * owners.
     */
    public boolean matchOwner(UndoOwner owner) {
        return owner == getOwner();
    }

    /**
     * Return true if this operation actually contains modification data.  The
     * default implementation always returns true.  If you return false, the
     * operation will be dropped when the final undo state is being built.
     */
    public boolean hasData() {
        return true;
    }

    /**
     * Return true if this operation can be merged with a later operation.
     * The default implementation always returns true.
     */
    public boolean allowMerge() {
        return true;
    }

    /**
     * Called when this undo state is being committed to the undo stack.
     * The implementation should perform the initial edits and save any state that
     * may be needed to undo them.
     */
    public abstract void commit();

    /**
     * Called when this undo state is being popped off the undo stack (in to
     * the temporary redo stack).  The implementation should remove the original
     * edits and thus restore the target object to its prior value.
     */
    public abstract void undo();

    /**
     * Called when this undo state is being pushed back from the transient
     * redo stack to the main undo stack.  The implementation should re-apply
     * the edits that were previously removed by {@link #undo}.
     */
    public abstract void redo();

    public int describeContents() {
        return 0;
    }
}
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content;

/**
 * Representation of an owner of {@link UndoOperation} objects in an {@link UndoManager}.
 */
public class UndoOwner {
    final String mTag;

    UndoManager mManager;
    Object mData;
    int mOpCount;

    // For saving/restoring state.
    int mStateSeq;
    int mSavedIdx;

    UndoOwner(String tag) {
        mTag = tag;
    }

    /**
     * Return the unique tag name identifying this owner.  This is the tag
     * supplied to {@link UndoManager#getOwner(String, Object) UndoManager.getOwner}
     * and is immutable.
     */
    public String getTag() {
        return mTag;
    }

    /**
     * Return the actual data object of the owner.  This is the data object
     * supplied to {@link UndoManager#getOwner(String, Object) UndoManager.getOwner}.  An
     * owner may have a null data if it was restored from a previously saved state with
     * no getOwner call to associate it with its data.
     */
    public Object getData() {
        return mData;
    }
}
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

/**
 * Parcelable containing a raw Parcel of data.
 * @hide
 */
public class ParcelableParcel implements Parcelable {
    final Parcel mParcel;
    final ClassLoader mClassLoader;

    public ParcelableParcel(ClassLoader loader) {
        mParcel = Parcel.obtain();
        mClassLoader = loader;
    }

    public ParcelableParcel(Parcel src, ClassLoader loader) {
        mParcel = Parcel.obtain();
        mClassLoader = loader;
        int size = src.readInt();
        int pos = src.dataPosition();
        mParcel.appendFrom(src, src.dataPosition(), size);
        src.setDataPosition(pos + size);
    }

    public Parcel getParcel() {
        mParcel.setDataPosition(0);
        return mParcel;
    }

    public ClassLoader getClassLoader() {
        return mClassLoader;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mParcel.dataSize());
        dest.appendFrom(mParcel, 0, mParcel.dataSize());
    }

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

        public ParcelableParcel createFromParcel(Parcel in, ClassLoader loader) {
            return new ParcelableParcel(in, loader);
        }

        public ParcelableParcel[] newArray(int size) {
            return new ParcelableParcel[size];
        }
    };
}
Loading