summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt17
-rw-r--r--api/system-current.txt17
-rw-r--r--core/java/android/app/ActivityThread.java4
-rw-r--r--core/java/android/app/PendingIntent.java14
-rw-r--r--core/java/android/inputmethodservice/ExtractEditText.java2
-rw-r--r--core/java/android/provider/AlarmClock.java121
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java5
-rw-r--r--core/java/android/widget/Editor.java237
-rw-r--r--core/java/android/widget/TextView.java75
-rw-r--r--data/fonts/fallback_fonts.xml10
-rw-r--r--data/fonts/fonts.xml6
-rw-r--r--graphics/java/android/graphics/drawable/RippleComponent.java4
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java91
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreProvider.java8
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java51
-rw-r--r--libs/hwui/FrameInfo.h16
-rw-r--r--libs/hwui/FrameInfoVisualizer.cpp117
-rw-r--r--libs/hwui/FrameInfoVisualizer.h16
-rw-r--r--libs/hwui/utils/RingBuffer.h4
-rw-r--r--media/java/android/media/tv/TvInputService.java10
-rw-r--r--packages/PrintSpooler/res/values/strings.xml2
-rw-r--r--packages/SystemUI/res/layout/recents_task_view.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java142
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java4
30 files changed, 810 insertions, 358 deletions
diff --git a/api/current.txt b/api/current.txt
index 0467b69..51a1e79 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24479,13 +24479,14 @@ package android.provider {
field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS";
- field public static final java.lang.String ACTION_VOICE_CANCEL_ALARM = "android.intent.action.VOICE_CANCEL_ALARM";
- field public static final java.lang.String ACTION_VOICE_DELETE_ALARM = "android.intent.action.VOICE_DELETE_ALARM";
- field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "all";
- field public static final java.lang.String ALARM_SEARCH_MODE_NEXT = "next";
- field public static final java.lang.String ALARM_SEARCH_MODE_NONE = "none";
- field public static final java.lang.String ALARM_SEARCH_MODE_TIME = "time";
- field public static final java.lang.String EXTRA_ALARM_SEARCH_MODE = "android.intent.extra.alarm.ALARM_SEARCH_MODE";
+ field public static final java.lang.String ACTION_DISMISS_ALARM = "android.intent.action.DISMISS_ALARM";
+ field public static final java.lang.String ACTION_SNOOZE_ALARM = "android.intent.action.SNOOZE_ALARM";
+ field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "android.all";
+ field public static final java.lang.String ALARM_SEARCH_MODE_LABEL = "android.label";
+ field public static final java.lang.String ALARM_SEARCH_MODE_NEXT = "android.next";
+ field public static final java.lang.String ALARM_SEARCH_MODE_TIME = "android.time";
+ field public static final java.lang.String EXTRA_ALARM_SEARCH_MODE = "android.intent.extra.alarm.SEARCH_MODE";
+ field public static final java.lang.String EXTRA_ALARM_SNOOZE_DURATION = "android.intent.extra.alarm.SNOOZE_DURATION";
field public static final java.lang.String EXTRA_DAYS = "android.intent.extra.alarm.DAYS";
field public static final java.lang.String EXTRA_HOUR = "android.intent.extra.alarm.HOUR";
field public static final java.lang.String EXTRA_IS_PM = "android.intent.extra.alarm.IS_PM";
@@ -41541,6 +41542,7 @@ package android.widget {
method public int getCompoundPaddingTop();
method public final int getCurrentHintTextColor();
method public final int getCurrentTextColor();
+ method public android.view.ActionMode.Callback getCustomInsertionActionModeCallback();
method public android.view.ActionMode.Callback getCustomSelectionActionModeCallback();
method protected boolean getDefaultEditable();
method protected android.text.method.MovementMethod getDefaultMovementMethod();
@@ -41643,6 +41645,7 @@ package android.widget {
method public void setCompoundDrawablesWithIntrinsicBounds(int, int, int, int);
method public void setCompoundDrawablesWithIntrinsicBounds(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
method public void setCursorVisible(boolean);
+ method public void setCustomInsertionActionModeCallback(android.view.ActionMode.Callback);
method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback);
method public final void setEditableFactory(android.text.Editable.Factory);
method public void setElegantTextHeight(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index d11fe85..7ce111c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -26409,13 +26409,14 @@ package android.provider {
field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS";
- field public static final java.lang.String ACTION_VOICE_CANCEL_ALARM = "android.intent.action.VOICE_CANCEL_ALARM";
- field public static final java.lang.String ACTION_VOICE_DELETE_ALARM = "android.intent.action.VOICE_DELETE_ALARM";
- field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "all";
- field public static final java.lang.String ALARM_SEARCH_MODE_NEXT = "next";
- field public static final java.lang.String ALARM_SEARCH_MODE_NONE = "none";
- field public static final java.lang.String ALARM_SEARCH_MODE_TIME = "time";
- field public static final java.lang.String EXTRA_ALARM_SEARCH_MODE = "android.intent.extra.alarm.ALARM_SEARCH_MODE";
+ field public static final java.lang.String ACTION_DISMISS_ALARM = "android.intent.action.DISMISS_ALARM";
+ field public static final java.lang.String ACTION_SNOOZE_ALARM = "android.intent.action.SNOOZE_ALARM";
+ field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "android.all";
+ field public static final java.lang.String ALARM_SEARCH_MODE_LABEL = "android.label";
+ field public static final java.lang.String ALARM_SEARCH_MODE_NEXT = "android.next";
+ field public static final java.lang.String ALARM_SEARCH_MODE_TIME = "android.time";
+ field public static final java.lang.String EXTRA_ALARM_SEARCH_MODE = "android.intent.extra.alarm.SEARCH_MODE";
+ field public static final java.lang.String EXTRA_ALARM_SNOOZE_DURATION = "android.intent.extra.alarm.SNOOZE_DURATION";
field public static final java.lang.String EXTRA_DAYS = "android.intent.extra.alarm.DAYS";
field public static final java.lang.String EXTRA_HOUR = "android.intent.extra.alarm.HOUR";
field public static final java.lang.String EXTRA_IS_PM = "android.intent.extra.alarm.IS_PM";
@@ -44120,6 +44121,7 @@ package android.widget {
method public int getCompoundPaddingTop();
method public final int getCurrentHintTextColor();
method public final int getCurrentTextColor();
+ method public android.view.ActionMode.Callback getCustomInsertionActionModeCallback();
method public android.view.ActionMode.Callback getCustomSelectionActionModeCallback();
method protected boolean getDefaultEditable();
method protected android.text.method.MovementMethod getDefaultMovementMethod();
@@ -44222,6 +44224,7 @@ package android.widget {
method public void setCompoundDrawablesWithIntrinsicBounds(int, int, int, int);
method public void setCompoundDrawablesWithIntrinsicBounds(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
method public void setCursorVisible(boolean);
+ method public void setCustomInsertionActionModeCallback(android.view.ActionMode.Callback);
method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback);
method public final void setEditableFactory(android.text.Editable.Factory);
method public void setElegantTextHeight(boolean);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3224d41..10d76f7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1640,6 +1640,10 @@ public final class ActivityThread {
return sCurrentActivityThread;
}
+ public static boolean isSystem() {
+ return (sCurrentActivityThread != null) ? sCurrentActivityThread.mSystemThread : false;
+ }
+
public static String currentOpPackageName() {
ActivityThread am = currentActivityThread();
return (am != null && am.getApplication() != null)
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 2cfc1fa4..031854a 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -25,11 +25,13 @@ import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.IntentSender;
import android.os.Bundle;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
import android.os.UserHandle;
import android.util.AndroidException;
@@ -206,10 +208,20 @@ public final class PendingIntent implements Parcelable {
private int mResultCode;
private String mResultData;
private Bundle mResultExtras;
+ private static Handler sDefaultSystemHandler;
FinishedDispatcher(PendingIntent pi, OnFinished who, Handler handler) {
mPendingIntent = pi;
mWho = who;
- mHandler = handler;
+ if (handler == null && ActivityThread.isSystem()) {
+ // We assign a default handler for the system process to avoid deadlocks when
+ // processing receivers in various components that hold global service locks.
+ if (sDefaultSystemHandler == null) {
+ sDefaultSystemHandler = new Handler(Looper.getMainLooper());
+ }
+ mHandler = sDefaultSystemHandler;
+ } else {
+ mHandler = handler;
+ }
}
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean serialized, boolean sticky, int sendingUser) {
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index 48b604c..f965f54 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -106,7 +106,7 @@ public class ExtractEditText extends EditText {
if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
// Mode was started on Extracted, needs to be stopped here.
// Cut and paste will change the text, which stops selection mode.
- if (id == android.R.id.copy) stopSelectionActionMode();
+ if (id == android.R.id.copy) stopTextActionMode();
return true;
}
return super.onTextContextMenuItem(id);
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
index 25a35e1..be37293 100644
--- a/core/java/android/provider/AlarmClock.java
+++ b/core/java/android/provider/AlarmClock.java
@@ -50,7 +50,7 @@ public final class AlarmClock {
* {@link android.app.Activity#isVoiceInteraction}, and if true, the implementation should
* report a deeplink of the created/enabled alarm using
* {@link android.app.VoiceInteractor.CompleteVoiceRequest}. This allows follow-on voice actions
- * such as {@link #ACTION_VOICE_CANCEL_ALARM} to cancel the alarm that was just enabled.
+ * such as {@link #ACTION_DISMISS_ALARM} to dismiss the alarm that was just enabled.
* </p>
* <h3>Request parameters</h3>
* <ul>
@@ -69,46 +69,61 @@ public final class AlarmClock {
public static final String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
/**
- * Voice Activity Action: Cancel an alarm.
- * Requires: The activity must check {@link android.app.Activity#isVoiceInteraction}, i.e. be
- * started in Voice Interaction mode.
+ * Activity Action: Dismiss an alarm.
* <p>
- * Cancels the specified alarm by voice. To cancel means to disable, but not delete, the alarm.
- * See {@link #ACTION_VOICE_DELETE_ALARM} to delete an alarm by voice.
- * </p><p>
- * The alarm to cancel can be specified or searched for in one of the following ways:
+ * The alarm to dismiss can be specified or searched for in one of the following ways:
* <ol>
- * <li>The Intent's data URI specifies a deeplink to the alarm.
- * <li>If the Intent's data URI is unspecified, then the extra {@link #EXTRA_ALARM_SEARCH_MODE} is
- * required to determine how to search for the alarm.
+ * <li>The Intent's data URI, which represents a deeplink to the alarm.
+ * <li>The extra {@link #EXTRA_ALARM_SEARCH_MODE} to determine how to search for the alarm.
+ * </ol>
+ * </p><p>
+ * If neither of the above are given then:
+ * <ul>
+ * <li>If exactly one active alarm exists, it is dismissed.
+ * <li>If more than one active alarm exists, the user is prompted to choose the alarm to dismiss.
+ * </ul>
+ * </p><p>
+ * If the extra {@link #EXTRA_ALARM_SEARCH_MODE} is used, and the search results contain two or
+ * more matching alarms, then the implementation should show an UI with the results and allow
+ * the user to select the alarm to dismiss. If the implementation supports
+ * {@link android.content.Intent.CATEGORY_VOICE} and the activity is started in Voice
+ * Interaction mode (i.e. check {@link android.app.Activity#isVoiceInteraction}), then the
+ * implementation should additionally use {@link android.app.VoiceInteractor.PickOptionRequest}
+ * to start a voice interaction follow-on flow to help the user disambiguate the alarm by voice.
+ * </p><p>
+ * If the specified alarm is a single occurrence alarm, then dismissing it effectively disables
+ * the alarm; it will never ring again unless explicitly re-enabled.
+ * </p><p>
+ * If the specified alarm is a repeating alarm, then dismissing it only prevents the upcoming
+ * instance from ringing. The alarm remains enabled so that it will still ring on the date and
+ * time of the next instance (i.e. the instance after the upcoming one).
+ * </p>
*
- * @see #ACTION_VOICE_DELETE_ALARM
* @see #EXTRA_ALARM_SEARCH_MODE
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_VOICE_CANCEL_ALARM =
- "android.intent.action.VOICE_CANCEL_ALARM";
+ public static final String ACTION_DISMISS_ALARM =
+ "android.intent.action.DISMISS_ALARM";
/**
- * Voice Activity Action: Delete an alarm.
- * Requires: The activity must check {@link android.app.Activity#isVoiceInteraction}, i.e. be
- * started in Voice Interaction mode.
+ * Activity Action: Snooze a currently ringing alarm.
* <p>
- * Deletes the specified alarm by voice.
- * See {@link #ACTION_VOICE_CANCEL_ALARM} to cancel (disable) an alarm by voice.
+ * Snoozes the currently ringing alarm. The extra {@link #EXTRA_ALARM_SNOOZE_DURATION} can be
+ * optionally set to specify the snooze duration; if unset, the implementation should use a
+ * reasonable default, for example 10 minutes. The alarm should ring again after the snooze
+ * duration.
* </p><p>
- * The alarm to delete can be specified or searched for in one of the following ways:
- * <ol>
- * <li>The Intent's data URI specifies a deeplink to the alarm.
- * <li>If the Intent's data URI is unspecified, then the extra {@link #EXTRA_ALARM_SEARCH_MODE} is
- * required to determine how to search for the alarm.
+ * Note: setting the extra {@link #EXTRA_ALARM_SNOOZE_DURATION} does not change the default
+ * snooze duration; it's only applied to the currently ringing alarm.
+ * </p><p>
+ * If there is no currently ringing alarm, then this is a no-op.
+ * </p>
*
- * @see #ACTION_VOICE_CANCEL_ALARM
- * @see #EXTRA_ALARM_SEARCH_MODE
+ * @see #EXTRA_ALARM_SNOOZE_DURATION
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_VOICE_DELETE_ALARM =
- "android.intent.action.VOICE_DELETE_ALARM";
+ public static final String ACTION_SNOOZE_ALARM =
+ "android.intent.action.SNOOZE_ALARM";
/**
* Activity Action: Set a timer.
@@ -149,10 +164,9 @@ public final class AlarmClock {
/**
* Bundle extra: Specify the type of search mode to look up an alarm.
* <p>
- * Used by {@link #ACTION_VOICE_CANCEL_ALARM} and {@link #ACTION_VOICE_DELETE_ALARM} to identify
- * the alarm(s) to cancel or delete, respectively.
+ * For example, used by {@link #ACTION_DISMISS_ALARM} to identify the alarm to dismiss.
* </p><p>
- * This extra is only required when the alarm is not already identified by a deeplink as
+ * This extra is only used when the alarm is not already identified by a deeplink as
* specified in the Intent's data URI.
* </p><p>
* The value of this extra is a {@link String}, restricted to the following set of supported
@@ -164,22 +178,19 @@ public final class AlarmClock {
* <li><i>Next alarm</i> - {@link #ALARM_SEARCH_MODE_NEXT}: Selects the alarm that will
* ring next, or the alarm that is currently ringing, if any.
* <li><i>All alarms</i> - {@link #ALARM_SEARCH_MODE_ALL}: Selects all alarms.
- * <li><i>None</i> - {@link #ALARM_SEARCH_MODE_NONE}: No search mode specified. The
- * implementation should ask the user to select a search mode using
- * {@link android.app.VoiceInteractor.PickOptionRequest} and proceed with a voice flow to
- * identify the alarm.
+ * <li><i>Label</i> - {@link #ALARM_SEARCH_MODE_LABEL}: Search by alarm label. Should return
+ * alarms that contain the word or phrase in given label.
* </ul>
- * </ol>
+ * </p>
*
* @see #ALARM_SEARCH_MODE_TIME
* @see #ALARM_SEARCH_MODE_NEXT
* @see #ALARM_SEARCH_MODE_ALL
- * @see #ALARM_SEARCH_MODE_NONE
- * @see #ACTION_VOICE_CANCEL_ALARM
- * @see #ACTION_VOICE_DELETE_ALARM
+ * @see #ALARM_SEARCH_MODE_LABEL
+ * @see #ACTION_DISMISS_ALARM
*/
public static final String EXTRA_ALARM_SEARCH_MODE =
- "android.intent.extra.alarm.ALARM_SEARCH_MODE";
+ "android.intent.extra.alarm.SEARCH_MODE";
/**
* Search for the alarm that is most closely matched by the search parameters
@@ -193,35 +204,33 @@ public final class AlarmClock {
*
* @see #EXTRA_ALARM_SEARCH_MODE
*/
- public static final String ALARM_SEARCH_MODE_TIME = "time";
+ public static final String ALARM_SEARCH_MODE_TIME = "android.time";
/**
* Selects the alarm that will ring next, or the alarm that is currently ringing, if any.
*
* @see #EXTRA_ALARM_SEARCH_MODE
*/
- public static final String ALARM_SEARCH_MODE_NEXT = "next";
+ public static final String ALARM_SEARCH_MODE_NEXT = "android.next";
/**
* Selects all alarms.
*
* @see #EXTRA_ALARM_SEARCH_MODE
*/
- public static final String ALARM_SEARCH_MODE_ALL = "all";
+ public static final String ALARM_SEARCH_MODE_ALL = "android.all";
/**
- * No search mode specified. The implementation should ask the user to select a search mode
- * using {@link android.app.VoiceInteractor.PickOptionRequest} and proceed with a voice flow to
- * identify the alarm.
+ * Search by alarm label. Should return alarms that contain the word or phrase in given label.
*
* @see #EXTRA_ALARM_SEARCH_MODE
*/
- public static final String ALARM_SEARCH_MODE_NONE = "none";
+ public static final String ALARM_SEARCH_MODE_LABEL = "android.label";
/**
* Bundle extra: The AM/PM of the alarm.
* <p>
- * Used by {@link #ACTION_VOICE_CANCEL_ALARM} and {@link #ACTION_VOICE_DELETE_ALARM}.
+ * Used by {@link #ACTION_DISMISS_ALARM}.
* </p><p>
* This extra is optional and only used when {@link #EXTRA_ALARM_SEARCH_MODE} is set to
* {@link #ALARM_SEARCH_MODE_TIME}. In this search mode, the {@link #EXTRA_IS_PM} is
@@ -233,13 +242,25 @@ public final class AlarmClock {
* The value is a {@link Boolean}, where false=AM and true=PM.
* </p>
*
- * @see #ACTION_VOICE_CANCEL_ALARM
- * @see #ACTION_VOICE_DELETE_ALARM
+ * @see #ACTION_DISMISS_ALARM
* @see #EXTRA_HOUR
* @see #EXTRA_MINUTES
*/
public static final String EXTRA_IS_PM = "android.intent.extra.alarm.IS_PM";
+
+ /**
+ * Bundle extra: The snooze duration of the alarm in minutes.
+ * <p>
+ * Used by {@link #ACTION_SNOOZE_ALARM}. This extra is optional and the value is an
+ * {@link Integer} that specifies the duration in minutes for which to snooze the alarm.
+ * </p>
+ *
+ * @see #ACTION_SNOOZE_ALARM
+ */
+ public static final String EXTRA_ALARM_SNOOZE_DURATION =
+ "android.intent.extra.alarm.SNOOZE_DURATION";
+
/**
* Bundle extra: Weekdays for repeating alarm.
* <p>
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 053b35c..1c8a79b 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1169,7 +1169,10 @@ public final class InputMethodManager {
// do its stuff.
// Life is good: let's hook everything up!
EditorInfo tba = new EditorInfo();
- tba.packageName = view.getContext().getPackageName();
+ // Note: Use Context#getOpPackageName() rather than Context#getPackageName() so that the
+ // system can verify the consistency between the uid of this process and package name passed
+ // from here. See comment of Context#getOpPackageName() for details.
+ tba.packageName = view.getContext().getOpPackageName();
tba.fieldId = view.getId();
InputConnection ic = view.onCreateInputConnection(tba);
if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 56f9b5c..4c98abf 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -134,7 +134,8 @@ public class Editor {
// Cursor Controllers.
InsertionPointCursorController mInsertionPointCursorController;
SelectionModifierCursorController mSelectionModifierCursorController;
- ActionMode mSelectionActionMode;
+ // Action mode used when text is selected or when actions on an insertion cursor are triggered.
+ ActionMode mTextActionMode;
boolean mInsertionControllerEnabled;
boolean mSelectionControllerEnabled;
@@ -205,13 +206,14 @@ public class Editor {
float mLastDownPositionX, mLastDownPositionY;
Callback mCustomSelectionActionModeCallback;
+ Callback mCustomInsertionActionModeCallback;
// Set when this TextView gained focus with some text selected. Will start selection mode.
boolean mCreatedWithASelection;
boolean mDoubleTap = false;
- private Runnable mSelectionModeWithoutSelectionRunnable;
+ private Runnable mInsertionActionModeRunnable;
// The span controller helps monitoring the changes to which the Editor needs to react:
// - EasyEditSpans, for which we have some UI to display on attach and on hide
@@ -236,8 +238,8 @@ public class Editor {
private final Runnable mHideFloatingToolbar = new Runnable() {
@Override
public void run() {
- if (mSelectionActionMode != null) {
- mSelectionActionMode.snooze(ActionMode.SNOOZE_TIME_DEFAULT);
+ if (mTextActionMode != null) {
+ mTextActionMode.snooze(ActionMode.SNOOZE_TIME_DEFAULT);
}
}
};
@@ -245,8 +247,8 @@ public class Editor {
private final Runnable mShowFloatingToolbar = new Runnable() {
@Override
public void run() {
- if (mSelectionActionMode != null) {
- mSelectionActionMode.snooze(0); // snooze off.
+ if (mTextActionMode != null) {
+ mTextActionMode.snooze(0); // snooze off.
}
}
};
@@ -310,7 +312,7 @@ public class Editor {
void replace() {
int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2;
- stopSelectionActionMode();
+ stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), middle);
showSuggestions();
}
@@ -343,7 +345,7 @@ public class Editor {
mTextView.setHasTransientState(false);
// We had an active selection from before, start the selection mode.
- startSelectionActionModeWithSelection();
+ startSelectionActionMode();
}
getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
@@ -372,8 +374,8 @@ public class Editor {
}
// Cancel the single tap delayed runnable.
- if (mSelectionModeWithoutSelectionRunnable != null) {
- mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
+ if (mInsertionActionModeRunnable != null) {
+ mTextView.removeCallbacks(mInsertionActionModeRunnable);
}
mTextView.removeCallbacks(mHideFloatingToolbar);
@@ -390,7 +392,7 @@ public class Editor {
mPreserveDetachedSelection = true;
hideControllers();
- stopSelectionActionMode();
+ stopTextActionMode();
mPreserveDetachedSelection = false;
mTemporaryDetach = false;
}
@@ -586,7 +588,7 @@ public class Editor {
}
if (!mSelectionControllerEnabled) {
- stopSelectionActionMode();
+ stopTextActionMode();
if (mSelectionModifierCursorController != null) {
mSelectionModifierCursorController.onDetached();
mSelectionModifierCursorController = null;
@@ -984,14 +986,14 @@ public class Editor {
mInsertionControllerEnabled) {
final int offset = mTextView.getOffsetForPosition(mLastDownPositionX,
mLastDownPositionY);
- stopSelectionActionMode();
+ stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), offset);
getInsertionController().show();
- startSelectionActionModeWithoutSelection();
+ startInsertionActionMode();
handled = true;
}
- if (!handled && mSelectionActionMode != null) {
+ if (!handled && mTextActionMode != null) {
if (touchPositionIsInSelection()) {
// Start a drag
final int start = mTextView.getSelectionStart();
@@ -1001,9 +1003,9 @@ public class Editor {
DragLocalState localState = new DragLocalState(mTextView, start, end);
mTextView.startDrag(data, getTextThumbnailBuilder(selectedText), localState,
View.DRAG_FLAG_GLOBAL);
- stopSelectionActionMode();
+ stopTextActionMode();
} else {
- stopSelectionActionMode();
+ stopTextActionMode();
selectCurrentWordAndStartDrag();
}
handled = true;
@@ -1101,12 +1103,12 @@ public class Editor {
final int selStart = mTextView.getSelectionStart();
final int selEnd = mTextView.getSelectionEnd();
hideControllers();
- stopSelectionActionMode();
+ stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd);
} else {
if (mTemporaryDetach) mPreserveDetachedSelection = true;
hideControllers();
- stopSelectionActionMode();
+ stopTextActionMode();
if (mTemporaryDetach) mPreserveDetachedSelection = false;
downgradeEasyCorrectionSpans();
}
@@ -1149,7 +1151,7 @@ public class Editor {
// We do not hide the span controllers, since they can be added when a new text is
// inserted into the text view (voice IME).
hideCursorControllers();
- stopSelectionActionMode();
+ stopTextActionMode();
}
private int getLastTapPosition() {
@@ -1216,7 +1218,7 @@ public class Editor {
}
private void updateFloatingToolbarVisibility(MotionEvent event) {
- if (mSelectionActionMode != null) {
+ if (mTextActionMode != null) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
hideFloatingToolbar();
@@ -1229,7 +1231,7 @@ public class Editor {
}
private void hideFloatingToolbar() {
- if (mSelectionActionMode != null) {
+ if (mTextActionMode != null) {
mTextView.removeCallbacks(mShowFloatingToolbar);
// Delay the "hide" a little bit just in case a "show" will happen almost immediately.
mTextView.postDelayed(mHideFloatingToolbar, 100);
@@ -1237,7 +1239,7 @@ public class Editor {
}
private void showFloatingToolbar() {
- if (mSelectionActionMode != null) {
+ if (mTextActionMode != null) {
mTextView.removeCallbacks(mHideFloatingToolbar);
// Delay "show" so it doesn't interfere with click confirmations
// or double-clicks that could "dismiss" the floating toolbar.
@@ -1701,26 +1703,20 @@ public class Editor {
/**
* @return true if the selection mode was actually started.
*/
- private boolean startSelectionActionModeWithoutSelection() {
+ private boolean startInsertionActionMode() {
+ if (mInsertionActionModeRunnable != null) {
+ mTextView.removeCallbacks(mInsertionActionModeRunnable);
+ }
if (extractedTextModeWillBeStarted()) {
- // Cancel the single tap delayed runnable.
- if (mSelectionModeWithoutSelectionRunnable != null) {
- mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
- }
-
return false;
}
+ stopTextActionMode();
- if (mSelectionActionMode != null) {
- // Selection action mode is already started
- // TODO: revisit invocations to minimize this case.
- mSelectionActionMode.invalidate();
- return false;
- }
- ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
- mSelectionActionMode = mTextView.startActionMode(
+ ActionMode.Callback actionModeCallback =
+ new TextActionModeCallback(false /* hasSelection */);
+ mTextActionMode = mTextView.startActionMode(
actionModeCallback, ActionMode.TYPE_FLOATING);
- return mSelectionActionMode != null;
+ return mTextActionMode != null;
}
/**
@@ -1730,8 +1726,8 @@ public class Editor {
*
* @return true if the selection mode was actually started.
*/
- boolean startSelectionActionModeWithSelection() {
- boolean selectionStarted = startSelectionActionModeWithSelectionInternal();
+ boolean startSelectionActionMode() {
+ boolean selectionStarted = startSelectionActionModeInternal();
if (selectionStarted) {
getSelectionController().show();
} else if (getInsertionController() != null) {
@@ -1749,13 +1745,13 @@ public class Editor {
private boolean selectCurrentWordAndStartDrag() {
if (extractedTextModeWillBeStarted()) {
// Cancel the single tap delayed runnable.
- if (mSelectionModeWithoutSelectionRunnable != null) {
- mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
+ if (mInsertionActionModeRunnable != null) {
+ mTextView.removeCallbacks(mInsertionActionModeRunnable);
}
return false;
}
- if (mSelectionActionMode != null) {
- mSelectionActionMode.finish();
+ if (mTextActionMode != null) {
+ mTextActionMode.finish();
}
if (!checkFieldAndSelectCurrentWord()) {
return false;
@@ -1784,10 +1780,10 @@ public class Editor {
return true;
}
- private boolean startSelectionActionModeWithSelectionInternal() {
- if (mSelectionActionMode != null) {
+ private boolean startSelectionActionModeInternal() {
+ if (mTextActionMode != null) {
// Selection action mode is already started
- mSelectionActionMode.invalidate();
+ mTextActionMode.invalidate();
return false;
}
@@ -1800,12 +1796,13 @@ public class Editor {
// Do not start the action mode when extracted text will show up full screen, which would
// immediately hide the newly created action bar and would be visually distracting.
if (!willExtract) {
- ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
- mSelectionActionMode = mTextView.startActionMode(
+ ActionMode.Callback actionModeCallback =
+ new TextActionModeCallback(true /* hasSelection */);
+ mTextActionMode = mTextView.startActionMode(
actionModeCallback, ActionMode.TYPE_FLOATING);
}
- final boolean selectionStarted = mSelectionActionMode != null || willExtract;
+ final boolean selectionStarted = mTextActionMode != null || willExtract;
if (selectionStarted && !mTextView.isTextSelectable() && mShowSoftInputOnFocus) {
// Show the IME to be able to replace text, except when selecting non editable text.
final InputMethodManager imm = InputMethodManager.peekInstance();
@@ -1906,7 +1903,7 @@ public class Editor {
void onTouchUpEvent(MotionEvent event) {
boolean selectAllGotFocus = mSelectAllOnFocus && mTextView.didTouchFocusSelect();
hideControllers();
- stopSelectionActionMode();
+ stopTextActionMode();
CharSequence text = mTextView.getText();
if (!selectAllGotFocus && text.length() > 0) {
// Move cursor
@@ -1920,8 +1917,8 @@ public class Editor {
if (!extractedTextModeWillBeStarted()) {
if (isCursorInsideEasyCorrectionSpan()) {
// Cancel the single tap delayed runnable.
- if (mSelectionModeWithoutSelectionRunnable != null) {
- mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
+ if (mInsertionActionModeRunnable != null) {
+ mTextView.removeCallbacks(mInsertionActionModeRunnable);
}
mShowSuggestionRunnable = new Runnable() {
@@ -1939,10 +1936,10 @@ public class Editor {
}
}
- protected void stopSelectionActionMode() {
- if (mSelectionActionMode != null) {
+ protected void stopTextActionMode() {
+ if (mTextActionMode != null) {
// This will hide the mSelectionModifierCursorController
- mSelectionActionMode.finish();
+ mTextActionMode.finish();
}
}
@@ -2027,7 +2024,7 @@ public class Editor {
mSuggestionsPopupWindow = new SuggestionsPopupWindow();
}
hideControllers();
- stopSelectionActionMode();
+ stopTextActionMode();
mSuggestionsPopupWindow.show();
}
@@ -2035,8 +2032,8 @@ public class Editor {
if (mPositionListener != null) {
mPositionListener.onScrollChanged();
}
- if (mSelectionActionMode != null) {
- mSelectionActionMode.invalidateContentRect();
+ if (mTextActionMode != null) {
+ mTextActionMode.invalidateContentRect();
}
}
@@ -3087,45 +3084,51 @@ public class Editor {
}
/**
- * An ActionMode Callback class that is used to provide actions while in text selection mode.
+ * An ActionMode Callback class that is used to provide actions while in text insertion or
+ * selection mode.
*
- * The default callback provides a subset of Select All, Cut, Copy and Paste actions, depending
- * on which of these this TextView supports.
+ * The default callback provides a subset of Select All, Cut, Copy, Paste, Share and Replace
+ * actions, depending on which of these this TextView supports and the current selection.
*/
- private class SelectionActionModeCallback extends ActionMode.Callback2 {
+ private class TextActionModeCallback extends ActionMode.Callback2 {
private final Path mSelectionPath = new Path();
private final RectF mSelectionBounds = new RectF();
-
- private int mSelectionHandleHeight;
- private int mInsertionHandleHeight;
-
- public SelectionActionModeCallback() {
- SelectionModifierCursorController selectionController = getSelectionController();
- if (selectionController.mStartHandle == null) {
- // As these are for initializing selectionController, hide() must be called.
- selectionController.initDrawables();
- selectionController.initHandles();
- selectionController.hide();
- }
- mSelectionHandleHeight = Math.max(
- mSelectHandleLeft.getMinimumHeight(), mSelectHandleRight.getMinimumHeight());
- InsertionPointCursorController insertionController = getInsertionController();
- if (insertionController != null) {
- insertionController.getHandle();
- mInsertionHandleHeight = mSelectHandleCenter.getMinimumHeight();
+ private final boolean mHasSelection;
+
+ private int mHandleHeight;
+
+ public TextActionModeCallback(boolean hasSelection) {
+ mHasSelection = hasSelection;
+ if (mHasSelection) {
+ SelectionModifierCursorController selectionController = getSelectionController();
+ if (selectionController.mStartHandle == null) {
+ // As these are for initializing selectionController, hide() must be called.
+ selectionController.initDrawables();
+ selectionController.initHandles();
+ selectionController.hide();
+ }
+ mHandleHeight = Math.max(
+ mSelectHandleLeft.getMinimumHeight(),
+ mSelectHandleRight.getMinimumHeight());
+ } else {
+ InsertionPointCursorController insertionController = getInsertionController();
+ if (insertionController != null) {
+ insertionController.getHandle();
+ mHandleHeight = mSelectHandleCenter.getMinimumHeight();
+ }
}
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- mode.setTitle(mTextView.getContext().getString(
- com.android.internal.R.string.textSelectionCABTitle));
+ mode.setTitle(null);
mode.setSubtitle(null);
mode.setTitleOptionalHint(true);
populateMenuWithItems(menu);
- if (mCustomSelectionActionModeCallback != null) {
- if (!mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu)) {
+ Callback customCallback = getCustomCallback();
+ if (customCallback != null) {
+ if (!customCallback.onCreateActionMode(mode, menu)) {
// The custom mode can choose to cancel the action mode
return false;
}
@@ -3141,6 +3144,12 @@ public class Editor {
}
}
+ private Callback getCustomCallback() {
+ return mHasSelection
+ ? mCustomSelectionActionModeCallback
+ : mCustomInsertionActionModeCallback;
+ }
+
private void populateMenuWithItems(Menu menu) {
if (mTextView.canCut()) {
menu.add(0, TextView.ID_CUT, 0, com.android.internal.R.string.cut).
@@ -3203,8 +3212,9 @@ public class Editor {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
updateReplaceItem(menu);
- if (mCustomSelectionActionModeCallback != null) {
- return mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu);
+ Callback customCallback = getCustomCallback();
+ if (customCallback != null) {
+ return customCallback.onPrepareActionMode(mode, menu);
}
return true;
}
@@ -3230,8 +3240,8 @@ public class Editor {
item.getIntent(), TextView.PROCESS_TEXT_REQUEST_CODE);
return true;
}
- if (mCustomSelectionActionModeCallback != null &&
- mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) {
+ Callback customCallback = getCustomCallback();
+ if (customCallback != null && customCallback.onActionItemClicked(mode, item)) {
return true;
}
return mTextView.onTextContextMenuItem(item.getItemId());
@@ -3239,8 +3249,9 @@ public class Editor {
@Override
public void onDestroyActionMode(ActionMode mode) {
- if (mCustomSelectionActionModeCallback != null) {
- mCustomSelectionActionModeCallback.onDestroyActionMode(mode);
+ Callback customCallback = getCustomCallback();
+ if (customCallback != null) {
+ customCallback.onDestroyActionMode(mode);
}
/*
@@ -3259,7 +3270,7 @@ public class Editor {
mSelectionModifierCursorController.resetTouchOffsets();
}
- mSelectionActionMode = null;
+ mTextActionMode = null;
}
@Override
@@ -3274,7 +3285,7 @@ public class Editor {
mTextView.getLayout().getSelectionPath(
mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mSelectionPath);
mSelectionPath.computeBounds(mSelectionBounds, true);
- mSelectionBounds.bottom += mSelectionHandleHeight;
+ mSelectionBounds.bottom += mHandleHeight;
} else if (mCursorCount == 2) {
// We have a split cursor. In this case, we take the rectangle that includes both
// parts of the cursor to ensure we don't obscure either of them.
@@ -3285,7 +3296,7 @@ public class Editor {
Math.min(firstCursorBounds.top, secondCursorBounds.top),
Math.max(firstCursorBounds.right, secondCursorBounds.right),
Math.max(firstCursorBounds.bottom, secondCursorBounds.bottom)
- + mInsertionHandleHeight);
+ + mHandleHeight);
} else {
// We have a single cursor.
int line = mTextView.getLayout().getLineForOffset(mTextView.getSelectionStart());
@@ -3295,7 +3306,7 @@ public class Editor {
primaryHorizontal,
mTextView.getLayout().getLineTop(line),
primaryHorizontal + 1,
- mTextView.getLayout().getLineTop(line + 1) + mInsertionHandleHeight);
+ mTextView.getLayout().getLineTop(line + 1) + mHandleHeight);
}
// Take TextView's padding and scroll into account.
int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset();
@@ -3847,25 +3858,25 @@ public class Editor {
SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime;
// Cancel the single tap delayed runnable.
- if (mSelectionModeWithoutSelectionRunnable != null
+ if (mInsertionActionModeRunnable != null
&& (mDoubleTap || isCursorInsideEasyCorrectionSpan())) {
- mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
+ mTextView.removeCallbacks(mInsertionActionModeRunnable);
}
// Prepare and schedule the single tap runnable to run exactly after the double tap
// timeout has passed.
if (!mDoubleTap && !isCursorInsideEasyCorrectionSpan()
&& (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) {
- if (mSelectionModeWithoutSelectionRunnable == null) {
- mSelectionModeWithoutSelectionRunnable = new Runnable() {
+ if (mInsertionActionModeRunnable == null) {
+ mInsertionActionModeRunnable = new Runnable() {
public void run() {
- startSelectionActionModeWithoutSelection();
+ startInsertionActionMode();
}
};
}
mTextView.postDelayed(
- mSelectionModeWithoutSelectionRunnable,
+ mInsertionActionModeRunnable,
ViewConfiguration.getDoubleTapTimeout() + 1);
}
@@ -3934,15 +3945,15 @@ public class Editor {
if (distanceSquared < touchSlop * touchSlop) {
// Tapping on the handle toggles the selection action mode.
- if (mSelectionActionMode != null) {
- mSelectionActionMode.finish();
+ if (mTextActionMode != null) {
+ mTextActionMode.finish();
} else {
- startSelectionActionModeWithoutSelection();
+ startInsertionActionMode();
}
}
} else {
- if (mSelectionActionMode != null) {
- mSelectionActionMode.invalidateContentRect();
+ if (mTextActionMode != null) {
+ mTextActionMode.invalidateContentRect();
}
}
hideAfterDelay();
@@ -3972,8 +3983,8 @@ public class Editor {
@Override
public void updatePosition(float x, float y) {
positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false);
- if (mSelectionActionMode != null) {
- mSelectionActionMode.invalidate();
+ if (mTextActionMode != null) {
+ mTextActionMode.invalidate();
}
}
@@ -4024,8 +4035,8 @@ public class Editor {
Selection.setSelection((Spannable) mTextView.getText(), offset,
mTextView.getSelectionEnd());
updateDrawable();
- if (mSelectionActionMode != null) {
- mSelectionActionMode.invalidate();
+ if (mTextActionMode != null) {
+ mTextActionMode.invalidate();
}
}
@@ -4150,8 +4161,8 @@ public class Editor {
public void updateSelection(int offset) {
Selection.setSelection((Spannable) mTextView.getText(),
mTextView.getSelectionStart(), offset);
- if (mSelectionActionMode != null) {
- mSelectionActionMode.invalidate();
+ if (mTextActionMode != null) {
+ mTextActionMode.invalidate();
}
updateDrawable();
}
@@ -4515,7 +4526,7 @@ public class Editor {
mEndHandle.showAtLocation(endOffset);
// No longer the first dragging motion, reset.
- startSelectionActionModeWithSelection();
+ startSelectionActionMode();
mDragAcceleratorActive = false;
mStartOffset = -1;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3a85476..6872caa 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1481,7 +1481,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
if (mEditor.hasSelectionController()) {
- mEditor.startSelectionActionModeWithSelection();
+ mEditor.startSelectionActionMode();
}
}
}
@@ -5282,7 +5282,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// - onFocusChanged cannot start it when focus is given to a view with selected text (after
// a screen rotation) since layout is not yet initialized at that point.
if (mEditor != null && mEditor.mCreatedWithASelection) {
- mEditor.startSelectionActionModeWithSelection();
+ mEditor.startSelectionActionMode();
mEditor.mCreatedWithASelection = false;
}
@@ -5290,7 +5290,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can
// not be set. Do the test here instead.
if (this instanceof ExtractEditText && hasSelection() && mEditor != null) {
- mEditor.startSelectionActionModeWithSelection();
+ mEditor.startSelectionActionMode();
}
unregisterForPreDraw();
@@ -5908,7 +5908,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
- boolean isInSelectionMode = mEditor != null && mEditor.mSelectionActionMode != null;
+ boolean isInSelectionMode = mEditor != null && mEditor.mTextActionMode != null;
if (isInSelectionMode) {
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
@@ -5923,7 +5923,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
state.handleUpEvent(event);
}
if (event.isTracking() && !event.isCanceled()) {
- stopSelectionActionMode();
+ stopTextActionMode();
return true;
}
}
@@ -6092,8 +6092,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Has to be done on key down (and not on key up) to correctly be intercepted.
case KeyEvent.KEYCODE_BACK:
- if (mEditor != null && mEditor.mSelectionActionMode != null) {
- stopSelectionActionMode();
+ if (mEditor != null && mEditor.mTextActionMode != null) {
+ stopTextActionMode();
return -1;
}
break;
@@ -6423,7 +6423,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// extracted mode will start. Some text is selected though, and will trigger an action mode
// in the extracted view.
mEditor.hideControllers();
- stopSelectionActionMode();
+ stopTextActionMode();
}
/**
@@ -8258,7 +8258,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
super.onVisibilityChanged(changedView, visibility);
if (mEditor != null && visibility != VISIBLE) {
mEditor.hideControllers();
- stopSelectionActionMode();
+ stopTextActionMode();
}
}
@@ -8976,7 +8976,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Selection.setSelection((Spannable) text, start, end);
// Make sure selection mode is engaged.
if (mEditor != null) {
- mEditor.startSelectionActionModeWithSelection();
+ mEditor.startSelectionActionMode();
}
return true;
}
@@ -9100,12 +9100,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case ID_CUT:
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
deleteText_internal(min, max);
- stopSelectionActionMode();
+ stopTextActionMode();
return true;
case ID_COPY:
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
- stopSelectionActionMode();
+ stopTextActionMode();
return true;
case ID_REPLACE:
@@ -9195,14 +9195,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* selection is initiated in this View.
*
* The standard implementation populates the menu with a subset of Select All, Cut, Copy,
- * Paste and Share actions, depending on what this View supports.
+ * Paste, Replace and Share actions, depending on what this View supports.
*
* A custom implementation can add new entries in the default menu in its
* {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The
* default actions can also be removed from the menu using
* {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
- * {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste} or
- * {@link android.R.id#shareText} ids as parameters.
+ * {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste},
+ * {@link android.R.id#replaceText} or {@link android.R.id#shareText} ids as parameters.
*
* Returning false from
* {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent
@@ -9230,11 +9230,48 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * If provided, this ActionMode.Callback will be used to create the ActionMode when text
+ * insertion is initiated in this View.
+ *
+ * The standard implementation populates the menu with a subset of Select All,
+ * Paste and Replace actions, depending on what this View supports.
+ *
+ * A custom implementation can add new entries in the default menu in its
+ * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The
+ * default actions can also be removed from the menu using
+ * {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
+ * {@link android.R.id#paste} or {@link android.R.id#replaceText} ids as parameters.
+ *
+ * Returning false from
+ * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent
+ * the action mode from being started.
+ *
+ * Action click events should be handled by the custom implementation of
+ * {@link android.view.ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}.
+ *
+ * Note that text insertion mode is not started when a TextView receives focus and the
+ * {@link android.R.attr#selectAllOnFocus} flag has been set.
+ */
+ public void setCustomInsertionActionModeCallback(ActionMode.Callback actionModeCallback) {
+ createEditorIfNeeded();
+ mEditor.mCustomInsertionActionModeCallback = actionModeCallback;
+ }
+
+ /**
+ * Retrieves the value set in {@link #setCustomInsertionActionModeCallback}. Default is null.
+ *
+ * @return The current custom insertion callback.
+ */
+ public ActionMode.Callback getCustomInsertionActionModeCallback() {
+ return mEditor == null ? null : mEditor.mCustomInsertionActionModeCallback;
+ }
+
+ /**
* @hide
*/
- protected void stopSelectionActionMode() {
+ protected void stopTextActionMode() {
if (mEditor != null) {
- mEditor.stopSelectionActionMode();
+ mEditor.stopTextActionMode();
}
}
@@ -9346,7 +9383,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
}
- stopSelectionActionMode();
+ stopTextActionMode();
sLastCutCopyOrTextChangedTime = 0;
}
}
@@ -9359,7 +9396,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT);
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
getContext().startActivity(Intent.createChooser(sharingIntent, null));
- stopSelectionActionMode();
+ stopTextActionMode();
}
}
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 5857de0..55c000c 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -407,6 +407,11 @@
</family>
<family>
<fileset>
+ <file>NotoSansSymbols-Regular-Subsetted.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file lang="zh-Hans">NotoSansSC-Regular.otf</file>
</fileset>
</family>
@@ -432,11 +437,6 @@
</family>
<family>
<fileset>
- <file>NotoSansSymbols-Regular-Subsetted.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
<file>NotoColorEmoji.ttf</file>
</fileset>
</family>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 62da0ff..dbe81fa 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -331,6 +331,9 @@
<family>
<font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
</family>
+ <family>
+ <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
+ </family>
<family lang="zh-Hans">
<font weight="400" style="normal">NotoSansSC-Regular.otf</font>
</family>
@@ -347,9 +350,6 @@
<font weight="400" style="normal">NanumGothic.ttf</font>
</family>
<family>
- <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
- </family>
- <family>
<font weight="400" style="normal">NotoColorEmoji.ttf</font>
</family>
<family>
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index aa2aa20..23a3ee3 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -233,6 +233,10 @@ abstract class RippleComponent {
if (mHasPendingHardwareAnimator) {
mHasPendingHardwareAnimator = false;
+
+ // Manually jump values to their exited state. Normally we'd do that
+ // later when starting the hardware exit, but we're aborting early.
+ jumpValuesToExit();
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
new file mode 100644
index 0000000..20db41b
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 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.security.keystore;
+
+import android.security.Credentials;
+import android.security.KeyStore;
+
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+/**
+ * {@link KeyFactorySpi} backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
+
+ private final KeyStore mKeyStore = KeyStore.getInstance();
+
+ @Override
+ protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
+ throws InvalidKeySpecException {
+ if (keySpecClass == null) {
+ throw new InvalidKeySpecException("keySpecClass == null");
+ }
+ if (!(key instanceof AndroidKeyStorePrivateKey)) {
+ throw new InvalidKeySpecException("Only Android KeyStore private keys supported: " +
+ ((key != null) ? key.getClass().getName() : "null"));
+ }
+ if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpecClass)) {
+ throw new InvalidKeySpecException(
+ "Key material export of Android KeyStore keys is not supported");
+ }
+ if (!KeyInfo.class.equals(keySpecClass)) {
+ throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
+ }
+ String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias();
+ String entryAlias;
+ if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
+ entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
+ } else {
+ throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
+ }
+
+ @SuppressWarnings("unchecked")
+ T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(
+ mKeyStore, entryAlias, keyAliasInKeystore);
+ return result;
+ }
+
+ @Override
+ protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException {
+ throw new UnsupportedOperationException(
+ "To generate a key pair in Android KeyStore, use KeyPairGenerator initialized with"
+ + " " + KeyGenParameterSpec.class.getName());
+ }
+
+ @Override
+ protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException {
+ throw new UnsupportedOperationException(
+ "To generate a key pair in Android KeyStore, use KeyPairGenerator initialized with"
+ + " " + KeyGenParameterSpec.class.getName());
+ }
+
+ @Override
+ protected Key engineTranslateKey(Key arg0) throws InvalidKeyException {
+ throw new UnsupportedOperationException(
+ "To import a key into Android KeyStore, use KeyStore.setEntry with "
+ + KeyProtection.class.getName());
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index de4213e..cb270bb 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -53,6 +53,10 @@ public class AndroidKeyStoreProvider extends Provider {
put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
+ // java.security.KeyFactory
+ putKeyFactoryImpl("EC");
+ putKeyFactoryImpl("RSA");
+
// javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1");
@@ -101,6 +105,10 @@ public class AndroidKeyStoreProvider extends Provider {
put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi");
}
+ private void putKeyFactoryImpl(String algorithm) {
+ put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi");
+ }
+
/**
* Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
* primitive.
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 455f170..8b00821 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -21,9 +21,8 @@ import android.security.KeyStore;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
-import libcore.util.EmptyArray;
-
import java.security.InvalidKeyException;
+import java.security.ProviderException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.ArrayList;
@@ -35,7 +34,7 @@ import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.SecretKeySpec;
/**
- * {@link SecretKeyFactorySpi} backed by Android KeyStore.
+ * {@link SecretKeyFactorySpi} backed by Android Keystore.
*
* @hide
*/
@@ -60,7 +59,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
if (!KeyInfo.class.equals(keySpecClass)) {
throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
}
- String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
+ String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias();
String entryAlias;
if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
@@ -68,11 +67,15 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
}
+ return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore);
+ }
+
+ static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore) {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode =
- mKeyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
+ keyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
- throw new InvalidKeySpecException("Failed to obtain information about key."
+ throw new ProviderException("Failed to obtain information about key."
+ " Keystore error: " + errorCode);
}
@@ -81,6 +84,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
int keySize;
@KeyProperties.PurposeEnum int purposes;
String[] encryptionPaddings;
+ String[] signaturePaddings;
@KeyProperties.DigestEnum String[] digests;
@KeyProperties.BlockModeEnum String[] blockModes;
int keymasterSwEnforcedUserAuthenticators;
@@ -95,29 +99,40 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
origin = KeyProperties.Origin.fromKeymaster(
keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
} else {
- throw new InvalidKeySpecException("Key origin not available");
+ throw new ProviderException("Key origin not available");
}
Integer keySizeInteger = keyCharacteristics.getInteger(KeymasterDefs.KM_TAG_KEY_SIZE);
if (keySizeInteger == null) {
- throw new InvalidKeySpecException("Key size not available");
+ throw new ProviderException("Key size not available");
}
keySize = keySizeInteger;
purposes = KeyProperties.Purpose.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PURPOSE));
List<String> encryptionPaddingsList = new ArrayList<String>();
+ List<String> signaturePaddingsList = new ArrayList<String>();
+ // Keymaster stores both types of paddings in the same array -- we split it into two.
for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) {
- @KeyProperties.EncryptionPaddingEnum String jcaPadding;
try {
- jcaPadding = KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
+ @KeyProperties.EncryptionPaddingEnum String jcaPadding =
+ KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
+ encryptionPaddingsList.add(jcaPadding);
} catch (IllegalArgumentException e) {
- throw new InvalidKeySpecException(
- "Unsupported encryption padding: " + keymasterPadding);
+ try {
+ @KeyProperties.SignaturePaddingEnum String padding =
+ KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding);
+ signaturePaddingsList.add(padding);
+ } catch (IllegalArgumentException e2) {
+ throw new ProviderException(
+ "Unsupported encryption padding: " + keymasterPadding);
+ }
}
- encryptionPaddingsList.add(jcaPadding);
+
}
encryptionPaddings =
encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
+ signaturePaddings =
+ signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]);
digests = KeyProperties.Digest.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST));
@@ -128,7 +143,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
keymasterHwEnforcedUserAuthenticators =
keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
} catch (IllegalArgumentException e) {
- throw new InvalidKeySpecException("Unsupported key characteristic", e);
+ throw new ProviderException("Unsupported key characteristic", e);
}
Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
@@ -164,7 +179,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
keyValidityForConsumptionEnd,
purposes,
encryptionPaddings,
- EmptyArray.STRING, // no signature paddings -- this is symmetric crypto
+ signaturePaddings,
digests,
blockModes,
userAuthenticationRequired,
@@ -175,12 +190,14 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
@Override
protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
throw new UnsupportedOperationException(
- "Key import into Android KeyStore is not supported");
+ "To generate secret key in Android KeyStore, use KeyGenerator initialized with "
+ + KeyGenParameterSpec.class.getName());
}
@Override
protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
throw new UnsupportedOperationException(
- "Key import into Android KeyStore is not supported");
+ "To import a secret key into Android KeyStore, use KeyStore.setEntry with "
+ + KeyProtection.class.getName());
}
}
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 6815254..23339ce 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -116,16 +116,28 @@ public:
set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag);
}
- int64_t operator[](FrameInfoIndex index) const {
+ inline int64_t operator[](FrameInfoIndex index) const {
if (index == FrameInfoIndex::NumIndexes) return 0;
return mFrameInfo[static_cast<int>(index)];
}
- int64_t operator[](int index) const {
+ inline int64_t operator[](int index) const {
if (index < 0 || index >= static_cast<int>(FrameInfoIndex::NumIndexes)) return 0;
return mFrameInfo[index];
}
+ inline int64_t duration(FrameInfoIndex start, FrameInfoIndex end) const {
+ int64_t endtime = mFrameInfo[static_cast<int>(end)];
+ int64_t starttime = mFrameInfo[static_cast<int>(start)];
+ int64_t gap = endtime - starttime;
+ gap = starttime > 0 ? gap : 0;
+ return gap > 0 ? gap : 0;
+ }
+
+ inline int64_t totalDuration() const {
+ return duration(FrameInfoIndex::IntendedVsync, FrameInfoIndex::FrameCompleted);
+ }
+
private:
inline int64_t& set(FrameInfoIndex index) {
return mFrameInfo[static_cast<int>(index)];
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 9557cb0..5b81ac9 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -30,11 +30,13 @@
// Must be NUM_ELEMENTS in size
static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
-static const SkColor BAR_ALPHA = 0xCF000000;
+static const SkColor BAR_FAST_ALPHA = 0x8F000000;
+static const SkColor BAR_JANKY_ALPHA = 0xDF000000;
// We could get this from TimeLord and use the actual frame interval, but
// this is good enough
#define FRAME_THRESHOLD 16
+#define FRAME_THRESHOLD_NS 16000000
namespace android {
namespace uirenderer {
@@ -45,12 +47,10 @@ struct BarSegment {
SkColor color;
};
-static const std::array<BarSegment,9> Bar {{
- { FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync, 0x00695C },
- { FrameInfoIndex::Vsync, FrameInfoIndex::HandleInputStart, 0x00796B },
- { FrameInfoIndex::HandleInputStart, FrameInfoIndex::AnimationStart, 0x00897B },
- { FrameInfoIndex::AnimationStart, FrameInfoIndex::PerformTraversalsStart, 0x009688 },
- { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x26A69A},
+static const std::array<BarSegment,7> Bar {{
+ { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, 0x00796B },
+ { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, 0x388E3C },
+ { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x689F38},
{ FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, 0x2196F3},
{ FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, 0x4FC3F7},
{ FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, 0xF44336},
@@ -74,7 +74,6 @@ void FrameInfoVisualizer::setDensity(float density) {
if (CC_UNLIKELY(mDensity != density)) {
mDensity = density;
mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
- mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
}
}
@@ -103,73 +102,109 @@ void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) {
}
if (mType == ProfileType::Bars) {
- initializeRects(canvas->getViewportHeight());
+ // Patch up the current frame to pretend we ended here. CanvasContext
+ // will overwrite these values with the real ones after we return.
+ // This is a bit nicer looking than the vague green bar, as we have
+ // valid data for almost all the stages and a very good idea of what
+ // the issue stage will look like, too
+ FrameInfo& info = mFrameSource.back();
+ info.markSwapBuffers();
+ info.markFrameCompleted();
+
+ initializeRects(canvas->getViewportHeight(), canvas->getViewportWidth());
drawGraph(canvas);
- drawCurrentFrame(canvas->getViewportHeight(), canvas);
drawThreshold(canvas);
}
}
void FrameInfoVisualizer::createData() {
- if (mRects.get()) return;
+ if (mFastRects.get()) return;
- mRects.reset(new float[mFrameSource.capacity() * 4]);
+ mFastRects.reset(new float[mFrameSource.capacity() * 4]);
+ mJankyRects.reset(new float[mFrameSource.capacity() * 4]);
}
void FrameInfoVisualizer::destroyData() {
- mRects.reset(nullptr);
+ mFastRects.reset(nullptr);
+ mJankyRects.reset(nullptr);
}
-void FrameInfoVisualizer::initializeRects(const int baseline) {
- float left = 0;
+void FrameInfoVisualizer::initializeRects(const int baseline, const int width) {
+ // Target the 95% mark for the current frame
+ float right = width * .95;
+ float baseLineWidth = right / mFrameSource.capacity();
+ mNumFastRects = 0;
+ mNumJankyRects = 0;
+ int fast_i = 0, janky_i = 0;
// Set the bottom of all the shapes to the baseline
- for (size_t i = 0; i < (mFrameSource.capacity() * 4); i += 4) {
+ for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
+ if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
+ continue;
+ }
+ float lineWidth = baseLineWidth;
+ float* rect;
+ int ri;
// Rects are LTRB
- mRects[i + 0] = left;
- mRects[i + 1] = baseline;
- left += mHorizontalUnit;
- mRects[i + 2] = left;
- mRects[i + 3] = baseline;
+ if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
+ rect = mFastRects.get();
+ ri = fast_i;
+ fast_i += 4;
+ mNumFastRects++;
+ } else {
+ rect = mJankyRects.get();
+ ri = janky_i;
+ janky_i += 4;
+ mNumJankyRects++;
+ lineWidth *= 2;
+ }
+
+ rect[ri + 0] = right - lineWidth;
+ rect[ri + 1] = baseline;
+ rect[ri + 2] = right;
+ rect[ri + 3] = baseline;
+ right -= lineWidth;
}
}
void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) {
- for (size_t fi = 0, ri = 0; fi < mFrameSource.size(); fi++, ri += 4) {
- // TODO: Skipped frames will leave little holes in the graph, but this
- // is better than bogus and freaky lines, so...
+ int fast_i = (mNumFastRects - 1) * 4;
+ int janky_i = (mNumJankyRects - 1) * 4;;
+ for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
continue;
}
+ float* rect;
+ int ri;
+ // Rects are LTRB
+ if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
+ rect = mFastRects.get();
+ ri = fast_i;
+ fast_i -= 4;
+ } else {
+ rect = mJankyRects.get();
+ ri = janky_i;
+ janky_i -= 4;
+ }
+
// Set the bottom to the old top (build upwards)
- mRects[ri + 3] = mRects[ri + 1];
+ rect[ri + 3] = rect[ri + 1];
// Move the top up by the duration
- mRects[ri + 1] -= mVerticalUnit * duration(fi, start, end);
+ rect[ri + 1] -= mVerticalUnit * duration(fi, start, end);
}
}
void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) {
SkPaint paint;
for (size_t i = 0; i < Bar.size(); i++) {
- paint.setColor(Bar[i].color | BAR_ALPHA);
nextBarSegment(Bar[i].start, Bar[i].end);
- canvas->drawRects(mRects.get(), (mFrameSource.size() - 1) * 4, &paint);
+ paint.setColor(Bar[i].color | BAR_FAST_ALPHA);
+ canvas->drawRects(mFastRects.get(), mNumFastRects * 4, &paint);
+ paint.setColor(Bar[i].color | BAR_JANKY_ALPHA);
+ canvas->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint);
}
}
-void FrameInfoVisualizer::drawCurrentFrame(const int baseline, OpenGLRenderer* canvas) {
- // This draws a solid rect over the entirety of the current frame's shape
- // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
- // which will therefore fully overlap the previously drawn rects
- SkPaint paint;
- paint.setColor(CURRENT_FRAME_COLOR);
- size_t fi = mFrameSource.size() - 1;
- size_t ri = fi * 4;
- float top = baseline - (mVerticalUnit * duration(fi,
- FrameInfoIndex::IntendedVsync, FrameInfoIndex::IssueDrawCommandsStart));
- canvas->drawRect(mRects[ri], top, mRects[ri + 2], baseline, &paint);
-}
-
void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) {
SkPaint paint;
paint.setColor(THRESHOLD_COLOR);
diff --git a/libs/hwui/FrameInfoVisualizer.h b/libs/hwui/FrameInfoVisualizer.h
index 3fa4586..f1dc954 100644
--- a/libs/hwui/FrameInfoVisualizer.h
+++ b/libs/hwui/FrameInfoVisualizer.h
@@ -54,10 +54,9 @@ private:
void createData();
void destroyData();
- void initializeRects(const int baseline);
+ void initializeRects(const int baseline, const int width);
void nextBarSegment(FrameInfoIndex start, FrameInfoIndex end);
void drawGraph(OpenGLRenderer* canvas);
- void drawCurrentFrame(const int baseline, OpenGLRenderer* canvas);
void drawThreshold(OpenGLRenderer* canvas);
inline float duration(size_t index, FrameInfoIndex start, FrameInfoIndex end) {
@@ -75,17 +74,12 @@ private:
FrameInfoSource& mFrameSource;
int mVerticalUnit = 0;
- int mHorizontalUnit = 0;
int mThresholdStroke = 0;
- /*
- * mRects represents an array of rect shapes, divided into NUM_ELEMENTS
- * groups such that each group is drawn with the same paint.
- * For example mRects[0] is the array of rect floats suitable for
- * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record
- * information.
- */
- std::unique_ptr<float[]> mRects;
+ int mNumFastRects;
+ std::unique_ptr<float[]> mFastRects;
+ int mNumJankyRects;
+ std::unique_ptr<float[]> mJankyRects;
bool mShowDirtyRegions = false;
SkRect mDirtyRegion;
diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h
index d822cb2..6895f07 100644
--- a/libs/hwui/utils/RingBuffer.h
+++ b/libs/hwui/utils/RingBuffer.h
@@ -43,11 +43,11 @@ public:
}
T& front() {
- return this[0];
+ return (*this)[0];
}
T& back() {
- return this[size() - 1];
+ return (*this)[size() - 1];
}
T& operator[](size_t index) {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 4b84090..d480696 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -933,6 +933,10 @@ public abstract class TvInputService extends Service {
* Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
* moment.
*
+ * <p>Note that the current playback position should be equal to or greater than the start
+ * playback position reported by {@link #onTimeShiftGetStartPosition}. Failure to notifying
+ * the correct current position might lead to bad user experience.
+ *
* @see #onTimeShiftResume
* @see #onTimeShiftPause
* @see #onTimeShiftSeekTo
@@ -1396,6 +1400,12 @@ public abstract class TvInputService extends Service {
notifyTimeShiftStartPositionChanged(startPositionMs);
}
long currentPositionMs = onTimeShiftGetCurrentPosition();
+ if (currentPositionMs < mStartPositionMs) {
+ Log.w(TAG, "Current position (" + currentPositionMs + ") cannot be earlier than"
+ + " start position (" + mStartPositionMs + "). Reset to the start "
+ + "position.");
+ currentPositionMs = mStartPositionMs;
+ }
if (mCurrentPositionMs != currentPositionMs) {
mCurrentPositionMs = currentPositionMs;
notifyTimeShiftCurrentPositionChanged(currentPositionMs);
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 9d67ccc..a8451f5 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -41,7 +41,7 @@
<string name="label_color">Color</string>
<!-- Label of the duplex mode widget. [CHAR LIMIT=20] -->
- <string name="label_duplex">Duplex</string>
+ <string name="label_duplex">Two-sided</string>
<!-- Label of the orientation widget. [CHAR LIMIT=20] -->
<string name="label_orientation">Orientation</string>
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 9c6f67c..2b82b05 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -27,7 +27,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/recents_task_view_header" />
- <FrameLayout
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/lock_to_app_fab"
android:layout_width="@dimen/recents_lock_to_app_size"
android:layout_height="@dimen/recents_lock_to_app_size"
@@ -42,7 +42,7 @@
android:layout_height="@dimen/recents_lock_to_app_icon_size"
android:layout_gravity="center"
android:src="@drawable/recents_lock_to_app_pin" />
- </FrameLayout>
+ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
</FrameLayout>
</com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index bbd3e60..7d2b5c87 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -32,6 +32,7 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
+import android.os.AsyncTask;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -40,6 +41,7 @@ import android.util.Pair;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
+
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SystemUI;
@@ -184,12 +186,16 @@ public class Recents extends SystemUI
// Header (for transition)
TaskViewHeader mHeaderBar;
+ final Object mHeaderBarLock = new Object();
TaskStackView mDummyStackView;
// Variables to keep track of if we need to start recents after binding
boolean mTriggeredFromAltTab;
long mLastToggleTime;
+ Bitmap mThumbnailTransitionBitmapCache;
+ Task mThumbnailTransitionBitmapCacheKey;
+
public Recents() {
}
@@ -360,13 +366,16 @@ public class Recents extends SystemUI
void preloadRecentsInternal() {
// Preload only the raw task list into a new load plan (which will be consumed by the
// RecentsActivity)
+ ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+ MutableBoolean topTaskHome = new MutableBoolean(true);
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
sInstanceLoadPlan = loader.createLoadPlan(mContext);
-
- ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- MutableBoolean isTopTaskHome = new MutableBoolean(true);
- if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
- sInstanceLoadPlan.preloadRawTasks(isTopTaskHome.value);
+ if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
+ sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
+ loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
+ TaskStack top = sInstanceLoadPlan.getAllTaskStacks().get(0);
+ preCacheThumbnailTransitionBitmapAsync(topTask, top, mDummyStackView,
+ topTaskHome.value);
}
}
@@ -513,12 +522,14 @@ public class Recents extends SystemUI
algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
Rect taskViewSize = algo.getUntransformedTaskViewSize();
int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
- mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
- false);
- mHeaderBar.measure(
- View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
- mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
+ synchronized (mHeaderBarLock) {
+ mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
+ false);
+ mHeaderBar.measure(
+ View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
+ mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
+ }
}
/** Prepares the search bar app widget */
@@ -607,30 +618,27 @@ public class Recents extends SystemUI
*/
ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
TaskStack stack, TaskStackView stackView) {
+
// Update the destination rect
Task toTask = new Task();
TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
topTask.id, toTask);
- if (toTransform != null && toTask.key != null) {
- Rect toTaskRect = toTransform.rect;
- int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
- int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
- Bitmap thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
- Bitmap.Config.ARGB_8888);
- if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
- thumbnail.eraseColor(0xFFff0000);
- } else {
- Canvas c = new Canvas(thumbnail);
- c.scale(toTransform.scale, toTransform.scale);
- mHeaderBar.rebindToTask(toTask);
- mHeaderBar.draw(c);
- c.setBitmap(null);
- }
- Bitmap thumbnailImmutable = thumbnail.createAshmemBitmap();
-
+ Rect toTaskRect = toTransform.rect;
+ Bitmap thumbnail;
+ if (mThumbnailTransitionBitmapCacheKey != null
+ && mThumbnailTransitionBitmapCacheKey.key != null
+ && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
+ thumbnail = mThumbnailTransitionBitmapCache;
+ mThumbnailTransitionBitmapCacheKey = null;
+ mThumbnailTransitionBitmapCache = null;
+ } else {
+ preloadIcon(topTask);
+ thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
+ }
+ if (thumbnail != null) {
mStartAnimationTriggered = false;
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- thumbnailImmutable, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
+ thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
toTaskRect.height(), mHandler, this);
}
@@ -638,6 +646,72 @@ public class Recents extends SystemUI
return getUnknownTransitionActivityOptions();
}
+ /**
+ * Preloads the icon of a task.
+ */
+ void preloadIcon(ActivityManager.RunningTaskInfo task) {
+
+ // Ensure that we load the running task's icon
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.runningTaskId = task.id;
+ launchOpts.loadThumbnails = false;
+ launchOpts.onlyLoadForCache = true;
+ RecentsTaskLoader.getInstance().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
+ }
+
+ /**
+ * Caches the header thumbnail used for a window animation asynchronously into
+ * {@link #mThumbnailTransitionBitmapCache}.
+ */
+ void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
+ TaskStack stack, TaskStackView stackView, boolean isTopTaskHome) {
+ preloadIcon(topTask);
+
+ // Update the destination rect
+ mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
+ final Task toTask = new Task();
+ final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+ topTask.id, toTask);
+ new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ return drawThumbnailTransitionBitmap(toTask, toTransform);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ mThumbnailTransitionBitmapCache = bitmap;
+ mThumbnailTransitionBitmapCacheKey = toTask;
+ }
+ }.execute();
+ }
+
+ /**
+ * Draws the header of a task used for the window animation into a bitmap.
+ */
+ Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+ if (toTransform != null && toTask.key != null) {
+ Bitmap thumbnail;
+ synchronized (mHeaderBarLock) {
+ int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
+ int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+ thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
+ Bitmap.Config.ARGB_8888);
+ if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
+ thumbnail.eraseColor(0xFFff0000);
+ } else {
+ Canvas c = new Canvas(thumbnail);
+ c.scale(toTransform.scale, toTransform.scale);
+ mHeaderBar.rebindToTask(toTask);
+ mHeaderBar.draw(c);
+ c.setBitmap(null);
+ }
+ }
+ return thumbnail.createAshmemBitmap();
+ }
+ return null;
+ }
+
/** Returns the transition rect for the given task id. */
TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
int runningTaskId, Task runningTaskOut) {
@@ -694,7 +768,9 @@ public class Recents extends SystemUI
return;
}
- loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
+ if (!sInstanceLoadPlan.hasTasks()) {
+ loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
+ }
ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
TaskStack stack = stacks.get(0);
@@ -706,12 +782,6 @@ public class Recents extends SystemUI
boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
if (useThumbnailTransition) {
- // Ensure that we load the running task's icon
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.runningTaskId = topTask.id;
- launchOpts.loadThumbnails = false;
- launchOpts.onlyLoadForCache = true;
- loader.loadTasks(mContext, sInstanceLoadPlan, launchOpts);
// Try starting with a thumbnail transition
ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index ad97f91..3885799 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -81,6 +81,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Runnables to finish the Recents activity
FinishRecentsRunnable mFinishLaunchHomeRunnable;
+ // Runnable to be executed after we paused ourselves
+ Runnable mAfterPauseRunnable;
+
/**
* A common Runnable to finish Recents either by calling finish() (with a custom animation) or
* launching Home with some ActivityOptions. Generally we always launch home when we exit
@@ -441,6 +444,19 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
if (mConfig.launchedHasConfigurationChanged) {
onEnterAnimationTriggered();
}
+
+ if (!mConfig.launchedHasConfigurationChanged) {
+ mRecentsView.disableLayersForOneFrame();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mAfterPauseRunnable != null) {
+ mRecentsView.post(mAfterPauseRunnable);
+ mAfterPauseRunnable = null;
+ }
}
@Override
@@ -624,6 +640,11 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
Recents.startScreenPinning(this, ssp);
}
+ @Override
+ public void runAfterPause(Runnable r) {
+ mAfterPauseRunnable = r;
+ }
+
/**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
index 4b5c0bd..3f5d0a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
@@ -73,4 +73,9 @@ public class FixedSizeImageView extends ImageView {
mAllowRelayout = true;
mAllowInvalidate = true;
}
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index cec613c..fa97a86 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -24,14 +24,19 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.Bundle;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewStub;
import android.view.WindowInsets;
+import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
+
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsAppWidgetHostView;
@@ -52,6 +57,8 @@ import java.util.List;
public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks,
RecentsPackageMonitor.PackageCallbacks {
+ private static final String TAG = "RecentsView";
+
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
public void onTaskViewClicked();
@@ -59,8 +66,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public void onAllTaskViewsDismissed();
public void onExitToHomeAnimationTriggered();
public void onScreenPinningRequest();
-
public void onTaskResize(Task t);
+ public void runAfterPause(Runnable r);
}
RecentsConfiguration mConfig;
@@ -431,11 +438,75 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
return false;
}
+ public void disableLayersForOneFrame() {
+ List<TaskStackView> stackViews = getTaskStackViews();
+ for (int i = 0; i < stackViews.size(); i++) {
+ stackViews.get(i).disableLayersForOneFrame();
+ }
+ }
+
+ private void postDrawHeaderThumbnailTransitionRunnable(final TaskView tv, final int offsetX,
+ final int offsetY, final TaskViewTransform transform,
+ final ActivityOptions.OnAnimationStartedListener animStartedListener) {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ // Disable any focused state before we draw the header
+ if (tv.isFocusedTask()) {
+ tv.unsetFocusedTask();
+ }
+
+ float scale = tv.getScaleX();
+ int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale);
+ int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale);
+
+ Bitmap b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
+ Bitmap.Config.ARGB_8888);
+ if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
+ b.eraseColor(0xFFff0000);
+ } else {
+ Canvas c = new Canvas(b);
+ c.scale(tv.getScaleX(), tv.getScaleY());
+ tv.mHeaderView.draw(c);
+ c.setBitmap(null);
+ }
+ b = b.createAshmemBitmap();
+ int[] pts = new int[2];
+ tv.getLocationOnScreen(pts);
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionAspectScaledThumb(b,
+ pts[0] + offsetX,
+ pts[1] + offsetY,
+ transform.rect.width(),
+ transform.rect.height(),
+ new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data)
+ throws RemoteException {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (animStartedListener != null) {
+ animStartedListener.onAnimationStarted();
+ }
+ }
+ });
+ }
+ }, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error overriding app transition", e);
+ }
+ }
+ };
+ mCb.runAfterPause(r);
+ }
/**** TaskStackView.TaskStackCallbacks Implementation ****/
@Override
public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
final TaskStack stack, final Task task, final boolean lockToTask) {
+
// Notify any callbacks of the launching of a new task
if (mCb != null) {
mCb.onTaskViewClicked();
@@ -466,31 +537,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
ActivityOptions opts = null;
if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
task.thumbnail.getHeight() > 0) {
- Bitmap b;
- if (tv != null) {
- // Disable any focused state before we draw the header
- if (tv.isFocusedTask()) {
- tv.unsetFocusedTask();
- }
-
- float scale = tv.getScaleX();
- int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale);
- int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale);
- b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
- Bitmap.Config.ARGB_8888);
- if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
- b.eraseColor(0xFFff0000);
- } else {
- Canvas c = new Canvas(b);
- c.scale(tv.getScaleX(), tv.getScaleY());
- tv.mHeaderView.draw(c);
- c.setBitmap(null);
- }
- } else {
- // Notify the system to skip the thumbnail layer by using an ALPHA_8 bitmap
- b = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
- }
- Bitmap bImmut = b.createAshmemBitmap();
ActivityOptions.OnAnimationStartedListener animStartedListener = null;
if (lockToTask) {
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@@ -509,6 +555,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
};
}
+ if (tv != null) {
+ postDrawHeaderThumbnailTransitionRunnable(tv, offsetX, offsetY, transform,
+ animStartedListener);
+ }
if (mConfig.multiStackEnabled) {
opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
R.anim.recents_from_unknown_enter,
@@ -516,7 +566,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
sourceView.getHandler(), animStartedListener);
} else {
opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
- bImmut, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
+ Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(),
+ offsetX, offsetY, transform.rect.width(), transform.rect.height(),
sourceView.getHandler(), animStartedListener);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 5f151e8..5711cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -19,6 +19,7 @@ package com.android.systemui.recents.views;
import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.LayoutInflater;
@@ -63,7 +64,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void onTaskResize(Task t);
}
-
RecentsConfiguration mConfig;
TaskStack mStack;
@@ -81,7 +81,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
boolean mDismissAllButtonAnimating;
int mFocusedTaskIndex = -1;
int mPrevAccessibilityFocusedIndex = -1;
-
// Optimizations
int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true;
@@ -99,6 +98,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ArrayList<TaskView> mTaskViews = new ArrayList<TaskView>();
List<TaskView> mImmutableTaskViews = new ArrayList<TaskView>();
LayoutInflater mInflater;
+ boolean mLayersDisabled;
// A convenience update listener to request updating clipping of tasks
ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
@@ -375,7 +375,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (tv == null) {
tv = mViewPool.pickUpViewFromPool(task, task);
-
+ if (mLayersDisabled) {
+ tv.disableLayersForOneFrame();
+ }
if (mStackViewsAnimationDuration > 0) {
// For items in the list, put them in start animating them from the
// approriate ends of the list where they are expected to appear
@@ -1031,6 +1033,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mUIDozeTrigger.poke();
}
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ mLayersDisabled = false;
+ super.dispatchDraw(canvas);
+ }
+
+ public void disableLayersForOneFrame() {
+ mLayersDisabled = true;
+ List<TaskView> taskViews = getTaskViews();
+ for (int i = 0; i < taskViews.size(); i++) {
+ taskViews.get(i).disableLayersForOneFrame();
+ }
+ }
+
/**** TaskStackCallbacks Implementation ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 4a5fef3..5906ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -367,7 +367,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
.setStartDelay(delay)
.setDuration(duration)
.setInterpolator(PhoneStatusBar.ALPHA_IN)
- .withLayer()
.start();
}
@@ -416,7 +415,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
.setStartDelay(0)
.setDuration(mConfig.taskViewExitToAppDuration)
.setInterpolator(mConfig.fastOutLinearInInterpolator)
- .withLayer()
.start();
} else {
// Hide the dismiss button
@@ -651,6 +649,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
}
+ public void disableLayersForOneFrame() {
+ mHeaderView.disableLayersForOneFrame();
+ }
+
/**** TaskCallbacks Implementation ****/
/** Binds this task view to the task */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 82f0f31..062ded2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -80,6 +80,8 @@ public class TaskViewHeader extends FrameLayout {
Paint mDimLayerPaint = new Paint();
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
+ boolean mLayersDisabled;
+
public TaskViewHeader(Context context) {
this(context, null);
}
@@ -172,7 +174,9 @@ public class TaskViewHeader extends FrameLayout {
void setDimAlpha(int alpha) {
mDimColorFilter.setColor(Color.argb(alpha, 0, 0, 0));
mDimLayerPaint.setColorFilter(mDimColorFilter);
- setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
+ if (!mLayersDisabled) {
+ setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
+ }
}
/** Returns the secondary color for a primary color. */
@@ -262,7 +266,6 @@ public class TaskViewHeader extends FrameLayout {
.setStartDelay(0)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
.setDuration(mConfig.taskViewExitToAppDuration)
- .withLayer()
.start();
}
}
@@ -277,7 +280,6 @@ public class TaskViewHeader extends FrameLayout {
.setStartDelay(0)
.setInterpolator(mConfig.fastOutLinearInInterpolator)
.setDuration(mConfig.taskViewEnterFromAppDuration)
- .withLayer()
.start();
}
}
@@ -304,6 +306,28 @@ public class TaskViewHeader extends FrameLayout {
return new int[] {};
}
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mLayersDisabled) {
+ mLayersDisabled = false;
+ postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ mLayersDisabled = false;
+ setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
+ }
+ });
+ }
+ }
+
+ public void disableLayersForOneFrame() {
+ mLayersDisabled = true;
+
+ // Disable layer for a frame so we can draw our first frame faster.
+ setLayerType(LAYER_TYPE_NONE, null);
+ }
+
/** Notifies the associated TaskView has been focused. */
void onTaskViewFocusChanged(boolean focused, boolean animateFocusedState) {
// If we are not animating the visible state, just return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index fa172a4..f05ac1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1030,9 +1030,7 @@ public abstract class BaseStatusBar extends SystemUI implements
@Override
public void toggleRecentApps() {
- int msg = MSG_TOGGLE_RECENTS_APPS;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
+ toggleRecents();
}
@Override