summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/ActivityManagerNative.java21
-rw-r--r--core/java/android/app/AppOpsManager.java38
-rw-r--r--core/java/android/app/IActivityController.aidl7
-rw-r--r--core/java/android/app/IActivityManager.java15
-rw-r--r--core/java/android/app/Service.java2
-rw-r--r--core/java/android/content/Intent.java4
-rw-r--r--core/java/android/content/RestrictionEntry.java3
-rw-r--r--core/java/android/content/UndoManager.java932
-rw-r--r--core/java/android/content/UndoOperation.java110
-rw-r--r--core/java/android/content/UndoOwner.java55
-rw-r--r--core/java/android/content/pm/PackageParser.java16
-rw-r--r--core/java/android/content/res/Resources.java10
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java2
-rw-r--r--core/java/android/hardware/location/GeofenceHardwareImpl.java74
-rw-r--r--core/java/android/net/nsd/NsdManager.java3
-rw-r--r--core/java/android/os/Binder.java33
-rw-r--r--core/java/android/os/ParcelableParcel.java75
-rw-r--r--core/java/android/os/Trace.java2
-rw-r--r--core/java/android/provider/Settings.java11
-rw-r--r--core/java/android/speech/tts/TextToSpeech.java38
-rw-r--r--core/java/android/text/method/LinkMovementMethod.java5
-rw-r--r--core/java/android/view/FocusFinder.java32
-rw-r--r--core/java/android/view/GLES20Canvas.java75
-rw-r--r--core/java/android/view/GLES20DisplayList.java9
-rw-r--r--core/java/android/view/GLES20RecordingCanvas.java11
-rw-r--r--core/java/android/view/GLES20RenderLayer.java7
-rw-r--r--core/java/android/view/GLES20TextureLayer.java5
-rw-r--r--core/java/android/view/GraphicBuffer.aidl19
-rw-r--r--core/java/android/view/GraphicBuffer.java229
-rw-r--r--core/java/android/view/HardwareLayer.java12
-rw-r--r--core/java/android/view/HardwareRenderer.java179
-rw-r--r--core/java/android/view/IAssetAtlas.aidl47
-rw-r--r--core/java/android/view/Surface.java150
-rw-r--r--core/java/android/view/SurfaceControl.java10
-rw-r--r--core/java/android/view/SurfaceView.java9
-rw-r--r--core/java/android/view/View.java8
-rw-r--r--core/java/android/view/ViewGroup.java25
-rw-r--r--core/java/android/view/ViewRootImpl.java67
-rw-r--r--core/java/android/widget/Editor.java178
-rw-r--r--core/java/android/widget/TextView.java72
40 files changed, 2316 insertions, 284 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 3dbb636..b197f90 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1916,6 +1916,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case HANG_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder who = data.readStrongBinder();
+ boolean allowRestart = data.readInt() != 0;
+ hang(who, allowRestart);
+ reply.writeNoException();
+ return true;
+ }
+
}
return super.onTransact(code, data, reply, flags);
@@ -4387,5 +4396,17 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ public void hang(IBinder who, boolean allowRestart) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(who);
+ data.writeInt(allowRestart ? 1 : 0);
+ mRemote.transact(HANG_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c9776f1..948210c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -95,8 +95,17 @@ public class AppOpsManager {
public static final int OP_PLAY_AUDIO = 28;
public static final int OP_READ_CLIPBOARD = 29;
public static final int OP_WRITE_CLIPBOARD = 30;
+ public static final int OP_TAKE_MEDIA_BUTTONS = 31;
+ public static final int OP_TAKE_AUDIO_FOCUS = 32;
+ public static final int OP_AUDIO_MASTER_VOLUME = 33;
+ public static final int OP_AUDIO_VOICE_VOLUME = 34;
+ public static final int OP_AUDIO_RING_VOLUME = 35;
+ public static final int OP_AUDIO_MEDIA_VOLUME = 36;
+ public static final int OP_AUDIO_ALARM_VOLUME = 37;
+ public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38;
+ public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39;
/** @hide */
- public static final int _NUM_OP = 31;
+ public static final int _NUM_OP = 40;
/**
* This maps each operation to the operation that serves as the
@@ -138,6 +147,15 @@ public class AppOpsManager {
OP_PLAY_AUDIO,
OP_READ_CLIPBOARD,
OP_WRITE_CLIPBOARD,
+ OP_TAKE_MEDIA_BUTTONS,
+ OP_TAKE_AUDIO_FOCUS,
+ OP_AUDIO_MASTER_VOLUME,
+ OP_AUDIO_VOICE_VOLUME,
+ OP_AUDIO_RING_VOLUME,
+ OP_AUDIO_MEDIA_VOLUME,
+ OP_AUDIO_ALARM_VOLUME,
+ OP_AUDIO_NOTIFICATION_VOLUME,
+ OP_AUDIO_BLUETOOTH_VOLUME,
};
/**
@@ -176,6 +194,15 @@ public class AppOpsManager {
"PLAY_AUDIO",
"READ_CLIPBOARD",
"WRITE_CLIPBOARD",
+ "TAKE_MEDIA_BUTTONS",
+ "TAKE_AUDIO_FOCUS",
+ "AUDIO_MASTER_VOLUME",
+ "AUDIO_VOICE_VOLUME",
+ "AUDIO_RING_VOLUME",
+ "AUDIO_MEDIA_VOLUME",
+ "AUDIO_ALARM_VOLUME",
+ "AUDIO_NOTIFICATION_VOLUME",
+ "AUDIO_BLUETOOTH_VOLUME",
};
/**
@@ -214,6 +241,15 @@ public class AppOpsManager {
null, // no permission for playing audio
null, // no permission for reading clipboard
null, // no permission for writing clipboard
+ null, // no permission for taking media buttons
+ null, // no permission for taking audio focus
+ null, // no permission for changing master volume
+ null, // no permission for changing voice volume
+ null, // no permission for changing ring volume
+ null, // no permission for changing media volume
+ null, // no permission for changing alarm volume
+ null, // no permission for changing notification volume
+ null, // no permission for changing bluetooth volume
};
/**
diff --git a/core/java/android/app/IActivityController.aidl b/core/java/android/app/IActivityController.aidl
index aca8305..952c900 100644
--- a/core/java/android/app/IActivityController.aidl
+++ b/core/java/android/app/IActivityController.aidl
@@ -58,4 +58,11 @@ interface IActivityController
* immediately.
*/
int appNotResponding(String processName, int pid, String processStats);
+
+ /**
+ * The system process watchdog has detected that the system seems to be
+ * hung. Return 1 to continue waiting, or -1 to let it continue with its
+ * normal kill.
+ */
+ int systemNotResponding(String msg);
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5a798de..334a304 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -386,6 +386,8 @@ public interface IActivityManager extends IInterface {
public void killUid(int uid, String reason) throws RemoteException;
+ public void hang(IBinder who, boolean allowRestart) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -654,10 +656,11 @@ public interface IActivityManager extends IInterface {
int REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+162;
int GET_LAUNCHED_FROM_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+163;
int KILL_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+164;
- int CREATE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+165;
- int MOVE_TASK_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+166;
- int RESIZE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+167;
- int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+168;
- int GET_STACKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+169;
- int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170;
+ int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+165;
+ int HANG_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+166;
+ int CREATE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+167;
+ int MOVE_TASK_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+168;
+ int RESIZE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+169;
+ int GET_STACKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170;
+ int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171;
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 4fbca73..3967740 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -633,7 +633,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
*
* @param id The identifier for this notification as per
* {@link NotificationManager#notify(int, Notification)
- * NotificationManager.notify(int, Notification)}.
+ * NotificationManager.notify(int, Notification)}; must not be 0.
* @param notification The Notification to be displayed.
*
* @see #stopForeground(boolean)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8352635..793736d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2355,6 +2355,8 @@ public class Intent implements Parcelable, Cloneable {
* </p>
*
* @see #ACTION_IDLE_MAINTENANCE_END
+ *
+ * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_IDLE_MAINTENANCE_START =
@@ -2382,6 +2384,8 @@ public class Intent implements Parcelable, Cloneable {
* by the system.
*
* @see #ACTION_IDLE_MAINTENANCE_START
+ *
+ * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_IDLE_MAINTENANCE_END =
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 217cf76..283a097 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -109,6 +109,7 @@ public class RestrictionEntry implements Parcelable {
*/
public RestrictionEntry(String key, String selectedString) {
this.key = key;
+ this.type = TYPE_CHOICE;
this.currentValue = selectedString;
}
@@ -119,6 +120,7 @@ public class RestrictionEntry implements Parcelable {
*/
public RestrictionEntry(String key, boolean selectedState) {
this.key = key;
+ this.type = TYPE_BOOLEAN;
setSelectedState(selectedState);
}
@@ -129,6 +131,7 @@ public class RestrictionEntry implements Parcelable {
*/
public RestrictionEntry(String key, String[] selectedStrings) {
this.key = key;
+ this.type = TYPE_MULTI_SELECT;
this.currentValues = selectedStrings;
}
diff --git a/core/java/android/content/UndoManager.java b/core/java/android/content/UndoManager.java
new file mode 100644
index 0000000..1c2db47
--- /dev/null
+++ b/core/java/android/content/UndoManager.java
@@ -0,0 +1,932 @@
+/*
+ * 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;
+import android.os.ParcelableParcel;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Top-level class for managing and interacting with the global undo state for
+ * a document or application. This class supports both undo and redo and has
+ * helpers for merging undoable operations together as they are performed.
+ *
+ * <p>A single undoable operation is represented by {@link UndoOperation} which
+ * apps implement to define their undo/redo behavior. The UndoManager keeps
+ * a stack of undo states; each state can have one or more undo operations
+ * inside of it.</p>
+ *
+ * <p>Updates to the stack must be done inside of a {@link #beginUpdate}/{@link #endUpdate()}
+ * pair. During this time you can add new operations to the stack with
+ * {@link #addOperation}, retrieve and modify existing operations with
+ * {@link #getLastOperation}, control the label shown to the user for this operation
+ * with {@link #setUndoLabel} and {@link #suggestUndoLabel}, etc.</p>
+ *
+ * <p>Every {link UndoOperation} is associated with an {@link UndoOwner}, which identifies
+ * the data it belongs to. The owner is used to indicate how operations are dependent
+ * on each other -- operations with the same owner are dependent on others with the
+ * same owner. For example, you may have a document with multiple embedded objects. If the
+ * document itself and each embedded object use different owners, then you
+ * can provide undo semantics appropriate to the user's context: while within
+ * an embedded object, only edits to that object are seen and the user can
+ * undo/redo them without needing to impact edits in other objects; while
+ * within the larger document, all edits can be seen and the user must
+ * undo/redo them as a single stream.</p>
+ */
+public class UndoManager {
+ private final HashMap<String, UndoOwner> mOwners = new HashMap<String, UndoOwner>();
+ private final ArrayList<UndoState> mUndos = new ArrayList<UndoState>();
+ private final ArrayList<UndoState> mRedos = new ArrayList<UndoState>();
+ private int mUpdateCount;
+ private int mHistorySize = 20;
+ private UndoState mWorking;
+ private int mCommitId = 1;
+ private boolean mInUndo;
+ private boolean mMerged;
+
+ private int mStateSeq;
+ private int mNextSavedIdx;
+ private UndoOwner[] mStateOwners;
+
+ /**
+ * Never merge with the last undo state.
+ */
+ public static final int MERGE_MODE_NONE = 0;
+
+ /**
+ * Allow merge with the last undo state only if it contains
+ * operations with the caller's owner.
+ */
+ public static final int MERGE_MODE_UNIQUE = 1;
+
+ /**
+ * Always allow merge with the last undo state, if possible.
+ */
+ public static final int MERGE_MODE_ANY = 2;
+
+ public UndoOwner getOwner(String tag, Object data) {
+ if (tag == null) {
+ throw new NullPointerException("tag can't be null");
+ }
+ if (data == null) {
+ throw new NullPointerException("data can't be null");
+ }
+ UndoOwner owner = mOwners.get(tag);
+ if (owner != null) {
+ if (owner.mData != data) {
+ if (owner.mData != null) {
+ throw new IllegalStateException("Owner " + owner + " already exists with data "
+ + owner.mData + " but giving different data " + data);
+ }
+ owner.mData = data;
+ }
+ return owner;
+ }
+
+ owner = new UndoOwner(tag);
+ owner.mManager = this;
+ owner.mData = data;
+ mOwners.put(tag, owner);
+ return owner;
+ }
+
+ void removeOwner(UndoOwner owner) {
+ // XXX need to figure out how to prune.
+ if (false) {
+ mOwners.remove(owner.mTag);
+ owner.mManager = null;
+ }
+ }
+
+ /**
+ * Flatten the current undo state into a Parcelable object, which can later be restored
+ * with {@link #restoreInstanceState(android.os.Parcelable)}.
+ */
+ public Parcelable saveInstanceState() {
+ if (mUpdateCount > 0) {
+ throw new IllegalStateException("Can't save state while updating");
+ }
+ ParcelableParcel pp = new ParcelableParcel(getClass().getClassLoader());
+ Parcel p = pp.getParcel();
+ mStateSeq++;
+ if (mStateSeq <= 0) {
+ mStateSeq = 0;
+ }
+ mNextSavedIdx = 0;
+ p.writeInt(mHistorySize);
+ p.writeInt(mOwners.size());
+ // XXX eventually we need to be smart here about limiting the
+ // number of undo states we write to not exceed X bytes.
+ int i = mUndos.size();
+ while (i > 0) {
+ p.writeInt(1);
+ i--;
+ mUndos.get(i).writeToParcel(p);
+ }
+ i = mRedos.size();
+ p.writeInt(i);
+ while (i > 0) {
+ p.writeInt(2);
+ i--;
+ mRedos.get(i).writeToParcel(p);
+ }
+ p.writeInt(0);
+ return pp;
+ }
+
+ void saveOwner(UndoOwner owner, Parcel out) {
+ if (owner.mStateSeq == mStateSeq) {
+ out.writeInt(owner.mSavedIdx);
+ } else {
+ owner.mStateSeq = mStateSeq;
+ owner.mSavedIdx = mNextSavedIdx;
+ out.writeInt(owner.mSavedIdx);
+ out.writeString(owner.mTag);
+ mNextSavedIdx++;
+ }
+ }
+
+ /**
+ * Restore an undo state previously created with {@link #saveInstanceState()}. This will
+ * restore the UndoManager's state to almost exactly what it was at the point it had
+ * been previously saved; the only information not restored is the data object
+ * associated with each {@link UndoOwner}, which requires separate calls to
+ * {@link #getOwner(String, Object)} to re-associate the owner with its data.
+ */
+ public void restoreInstanceState(Parcelable state) {
+ if (mUpdateCount > 0) {
+ throw new IllegalStateException("Can't save state while updating");
+ }
+ forgetUndos(null, -1);
+ forgetRedos(null, -1);
+ ParcelableParcel pp = (ParcelableParcel)state;
+ Parcel p = pp.getParcel();
+ mHistorySize = p.readInt();
+ mStateOwners = new UndoOwner[p.readInt()];
+
+ int stype;
+ while ((stype=p.readInt()) != 0) {
+ UndoState ustate = new UndoState(this, p, pp.getClassLoader());
+ if (stype == 1) {
+ mUndos.add(0, ustate);
+ } else {
+ mRedos.add(0, ustate);
+ }
+ }
+ }
+
+ UndoOwner restoreOwner(Parcel in) {
+ int idx = in.readInt();
+ UndoOwner owner = mStateOwners[idx];
+ if (owner == null) {
+ String tag = in.readString();
+ owner = new UndoOwner(tag);
+ mStateOwners[idx] = owner;
+ mOwners.put(tag, owner);
+ }
+ return owner;
+ }
+
+ /**
+ * Set the maximum number of undo states that will be retained.
+ */
+ public void setHistorySize(int size) {
+ mHistorySize = size;
+ if (mHistorySize >= 0 && countUndos(null) > mHistorySize) {
+ forgetUndos(null, countUndos(null) - mHistorySize);
+ }
+ }
+
+ /**
+ * Return the current maximum number of undo states.
+ */
+ public int getHistorySize() {
+ return mHistorySize;
+ }
+
+ /**
+ * Perform undo of last/top <var>count</var> undo states. The states impacted
+ * by this can be limited through <var>owners</var>.
+ * @param owners Optional set of owners that should be impacted. If null, all
+ * undo states will be visible and available for undo. If non-null, only those
+ * states that contain one of the owners specified here will be visible.
+ * @param count Number of undo states to pop.
+ * @return Returns the number of undo states that were actually popped.
+ */
+ public int undo(UndoOwner[] owners, int count) {
+ if (mWorking != null) {
+ throw new IllegalStateException("Can't be called during an update");
+ }
+
+ int num = 0;
+ int i = -1;
+
+ mInUndo = true;
+
+ UndoState us = getTopUndo(null);
+ if (us != null) {
+ us.makeExecuted();
+ }
+
+ while (count > 0 && (i=findPrevState(mUndos, owners, i)) >= 0) {
+ UndoState state = mUndos.remove(i);
+ state.undo();
+ mRedos.add(state);
+ count--;
+ num++;
+ }
+
+ mInUndo = false;
+
+ return num;
+ }
+
+ /**
+ * Perform redo of last/top <var>count</var> undo states in the transient redo stack.
+ * The states impacted by this can be limited through <var>owners</var>.
+ * @param owners Optional set of owners that should be impacted. If null, all
+ * undo states will be visible and available for undo. If non-null, only those
+ * states that contain one of the owners specified here will be visible.
+ * @param count Number of undo states to pop.
+ * @return Returns the number of undo states that were actually redone.
+ */
+ public int redo(UndoOwner[] owners, int count) {
+ if (mWorking != null) {
+ throw new IllegalStateException("Can't be called during an update");
+ }
+
+ int num = 0;
+ int i = -1;
+
+ mInUndo = true;
+
+ while (count > 0 && (i=findPrevState(mRedos, owners, i)) >= 0) {
+ UndoState state = mRedos.remove(i);
+ state.redo();
+ mUndos.add(state);
+ count--;
+ num++;
+ }
+
+ mInUndo = false;
+
+ return num;
+ }
+
+ /**
+ * Returns true if we are currently inside of an undo/redo operation. This is
+ * useful for editors to know whether they should be generating new undo state
+ * when they see edit operations happening.
+ */
+ public boolean isInUndo() {
+ return mInUndo;
+ }
+
+ public int forgetUndos(UndoOwner[] owners, int count) {
+ if (count < 0) {
+ count = mUndos.size();
+ }
+
+ int removed = 0;
+ for (int i=0; i<mUndos.size() && removed < count; i++) {
+ UndoState state = mUndos.get(i);
+ if (count > 0 && matchOwners(state, owners)) {
+ state.destroy();
+ mUndos.remove(i);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ public int forgetRedos(UndoOwner[] owners, int count) {
+ if (count < 0) {
+ count = mRedos.size();
+ }
+
+ int removed = 0;
+ for (int i=0; i<mRedos.size() && removed < count; i++) {
+ UndoState state = mRedos.get(i);
+ if (count > 0 && matchOwners(state, owners)) {
+ state.destroy();
+ mRedos.remove(i);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /**
+ * Return the number of undo states on the undo stack.
+ * @param owners If non-null, only those states containing an operation with one of
+ * the owners supplied here will be counted.
+ */
+ public int countUndos(UndoOwner[] owners) {
+ if (owners == null) {
+ return mUndos.size();
+ }
+
+ int count=0;
+ int i=0;
+ while ((i=findNextState(mUndos, owners, i)) >= 0) {
+ count++;
+ i++;
+ }
+ return count;
+ }
+
+ /**
+ * Return the number of redo states on the undo stack.
+ * @param owners If non-null, only those states containing an operation with one of
+ * the owners supplied here will be counted.
+ */
+ public int countRedos(UndoOwner[] owners) {
+ if (owners == null) {
+ return mRedos.size();
+ }
+
+ int count=0;
+ int i=0;
+ while ((i=findNextState(mRedos, owners, i)) >= 0) {
+ count++;
+ i++;
+ }
+ return count;
+ }
+
+ /**
+ * Return the user-visible label for the top undo state on the stack.
+ * @param owners If non-null, will select the top-most undo state containing an
+ * operation with one of the owners supplied here.
+ */
+ public CharSequence getUndoLabel(UndoOwner[] owners) {
+ UndoState state = getTopUndo(owners);
+ return state != null ? state.getLabel() : null;
+ }
+
+ /**
+ * Return the user-visible label for the top redo state on the stack.
+ * @param owners If non-null, will select the top-most undo state containing an
+ * operation with one of the owners supplied here.
+ */
+ public CharSequence getRedoLabel(UndoOwner[] owners) {
+ UndoState state = getTopRedo(owners);
+ return state != null ? state.getLabel() : null;
+ }
+
+ /**
+ * Start creating a new undo state. Multiple calls to this function will nest until
+ * they are all matched by a later call to {@link #endUpdate}.
+ * @param label Optional user-visible label for this new undo state.
+ */
+ public void beginUpdate(CharSequence label) {
+ if (mInUndo) {
+ throw new IllegalStateException("Can't being update while performing undo/redo");
+ }
+ if (mUpdateCount <= 0) {
+ createWorkingState();
+ mMerged = false;
+ mUpdateCount = 0;
+ }
+
+ mWorking.updateLabel(label);
+ mUpdateCount++;
+ }
+
+ private void createWorkingState() {
+ mWorking = new UndoState(this, mCommitId++);
+ if (mCommitId < 0) {
+ mCommitId = 1;
+ }
+ }
+
+ /**
+ * Returns true if currently inside of a {@link #beginUpdate}.
+ */
+ public boolean isInUpdate() {
+ return mUpdateCount > 0;
+ }
+
+ /**
+ * Forcibly set a new for the new undo state being built within a {@link #beginUpdate}.
+ * Any existing label will be replaced with this one.
+ */
+ public void setUndoLabel(CharSequence label) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ mWorking.setLabel(label);
+ }
+
+ /**
+ * Set a new for the new undo state being built within a {@link #beginUpdate}, but
+ * only if there is not a label currently set for it.
+ */
+ public void suggestUndoLabel(CharSequence label) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ mWorking.updateLabel(label);
+ }
+
+ /**
+ * Return the number of times {@link #beginUpdate} has been called without a matching
+ * {@link #endUpdate} call.
+ */
+ public int getUpdateNestingLevel() {
+ return mUpdateCount;
+ }
+
+ /**
+ * Check whether there is an {@link UndoOperation} in the current {@link #beginUpdate}
+ * undo state.
+ * @param owner Optional owner of the operation to look for. If null, will succeed
+ * if there is any operation; if non-null, will only succeed if there is an operation
+ * with the given owner.
+ * @return Returns true if there is a matching operation in the current undo state.
+ */
+ public boolean hasOperation(UndoOwner owner) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ return mWorking.hasOperation(owner);
+ }
+
+ /**
+ * Return the most recent {@link UndoOperation} that was added to the update.
+ * @param mergeMode May be either {@link #MERGE_MODE_NONE} or {@link #MERGE_MODE_ANY}.
+ */
+ public UndoOperation<?> getLastOperation(int mergeMode) {
+ return getLastOperation(null, null, mergeMode);
+ }
+
+ /**
+ * Return the most recent {@link UndoOperation} that was added to the update and
+ * has the given owner.
+ * @param owner Optional owner of last operation to retrieve. If null, the last
+ * operation regardless of owner will be retrieved; if non-null, the last operation
+ * matching the given owner will be retrieved.
+ * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE},
+ * or {@link #MERGE_MODE_ANY}.
+ */
+ public UndoOperation<?> getLastOperation(UndoOwner owner, int mergeMode) {
+ return getLastOperation(null, owner, mergeMode);
+ }
+
+ /**
+ * Return the most recent {@link UndoOperation} that was added to the update and
+ * has the given owner.
+ * @param clazz Optional class of the last operation to retrieve. If null, the
+ * last operation regardless of class will be retrieved; if non-null, the last
+ * operation whose class is the same as the given class will be retrieved.
+ * @param owner Optional owner of last operation to retrieve. If null, the last
+ * operation regardless of owner will be retrieved; if non-null, the last operation
+ * matching the given owner will be retrieved.
+ * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE},
+ * or {@link #MERGE_MODE_ANY}.
+ */
+ public <T extends UndoOperation> T getLastOperation(Class<T> clazz, UndoOwner owner,
+ int mergeMode) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ if (mergeMode != MERGE_MODE_NONE && !mMerged && !mWorking.hasData()) {
+ UndoState state = getTopUndo(null);
+ UndoOperation<?> last;
+ if (state != null && (mergeMode == MERGE_MODE_ANY || !state.hasMultipleOwners())
+ && state.canMerge() && (last=state.getLastOperation(clazz, owner)) != null) {
+ if (last.allowMerge()) {
+ mWorking.destroy();
+ mWorking = state;
+ mUndos.remove(state);
+ mMerged = true;
+ return (T)last;
+ }
+ }
+ }
+
+ return mWorking.getLastOperation(clazz, owner);
+ }
+
+ /**
+ * Add a new UndoOperation to the current update.
+ * @param op The new operation to add.
+ * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE},
+ * or {@link #MERGE_MODE_ANY}.
+ */
+ public void addOperation(UndoOperation<?> op, int mergeMode) {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ UndoOwner owner = op.getOwner();
+ if (owner.mManager != this) {
+ throw new IllegalArgumentException(
+ "Given operation's owner is not in this undo manager.");
+ }
+ if (mergeMode != MERGE_MODE_NONE && !mMerged && !mWorking.hasData()) {
+ UndoState state = getTopUndo(null);
+ if (state != null && (mergeMode == MERGE_MODE_ANY || !state.hasMultipleOwners())
+ && state.canMerge() && state.hasOperation(op.getOwner())) {
+ mWorking.destroy();
+ mWorking = state;
+ mUndos.remove(state);
+ mMerged = true;
+ }
+ }
+ mWorking.addOperation(op);
+ }
+
+ /**
+ * Finish the creation of an undo state, matching a previous call to
+ * {@link #beginUpdate}.
+ */
+ public void endUpdate() {
+ if (mWorking == null) {
+ throw new IllegalStateException("Must be called during an update");
+ }
+ mUpdateCount--;
+
+ if (mUpdateCount == 0) {
+ pushWorkingState();
+ }
+ }
+
+ private void pushWorkingState() {
+ int N = mUndos.size() + 1;
+
+ if (mWorking.hasData()) {
+ mUndos.add(mWorking);
+ forgetRedos(null, -1);
+ mWorking.commit();
+ if (N >= 2) {
+ // The state before this one can no longer be merged, ever.
+ // The only way to get back to it is for the user to perform
+ // an undo.
+ mUndos.get(N-2).makeExecuted();
+ }
+ } else {
+ mWorking.destroy();
+ }
+ mWorking = null;
+
+ if (mHistorySize >= 0 && N > mHistorySize) {
+ forgetUndos(null, N - mHistorySize);
+ }
+ }
+
+ /**
+ * Commit the last finished undo state. This undo state can no longer be
+ * modified with further {@link #MERGE_MODE_UNIQUE} or
+ * {@link #MERGE_MODE_ANY} merge modes. If called while inside of an update,
+ * this will push any changes in the current update on to the undo stack
+ * and result with a fresh undo state, behaving as if {@link #endUpdate()}
+ * had been called enough to unwind the current update, then the last state
+ * committed, and {@link #beginUpdate} called to restore the update nesting.
+ * @param owner The optional owner to determine whether to perform the commit.
+ * If this is non-null, the commit will only execute if the current top undo
+ * state contains an operation with the given owner.
+ * @return Returns an integer identifier for the committed undo state, which
+ * can later be used to try to uncommit the state to perform further edits on it.
+ */
+ public int commitState(UndoOwner owner) {
+ if (mWorking != null && mWorking.hasData()) {
+ if (owner == null || mWorking.hasOperation(owner)) {
+ mWorking.setCanMerge(false);
+ int commitId = mWorking.getCommitId();
+ pushWorkingState();
+ createWorkingState();
+ mMerged = true;
+ return commitId;
+ }
+ } else {
+ UndoState state = getTopUndo(null);
+ if (state != null && (owner == null || state.hasOperation(owner))) {
+ state.setCanMerge(false);
+ return state.getCommitId();
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Attempt to undo a previous call to {@link #commitState}. This will work
+ * if the undo state at the top of the stack has the given id, and has not been
+ * involved in an undo operation. Otherwise false is returned.
+ * @param commitId The identifier for the state to be uncommitted, as returned
+ * by {@link #commitState}.
+ * @param owner Optional owner that must appear in the committed state.
+ * @return Returns true if the uncommit is successful, else false.
+ */
+ public boolean uncommitState(int commitId, UndoOwner owner) {
+ if (mWorking != null && mWorking.getCommitId() == commitId) {
+ if (owner == null || mWorking.hasOperation(owner)) {
+ return mWorking.setCanMerge(true);
+ }
+ } else {
+ UndoState state = getTopUndo(null);
+ if (state != null && (owner == null || state.hasOperation(owner))) {
+ if (state.getCommitId() == commitId) {
+ return state.setCanMerge(true);
+ }
+ }
+ }
+ return false;
+ }
+
+ UndoState getTopUndo(UndoOwner[] owners) {
+ if (mUndos.size() <= 0) {
+ return null;
+ }
+ int i = findPrevState(mUndos, owners, -1);
+ return i >= 0 ? mUndos.get(i) : null;
+ }
+
+ UndoState getTopRedo(UndoOwner[] owners) {
+ if (mRedos.size() <= 0) {
+ return null;
+ }
+ int i = findPrevState(mRedos, owners, -1);
+ return i >= 0 ? mRedos.get(i) : null;
+ }
+
+ boolean matchOwners(UndoState state, UndoOwner[] owners) {
+ if (owners == null) {
+ return true;
+ }
+ for (int i=0; i<owners.length; i++) {
+ if (state.matchOwner(owners[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int findPrevState(ArrayList<UndoState> states, UndoOwner[] owners, int from) {
+ final int N = states.size();
+
+ if (from == -1) {
+ from = N-1;
+ }
+ if (from >= N) {
+ return -1;
+ }
+ if (owners == null) {
+ return from;
+ }
+
+ while (from >= 0) {
+ UndoState state = states.get(from);
+ if (matchOwners(state, owners)) {
+ return from;
+ }
+ from--;
+ }
+
+ return -1;
+ }
+
+ int findNextState(ArrayList<UndoState> states, UndoOwner[] owners, int from) {
+ final int N = states.size();
+
+ if (from < 0) {
+ from = 0;
+ }
+ if (from >= N) {
+ return -1;
+ }
+ if (owners == null) {
+ return from;
+ }
+
+ while (from < N) {
+ UndoState state = states.get(from);
+ if (matchOwners(state, owners)) {
+ return from;
+ }
+ from++;
+ }
+
+ return -1;
+ }
+
+ final static class UndoState {
+ private final UndoManager mManager;
+ private final int mCommitId;
+ private final ArrayList<UndoOperation<?>> mOperations = new ArrayList<UndoOperation<?>>();
+ private ArrayList<UndoOperation<?>> mRecent;
+ private CharSequence mLabel;
+ private boolean mCanMerge = true;
+ private boolean mExecuted;
+
+ UndoState(UndoManager manager, int commitId) {
+ mManager = manager;
+ mCommitId = commitId;
+ }
+
+ UndoState(UndoManager manager, Parcel p, ClassLoader loader) {
+ mManager = manager;
+ mCommitId = p.readInt();
+ mCanMerge = p.readInt() != 0;
+ mExecuted = p.readInt() != 0;
+ mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
+ final int N = p.readInt();
+ for (int i=0; i<N; i++) {
+ UndoOwner owner = mManager.restoreOwner(p);
+ UndoOperation op = (UndoOperation)p.readParcelable(loader);
+ op.mOwner = owner;
+ mOperations.add(op);
+ }
+ }
+
+ void writeToParcel(Parcel p) {
+ if (mRecent != null) {
+ throw new IllegalStateException("Can't save state before committing");
+ }
+ p.writeInt(mCommitId);
+ p.writeInt(mCanMerge ? 1 : 0);
+ p.writeInt(mExecuted ? 1 : 0);
+ TextUtils.writeToParcel(mLabel, p, 0);
+ final int N = mOperations.size();
+ p.writeInt(N);
+ for (int i=0; i<N; i++) {
+ UndoOperation op = mOperations.get(i);
+ mManager.saveOwner(op.mOwner, p);
+ p.writeParcelable(op, 0);
+ }
+ }
+
+ int getCommitId() {
+ return mCommitId;
+ }
+
+ void setLabel(CharSequence label) {
+ mLabel = label;
+ }
+
+ void updateLabel(CharSequence label) {
+ if (mLabel != null) {
+ mLabel = label;
+ }
+ }
+
+ CharSequence getLabel() {
+ return mLabel;
+ }
+
+ boolean setCanMerge(boolean state) {
+ // Don't allow re-enabling of merging if state has been executed.
+ if (state && mExecuted) {
+ return false;
+ }
+ mCanMerge = state;
+ return true;
+ }
+
+ void makeExecuted() {
+ mExecuted = true;
+ }
+
+ boolean canMerge() {
+ return mCanMerge && !mExecuted;
+ }
+
+ int countOperations() {
+ return mOperations.size();
+ }
+
+ boolean hasOperation(UndoOwner owner) {
+ final int N = mOperations.size();
+ if (owner == null) {
+ return N != 0;
+ }
+ for (int i=0; i<N; i++) {
+ if (mOperations.get(i).getOwner() == owner) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean hasMultipleOwners() {
+ final int N = mOperations.size();
+ if (N <= 1) {
+ return false;
+ }
+ UndoOwner owner = mOperations.get(0).getOwner();
+ for (int i=1; i<N; i++) {
+ if (mOperations.get(i).getOwner() != owner) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void addOperation(UndoOperation<?> op) {
+ if (mOperations.contains(op)) {
+ throw new IllegalStateException("Already holds " + op);
+ }
+ mOperations.add(op);
+ if (mRecent == null) {
+ mRecent = new ArrayList<UndoOperation<?>>();
+ mRecent.add(op);
+ }
+ op.mOwner.mOpCount++;
+ }
+
+ <T extends UndoOperation> T getLastOperation(Class<T> clazz, UndoOwner owner) {
+ final int N = mOperations.size();
+ if (clazz == null && owner == null) {
+ return N > 0 ? (T)mOperations.get(N-1) : null;
+ }
+ // First look for the top-most operation with the same owner.
+ for (int i=N-1; i>=0; i--) {
+ UndoOperation<?> op = mOperations.get(i);
+ if (owner != null && op.getOwner() != owner) {
+ continue;
+ }
+ // Return this operation if it has the same class that the caller wants.
+ // Note that we don't search deeper for the class, because we don't want
+ // to end up with a different order of operations for the same owner.
+ if (clazz != null && op.getClass() != clazz) {
+ return null;
+ }
+ return (T)op;
+ }
+
+ return null;
+ }
+
+ boolean matchOwner(UndoOwner owner) {
+ for (int i=mOperations.size()-1; i>=0; i--) {
+ if (mOperations.get(i).matchOwner(owner)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean hasData() {
+ for (int i=mOperations.size()-1; i>=0; i--) {
+ if (mOperations.get(i).hasData()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void commit() {
+ final int N = mRecent != null ? mRecent.size() : 0;
+ for (int i=0; i<N; i++) {
+ mRecent.get(i).commit();
+ }
+ mRecent = null;
+ }
+
+ void undo() {
+ for (int i=mOperations.size()-1; i>=0; i--) {
+ mOperations.get(i).undo();
+ }
+ }
+
+ void redo() {
+ final int N = mOperations.size();
+ for (int i=0; i<N; i++) {
+ mOperations.get(i).redo();
+ }
+ }
+
+ void destroy() {
+ for (int i=mOperations.size()-1; i>=0; i--) {
+ UndoOwner owner = mOperations.get(i).mOwner;
+ owner.mOpCount--;
+ if (owner.mOpCount <= 0) {
+ if (owner.mOpCount < 0) {
+ throw new IllegalStateException("Underflow of op count on owner " + owner
+ + " in op " + mOperations.get(i));
+ }
+ mManager.removeOwner(owner);
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/content/UndoOperation.java b/core/java/android/content/UndoOperation.java
new file mode 100644
index 0000000..8084b1f
--- /dev/null
+++ b/core/java/android/content/UndoOperation.java
@@ -0,0 +1,110 @@
+/*
+ * 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;
+ }
+}
diff --git a/core/java/android/content/UndoOwner.java b/core/java/android/content/UndoOwner.java
new file mode 100644
index 0000000..a279de6
--- /dev/null
+++ b/core/java/android/content/UndoOwner.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+ }
+}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 64eaf9b..1b997f0 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -293,7 +293,10 @@ public class PackageParser {
pi.sharedUserLabel = p.mSharedUserLabel;
pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
pi.installLocation = p.installLocation;
- pi.requiredForAllUsers = p.mRequiredForAllUsers;
+ if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0
+ || (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ pi.requiredForAllUsers = p.mRequiredForAllUsers;
+ }
pi.restrictedAccountType = p.mRestrictedAccountType;
pi.requiredAccountType = p.mRequiredAccountType;
pi.firstInstallTime = firstInstallTime;
@@ -1893,11 +1896,12 @@ public class PackageParser {
false)) {
ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
}
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers,
- false)) {
- owner.mRequiredForAllUsers = true;
- }
+ }
+
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers,
+ false)) {
+ owner.mRequiredForAllUsers = true;
}
String restrictedAccountType = sa.getString(com.android.internal.R.styleable
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 42f4faf..c7976c3 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -16,8 +16,6 @@
package android.content.res;
-import android.os.Trace;
-import android.view.View;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -30,6 +28,7 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.os.Build;
import android.os.Bundle;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -1985,6 +1984,13 @@ public class Resources {
}
}
+ /**
+ * @hide
+ */
+ public LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
+ return sPreloadedDrawables[0];
+ }
+
private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
int resourceId, String name) {
// We allow preloading of resources even if they vary by font scale (which
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 4f59e8e..725a1ff 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -30,9 +30,9 @@ import android.util.Log;
import android.util.LruCache;
import android.util.Printer;
-import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.Map;
import java.util.regex.Pattern;
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index e3362a7..9823c49 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -470,11 +470,12 @@ public final class GeofenceHardwareImpl {
synchronized (mGeofences) {
callback = mGeofences.get(geofenceId);
}
- if (callback == null) return;
- try {
- callback.onGeofenceAdd(geofenceId, msg.arg2);
- } catch (RemoteException e) {Log.i(TAG, "Remote Exception:" + e);}
+ if (callback != null) {
+ try {
+ callback.onGeofenceAdd(geofenceId, msg.arg2);
+ } catch (RemoteException e) {Log.i(TAG, "Remote Exception:" + e);}
+ }
releaseWakeLock();
break;
case REMOVE_GEOFENCE_CALLBACK:
@@ -482,13 +483,14 @@ public final class GeofenceHardwareImpl {
synchronized (mGeofences) {
callback = mGeofences.get(geofenceId);
}
- if (callback == null) return;
- try {
- callback.onGeofenceRemove(geofenceId, msg.arg2);
- } catch (RemoteException e) {}
- synchronized (mGeofences) {
- mGeofences.remove(geofenceId);
+ if (callback != null) {
+ try {
+ callback.onGeofenceRemove(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ synchronized (mGeofences) {
+ mGeofences.remove(geofenceId);
+ }
}
releaseWakeLock();
break;
@@ -498,11 +500,12 @@ public final class GeofenceHardwareImpl {
synchronized (mGeofences) {
callback = mGeofences.get(geofenceId);
}
- if (callback == null) return;
- try {
- callback.onGeofencePause(geofenceId, msg.arg2);
- } catch (RemoteException e) {}
+ if (callback != null) {
+ try {
+ callback.onGeofencePause(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ }
releaseWakeLock();
break;
@@ -511,11 +514,12 @@ public final class GeofenceHardwareImpl {
synchronized (mGeofences) {
callback = mGeofences.get(geofenceId);
}
- if (callback == null) return;
- try {
- callback.onGeofenceResume(geofenceId, msg.arg2);
- } catch (RemoteException e) {}
+ if (callback != null) {
+ try {
+ callback.onGeofenceResume(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ }
releaseWakeLock();
break;
@@ -530,12 +534,14 @@ public final class GeofenceHardwareImpl {
" Transition: " + geofenceTransition.mTransition +
" Location: " + geofenceTransition.mLocation + ":" + mGeofences);
- try {
- callback.onGeofenceTransition(
- geofenceTransition.mGeofenceId, geofenceTransition.mTransition,
- geofenceTransition.mLocation, geofenceTransition.mTimestamp,
- GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE);
- } catch (RemoteException e) {}
+ if (callback != null) {
+ try {
+ callback.onGeofenceTransition(
+ geofenceTransition.mGeofenceId, geofenceTransition.mTransition,
+ geofenceTransition.mLocation, geofenceTransition.mTimestamp,
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE);
+ } catch (RemoteException e) {}
+ }
releaseWakeLock();
break;
case GEOFENCE_CALLBACK_BINDER_DIED:
@@ -572,16 +578,16 @@ public final class GeofenceHardwareImpl {
available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ?
true : false);
callbackList = mCallbacks[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE];
- if (callbackList == null) return;
-
- if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available);
-
- for (IGeofenceHardwareMonitorCallback c: callbackList) {
- try {
- c.onMonitoringSystemChange(
- GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available,
- location);
- } catch (RemoteException e) {}
+ if (callbackList != null) {
+ if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available);
+
+ for (IGeofenceHardwareMonitorCallback c: callbackList) {
+ try {
+ c.onMonitoringSystemChange(
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available,
+ location);
+ } catch (RemoteException e) {}
+ }
}
releaseWakeLock();
break;
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index 08ba728..9c3e405 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -306,10 +306,9 @@ public final class NsdManager {
switch (message.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
- mConnected.countDown();
break;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- // Ignore
+ mConnected.countDown();
break;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Log.e(TAG, "Channel lost");
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index e9e7551..7ffd30b 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -49,6 +49,11 @@ public class Binder implements IBinder {
private static final boolean FIND_POTENTIAL_LEAKS = false;
private static final String TAG = "Binder";
+ /**
+ * Control whether dump() calls are allowed.
+ */
+ private static String sDumpDisabled = null;
+
/* mObject is used by native code, do not remove or rename */
private int mObject;
private IInterface mOwner;
@@ -224,7 +229,23 @@ public class Binder implements IBinder {
}
return null;
}
-
+
+ /**
+ * Control disabling of dump calls in this process. This is used by the system
+ * process watchdog to disable incoming dump calls while it has detecting the system
+ * is hung and is reporting that back to the activity controller. This is to
+ * prevent the controller from getting hung up on bug reports at this point.
+ * @hide
+ *
+ * @param msg The message to show instead of the dump; if null, dumps are
+ * re-enabled.
+ */
+ public static void setDumpDisabled(String msg) {
+ synchronized (Binder.class) {
+ sDumpDisabled = msg;
+ }
+ }
+
/**
* Default implementation is a stub that returns false. You will want
* to override this to do the appropriate unmarshalling of transactions.
@@ -269,7 +290,15 @@ public class Binder implements IBinder {
FileOutputStream fout = new FileOutputStream(fd);
PrintWriter pw = new PrintWriter(fout);
try {
- dump(fd, pw, args);
+ final String disabled;
+ synchronized (Binder.class) {
+ disabled = sDumpDisabled;
+ }
+ if (disabled == null) {
+ dump(fd, pw, args);
+ } else {
+ pw.println(sDumpDisabled);
+ }
} finally {
pw.flush();
}
diff --git a/core/java/android/os/ParcelableParcel.java b/core/java/android/os/ParcelableParcel.java
new file mode 100644
index 0000000..11785f1
--- /dev/null
+++ b/core/java/android/os/ParcelableParcel.java
@@ -0,0 +1,75 @@
+/*
+ * 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];
+ }
+ };
+}
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 3307a8c..e53cb5e 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -65,6 +65,8 @@ public final class Trace {
public static final long TRACE_TAG_APP = 1L << 12;
/** @hide */
public static final long TRACE_TAG_RESOURCES = 1L << 13;
+ /** @hide */
+ public static final long TRACE_TAG_DALVIK = 1L << 14;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9164aa6..163709a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4084,6 +4084,13 @@ public final class Settings {
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
/**
+ * Whether or not to enable the dial pad autocomplete functionality.
+ *
+ * @hide
+ */
+ public static final String DIALPAD_AUTOCOMPLETE = "dialpad_autocomplete";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -4123,7 +4130,8 @@ public final class Settings {
MOUNT_UMS_AUTOSTART,
MOUNT_UMS_PROMPT,
MOUNT_UMS_NOTIFY_ENABLED,
- UI_NIGHT_MODE
+ UI_NIGHT_MODE,
+ DIALPAD_AUTOCOMPLETE
};
/**
@@ -5481,7 +5489,6 @@ public final class Settings {
WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
- WIFI_SCAN_ALWAYS_AVAILABLE,
WIFI_NUM_OPEN_NETWORKS_KEPT,
EMERGENCY_TONE,
CALL_AUTO_RETRY,
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 73d400e..578a86e 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -42,6 +42,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.MissingResourceException;
import java.util.Set;
/**
@@ -1128,9 +1129,23 @@ public class TextToSpeech {
if (loc == null) {
return LANG_NOT_SUPPORTED;
}
- String language = loc.getISO3Language();
- String country = loc.getISO3Country();
+ String language = null, country = null;
+ try {
+ language = loc.getISO3Language();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
+ return LANG_NOT_SUPPORTED;
+ }
+
+ try {
+ country = loc.getISO3Country();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
+ return LANG_NOT_SUPPORTED;
+ }
+
String variant = loc.getVariant();
+
// Check if the language, country, variant are available, and cache
// the available parts.
// Note that the language is not actually set here, instead it is cached so it
@@ -1195,8 +1210,23 @@ public class TextToSpeech {
return runAction(new Action<Integer>() {
@Override
public Integer run(ITextToSpeechService service) throws RemoteException {
- return service.isLanguageAvailable(loc.getISO3Language(),
- loc.getISO3Country(), loc.getVariant());
+ String language = null, country = null;
+
+ try {
+ language = loc.getISO3Language();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
+ return LANG_NOT_SUPPORTED;
+ }
+
+ try {
+ country = loc.getISO3Country();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
+ return LANG_NOT_SUPPORTED;
+ }
+
+ return service.isLanguageAvailable(language, country, loc.getVariant());
}
}, LANG_NOT_SUPPORTED, "isLanguageAvailable");
}
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index aff233d..3855ff3 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -36,6 +36,11 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
private static final int DOWN = 3;
@Override
+ public boolean canSelectArbitrarily() {
+ return true;
+ }
+
+ @Override
protected boolean handleMovementKey(TextView widget, Spannable buffer, int keyCode,
int movementMetaState, KeyEvent event) {
switch (keyCode) {
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index b2988ed..c2c247e 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -172,6 +172,7 @@ public class FocusFinder {
try {
// Note: This sort is stable.
mSequentialFocusComparator.setRoot(root);
+ mSequentialFocusComparator.setIsLayoutRtl(root.isLayoutRtl());
Collections.sort(focusables, mSequentialFocusComparator);
} finally {
mSequentialFocusComparator.recycle();
@@ -180,9 +181,9 @@ public class FocusFinder {
final int count = focusables.size();
switch (direction) {
case View.FOCUS_FORWARD:
- return getForwardFocusable(root, focused, focusables, count);
+ return getNextFocusable(focused, focusables, count);
case View.FOCUS_BACKWARD:
- return getBackwardFocusable(root, focused, focusables, count);
+ return getPreviousFocusable(focused, focusables, count);
}
return focusables.get(count - 1);
}
@@ -239,13 +240,6 @@ public class FocusFinder {
return closest;
}
- private static View getForwardFocusable(ViewGroup root, View focused,
- ArrayList<View> focusables, int count) {
- return (root.isLayoutRtl()) ?
- getPreviousFocusable(focused, focusables, count) :
- getNextFocusable(focused, focusables, count);
- }
-
private static View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
if (focused != null) {
int position = focusables.lastIndexOf(focused);
@@ -259,13 +253,6 @@ public class FocusFinder {
return null;
}
- private static View getBackwardFocusable(ViewGroup root, View focused,
- ArrayList<View> focusables, int count) {
- return (root.isLayoutRtl()) ?
- getNextFocusable(focused, focusables, count) :
- getPreviousFocusable(focused, focusables, count);
- }
-
private static View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
if (focused != null) {
int position = focusables.indexOf(focused);
@@ -619,6 +606,7 @@ public class FocusFinder {
private final Rect mFirstRect = new Rect();
private final Rect mSecondRect = new Rect();
private ViewGroup mRoot;
+ private boolean mIsLayoutRtl;
public void recycle() {
mRoot = null;
@@ -628,6 +616,10 @@ public class FocusFinder {
mRoot = root;
}
+ public void setIsLayoutRtl(boolean b) {
+ mIsLayoutRtl = b;
+ }
+
public int compare(View first, View second) {
if (first == second) {
return 0;
@@ -641,17 +633,17 @@ public class FocusFinder {
} else if (mFirstRect.top > mSecondRect.top) {
return 1;
} else if (mFirstRect.left < mSecondRect.left) {
- return -1;
+ return mIsLayoutRtl ? 1 : -1;
} else if (mFirstRect.left > mSecondRect.left) {
- return 1;
+ return mIsLayoutRtl ? -1 : 1;
} else if (mFirstRect.bottom < mSecondRect.bottom) {
return -1;
} else if (mFirstRect.bottom > mSecondRect.bottom) {
return 1;
} else if (mFirstRect.right < mSecondRect.right) {
- return -1;
+ return mIsLayoutRtl ? 1 : -1;
} else if (mFirstRect.right > mSecondRect.right) {
- return 1;
+ return mIsLayoutRtl ? -1 : 1;
} else {
// The view are distinct but completely coincident so we consider
// them equal for our purposes. Since the sort is stable, this
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 2ec9a7d..cc7d948 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -21,6 +21,7 @@ import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.DrawFilter;
import android.graphics.Matrix;
+import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
@@ -273,6 +274,18 @@ class GLES20Canvas extends HardwareCanvas {
private static native int nGetStencilSize();
+ void setCountOverdrawEnabled(boolean enabled) {
+ nSetCountOverdrawEnabled(mRenderer, enabled);
+ }
+
+ static native void nSetCountOverdrawEnabled(int renderer, boolean enabled);
+
+ float getOverdraw() {
+ return nGetOverdraw(mRenderer);
+ }
+
+ static native float nGetOverdraw(int renderer);
+
///////////////////////////////////////////////////////////////////////////
// Functor
///////////////////////////////////////////////////////////////////////////
@@ -314,21 +327,21 @@ class GLES20Canvas extends HardwareCanvas {
*
* @see #flushCaches(int)
*/
- public static final int FLUSH_CACHES_LAYERS = 0;
+ static final int FLUSH_CACHES_LAYERS = 0;
/**
* Must match Caches::FlushMode values
*
* @see #flushCaches(int)
*/
- public static final int FLUSH_CACHES_MODERATE = 1;
+ static final int FLUSH_CACHES_MODERATE = 1;
/**
* Must match Caches::FlushMode values
*
* @see #flushCaches(int)
*/
- public static final int FLUSH_CACHES_FULL = 2;
+ static final int FLUSH_CACHES_FULL = 2;
/**
* Flush caches to reclaim as much memory as possible. The amount of memory
@@ -338,10 +351,8 @@ class GLES20Canvas extends HardwareCanvas {
* {@link #FLUSH_CACHES_FULL}.
*
* @param level Hint about the amount of memory to reclaim
- *
- * @hide
*/
- public static void flushCaches(int level) {
+ static void flushCaches(int level) {
nFlushCaches(level);
}
@@ -353,21 +364,28 @@ class GLES20Canvas extends HardwareCanvas {
*
* @hide
*/
- public static void terminateCaches() {
+ static void terminateCaches() {
nTerminateCaches();
}
private static native void nTerminateCaches();
- /**
- * @hide
- */
- public static void initCaches() {
- nInitCaches();
+ static boolean initCaches() {
+ return nInitCaches();
}
- private static native void nInitCaches();
-
+ private static native boolean nInitCaches();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Atlas
+ ///////////////////////////////////////////////////////////////////////////
+
+ static void initAtlas(GraphicBuffer buffer, int[] map) {
+ nInitAtlas(buffer, map, map.length);
+ }
+
+ private static native void nInitAtlas(GraphicBuffer buffer, int[] map, int count);
+
///////////////////////////////////////////////////////////////////////////
// Display list
///////////////////////////////////////////////////////////////////////////
@@ -718,20 +736,21 @@ class GLES20Canvas extends HardwareCanvas {
}
@Override
- public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+ public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
+ Bitmap bitmap = patch.getBitmap();
if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
// Shaders are ignored when drawing patches
int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
try {
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
+ nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
} finally {
if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
}
}
- private static native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks,
+ private static native void nDrawPatch(int renderer, int bitmap, byte[] chunks,
float left, float top, float right, float bottom, int paint);
@Override
@@ -741,14 +760,14 @@ class GLES20Canvas extends HardwareCanvas {
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
try {
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
- private static native void nDrawBitmap(
- int renderer, int bitmap, byte[] buffer, float left, float top, int paint);
+ private static native void nDrawBitmap(int renderer, int bitmap,
+ float left, float top, int paint);
@Override
public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
@@ -757,15 +776,13 @@ class GLES20Canvas extends HardwareCanvas {
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
try {
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
- matrix.native_instance, nativePaint);
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
- private static native void nDrawBitmap(int renderer, int bitmap, byte[] buff,
- int matrix, int paint);
+ private static native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint);
@Override
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
@@ -787,7 +804,7 @@ class GLES20Canvas extends HardwareCanvas {
bottom = src.bottom;
}
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
@@ -814,14 +831,14 @@ class GLES20Canvas extends HardwareCanvas {
bottom = src.bottom;
}
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
- private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
+ private static native void nDrawBitmap(int renderer, int bitmap,
float srcLeft, float srcTop, float srcRight, float srcBottom,
float left, float top, float right, float bottom, int paint);
@@ -891,14 +908,14 @@ class GLES20Canvas extends HardwareCanvas {
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
try {
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
+ nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, meshWidth, meshHeight,
verts, vertOffset, colors, colorOffset, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
- private static native void nDrawBitmapMesh(int renderer, int bitmap, byte[] buffer,
+ private static native void nDrawBitmapMesh(int renderer, int bitmap,
int meshWidth, int meshHeight, float[] verts, int vertOffset,
int[] colors, int colorOffset, int paint);
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 3272504..d367267 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -18,6 +18,7 @@ package android.view;
import android.graphics.Bitmap;
import android.graphics.Matrix;
+import android.graphics.NinePatch;
import java.util.ArrayList;
@@ -29,7 +30,8 @@ class GLES20DisplayList extends DisplayList {
// alive as long as the DisplayList is alive. The Bitmap and DisplayList lists
// are populated by the GLES20RecordingCanvas during appropriate drawing calls and are
// cleared at the start of a new drawing frame or when the view is detached from the window.
- final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5);
+ final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(10);
+ final ArrayList<NinePatch> mNinePatches = new ArrayList<NinePatch>(10);
final ArrayList<DisplayList> mChildDisplayLists = new ArrayList<DisplayList>();
private GLES20RecordingCanvas mCanvas;
@@ -83,7 +85,12 @@ class GLES20DisplayList extends DisplayList {
}
mValid = false;
+ clearReferences();
+ }
+
+ void clearReferences() {
mBitmaps.clear();
+ mNinePatches.clear();
mChildDisplayLists.clear();
}
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index 7da2451..ec059d5 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -19,6 +19,7 @@ package android.view;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Matrix;
+import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
@@ -62,8 +63,7 @@ class GLES20RecordingCanvas extends GLES20Canvas {
}
void start() {
- mDisplayList.mBitmaps.clear();
- mDisplayList.mChildDisplayLists.clear();
+ mDisplayList.clearReferences();
}
int end(int nativeDisplayList) {
@@ -80,9 +80,10 @@ class GLES20RecordingCanvas extends GLES20Canvas {
}
@Override
- public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
- super.drawPatch(bitmap, chunks, dst, paint);
- mDisplayList.mBitmaps.add(bitmap);
+ public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
+ super.drawPatch(patch, dst, paint);
+ mDisplayList.mBitmaps.add(patch.getBitmap());
+ mDisplayList.mNinePatches.add(patch);
// Shaders in the Paint are ignored when drawing a Bitmap
}
diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java
index 685dc70..68ba77c 100644
--- a/core/java/android/view/GLES20RenderLayer.java
+++ b/core/java/android/view/GLES20RenderLayer.java
@@ -100,12 +100,17 @@ class GLES20RenderLayer extends GLES20Layer {
@Override
HardwareCanvas start(Canvas currentCanvas) {
+ return start(currentCanvas, null);
+ }
+
+ @Override
+ HardwareCanvas start(Canvas currentCanvas, Rect dirty) {
if (currentCanvas instanceof GLES20Canvas) {
((GLES20Canvas) currentCanvas).interrupt();
}
HardwareCanvas canvas = getCanvas();
canvas.setViewport(mWidth, mHeight);
- canvas.onPreDraw(null);
+ canvas.onPreDraw(dirty);
return canvas;
}
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index e863e49..a4cc630 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -63,6 +63,11 @@ class GLES20TextureLayer extends GLES20Layer {
}
@Override
+ HardwareCanvas start(Canvas currentCanvas, Rect dirty) {
+ return null;
+ }
+
+ @Override
void end(Canvas currentCanvas) {
}
diff --git a/core/java/android/view/GraphicBuffer.aidl b/core/java/android/view/GraphicBuffer.aidl
new file mode 100644
index 0000000..6dc6bed
--- /dev/null
+++ b/core/java/android/view/GraphicBuffer.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.view;
+
+parcelable GraphicBuffer;
diff --git a/core/java/android/view/GraphicBuffer.java b/core/java/android/view/GraphicBuffer.java
new file mode 100644
index 0000000..b4576f3
--- /dev/null
+++ b/core/java/android/view/GraphicBuffer.java
@@ -0,0 +1,229 @@
+/*
+ * 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.view;
+
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Simple wrapper for the native GraphicBuffer class.
+ *
+ * @hide
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class GraphicBuffer implements Parcelable {
+ // Note: keep usage flags in sync with GraphicBuffer.h and gralloc.h
+ public static final int USAGE_SW_READ_NEVER = 0x0;
+ public static final int USAGE_SW_READ_RARELY = 0x2;
+ public static final int USAGE_SW_READ_OFTEN = 0x3;
+ public static final int USAGE_SW_READ_MASK = 0xF;
+
+ public static final int USAGE_SW_WRITE_NEVER = 0x0;
+ public static final int USAGE_SW_WRITE_RARELY = 0x20;
+ public static final int USAGE_SW_WRITE_OFTEN = 0x30;
+ public static final int USAGE_SW_WRITE_MASK = 0xF0;
+
+ public static final int USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK | USAGE_SW_WRITE_MASK;
+
+ public static final int USAGE_PROTECTED = 0x4000;
+
+ public static final int USAGE_HW_TEXTURE = 0x100;
+ public static final int USAGE_HW_RENDER = 0x200;
+ public static final int USAGE_HW_2D = 0x400;
+ public static final int USAGE_HW_COMPOSER = 0x800;
+ public static final int USAGE_HW_VIDEO_ENCODER = 0x10000;
+ public static final int USAGE_HW_MASK = 0x71F00;
+
+ private final int mWidth;
+ private final int mHeight;
+ private final int mFormat;
+ private final int mUsage;
+ // Note: do not rename, this field is used by native code
+ private final int mNativeObject;
+
+ // These two fields are only used by lock/unlockCanvas()
+ private Canvas mCanvas;
+ private int mSaveCount;
+
+ /**
+ * Creates new <code>GraphicBuffer</code> instance. This method will return null
+ * if the buffer cannot be created.
+ *
+ * @param width The width in pixels of the buffer
+ * @param height The height in pixels of the buffer
+ * @param format The format of each pixel as specified in {@link PixelFormat}
+ * @param usage Hint indicating how the buffer will be used
+ *
+ * @return A <code>GraphicBuffer</code> instance or null
+ */
+ public static GraphicBuffer create(int width, int height, int format, int usage) {
+ int nativeObject = nCreateGraphicBuffer(width, height, format, usage);
+ if (nativeObject != 0) {
+ return new GraphicBuffer(width, height, format, usage, nativeObject);
+ }
+ return null;
+ }
+
+ /**
+ * Private use only. See {@link #create(int, int, int, int)}.
+ */
+ private GraphicBuffer(int width, int height, int format, int usage, int nativeObject) {
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mUsage = usage;
+ mNativeObject = nativeObject;
+ }
+
+ /**
+ * Returns the width of this buffer in pixels.
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Returns the height of this buffer in pixels.
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Returns the pixel format of this buffer. The pixel format must be one of
+ * the formats defined in {@link PixelFormat}.
+ */
+ public int getFormat() {
+ return mFormat;
+ }
+
+ /**
+ * Returns the usage hint set on this buffer.
+ */
+ public int getUsage() {
+ return mUsage;
+ }
+
+ /**
+ * <p>Start editing the pixels in the buffer. A null is returned if the buffer
+ * cannot be locked for editing.</p>
+ *
+ * <p>The content of the buffer is preserved between unlockCanvas()
+ * and lockCanvas().</p>
+ *
+ * @return A Canvas used to draw into the buffer, or null.
+ *
+ * @see #lockCanvas(android.graphics.Rect)
+ * @see #unlockCanvasAndPost(android.graphics.Canvas)
+ */
+ public Canvas lockCanvas() {
+ return lockCanvas(null);
+ }
+
+ /**
+ * Just like {@link #lockCanvas()} but allows specification of a dirty
+ * rectangle.
+ *
+ * @param dirty Area of the buffer that may be modified.
+
+ * @return A Canvas used to draw into the surface or null
+ *
+ * @see #lockCanvas()
+ * @see #unlockCanvasAndPost(android.graphics.Canvas)
+ */
+ public Canvas lockCanvas(Rect dirty) {
+ if (mCanvas == null) {
+ mCanvas = new Canvas();
+ }
+
+ if (nLockCanvas(mNativeObject, mCanvas, dirty)) {
+ mSaveCount = mCanvas.save();
+ return mCanvas;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finish editing pixels in the buffer.
+ *
+ * @param canvas The Canvas previously returned by lockCanvas()
+ *
+ * @see #lockCanvas()
+ * @see #lockCanvas(android.graphics.Rect)
+ */
+ public void unlockCanvasAndPost(Canvas canvas) {
+ if (mCanvas != null && canvas == mCanvas) {
+ canvas.restoreToCount(mSaveCount);
+ mSaveCount = 0;
+
+ nUnlockCanvasAndPost(mNativeObject, mCanvas);
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nDestroyGraphicBuffer(mNativeObject);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ dest.writeInt(mFormat);
+ dest.writeInt(mUsage);
+ nWriteGraphicBufferToParcel(mNativeObject, dest);
+ }
+
+ public static final Parcelable.Creator<GraphicBuffer> CREATOR =
+ new Parcelable.Creator<GraphicBuffer>() {
+ public GraphicBuffer createFromParcel(Parcel in) {
+ int width = in.readInt();
+ int height = in.readInt();
+ int format = in.readInt();
+ int usage = in.readInt();
+ int nativeObject = nReadGraphicBufferFromParcel(in);
+ if (nativeObject != 0) {
+ return new GraphicBuffer(width, height, format, usage, nativeObject);
+ }
+ return null;
+ }
+
+ public GraphicBuffer[] newArray(int size) {
+ return new GraphicBuffer[size];
+ }
+ };
+
+ private static native int nCreateGraphicBuffer(int width, int height, int format, int usage);
+ private static native void nDestroyGraphicBuffer(int nativeObject);
+ private static native void nWriteGraphicBufferToParcel(int nativeObject, Parcel dest);
+ private static native int nReadGraphicBufferFromParcel(Parcel in);
+ private static native boolean nLockCanvas(int nativeObject, Canvas canvas, Rect dirty);
+ private static native boolean nUnlockCanvasAndPost(int nativeObject, Canvas canvas);
+}
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 18b838b..23383d9 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -158,14 +158,22 @@ abstract class HardwareLayer {
/**
* This must be invoked before drawing onto this layer.
*
- * @param currentCanvas
+ * @param currentCanvas The canvas whose rendering needs to be interrupted
*/
abstract HardwareCanvas start(Canvas currentCanvas);
/**
+ * This must be invoked before drawing onto this layer.
+ *
+ * @param dirty The dirty area to repaint
+ * @param currentCanvas The canvas whose rendering needs to be interrupted
+ */
+ abstract HardwareCanvas start(Canvas currentCanvas, Rect dirty);
+
+ /**
* This must be invoked after drawing onto this layer.
*
- * @param currentCanvas
+ * @param currentCanvas The canvas whose rendering needs to be resumed
*/
abstract void end(Canvas currentCanvas);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 8308459..e8c1653 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,6 +17,7 @@
package android.view;
import android.content.ComponentCallbacks2;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
@@ -24,7 +25,10 @@ import android.opengl.EGL14;
import android.opengl.GLUtils;
import android.opengl.ManagedEGLContext;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
@@ -42,7 +46,6 @@ import javax.microedition.khronos.opengles.GL;
import java.io.File;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
import static javax.microedition.khronos.egl.EGL10.*;
@@ -162,15 +165,32 @@ public abstract class HardwareRenderer {
"debug.hwui.show_layers_updates";
/**
- * Turn on to show overdraw level.
+ * Controls overdraw debugging.
*
* Possible values:
- * "true", to enable overdraw debugging
* "false", to disable overdraw debugging
+ * "show", to show overdraw areas on screen
+ * "count", to display an overdraw counter
*
* @hide
*/
- public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";
+ public static final String DEBUG_OVERDRAW_PROPERTY = "debug.hwui.overdraw";
+
+ /**
+ * Value for {@link #DEBUG_OVERDRAW_PROPERTY}. When the property is set to this
+ * value, overdraw will be shown on screen by coloring pixels.
+ *
+ * @hide
+ */
+ public static final String OVERDRAW_PROPERTY_SHOW = "show";
+
+ /**
+ * Value for {@link #DEBUG_OVERDRAW_PROPERTY}. When the property is set to this
+ * value, an overdraw counter will be shown on screen.
+ *
+ * @hide
+ */
+ public static final String OVERDRAW_PROPERTY_COUNT = "count";
/**
* Turn on to debug non-rectangular clip operations.
@@ -762,6 +782,17 @@ public abstract class HardwareRenderer {
private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
private static final int PROFILE_DRAW_DP_PER_MS = 7;
+ private static final String[] VISUALIZERS = {
+ PROFILE_PROPERTY_VISUALIZE_BARS,
+ PROFILE_PROPERTY_VISUALIZE_LINES
+ };
+
+ private static final String[] OVERDRAW = {
+ OVERDRAW_PROPERTY_SHOW,
+ OVERDRAW_PROPERTY_COUNT
+ };
+ private static final int OVERDRAW_TYPE_COUNT = 1;
+
static EGL10 sEgl;
static EGLDisplay sEglDisplay;
static EGLConfig sEglConfig;
@@ -807,7 +838,9 @@ public abstract class HardwareRenderer {
Paint mProfilePaint;
boolean mDebugDirtyRegions;
- boolean mShowOverdraw;
+ int mDebugOverdraw = -1;
+ HardwareLayer mDebugOverdrawLayer;
+ Paint mDebugOverdrawPaint;
final int mGlVersion;
final boolean mTranslucent;
@@ -826,18 +859,13 @@ public abstract class HardwareRenderer {
loadSystemProperties(null);
}
- private static final String[] VISUALIZERS = {
- PROFILE_PROPERTY_VISUALIZE_BARS,
- PROFILE_PROPERTY_VISUALIZE_LINES
- };
-
@Override
boolean loadSystemProperties(Surface surface) {
boolean value;
boolean changed = false;
String profiling = SystemProperties.get(PROFILE_PROPERTY);
- int graphType = Arrays.binarySearch(VISUALIZERS, profiling);
+ int graphType = search(VISUALIZERS, profiling);
value = graphType >= 0;
if (graphType != mProfileVisualizerType) {
@@ -894,11 +922,19 @@ public abstract class HardwareRenderer {
}
}
- value = SystemProperties.getBoolean(
- HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY, false);
- if (value != mShowOverdraw) {
+ String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY);
+ int debugOverdraw = search(OVERDRAW, overdraw);
+ if (debugOverdraw != mDebugOverdraw) {
changed = true;
- mShowOverdraw = value;
+ mDebugOverdraw = debugOverdraw;
+
+ if (mDebugOverdraw != OVERDRAW_TYPE_COUNT) {
+ if (mDebugOverdrawLayer != null) {
+ mDebugOverdrawLayer.destroy();
+ mDebugOverdrawLayer = null;
+ mDebugOverdrawPaint = null;
+ }
+ }
}
if (nLoadProperties()) {
@@ -908,6 +944,13 @@ public abstract class HardwareRenderer {
return changed;
}
+ private static int search(String[] values, String value) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].equals(value)) return i;
+ }
+ return -1;
+ }
+
@Override
void dumpGfxInfo(PrintWriter pw) {
if (mProfileEnabled) {
@@ -968,7 +1011,7 @@ public abstract class HardwareRenderer {
if (fallback) {
// we'll try again if it was context lost
setRequested(false);
- Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
+ Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
+ "Switching back to software rendering.");
}
}
@@ -976,7 +1019,7 @@ public abstract class HardwareRenderer {
@Override
boolean initialize(Surface surface) throws Surface.OutOfResourcesException {
if (isRequested() && !isEnabled()) {
- initializeEgl();
+ boolean contextCreated = initializeEgl();
mGl = createEglSurface(surface);
mDestroyed = false;
@@ -991,6 +1034,10 @@ public abstract class HardwareRenderer {
mCanvas.setName(mName);
}
setEnabled(true);
+
+ if (contextCreated) {
+ initAtlas();
+ }
}
return mCanvas != null;
@@ -1010,7 +1057,7 @@ public abstract class HardwareRenderer {
abstract int[] getConfig(boolean dirtyRegions);
- void initializeEgl() {
+ boolean initializeEgl() {
synchronized (sEglLock) {
if (sEgl == null && sEglConfig == null) {
sEgl = (EGL10) EGLContext.getEGL();
@@ -1043,7 +1090,10 @@ public abstract class HardwareRenderer {
if (mEglContext == null) {
mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
sEglContextStorage.set(createManagedContext(mEglContext));
+ return true;
}
+
+ return false;
}
private EGLConfig loadEglConfig() {
@@ -1181,6 +1231,7 @@ public abstract class HardwareRenderer {
}
abstract void initCaches();
+ abstract void initAtlas();
EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
@@ -1193,6 +1244,7 @@ public abstract class HardwareRenderer {
"Could not create an EGL context. eglCreateContext failed with error: " +
GLUtils.getEGLErrorString(sEgl.eglGetError()));
}
+
return context;
}
@@ -1380,6 +1432,8 @@ public abstract class HardwareRenderer {
canvas.restoreToCount(saveCount);
view.mRecreateDisplayList = false;
+ debugOverdraw(attachInfo, dirty, canvas, displayList);
+
mFrameCount++;
debugDirtyRegions(dirty, canvas);
@@ -1399,6 +1453,61 @@ public abstract class HardwareRenderer {
}
}
+ abstract void countOverdraw(HardwareCanvas canvas);
+ abstract float getOverdraw(HardwareCanvas canvas);
+
+ private void debugOverdraw(View.AttachInfo attachInfo, Rect dirty,
+ HardwareCanvas canvas, DisplayList displayList) {
+
+ if (mDebugOverdraw == OVERDRAW_TYPE_COUNT) {
+ // TODO: Use an alpha layer allocated from a GraphicBuffer
+ // The alpha format will help with rendering performance and
+ // the GraphicBuffer will let us skip the read pixels step
+ if (mDebugOverdrawLayer == null) {
+ mDebugOverdrawLayer = createHardwareLayer(mWidth, mHeight, true);
+ } else if (mDebugOverdrawLayer.getWidth() != mWidth ||
+ mDebugOverdrawLayer.getHeight() != mHeight) {
+ mDebugOverdrawLayer.resize(mWidth, mHeight);
+ }
+
+ if (!mDebugOverdrawLayer.isValid()) {
+ mDebugOverdraw = -1;
+ return;
+ }
+
+ HardwareCanvas layerCanvas = mDebugOverdrawLayer.start(canvas, dirty);
+ countOverdraw(layerCanvas);
+ final int restoreCount = layerCanvas.save();
+ layerCanvas.drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN);
+ layerCanvas.restoreToCount(restoreCount);
+ mDebugOverdrawLayer.end(canvas);
+
+ float overdraw = getOverdraw(layerCanvas);
+ DisplayMetrics metrics = attachInfo.mRootView.getResources().getDisplayMetrics();
+
+ drawOverdrawCounter(canvas, overdraw, metrics.density);
+ }
+ }
+
+ private void drawOverdrawCounter(HardwareCanvas canvas, float overdraw, float density) {
+ final String text = String.format("%.2fx", overdraw);
+ final Paint paint = setupPaint(density);
+ // HSBtoColor will clamp the values in the 0..1 range
+ paint.setColor(Color.HSBtoColor(0.28f - 0.28f * overdraw / 3.5f, 0.8f, 1.0f));
+
+ canvas.drawText(text, density * 4.0f, mHeight - paint.getFontMetrics().bottom, paint);
+ }
+
+ private Paint setupPaint(float density) {
+ if (mDebugOverdrawPaint == null) {
+ mDebugOverdrawPaint = new Paint();
+ mDebugOverdrawPaint.setAntiAlias(true);
+ mDebugOverdrawPaint.setShadowLayer(density * 3.0f, 0.0f, 0.0f, 0xff000000);
+ mDebugOverdrawPaint.setTextSize(density * 20.0f);
+ }
+ return mDebugOverdrawPaint;
+ }
+
private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
== View.PFLAG_INVALIDATED;
@@ -1788,7 +1897,29 @@ public abstract class HardwareRenderer {
@Override
void initCaches() {
- GLES20Canvas.initCaches();
+ if (GLES20Canvas.initCaches()) {
+ // Caches were (re)initialized, rebind atlas
+ initAtlas();
+ }
+ }
+
+ @Override
+ void initAtlas() {
+ IBinder binder = ServiceManager.getService("assetatlas");
+ if (binder == null) return;
+
+ IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
+ try {
+ GraphicBuffer buffer = atlas.getBuffer();
+ if (buffer != null) {
+ int[] map = atlas.getMap();
+ if (map != null) {
+ GLES20Canvas.initAtlas(buffer, map);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Could not acquire atlas", e);
+ }
}
@Override
@@ -1985,6 +2116,16 @@ public abstract class HardwareRenderer {
}
@Override
+ void countOverdraw(HardwareCanvas canvas) {
+ ((GLES20Canvas) canvas).setCountOverdrawEnabled(true);
+ }
+
+ @Override
+ float getOverdraw(HardwareCanvas canvas) {
+ return ((GLES20Canvas) canvas).getOverdraw();
+ }
+
+ @Override
public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
return ((GLES20TextureLayer) layer).getSurfaceTexture();
}
diff --git a/core/java/android/view/IAssetAtlas.aidl b/core/java/android/view/IAssetAtlas.aidl
new file mode 100644
index 0000000..2595179
--- /dev/null
+++ b/core/java/android/view/IAssetAtlas.aidl
@@ -0,0 +1,47 @@
+/**
+ * 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.view;
+
+import android.view.GraphicBuffer;
+
+/**
+ * Programming interface to the system assets atlas. This atlas, when
+ * present, holds preloaded drawable in a single, shareable graphics
+ * buffer. This allows multiple processes to share the same data to
+ * save up on memory.
+ *
+ * @hide
+ */
+interface IAssetAtlas {
+ /**
+ * Returns the atlas buffer (texture) or null if the atlas is
+ * not available yet.
+ */
+ GraphicBuffer getBuffer();
+
+ /**
+ * Returns the map of the bitmaps stored in the atlas or null
+ * if the atlas is not available yet.
+ *
+ * Each bitmap is represented by several entries in the array:
+ * int0: SkBitmap*, the native bitmap object
+ * int1: x position
+ * int2: y position
+ * int3: rotated, 1 if the bitmap must be rotated, 0 otherwise
+ */
+ int[] getMap();
+}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 4989c3a..e0786f7 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -34,19 +34,21 @@ public class Surface implements Parcelable {
private static native int nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
throws OutOfResourcesException;
+ private static native int nativeCreateFromSurfaceControl(int surfaceControlNativeObject);
- private native Canvas nativeLockCanvas(int nativeObject, Rect dirty);
- private native void nativeUnlockCanvasAndPost(int nativeObject, Canvas canvas);
+ private static native void nativeLockCanvas(int nativeObject, Canvas canvas, Rect dirty)
+ throws OutOfResourcesException;
+ private static native void nativeUnlockCanvasAndPost(int nativeObject, Canvas canvas);
private static native void nativeRelease(int nativeObject);
private static native boolean nativeIsValid(int nativeObject);
private static native boolean nativeIsConsumerRunningBehind(int nativeObject);
- private static native int nativeCopyFrom(int nativeObject, int surfaceControlNativeObject);
private static native int nativeReadFromParcel(int nativeObject, Parcel source);
private static native void nativeWriteToParcel(int nativeObject, Parcel dest);
public static final Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
+ @Override
public Surface createFromParcel(Parcel source) {
try {
Surface s = new Surface();
@@ -57,26 +59,20 @@ public class Surface implements Parcelable {
return null;
}
}
+
+ @Override
public Surface[] newArray(int size) {
return new Surface[size];
}
};
private final CloseGuard mCloseGuard = CloseGuard.get();
- private String mName;
- // Note: These fields are accessed by native code.
- // The mSurfaceControl will only be present for Surfaces used by the window
- // server or system processes. When this class is parceled we defer to the
- // mSurfaceControl to do the parceling. Otherwise we parcel the
- // mNativeSurface.
+ // Guarded state.
+ final Object mLock = new Object(); // protects the native state
+ private String mName;
int mNativeObject; // package scope only for SurfaceControl access
-
- // protects the native state
- private final Object mNativeObjectLock = new Object();
-
- private int mGenerationId; // incremented each time mNativeSurface changes
- @SuppressWarnings("UnusedDeclaration")
+ private int mGenerationId; // incremented each time mNativeObject changes
private final Canvas mCanvas = new CompatibleCanvas();
// A matrix to scale the matrix set by application. This is set to null for
@@ -125,21 +121,22 @@ public class Surface implements Parcelable {
throw new IllegalArgumentException("surfaceTexture must not be null");
}
- mName = surfaceTexture.toString();
- try {
- mNativeObject = nativeCreateFromSurfaceTexture(surfaceTexture);
- } catch (OutOfResourcesException ex) {
- // We can't throw OutOfResourcesException because it would be an API change.
- throw new RuntimeException(ex);
+ synchronized (mLock) {
+ mName = surfaceTexture.toString();
+ try {
+ setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
+ } catch (OutOfResourcesException ex) {
+ // We can't throw OutOfResourcesException because it would be an API change.
+ throw new RuntimeException(ex);
+ }
}
-
- mCloseGuard.open("release");
}
/* called from android_view_Surface_createFromIGraphicBufferProducer() */
private Surface(int nativeObject) {
- mNativeObject = nativeObject;
- mCloseGuard.open("release");
+ synchronized (mLock) {
+ setNativeObjectLocked(nativeObject);
+ }
}
@Override
@@ -160,13 +157,11 @@ public class Surface implements Parcelable {
* This will make the surface invalid.
*/
public void release() {
- synchronized (mNativeObjectLock) {
+ synchronized (mLock) {
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
- mNativeObject = 0;
- mGenerationId++;
+ setNativeObjectLocked(0);
}
- mCloseGuard.close();
}
}
@@ -187,7 +182,7 @@ public class Surface implements Parcelable {
* Otherwise returns false.
*/
public boolean isValid() {
- synchronized (mNativeObjectLock) {
+ synchronized (mLock) {
if (mNativeObject == 0) return false;
return nativeIsValid(mNativeObject);
}
@@ -201,7 +196,9 @@ public class Surface implements Parcelable {
* @hide
*/
public int getGenerationId() {
- return mGenerationId;
+ synchronized (mLock) {
+ return mGenerationId;
+ }
}
/**
@@ -211,7 +208,7 @@ public class Surface implements Parcelable {
* @hide
*/
public boolean isConsumerRunningBehind() {
- synchronized (mNativeObjectLock) {
+ synchronized (mLock) {
checkNotReleasedLocked();
return nativeIsConsumerRunningBehind(mNativeObject);
}
@@ -234,9 +231,10 @@ public class Surface implements Parcelable {
*/
public Canvas lockCanvas(Rect inOutDirty)
throws OutOfResourcesException, IllegalArgumentException {
- synchronized (mNativeObjectLock) {
+ synchronized (mLock) {
checkNotReleasedLocked();
- return nativeLockCanvas(mNativeObject, inOutDirty);
+ nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
+ return mCanvas;
}
}
@@ -247,7 +245,12 @@ public class Surface implements Parcelable {
* @param canvas The canvas previously obtained from {@link #lockCanvas}.
*/
public void unlockCanvasAndPost(Canvas canvas) {
- synchronized (mNativeObjectLock) {
+ if (canvas != mCanvas) {
+ throw new IllegalArgumentException("canvas object must be the same instance that "
+ + "was previously returned by lockCanvas");
+ }
+
+ synchronized (mLock) {
checkNotReleasedLocked();
nativeUnlockCanvasAndPost(mNativeObject, canvas);
}
@@ -273,7 +276,6 @@ public class Surface implements Parcelable {
}
}
-
/**
* Copy another surface to this one. This surface now holds a reference
* to the same data as the original surface, and is -not- the owner.
@@ -287,22 +289,24 @@ public class Surface implements Parcelable {
if (other == null) {
throw new IllegalArgumentException("other must not be null");
}
- if (other.mNativeObject == 0) {
+
+ int surfaceControlPtr = other.mNativeObject;
+ if (surfaceControlPtr == 0) {
throw new NullPointerException(
"SurfaceControl native object is null. Are you using a released SurfaceControl?");
}
- synchronized (mNativeObjectLock) {
- mNativeObject = nativeCopyFrom(mNativeObject, other.mNativeObject);
- if (mNativeObject == 0) {
- // nativeCopyFrom released our reference
- mCloseGuard.close();
+ int newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
+
+ synchronized (mLock) {
+ if (mNativeObject != 0) {
+ nativeRelease(mNativeObject);
}
- mGenerationId++;
+ setNativeObjectLocked(newNativeObject);
}
}
/**
- * This is intended to be used by {@link SurfaceView.updateWindow} only.
+ * This is intended to be used by {@link SurfaceView#updateWindow} only.
* @param other access is not thread safe
* @hide
* @deprecated
@@ -313,21 +317,18 @@ public class Surface implements Parcelable {
throw new IllegalArgumentException("other must not be null");
}
if (other != this) {
- synchronized (mNativeObjectLock) {
+ final int newPtr;
+ synchronized (other.mLock) {
+ newPtr = other.mNativeObject;
+ other.setNativeObjectLocked(0);
+ }
+
+ synchronized (mLock) {
if (mNativeObject != 0) {
- // release our reference to our native object
nativeRelease(mNativeObject);
}
- // transfer the reference from other to us
- if (other.mNativeObject != 0 && mNativeObject == 0) {
- mCloseGuard.open("release");
- }
- mNativeObject = other.mNativeObject;
- mGenerationId++;
+ setNativeObjectLocked(newPtr);
}
- other.mNativeObject = 0;
- other.mGenerationId++;
- other.mCloseGuard.close();
}
}
@@ -340,14 +341,10 @@ public class Surface implements Parcelable {
if (source == null) {
throw new IllegalArgumentException("source must not be null");
}
- synchronized (mNativeObjectLock) {
+
+ synchronized (mLock) {
mName = source.readString();
- int nativeObject = nativeReadFromParcel(mNativeObject, source);
- if (nativeObject !=0 && mNativeObject == 0) {
- mCloseGuard.open("release");
- }
- mNativeObject = nativeObject;
- mGenerationId++;
+ setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
}
}
@@ -356,7 +353,7 @@ public class Surface implements Parcelable {
if (dest == null) {
throw new IllegalArgumentException("dest must not be null");
}
- synchronized (mNativeObjectLock) {
+ synchronized (mLock) {
dest.writeString(mName);
nativeWriteToParcel(mNativeObject, dest);
}
@@ -367,7 +364,27 @@ public class Surface implements Parcelable {
@Override
public String toString() {
- return "Surface(name=" + mName + ")";
+ synchronized (mLock) {
+ return "Surface(name=" + mName + ")";
+ }
+ }
+
+ private void setNativeObjectLocked(int ptr) {
+ if (mNativeObject != ptr) {
+ if (mNativeObject == 0 && ptr != 0) {
+ mCloseGuard.open("release");
+ } else if (mNativeObject != 0 && ptr == 0) {
+ mCloseGuard.close();
+ }
+ mNativeObject = ptr;
+ mGenerationId += 1;
+ }
+ }
+
+ private void checkNotReleasedLocked() {
+ if (mNativeObject == 0) {
+ throw new IllegalStateException("Surface has already been released.");
+ }
}
/**
@@ -451,9 +468,4 @@ public class Surface implements Parcelable {
mOrigMatrix.set(m);
}
}
-
- private void checkNotReleasedLocked() {
- if (mNativeObject == 0) throw new NullPointerException(
- "mNativeObject is null. Have you called release() already?");
- }
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e869d09..6b530ef 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -496,8 +496,14 @@ public class SurfaceControl {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- int nativeSurface = surface != null ? surface.mNativeObject : 0;
- nativeSetDisplaySurface(displayToken, nativeSurface);
+
+ if (surface != null) {
+ synchronized (surface.mLock) {
+ nativeSetDisplaySurface(displayToken, surface.mNativeObject);
+ }
+ } else {
+ nativeSetDisplaySurface(displayToken, 0);
+ }
}
public static IBinder createDisplay(String name, boolean secure) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 793fb5e..8b2b556 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -160,7 +160,6 @@ public class SurfaceView extends View {
int mHeight = -1;
int mFormat = -1;
final Rect mSurfaceFrame = new Rect();
- Rect mTmpDirty;
int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
boolean mUpdateWindowNeeded;
boolean mReportDrawNeeded;
@@ -795,14 +794,6 @@ public class SurfaceView extends View {
Canvas c = null;
if (!mDrawingStopped && mWindow != null) {
- if (dirty == null) {
- if (mTmpDirty == null) {
- mTmpDirty = new Rect();
- }
- mTmpDirty.set(mSurfaceFrame);
- dirty = mTmpDirty;
- }
-
try {
c = mSurface.lockCanvas(dirty);
} catch (Exception e) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ed6dc6c..25c380e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11764,10 +11764,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Resolve all RTL related properties.
*
+ * @return true if resolution of RTL properties has been done
+ *
* @hide
*/
- public void resolveRtlPropertiesIfNeeded() {
- if (!needRtlPropertiesResolution()) return;
+ public boolean resolveRtlPropertiesIfNeeded() {
+ if (!needRtlPropertiesResolution()) return false;
// Order is important here: LayoutDirection MUST be resolved first
if (!isLayoutDirectionResolved()) {
@@ -11788,6 +11790,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
resolveDrawables();
}
onRtlPropertiesChanged(getLayoutDirection());
+ return true;
}
/**
@@ -11840,6 +11843,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* @return true if RTL properties need resolution.
+ *
*/
private boolean needRtlPropertiesResolution() {
return (mPrivateFlags2 & ALL_RTL_PROPERTIES_RESOLVED) != ALL_RTL_PROPERTIES_RESOLVED;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index dfe5f88..f21c63e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4514,16 +4514,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
public void offsetChildrenTopAndBottom(int offset) {
final int count = mChildrenCount;
final View[] children = mChildren;
+ boolean invalidate = false;
for (int i = 0; i < count; i++) {
final View v = children[i];
v.mTop += offset;
v.mBottom += offset;
if (v.mDisplayList != null) {
+ invalidate = true;
v.mDisplayList.offsetTopAndBottom(offset);
- invalidateViewProperty(false, false);
}
}
+
+ if (invalidate) {
+ invalidateViewProperty(false, false);
+ }
}
/**
@@ -5525,15 +5530,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @hide
*/
@Override
- public void resolveRtlPropertiesIfNeeded() {
- super.resolveRtlPropertiesIfNeeded();
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.isLayoutDirectionInherited()) {
- child.resolveRtlPropertiesIfNeeded();
+ public boolean resolveRtlPropertiesIfNeeded() {
+ final boolean result = super.resolveRtlPropertiesIfNeeded();
+ // We dont need to resolve the children RTL properties if nothing has changed for the parent
+ if (result) {
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.isLayoutDirectionInherited()) {
+ child.resolveRtlPropertiesIfNeeded();
+ }
}
}
+ return result;
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f34d390..fc005d1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -23,7 +23,6 @@ import android.content.ClipDescription;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -108,8 +107,6 @@ public final class ViewRootImpl implements ViewParent,
private static final boolean DEBUG_FPS = false;
private static final boolean DEBUG_INPUT_PROCESSING = false || LOCAL_LOGV;
- private static final boolean USE_RENDER_THREAD = false;
-
/**
* Set this system property to true to force the view hierarchy to render
* at 60 Hz. This can be used to measure the potential framerate.
@@ -131,10 +128,6 @@ public final class ViewRootImpl implements ViewParent,
static final ArrayList<ComponentCallbacks> sConfigCallbacks
= new ArrayList<ComponentCallbacks>();
- private static boolean sUseRenderThread = false;
- private static boolean sRenderThreadQueried = false;
- private static final Object[] sRenderThreadQueryLock = new Object[0];
-
final Context mContext;
final IWindowSession mWindowSession;
final Display mDisplay;
@@ -212,6 +205,7 @@ public final class ViewRootImpl implements ViewParent,
boolean mHasHadWindowFocus;
boolean mLastWasImTarget;
boolean mWindowsAnimating;
+ boolean mDrawDuringWindowsAnimating;
boolean mIsDrawing;
int mLastSystemUiVisibility;
int mClientWindowLayoutFlags;
@@ -375,35 +369,6 @@ public final class ViewRootImpl implements ViewParent,
loadSystemProperties();
}
- /**
- * @return True if the application requests the use of a separate render thread,
- * false otherwise
- */
- private static boolean isRenderThreadRequested(Context context) {
- if (USE_RENDER_THREAD) {
- synchronized (sRenderThreadQueryLock) {
- if (!sRenderThreadQueried) {
- final PackageManager packageManager = context.getPackageManager();
- final String packageName = context.getApplicationInfo().packageName;
- try {
- ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName,
- PackageManager.GET_META_DATA);
- if (applicationInfo.metaData != null) {
- sUseRenderThread = applicationInfo.metaData.getBoolean(
- "android.graphics.renderThread", false);
- }
- } catch (PackageManager.NameNotFoundException e) {
- } finally {
- sRenderThreadQueried = true;
- }
- }
- return sUseRenderThread;
- }
- } else {
- return false;
- }
- }
-
public static void addFirstDrawHandler(Runnable callback) {
synchronized (sFirstDrawHandlers) {
if (!sFirstDrawComplete) {
@@ -481,7 +446,7 @@ public final class ViewRootImpl implements ViewParent,
// If the application owns the surface, don't enable hardware acceleration
if (mSurfaceHolder == null) {
- enableHardwareAcceleration(mView.getContext(), attrs);
+ enableHardwareAcceleration(attrs);
}
boolean restore = false;
@@ -689,7 +654,7 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) {
+ private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
@@ -729,11 +694,6 @@ public final class ViewRootImpl implements ViewParent,
return;
}
- final boolean renderThread = isRenderThreadRequested(context);
- if (renderThread) {
- Log.i(HardwareRenderer.LOG_TAG, "Render threat initiated");
- }
-
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.destroy(true);
}
@@ -1399,8 +1359,10 @@ public final class ViewRootImpl implements ViewParent,
final int surfaceGenerationId = mSurface.getGenerationId();
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
- mWindowsAnimating |=
- (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0;
+ if (!mDrawDuringWindowsAnimating) {
+ mWindowsAnimating |=
+ (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0;
+ }
if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
+ " overscan=" + mPendingOverscanInsets.toShortString()
@@ -2575,6 +2537,16 @@ public final class ViewRootImpl implements ViewParent,
displayLists.clear();
}
+ /**
+ * @hide
+ */
+ public void setDrawDuringWindowsAnimating(boolean value) {
+ mDrawDuringWindowsAnimating = value;
+ if (value) {
+ handleDispatchDoneAnimating();
+ }
+ }
+
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
final View.AttachInfo attachInfo = mAttachInfo;
final Rect ci = attachInfo.mContentInsets;
@@ -2712,7 +2684,6 @@ public final class ViewRootImpl implements ViewParent,
AccessibilityNodeInfo focusNode = mAccessibilityFocusedVirtualView;
View focusHost = mAccessibilityFocusedHost;
- focusHost.clearAccessibilityFocusNoCallbacks();
// Wipe the state of the current accessibility focus since
// the call into the provider to clear accessibility focus
@@ -2722,6 +2693,10 @@ public final class ViewRootImpl implements ViewParent,
mAccessibilityFocusedHost = null;
mAccessibilityFocusedVirtualView = null;
+ // Clear accessibility focus on the host after clearing state since
+ // this method may be reentrant.
+ focusHost.clearAccessibilityFocusNoCallbacks();
+
AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider();
if (provider != null) {
// Invalidate the area of the cleared accessibility focus.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4312dee..91c989d 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,6 +16,13 @@
package android.widget;
+import android.content.UndoManager;
+import android.content.UndoOperation;
+import android.content.UndoOwner;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.InputFilter;
+import android.text.SpannableString;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.EditableInputConnection;
@@ -107,11 +114,16 @@ import java.util.HashMap;
*/
public class Editor {
private static final String TAG = "Editor";
+ 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;
+ UndoManager mUndoManager;
+ UndoOwner mUndoOwner;
+ InputFilter mUndoInputFilter;
+
// Cursor Controllers.
InsertionPointCursorController mInsertionPointCursorController;
SelectionModifierCursorController mSelectionModifierCursorController;
@@ -1884,7 +1896,7 @@ public class Editor {
/**
* Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
* pop-up should be displayed.
- * Also monitors {@link SelectionSpan} to call back to the attached input method.
+ * Also monitors {@link Selection} to call back to the attached input method.
*/
class SpanController implements SpanWatcher {
@@ -2014,7 +2026,7 @@ public class Editor {
/**
* Displays the actions associated to an {@link EasyEditSpan}. The pop-up is controlled
- * by {@link EasyEditSpanController}.
+ * by {@link SpanController}.
*/
private class EasyEditPopupWindow extends PinnedPopupWindow
implements OnClickListener {
@@ -3959,4 +3971,166 @@ public class Editor {
mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
}
}
+
+ public static class UndoInputFilter implements InputFilter {
+ final Editor mEditor;
+
+ public UndoInputFilter(Editor editor) {
+ mEditor = editor;
+ }
+
+ @Override
+ public CharSequence filter(CharSequence source, int start, int end,
+ Spanned dest, int dstart, int dend) {
+ if (DEBUG_UNDO) {
+ Log.d(TAG, "filter: source=" + source + " (" + start + "-" + end + ")");
+ Log.d(TAG, "filter: dest=" + dest + " (" + dstart + "-" + dend + ")");
+ }
+ final UndoManager um = mEditor.mUndoManager;
+ if (um.isInUndo()) {
+ if (DEBUG_UNDO) Log.d(TAG, "*** skipping, currently performing undo/redo");
+ return null;
+ }
+
+ um.beginUpdate("Edit text");
+ TextModifyOperation op = um.getLastOperation(
+ TextModifyOperation.class, mEditor.mUndoOwner, UndoManager.MERGE_MODE_UNIQUE);
+ if (op != null) {
+ if (DEBUG_UNDO) Log.d(TAG, "Last op: range=(" + op.mRangeStart + "-" + op.mRangeEnd
+ + "), oldText=" + op.mOldText);
+ // See if we can continue modifying this operation.
+ if (op.mOldText == null) {
+ // 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.
+ if (start < end && ((dstart >= op.mRangeStart && dend <= op.mRangeEnd)
+ || (dstart == op.mRangeEnd && dend == op.mRangeEnd))) {
+ op.mRangeEnd = dstart + (end-start);
+ um.endUpdate();
+ if (DEBUG_UNDO) Log.d(TAG, "*** merging with last op, mRangeEnd="
+ + op.mRangeEnd);
+ return null;
+ }
+ } else {
+ // The current operation is a delete... can we delete more?
+ if (start == end && dend == op.mRangeStart-1) {
+ SpannableStringBuilder str;
+ if (op.mOldText instanceof SpannableString) {
+ str = (SpannableStringBuilder)op.mOldText;
+ } else {
+ str = new SpannableStringBuilder(op.mOldText);
+ }
+ str.insert(0, dest, dstart, dend);
+ op.mRangeStart = dstart;
+ op.mOldText = str;
+ um.endUpdate();
+ if (DEBUG_UNDO) Log.d(TAG, "*** merging with last op, range=("
+ + op.mRangeStart + "-" + op.mRangeEnd
+ + "), oldText=" + op.mOldText);
+ return null;
+ }
+ }
+
+ // Couldn't add to the current undo operation, need to start a new
+ // undo state for a new undo operation.
+ um.commitState(null);
+ um.setUndoLabel("Edit text");
+ }
+
+ // Create a new undo state reflecting the operation being performed.
+ op = new TextModifyOperation(mEditor.mUndoOwner);
+ op.mRangeStart = dstart;
+ if (start < end) {
+ op.mRangeEnd = dstart + (end-start);
+ } else {
+ op.mRangeEnd = dstart;
+ }
+ if (dstart < dend) {
+ op.mOldText = dest.subSequence(dstart, dend);
+ }
+ if (DEBUG_UNDO) Log.d(TAG, "*** adding new op, range=(" + op.mRangeStart
+ + "-" + op.mRangeEnd + "), oldText=" + op.mOldText);
+ um.addOperation(op, UndoManager.MERGE_MODE_NONE);
+ um.endUpdate();
+ return null;
+ }
+ }
+
+ public static class TextModifyOperation extends UndoOperation<TextView> {
+ int mRangeStart, mRangeEnd;
+ CharSequence mOldText;
+
+ public TextModifyOperation(UndoOwner owner) {
+ super(owner);
+ }
+
+ public TextModifyOperation(Parcel src, ClassLoader loader) {
+ super(src, loader);
+ mRangeStart = src.readInt();
+ mRangeEnd = src.readInt();
+ mOldText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(src);
+ }
+
+ @Override
+ public void commit() {
+ }
+
+ @Override
+ public void undo() {
+ swapText();
+ }
+
+ @Override
+ public void redo() {
+ swapText();
+ }
+
+ 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();
+ CharSequence curText;
+ if (mRangeStart >= mRangeEnd) {
+ curText = null;
+ } else {
+ curText = editable.subSequence(mRangeStart, mRangeEnd);
+ }
+ if (DEBUG_UNDO) {
+ Log.d(TAG, "Swap: range=(" + mRangeStart + "-" + mRangeEnd
+ + "), oldText=" + mOldText);
+ Log.d(TAG, "Swap: curText=" + curText);
+ }
+ if (mOldText == null) {
+ editable.delete(mRangeStart, mRangeEnd);
+ mRangeEnd = mRangeStart;
+ } else {
+ editable.replace(mRangeStart, mRangeEnd, mOldText);
+ mRangeEnd = mRangeStart + mOldText.length();
+ }
+ mOldText = curText;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mRangeStart);
+ dest.writeInt(mRangeEnd);
+ TextUtils.writeToParcel(mOldText, dest, flags);
+ }
+
+ public static final Parcelable.ClassLoaderCreator<TextModifyOperation> CREATOR
+ = new Parcelable.ClassLoaderCreator<TextModifyOperation>() {
+ public TextModifyOperation createFromParcel(Parcel in) {
+ return new TextModifyOperation(in, null);
+ }
+
+ public TextModifyOperation createFromParcel(Parcel in, ClassLoader loader) {
+ return new TextModifyOperation(in, loader);
+ }
+
+ public TextModifyOperation[] newArray(int size) {
+ return new TextModifyOperation[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 698f101..8d90973 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -20,6 +20,7 @@ import android.R;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
+import android.content.UndoManager;
import android.content.res.ColorStateList;
import android.content.res.CompatibilityInfo;
import android.content.res.Resources;
@@ -1510,6 +1511,47 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Retrieve the {@link android.content.UndoManager} that is currently associated
+ * with this TextView. By default there is no associated UndoManager, so null
+ * is returned. One can be associated with the TextView through
+ * {@link #setUndoManager(android.content.UndoManager, String)}
+ */
+ public final UndoManager getUndoManager() {
+ return mEditor == null ? null : mEditor.mUndoManager;
+ }
+
+ /**
+ * Associate an {@link android.content.UndoManager} with this TextView. Once
+ * done, all edit operations on the TextView will result in appropriate
+ * {@link android.content.UndoOperation} objects pushed on the given UndoManager's
+ * stack.
+ *
+ * @param undoManager The {@link android.content.UndoManager} to associate with
+ * this TextView, or null to clear any existing association.
+ * @param tag String tag identifying this particular TextView owner in the
+ * UndoManager. This is used to keep the correct association with the
+ * {@link android.content.UndoOwner} of any operations inside of the UndoManager.
+ */
+ 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;
+ }
+ }
+
+ /**
* @return the current key listener for this TextView.
* This will frequently be null for non-EditText TextViews.
*
@@ -4401,16 +4443,30 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* and includes mInput in the list if it is an InputFilter.
*/
private void setFilters(Editable e, InputFilter[] filters) {
- if (mEditor != null && mEditor.mKeyListener instanceof InputFilter) {
- InputFilter[] nf = new InputFilter[filters.length + 1];
-
- System.arraycopy(filters, 0, nf, 0, filters.length);
- nf[filters.length] = (InputFilter) mEditor.mKeyListener;
+ if (mEditor != null) {
+ final boolean undoFilter = mEditor.mUndoInputFilter != null;
+ final boolean keyFilter = mEditor.mKeyListener instanceof InputFilter;
+ int num = 0;
+ if (undoFilter) num++;
+ if (keyFilter) num++;
+ if (num > 0) {
+ InputFilter[] nf = new InputFilter[filters.length + num];
+
+ System.arraycopy(filters, 0, nf, 0, filters.length);
+ num = 0;
+ if (undoFilter) {
+ nf[filters.length] = mEditor.mUndoInputFilter;
+ num++;
+ }
+ if (keyFilter) {
+ nf[filters.length + num] = (InputFilter) mEditor.mKeyListener;
+ }
- e.setFilters(nf);
- } else {
- e.setFilters(filters);
+ e.setFilters(nf);
+ return;
+ }
}
+ e.setFilters(filters);
}
/**