Merge "Delayed ASEC allocation, refine progress handling." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index e39cc22..0eb67be 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1062,6 +1062,7 @@
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
field public static final int restrictedAccountType = 16843733; // 0x10103d5
field public static final int restrictionType = 16843923; // 0x1010493
+ field public static final int resumeWhilePausing = 16843955; // 0x10104b3
field public static final int reversible = 16843851; // 0x101044b
field public static final int right = 16843183; // 0x10101af
field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
@@ -5525,8 +5526,8 @@
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_PORT = "android.app.extra.PROVISIONING_WIFI_PROXY_PORT";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE = "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SSID = "android.app.extra.PROVISIONING_WIFI_SSID";
- field public static int FLAG_MANAGED_CAN_ACCESS_PARENT;
- field public static int FLAG_PARENT_CAN_ACCESS_MANAGED;
+ field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
+ field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
field public static final int KEYGUARD_DISABLE_FINGERPRINT = 32; // 0x20
@@ -8411,6 +8412,7 @@
field public static final int FLAG_MULTIPROCESS = 1; // 0x1
field public static final int FLAG_NO_HISTORY = 128; // 0x80
field public static final int FLAG_RELINQUISH_TASK_IDENTITY = 4096; // 0x1000
+ field public static final int FLAG_RESUME_WHILE_PAUSING = 16384; // 0x4000
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10
field public static final int LAUNCH_MULTIPLE = 0; // 0x0
@@ -8880,6 +8882,7 @@
field public static final java.lang.String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
field public static final java.lang.String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
+ field public static final java.lang.String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
field public static final java.lang.String FEATURE_BACKUP = "android.software.backup";
field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
field public static final java.lang.String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
@@ -14050,6 +14053,7 @@
field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+ field public static final android.os.Parcelable.Creator CREATOR;
field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
field public static final int USAGE_ALARM = 4; // 0x4
field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
@@ -14235,9 +14239,9 @@
field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0
field public static final int ERROR = -1; // 0xffffffff
field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
- field public static final java.lang.String EXTRA_AUDIO_PLUG_STATE = "android.media.extra.audio_plug_state";
- field public static final java.lang.String EXTRA_ENCODINGS = "android.media.extra.encodings";
- field public static final java.lang.String EXTRA_MAX_CHANNEL_COUNT = "android.media.extra.max_channel_count";
+ field public static final java.lang.String EXTRA_AUDIO_PLUG_STATE = "android.media.extra.AUDIO_PLUG_STATE";
+ field public static final java.lang.String EXTRA_ENCODINGS = "android.media.extra.ENCODINGS";
+ field public static final java.lang.String EXTRA_MAX_CHANNEL_COUNT = "android.media.extra.MAX_CHANNEL_COUNT";
field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE";
field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE";
@@ -27424,11 +27428,12 @@
ctor public TextToSpeech(android.content.Context, android.speech.tts.TextToSpeech.OnInitListener);
ctor public TextToSpeech(android.content.Context, android.speech.tts.TextToSpeech.OnInitListener, java.lang.String);
method public int addEarcon(java.lang.String, java.lang.String, int);
- method public int addEarcon(java.lang.String, java.lang.String);
+ method public deprecated int addEarcon(java.lang.String, java.lang.String);
+ method public int addEarcon(java.lang.String, java.io.File);
method public int addSpeech(java.lang.String, java.lang.String, int);
method public int addSpeech(java.lang.CharSequence, java.lang.String, int);
method public int addSpeech(java.lang.String, java.lang.String);
- method public int addSpeech(java.lang.CharSequence, java.lang.String);
+ method public int addSpeech(java.lang.CharSequence, java.io.File);
method public deprecated boolean areDefaultsEnforced();
method public java.util.Set<java.util.Locale> getAvailableLanguages();
method public java.lang.String getDefaultEngine();
@@ -27442,7 +27447,7 @@
method public java.util.Set<android.speech.tts.Voice> getVoices();
method public int isLanguageAvailable(java.util.Locale);
method public boolean isSpeaking();
- method public int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+ method public int playEarcon(java.lang.String, int, android.os.Bundle, java.lang.String);
method public deprecated int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
method public deprecated int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
@@ -27455,10 +27460,10 @@
method public int setSpeechRate(float);
method public int setVoice(android.speech.tts.Voice);
method public void shutdown();
- method public int speak(java.lang.CharSequence, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+ method public int speak(java.lang.CharSequence, int, android.os.Bundle, java.lang.String);
method public deprecated int speak(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
method public int stop();
- method public int synthesizeToFile(java.lang.CharSequence, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String, java.lang.String);
+ method public int synthesizeToFile(java.lang.CharSequence, android.os.Bundle, java.io.File, java.lang.String);
method public deprecated int synthesizeToFile(java.lang.String, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
field public static final java.lang.String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
field public static final int ERROR = -1; // 0xffffffff
@@ -27531,15 +27536,15 @@
public abstract class TextToSpeechService extends android.app.Service {
ctor public TextToSpeechService();
- method protected int isValidVoiceName(java.lang.String);
method public android.os.IBinder onBind(android.content.Intent);
method protected java.lang.String onGetDefaultVoiceNameFor(java.lang.String, java.lang.String, java.lang.String);
method protected java.util.Set<java.lang.String> onGetFeaturesForLanguage(java.lang.String, java.lang.String, java.lang.String);
method protected abstract java.lang.String[] onGetLanguage();
- method protected java.util.List<android.speech.tts.Voice> onGetVoices();
+ method public java.util.List<android.speech.tts.Voice> onGetVoices();
method protected abstract int onIsLanguageAvailable(java.lang.String, java.lang.String, java.lang.String);
+ method public int onIsValidVoiceName(java.lang.String);
method protected abstract int onLoadLanguage(java.lang.String, java.lang.String, java.lang.String);
- method protected int onLoadVoice(java.lang.String);
+ method public int onLoadVoice(java.lang.String);
method protected abstract void onStop();
method protected abstract void onSynthesizeText(android.speech.tts.SynthesisRequest, android.speech.tts.SynthesisCallback);
}
@@ -27560,7 +27565,7 @@
method public java.util.Locale getLocale();
method public java.lang.String getName();
method public int getQuality();
- method public boolean getRequiresNetworkConnection();
+ method public boolean isNetworkConnectionRequired();
method public void writeToParcel(android.os.Parcel, int);
field public static final int LATENCY_HIGH = 400; // 0x190
field public static final int LATENCY_LOW = 200; // 0xc8
@@ -28230,17 +28235,19 @@
public abstract class Conference {
ctor public Conference(android.telecomm.PhoneAccountHandle);
- method public boolean addConnection(android.telecomm.Connection);
- method public void destroy();
+ method public final boolean addConnection(android.telecomm.Connection);
+ method public final void destroy();
method public final int getCapabilities();
method public final java.util.List<android.telecomm.Connection> getConnections();
method public final android.telecomm.PhoneAccountHandle getPhoneAccount();
method public final int getState();
method public void onDisconnect();
method public void onHold();
+ method public void onMerge();
method public void onSeparate(android.telecomm.Connection);
+ method public void onSwap();
method public void onUnhold();
- method public void removeConnection(android.telecomm.Connection);
+ method public final void removeConnection(android.telecomm.Connection);
method public final void setActive();
method public final void setCapabilities(int);
method public final void setDisconnected(int, java.lang.String);
@@ -28391,13 +28398,13 @@
method public static java.lang.String toString(int);
field public static final int ADD_CALL = 16; // 0x10
field public static final int ALL = 255; // 0xff
- field public static final int GENERIC_CONFERENCE = 128; // 0x80
field public static final int HOLD = 1; // 0x1
- field public static final int MERGE_CALLS = 4; // 0x4
+ field public static final int MANAGE_CONFERENCE = 128; // 0x80
+ field public static final int MERGE_CONFERENCE = 4; // 0x4
field public static final int MUTE = 64; // 0x40
field public static final int RESPOND_VIA_TEXT = 32; // 0x20
field public static final int SUPPORT_HOLD = 2; // 0x2
- field public static final int SWAP_CALLS = 8; // 0x8
+ field public static final int SWAP_CONFERENCE = 8; // 0x8
}
public class PropertyPresentation {
@@ -29003,10 +29010,7 @@
}
public class TelephonyManager {
- method public void enableSimplifiedNetworkSettings(boolean);
- method public void enableSimplifiedNetworkSettings(long, boolean);
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
- method public int getCalculatedPreferredNetworkType();
method public int getCallState();
method public android.telephony.CellLocation getCellLocation();
method public int getDataActivity();
@@ -29023,14 +29027,11 @@
method public java.lang.String getNetworkOperatorName();
method public int getNetworkType();
method public int getPhoneType();
- method public int getPreferredNetworkType();
method public java.lang.String getSimCountryIso();
method public java.lang.String getSimOperator();
method public java.lang.String getSimOperatorName();
method public java.lang.String getSimSerialNumber();
method public int getSimState();
- method public boolean getSimplifiedNetworkSettingsEnabled();
- method public boolean getSimplifiedNetworkSettingsEnabled(long);
method public java.lang.String getSubscriberId();
method public java.lang.String getVoiceMailAlphaTag();
method public java.lang.String getVoiceMailNumber();
@@ -29045,11 +29046,10 @@
method public boolean isSmsCapable();
method public void listen(android.telephony.PhoneStateListener, int);
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
- method public boolean setCdmaSubscription(int);
+ method public boolean setGlobalPreferredNetworkType();
method public void setLine1NumberForDisplay(java.lang.String, java.lang.String);
method public void setLine1NumberForDisplay(long, java.lang.String, java.lang.String);
- method public boolean setOperatorBrandOverride(java.lang.String, java.lang.String);
- method public boolean setPreferredNetworkType(int);
+ method public boolean setOperatorBrandOverride(java.lang.String);
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final int CALL_STATE_IDLE = 0; // 0x0
@@ -35900,8 +35900,8 @@
public final class CursorAnchorInfo implements android.os.Parcelable {
ctor public CursorAnchorInfo(android.os.Parcel);
method public int describeContents();
- method public android.graphics.RectF getCharacterRect(int);
- method public int getCharacterRectFlags(int);
+ method public android.graphics.RectF getCharacterBounds(int);
+ method public int getCharacterBoundsFlags(int);
method public java.lang.CharSequence getComposingText();
method public int getComposingTextStart();
method public float getInsertionMarkerBaseline();
@@ -35916,11 +35916,12 @@
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int FLAG_HAS_INVISIBLE_REGION = 2; // 0x2
field public static final int FLAG_HAS_VISIBLE_REGION = 1; // 0x1
+ field public static final int FLAG_IS_RTL = 4; // 0x4
}
public static final class CursorAnchorInfo.Builder {
ctor public CursorAnchorInfo.Builder();
- method public android.view.inputmethod.CursorAnchorInfo.Builder addCharacterRect(int, float, float, float, float, int);
+ method public android.view.inputmethod.CursorAnchorInfo.Builder addCharacterBounds(int, float, float, float, float, int);
method public android.view.inputmethod.CursorAnchorInfo build();
method public void reset();
method public android.view.inputmethod.CursorAnchorInfo.Builder setComposingText(int, java.lang.CharSequence);
@@ -37563,6 +37564,7 @@
method public android.widget.CalendarView getCalendarView();
method public boolean getCalendarViewShown();
method public int getDayOfMonth();
+ method public int getFirstDayOfWeek();
method public long getMaxDate();
method public long getMinDate();
method public int getMonth();
@@ -37570,6 +37572,7 @@
method public int getYear();
method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
method public void setCalendarViewShown(boolean);
+ method public void setFirstDayOfWeek(int);
method public void setMaxDate(long);
method public void setMinDate(long);
method public void setSpinnersShown(boolean);
diff --git a/api/removed.txt b/api/removed.txt
index a272cf4..3c16276 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,11 +1,3 @@
-package android {
-
- public static final class R.attr {
- field public static final int __removed1 = 16843955; // 0x10104b3
- }
-
-}
-
package android.media {
public class AudioFormat {
@@ -53,6 +45,8 @@
}
public final class CursorAnchorInfo implements android.os.Parcelable {
+ method public android.graphics.RectF getCharacterRect(int);
+ method public int getCharacterRectFlags(int);
method public boolean isInsertionMarkerClipped();
field public static final int CHARACTER_RECT_TYPE_FULLY_VISIBLE = 1; // 0x1
field public static final int CHARACTER_RECT_TYPE_INVISIBLE = 3; // 0x3
@@ -63,6 +57,7 @@
}
public static final class CursorAnchorInfo.Builder {
+ method public android.view.inputmethod.CursorAnchorInfo.Builder addCharacterRect(int, float, float, float, float, int);
method public android.view.inputmethod.CursorAnchorInfo.Builder setInsertionMarkerLocation(float, float, float, float, boolean);
}
@@ -78,11 +73,3 @@
}
-package com.android.internal {
-
- public static final class R.attr {
- field public static final int __removed1 = 16843955; // 0x10104b3
- }
-
-}
-
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 9156eeb..7c13dbe 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -504,6 +504,10 @@
mStarted = true;
mPaused = false;
+ for (Node node : mNodes) {
+ node.animation.setAllowRunningAsynchronously(false);
+ }
+
if (mDuration >= 0) {
// If the duration was set on this AnimatorSet, pass it along to all child animations
for (Node node : mNodes) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3e03893..9486a72 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2656,17 +2656,24 @@
/**
* Start an activity in this task. Brings the task to the foreground. If this task
- * is not currently active (that is, its id < 0), then the activity being started
- * needs to be started as a new task and the Intent's ComponentName must match the
- * base ComponenentName of the recent task entry. Otherwise, the activity being
- * started must <b>not</b> be launched as a new task -- not through explicit intent
- * flags nor implicitly as the singleTask or singleInstance launch modes.
+ * is not currently active (that is, its id < 0), then a new activity for the given
+ * Intent will be launched as the root of the task and the task brought to the
+ * foreground. Otherwise, if this task is currently active and the Intent does not specify
+ * an activity to launch in a new task, then a new activity for the given Intent will
+ * be launched on top of the task and the task brought to the foreground. If this
+ * task is currently active and the Intent specifies {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+ * or would otherwise be launched in to a new task, then the activity not launched but
+ * this task be brought to the foreground and a new intent delivered to the top
+ * activity if appropriate.
*
- * <p>See {@link Activity#startActivity(android.content.Intent, android.os.Bundle)
- * Activity.startActivity} for more information.</p>
+ * <p>In other words, you generally want to use an Intent here that does not specify
+ * {@link Intent#FLAG_ACTIVITY_NEW_TASK} or {@link Intent#FLAG_ACTIVITY_NEW_DOCUMENT},
+ * and let the system do the right thing.</p>
*
* @param intent The Intent describing the new activity to be launched on the task.
* @param options Optional launch options.
+ *
+ * @see Activity#startActivity(android.content.Intent, android.os.Bundle)
*/
public void startActivity(Context context, Intent intent, Bundle options) {
ActivityThread thread = ActivityThread.currentActivityThread();
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1f7e450..394b183 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -509,8 +509,7 @@
case ACTIVITY_PAUSED_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
- PersistableBundle persistentState = data.readPersistableBundle();
- activityPaused(token, persistentState);
+ activityPaused(token);
reply.writeNoException();
return true;
}
@@ -2829,13 +2828,12 @@
data.recycle();
reply.recycle();
}
- public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException
+ public void activityPaused(IBinder token) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
- data.writePersistableBundle(persistentState);
mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 9dd4605..ffffb6c 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -84,13 +84,13 @@
* Initial width of the animation.
* @hide
*/
- public static final String KEY_ANIM_START_WIDTH = "android:animStartWidth";
+ public static final String KEY_ANIM_WIDTH = "android:animWidth";
/**
* Initial height of the animation.
* @hide
*/
- public static final String KEY_ANIM_START_HEIGHT = "android:animStartHeight";
+ public static final String KEY_ANIM_HEIGHT = "android:animHeight";
/**
* Callback for when animation is started.
@@ -140,8 +140,8 @@
private Bitmap mThumbnail;
private int mStartX;
private int mStartY;
- private int mStartWidth;
- private int mStartHeight;
+ private int mWidth;
+ private int mHeight;
private IRemoteCallback mAnimationStartedListener;
private ResultReceiver mTransitionReceiver;
private boolean mIsReturning;
@@ -238,13 +238,13 @@
* defines the coordinate space for <var>startX</var> and <var>startY</var>.
* @param startX The x starting location of the new activity, relative to <var>source</var>.
* @param startY The y starting location of the activity, relative to <var>source</var>.
- * @param startWidth The initial width of the new activity.
- * @param startHeight The initial height of the new activity.
+ * @param width The initial width of the new activity.
+ * @param height The initial height of the new activity.
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
*/
public static ActivityOptions makeScaleUpAnimation(View source,
- int startX, int startY, int startWidth, int startHeight) {
+ int startX, int startY, int width, int height) {
ActivityOptions opts = new ActivityOptions();
opts.mPackageName = source.getContext().getPackageName();
opts.mAnimationType = ANIM_SCALE_UP;
@@ -252,8 +252,8 @@
source.getLocationOnScreen(pts);
opts.mStartX = pts[0] + startX;
opts.mStartY = pts[1] + startY;
- opts.mStartWidth = startWidth;
- opts.mStartHeight = startHeight;
+ opts.mWidth = width;
+ opts.mHeight = height;
return opts;
}
@@ -359,9 +359,10 @@
* @hide
*/
public static ActivityOptions makeThumbnailAspectScaleUpAnimation(View source,
- Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
- return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, listener,
- true);
+ Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
+ OnAnimationStartedListener listener) {
+ return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
+ targetWidth, targetHeight, listener, true);
}
/**
@@ -382,13 +383,15 @@
* @hide
*/
public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
- Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
- return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, listener,
- false);
+ Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
+ OnAnimationStartedListener listener) {
+ return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
+ targetWidth, targetHeight, listener, false);
}
private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail,
- int startX, int startY, OnAnimationStartedListener listener, boolean scaleUp) {
+ int startX, int startY, int targetWidth, int targetHeight,
+ OnAnimationStartedListener listener, boolean scaleUp) {
ActivityOptions opts = new ActivityOptions();
opts.mPackageName = source.getContext().getPackageName();
opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP :
@@ -398,6 +401,8 @@
source.getLocationOnScreen(pts);
opts.mStartX = pts[0] + startX;
opts.mStartY = pts[1] + startY;
+ opts.mWidth = targetWidth;
+ opts.mHeight = targetHeight;
opts.setOnAnimationStartedListener(source.getHandler(), listener);
return opts;
}
@@ -543,8 +548,8 @@
case ANIM_SCALE_UP:
mStartX = opts.getInt(KEY_ANIM_START_X, 0);
mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
- mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0);
- mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0);
+ mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
+ mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
break;
case ANIM_THUMBNAIL_SCALE_UP:
@@ -554,6 +559,8 @@
mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL);
mStartX = opts.getInt(KEY_ANIM_START_X, 0);
mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
+ mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
+ mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
opts.getBinder(KEY_ANIM_START_LISTENER));
break;
@@ -605,13 +612,13 @@
}
/** @hide */
- public int getStartWidth() {
- return mStartWidth;
+ public int getWidth() {
+ return mWidth;
}
/** @hide */
- public int getStartHeight() {
- return mStartHeight;
+ public int getHeight() {
+ return mHeight;
}
/** @hide */
@@ -690,8 +697,8 @@
case ANIM_SCALE_UP:
mStartX = otherOptions.mStartX;
mStartY = otherOptions.mStartY;
- mStartWidth = otherOptions.mStartWidth;
- mStartHeight = otherOptions.mStartHeight;
+ mWidth = otherOptions.mWidth;
+ mHeight = otherOptions.mHeight;
if (mAnimationStartedListener != null) {
try {
mAnimationStartedListener.sendResult(null);
@@ -707,6 +714,8 @@
mThumbnail = otherOptions.mThumbnail;
mStartX = otherOptions.mStartX;
mStartY = otherOptions.mStartY;
+ mWidth = otherOptions.mWidth;
+ mHeight = otherOptions.mHeight;
if (mAnimationStartedListener != null) {
try {
mAnimationStartedListener.sendResult(null);
@@ -755,8 +764,8 @@
case ANIM_SCALE_UP:
b.putInt(KEY_ANIM_START_X, mStartX);
b.putInt(KEY_ANIM_START_Y, mStartY);
- b.putInt(KEY_ANIM_START_WIDTH, mStartWidth);
- b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight);
+ b.putInt(KEY_ANIM_WIDTH, mWidth);
+ b.putInt(KEY_ANIM_HEIGHT, mHeight);
break;
case ANIM_THUMBNAIL_SCALE_UP:
case ANIM_THUMBNAIL_SCALE_DOWN:
@@ -765,6 +774,8 @@
b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
b.putInt(KEY_ANIM_START_X, mStartX);
b.putInt(KEY_ANIM_START_Y, mStartY);
+ b.putInt(KEY_ANIM_WIDTH, mWidth);
+ b.putInt(KEY_ANIM_HEIGHT, mHeight);
b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
!= null ? mAnimationStartedListener.asBinder() : null);
break;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4f2a3bc..3a39900 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -563,11 +563,11 @@
}
public final void schedulePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges) {
+ boolean userLeaving, int configChanges, boolean dontReport) {
sendMessage(
finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
token,
- (userLeaving ? 1 : 0),
+ (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
configChanges);
}
@@ -1283,13 +1283,15 @@
} break;
case PAUSE_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
+ handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
+ (msg.arg1&2) != 0);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case PAUSE_ACTIVITY_FINISHING:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
+ handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,
+ (msg.arg1&1) != 0);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_ACTIVITY_SHOW:
@@ -3142,7 +3144,7 @@
}
private void handlePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges) {
+ boolean userLeaving, int configChanges, boolean dontReport) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
//Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
@@ -3159,9 +3161,11 @@
}
// Tell the activity manager we have paused.
- try {
- ActivityManagerNative.getDefault().activityPaused(token, r.persistentState);
- } catch (RemoteException ex) {
+ if (!dontReport) {
+ try {
+ ActivityManagerNative.getDefault().activityPaused(token);
+ } catch (RemoteException ex) {
+ }
}
mSomeActivitiesChanged = true;
}
@@ -4283,6 +4287,8 @@
AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
+ Message.updateCheckRecycle(data.appInfo.targetSdkVersion);
+
/*
* Before spawning a new process, reset the time zone to be the system time zone.
* This needs to be done because the system time zone could have changed after the
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9cd6d49..404268c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1693,7 +1693,7 @@
if (dr == null) {
dr = itemInfo.loadDefaultIcon(this);
}
- return getUserBadgedDrawableForDensity(dr, new UserHandle(mContext.getUserId()), null, 0);
+ return getUserBadgedIcon(dr, new UserHandle(mContext.getUserId()));
}
private Drawable getBadgedDrawable(Drawable drawable, Drawable badgeDrawable,
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 63e8707..0123e16 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -78,7 +78,8 @@
boolean finished = data.readInt() != 0;
boolean userLeaving = data.readInt() != 0;
int configChanges = data.readInt();
- schedulePauseActivity(b, finished, userLeaving, configChanges);
+ boolean dontReport = data.readInt() != 0;
+ schedulePauseActivity(b, finished, userLeaving, configChanges, dontReport);
return true;
}
@@ -689,13 +690,14 @@
}
public final void schedulePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges) throws RemoteException {
+ boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
data.writeInt(finished ? 1 : 0);
data.writeInt(userLeaving ? 1 :0);
data.writeInt(configChanges);
+ data.writeInt(dontReport ? 1 : 0);
mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 91a0aed..cc5c643 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2121,7 +2121,9 @@
public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
throws NameNotFoundException {
final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
- if (packageName.equals("system") || packageName.equals("android")) {
+ if ((packageName.equals("system") || packageName.equals("android"))
+ && user.getIdentifier() == UserHandle.getUserId(
+ mPackageInfo.getApplicationInfo().uid)) {
return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
user, restricted, mDisplay, mOverrideConfiguration);
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 99428e8..9483680 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -111,7 +111,7 @@
public void activityResumed(IBinder token) throws RemoteException;
public void activityIdle(IBinder token, Configuration config,
boolean stopProfiling) throws RemoteException;
- public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException;
+ public void activityPaused(IBinder token) throws RemoteException;
public void activityStopped(IBinder token, Bundle state,
PersistableBundle persistentState, CharSequence description) throws RemoteException;
public void activitySlept(IBinder token) throws RemoteException;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index a7546d9..f53075c 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -49,7 +49,7 @@
*/
public interface IApplicationThread extends IInterface {
void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving,
- int configChanges) throws RemoteException;
+ int configChanges, boolean dontReport) throws RemoteException;
void scheduleStopActivity(IBinder token, boolean showWindow,
int configChanges) throws RemoteException;
void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 112fc82..926a348 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -441,13 +441,13 @@
* Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from a
* managed profile to its parent.
*/
- public static int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001;
+ public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001;
/**
* Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from the
* parent to its managed profile.
*/
- public static int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002;
+ public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002;
/**
* Return true if the given administrator component is currently
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index dbf49c5..8d3126d 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -255,16 +255,22 @@
* Bit in {@link #flags}: If set, a task rooted at this activity will have its
* baseIntent replaced by the activity immediately above this. Each activity may further
* relinquish its identity to the activity above it using this flag. Set from the
- * android.R.attr#relinquishTaskIdentity attribute.
+ * {@link android.R.attr#relinquishTaskIdentity} attribute.
*/
public static final int FLAG_RELINQUISH_TASK_IDENTITY = 0x1000;
/**
* Bit in {@link #flags} indicating that tasks started with this activity are to be
* removed from the recent list of tasks when the last activity in the task is finished.
- * {@link android.R.attr#autoRemoveFromRecents}
+ * Corresponds to {@link android.R.attr#autoRemoveFromRecents}
*/
public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 0x2000;
/**
+ * Bit in {@link #flags} indicating that this activity can start is creation/resume
+ * while the previous activity is still pausing. Corresponds to
+ * {@link android.R.attr#resumeWhilePausing}
+ */
+ public static final int FLAG_RESUME_WHILE_PAUSING = 0x4000;
+ /**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the primary user. Only works with broadcast receivers. Set from the
* android.R.attr#primaryUserOnly attribute.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e87adda..748fca2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -976,6 +976,14 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device includes at least one form of audio
+ * output, such as speakers, audio jack or streaming over bluetooth
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device is capable of communicating with
* other devices via Bluetooth.
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6d40dcf..e0fd532 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3110,6 +3110,12 @@
false)) {
a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
}
+
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestActivity_resumeWhilePausing,
+ false)) {
+ a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+ }
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 2684e6c..e578822 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -773,6 +773,7 @@
private native final String[] getArrayStringResource(int arrayRes);
private native final int[] getArrayStringInfo(int arrayRes);
/*package*/ native final int[] getArrayIntResource(int arrayRes);
+ /*package*/ native final int[] getStyleAttributes(int themeRes);
private native final void init(boolean isSystem);
private native final void destroy();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 31813c10..cabe228 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1572,6 +1572,16 @@
}
/**
+ * Gets all of the attribute ids associated with this {@link Theme}. For debugging only.
+ *
+ * @return The int array containing attribute ids associated with this {@link Theme}.
+ * @hide
+ */
+ public int[] getAllAttributes() {
+ return mAssets.getStyleAttributes(getAppliedStyleResId());
+ }
+
+ /**
* Returns the resources to which this theme belongs.
*
* @return Resources to which this theme belongs.
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 7a49eb5..7390e2b 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -480,7 +480,7 @@
int capturePreambleMs = in.readInt();
boolean triggerInData = in.readByte() == 1;
AudioFormat captureFormat = null;
- if (triggerInData) {
+ if (in.readByte() == 1) {
int sampleRate = in.readInt();
int encoding = in.readInt();
int channelMask = in.readInt();
@@ -508,7 +508,8 @@
dest.writeInt(captureSession);
dest.writeInt(captureDelayMs);
dest.writeInt(capturePreambleMs);
- if (triggerInData && (captureFormat != null)) {
+ dest.writeByte((byte) (triggerInData ? 1 : 0));
+ if (captureFormat != null) {
dest.writeByte((byte)1);
dest.writeInt(captureFormat.getSampleRate());
dest.writeInt(captureFormat.getEncoding());
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fe9f79b..461469c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1667,7 +1667,7 @@
*/
public abstract long[] getChargeStepDurationsArray();
- public abstract Map<String, ? extends LongCounter> getWakeupReasonStats();
+ public abstract Map<String, ? extends Timer> getWakeupReasonStats();
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
@@ -2045,11 +2045,15 @@
sb.toString());
}
}
- Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+ Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
if (wakeupReasons.size() > 0) {
- for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+ for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
+ // Not doing the regular wake lock formatting to remain compatible
+ // with the old checkin format.
+ long totalTimeMicros = ent.getValue().getTotalTimeLocked(rawRealtime, which);
+ int count = ent.getValue().getCountLocked(which);
dumpLine(pw, 0 /* uid */, category, WAKEUP_REASON_DATA,
- "\"" + ent.getKey() + "\"", ent.getValue().getCountLocked(which));
+ "\"" + ent.getKey() + "\"", (totalTimeMicros + 500) / 1000, count);
}
}
}
@@ -2921,14 +2925,14 @@
pw.println();
}
- Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+ Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
if (wakeupReasons.size() > 0) {
pw.print(prefix); pw.println(" All wakeup reasons:");
final ArrayList<TimerEntry> reasons = new ArrayList<TimerEntry>();
- for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
- BatteryStats.LongCounter counter = ent.getValue();
- reasons.add(new TimerEntry(ent.getKey(), 0, null,
- ent.getValue().getCountLocked(which)));
+ for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
+ Timer timer = ent.getValue();
+ reasons.add(new TimerEntry(ent.getKey(), 0, timer,
+ timer.getCountLocked(which)));
}
Collections.sort(reasons, timerComparator);
for (int i=0; i<reasons.size(); i++) {
@@ -2938,9 +2942,8 @@
sb.append(prefix);
sb.append(" Wakeup reason ");
sb.append(timer.mName);
- sb.append(": ");
- formatTimeMs(sb, timer.mTime);
- sb.append("realtime");
+ printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
+ sb.append(" realtime");
pw.println(sb.toString());
}
pw.println();
@@ -3138,7 +3141,7 @@
}
Map<String, ? extends Timer> jobs = u.getJobStats();
- if (syncs.size() > 0) {
+ if (jobs.size() > 0) {
for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
Timer timer = ent.getValue();
// Convert from microseconds to milliseconds with rounding
@@ -3952,7 +3955,7 @@
prepareForDumpLocked();
dumpLine(pw, 0 /* uid */, "i" /* category */, VERSION_DATA,
- "10", getParcelVersion(), getStartPlatformVersion(), getEndPlatformVersion());
+ "11", getParcelVersion(), getStartPlatformVersion(), getEndPlatformVersion());
long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 1a5811c..b6b70cc 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -113,6 +113,8 @@
private static final int MAX_POOL_SIZE = 50;
+ private static boolean gCheckRecycle = true;
+
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
@@ -256,6 +258,13 @@
return m;
}
+ /** @hide */
+ public static void updateCheckRecycle(int targetSdkVersion) {
+ if (targetSdkVersion < Build.VERSION_CODES.L) {
+ gCheckRecycle = false;
+ }
+ }
+
/**
* Return a Message instance to the global pool.
* <p>
@@ -266,8 +275,11 @@
*/
public void recycle() {
if (isInUse()) {
- throw new IllegalStateException("This message cannot be recycled because it "
- + "is still in use.");
+ if (gCheckRecycle) {
+ throw new IllegalStateException("This message cannot be recycled because it "
+ + "is still in use.");
+ }
+ return;
}
recycleUnchecked();
}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 7245975..a4b6e92 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -578,7 +578,7 @@
*
* @deprecated Starting from API level 20, to select network synthesis, call
* ({@link TextToSpeech#getVoices()}, find a suitable network voice
- * ({@link Voice#getRequiresNetworkConnection()}) and pass it
+ * ({@link Voice#isNetworkConnectionRequired()}) and pass it
* to {@link TextToSpeech#setVoice(Voice)}).
*/
@Deprecated
@@ -596,7 +596,7 @@
* @deprecated Starting from API level 20, to select embedded synthesis, call
* ({@link TextToSpeech#getVoices()}, find a suitable embedded voice
- * ({@link Voice#getRequiresNetworkConnection()}) and pass it
+ * ({@link Voice#isNetworkConnectionRequired()}) and pass it
* to {@link TextToSpeech#setVoice(Voice)}).
*/
@Deprecated
@@ -957,20 +957,18 @@
*
* @param text
* The string of text. Example: <code>"south_south_east"</code>
- * @param filename
- * The full path to the sound file (for example:
- * "/sdcard/mysounds/hello.wav")
+ * @param file
+ * File object pointing to the sound file.
*
* @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
*/
- public int addSpeech(CharSequence text, String filename) {
+ public int addSpeech(CharSequence text, File file) {
synchronized (mStartLock) {
- mUtterances.put(text, Uri.parse(filename));
+ mUtterances.put(text, Uri.fromFile(file));
return SUCCESS;
}
}
-
/**
* Adds a mapping between a string of text and a sound resource in a
* package. Use this to add custom earcons.
@@ -1017,7 +1015,11 @@
* "/sdcard/mysounds/tick.wav")
*
* @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ *
+ * @deprecated As of API level 20, replaced by
+ * {@link #addEarcon(String, File)}.
*/
+ @Deprecated
public int addEarcon(String earcon, String filename) {
synchronized(mStartLock) {
mEarcons.put(earcon, Uri.parse(filename));
@@ -1025,6 +1027,27 @@
}
}
+ /**
+ * Adds a mapping between a string of text and a sound file.
+ * Use this to add custom earcons.
+ *
+ * @see #playEarcon(String, int, HashMap)
+ *
+ * @param earcon
+ * The name of the earcon.
+ * Example: <code>"[tick]"</code>
+ * @param file
+ * File object pointing to the sound file.
+ *
+ * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ */
+ public int addEarcon(String earcon, File file) {
+ synchronized(mStartLock) {
+ mEarcons.put(earcon, Uri.fromFile(file));
+ return SUCCESS;
+ }
+ }
+
private Uri makeResourceUri(String packageName, int resourceId) {
return new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
@@ -1061,7 +1084,7 @@
*/
public int speak(final CharSequence text,
final int queueMode,
- final HashMap<String, String> params,
+ final Bundle params,
final String utteranceId) {
return runAction(new Action<Integer>() {
@Override
@@ -1103,11 +1126,11 @@
*
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
* @deprecated As of API level 20, replaced by
- * {@link #speak(CharSequence, int, HashMap, String)}.
+ * {@link #speak(CharSequence, int, Bundle, String)}.
*/
@Deprecated
public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
- return speak(text, queueMode, params,
+ return speak(text, queueMode, convertParamsHashMaptoBundle(params),
params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
}
@@ -1135,7 +1158,7 @@
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
*/
public int playEarcon(final String earcon, final int queueMode,
- final HashMap<String, String> params, final String utteranceId) {
+ final Bundle params, final String utteranceId) {
return runAction(new Action<Integer>() {
@Override
public Integer run(ITextToSpeechService service) throws RemoteException {
@@ -1173,12 +1196,12 @@
*
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
* @deprecated As of API level 20, replaced by
- * {@link #playEarcon(String, int, HashMap, String)}.
+ * {@link #playEarcon(String, int, Bundle, String)}.
*/
@Deprecated
public int playEarcon(final String earcon, final int queueMode,
final HashMap<String, String> params) {
- return playEarcon(earcon, queueMode, params,
+ return playEarcon(earcon, queueMode, convertParamsHashMaptoBundle(params),
params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
}
@@ -1757,22 +1780,20 @@
* must be prefixed by the name of the engine they are intended for. For example
* the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
* engine named "com.svox.pico" if it is being used.
- * @param filename Absolute file filename to write the generated audio data to.It should be
- * something like "/sdcard/myappsounds/mysound.wav".
+ * @param file File to write the generated audio data to.
* @param utteranceId An unique identifier for this request.
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
*/
- public int synthesizeToFile(final CharSequence text, final HashMap<String, String> params,
- final String filename, final String utteranceId) {
+ public int synthesizeToFile(final CharSequence text, final Bundle params,
+ final File file, final String utteranceId) {
return runAction(new Action<Integer>() {
@Override
public Integer run(ITextToSpeechService service) throws RemoteException {
ParcelFileDescriptor fileDescriptor;
int returnValue;
try {
- File file = new File(filename);
if(file.exists() && !file.canWrite()) {
- Log.e(TAG, "Can't write to " + filename);
+ Log.e(TAG, "Can't write to " + file);
return ERROR;
}
fileDescriptor = ParcelFileDescriptor.open(file,
@@ -1784,10 +1805,10 @@
fileDescriptor.close();
return returnValue;
} catch (FileNotFoundException e) {
- Log.e(TAG, "Opening file " + filename + " failed", e);
+ Log.e(TAG, "Opening file " + file + " failed", e);
return ERROR;
} catch (IOException e) {
- Log.e(TAG, "Closing file " + filename + " failed", e);
+ Log.e(TAG, "Closing file " + file + " failed", e);
return ERROR;
}
}
@@ -1817,16 +1838,18 @@
*
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
* @deprecated As of API level 20, replaced by
- * {@link #synthesizeToFile(CharSequence, HashMap, String, String)}.
+ * {@link #synthesizeToFile(CharSequence, Bundle, File, String)}.
*/
+ @Deprecated
public int synthesizeToFile(final String text, final HashMap<String, String> params,
final String filename) {
- return synthesizeToFile(text, params, filename, params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+ return synthesizeToFile(text, convertParamsHashMaptoBundle(params),
+ new File(filename), params.get(Engine.KEY_PARAM_UTTERANCE_ID));
}
- private Bundle getParams(HashMap<String, String> params) {
+ private Bundle convertParamsHashMaptoBundle(HashMap<String, String> params) {
if (params != null && !params.isEmpty()) {
- Bundle bundle = new Bundle(mParams);
+ Bundle bundle = new Bundle();
copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID);
copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
@@ -1852,11 +1875,85 @@
}
return bundle;
+ }
+ return null;
+ }
+
+ private Bundle getParams(Bundle params) {
+ if (params != null && !params.isEmpty()) {
+ Bundle bundle = new Bundle(mParams);
+ bundle.putAll(params);
+
+ verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_STREAM);
+ verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_SESSION_ID);
+ verifyStringBundleParam(bundle, Engine.KEY_PARAM_UTTERANCE_ID);
+ verifyFloatBundleParam(bundle, Engine.KEY_PARAM_VOLUME);
+ verifyFloatBundleParam(bundle, Engine.KEY_PARAM_PAN);
+
+ // Copy feature strings defined by the framework.
+ verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
+ verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
+ verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_TIMEOUT_MS);
+ verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_RETRIES_COUNT);
+
+ return bundle;
} else {
return mParams;
}
}
+ private static boolean verifyIntegerBundleParam(Bundle bundle, String key) {
+ if (bundle.containsKey(key)) {
+ if (!(bundle.get(key) instanceof Integer ||
+ bundle.get(key) instanceof Long)) {
+ bundle.remove(key);
+ Log.w(TAG, "Synthesis request paramter " + key + " containst value "
+ + " with invalid type. Should be an Integer or a Long");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean verifyStringBundleParam(Bundle bundle, String key) {
+ if (bundle.containsKey(key)) {
+ if (!(bundle.get(key) instanceof String)) {
+ bundle.remove(key);
+ Log.w(TAG, "Synthesis request paramter " + key + " containst value "
+ + " with invalid type. Should be a String");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean verifyBooleanBundleParam(Bundle bundle, String key) {
+ if (bundle.containsKey(key)) {
+ if (!(bundle.get(key) instanceof Boolean ||
+ bundle.get(key) instanceof String)) {
+ bundle.remove(key);
+ Log.w(TAG, "Synthesis request paramter " + key + " containst value "
+ + " with invalid type. Should be a Boolean or String");
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ private static boolean verifyFloatBundleParam(Bundle bundle, String key) {
+ if (bundle.containsKey(key)) {
+ if (!(bundle.get(key) instanceof Float ||
+ bundle.get(key) instanceof Double)) {
+ bundle.remove(key);
+ Log.w(TAG, "Synthesis request paramter " + key + " containst value "
+ + " with invalid type. Should be a Float or a Double");
+ return false;
+ }
+ }
+ return true;
+ }
+
private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) {
String value = params.get(key);
if (value != null) {
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 4fea109..d00a433 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -84,7 +84,7 @@
* the following methods:
* <ul>
* <li>{@link #onGetVoices()}</li>
- * <li>{@link #isValidVoiceName(String)}</li>
+ * <li>{@link #onIsValidVoiceName(String)}</li>
* <li>{@link #onLoadVoice(String)}</li>
* <li>{@link #onGetDefaultVoiceNameFor(String, String, String)}</li>
* </ul>
@@ -278,7 +278,7 @@
*
* @return A list of voices supported.
*/
- protected List<Voice> onGetVoices() {
+ public List<Voice> onGetVoices() {
// Enumerate all locales and check if they are available
ArrayList<Voice> voices = new ArrayList<Voice>();
for (Locale locale : Locale.getAvailableLocales()) {
@@ -335,7 +335,7 @@
}
Locale properLocale = TtsEngines.normalizeTTSLocale(iso3Locale);
String voiceName = properLocale.toLanguageTag();
- if (isValidVoiceName(voiceName) == TextToSpeech.SUCCESS) {
+ if (onIsValidVoiceName(voiceName) == TextToSpeech.SUCCESS) {
return voiceName;
} else {
return null;
@@ -357,7 +357,7 @@
* @param voiceName Name of the voice.
* @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}.
*/
- protected int onLoadVoice(String voiceName) {
+ public int onLoadVoice(String voiceName) {
Locale locale = Locale.forLanguageTag(voiceName);
if (locale == null) {
return TextToSpeech.ERROR;
@@ -388,7 +388,7 @@
* @param voiceName Name of the voice.
* @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}.
*/
- protected int isValidVoiceName(String voiceName) {
+ public int onIsValidVoiceName(String voiceName) {
Locale locale = Locale.forLanguageTag(voiceName);
if (locale == null) {
return TextToSpeech.ERROR;
@@ -1275,7 +1275,7 @@
if (!checkNonNull(voiceName)) {
return TextToSpeech.ERROR;
}
- int retVal = isValidVoiceName(voiceName);
+ int retVal = onIsValidVoiceName(voiceName);
if (retVal == TextToSpeech.SUCCESS) {
SpeechItem item = new LoadVoiceItem(caller, Binder.getCallingUid(),
diff --git a/core/java/android/speech/tts/Voice.java b/core/java/android/speech/tts/Voice.java
index a97141c..a1fa51d 100644
--- a/core/java/android/speech/tts/Voice.java
+++ b/core/java/android/speech/tts/Voice.java
@@ -162,7 +162,7 @@
/**
* @return Does the Voice require a network connection to work.
*/
- public boolean getRequiresNetworkConnection() {
+ public boolean isNetworkConnectionRequired() {
return mRequiresNetworkConnection;
}
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 0d1b568..40bb6ec 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -1417,9 +1417,9 @@
}
capturePropagationValues(values);
if (start) {
- addViewValues(mStartValues, view, values);
+ addViewValues(mStartValues, view, values, true);
} else {
- addViewValues(mEndValues, view, values);
+ addViewValues(mEndValues, view, values, true);
}
}
}
@@ -1460,7 +1460,7 @@
}
static void addViewValues(TransitionValuesMaps transitionValuesMaps,
- View view, TransitionValues transitionValues) {
+ View view, TransitionValues transitionValues, boolean setTransientState) {
transitionValuesMaps.viewValues.put(view, transitionValues);
int id = view.getId();
if (id >= 0) {
@@ -1489,11 +1489,15 @@
// Duplicate item IDs: cannot match by item ID.
View alreadyMatched = transitionValuesMaps.itemIdValues.get(itemId);
if (alreadyMatched != null) {
- alreadyMatched.setHasTransientState(false);
+ if (setTransientState) {
+ alreadyMatched.setHasTransientState(false);
+ }
transitionValuesMaps.itemIdValues.put(itemId, null);
}
} else {
- view.setHasTransientState(true);
+ if (setTransientState) {
+ view.setHasTransientState(true);
+ }
transitionValuesMaps.itemIdValues.put(itemId, view);
}
}
@@ -1560,9 +1564,9 @@
}
capturePropagationValues(values);
if (start) {
- addViewValues(mStartValues, view, values);
+ addViewValues(mStartValues, view, values, true);
} else {
- addViewValues(mEndValues, view, values);
+ addViewValues(mEndValues, view, values, true);
}
}
if (view instanceof ViewGroup) {
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index f6499ae..56db674 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -408,7 +408,7 @@
for (int i = 0; i < numValues; i++) {
View view = values.viewValues.keyAt(i);
if (isValidTarget(view)) {
- addViewValues(included, view, values.viewValues.valueAt(i));
+ addViewValues(included, view, values.viewValues.valueAt(i), false);
}
}
return included;
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 0b02552..d648ca6 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -358,12 +358,16 @@
overlayView = startView;
} else if (startView.getParent() instanceof View) {
View startParent = (View) startView.getParent();
- if (!isValidTarget(startParent)) {
- if (startView.isAttachedToWindow()) {
- overlayView = copyViewImage(startView);
- } else {
- overlayView = startView;
- }
+ VisibilityInfo parentVisibilityInfo = null;
+ TransitionValues endParentValues = getMatchedTransitionValues(startParent,
+ true);
+ if (endParentValues != null) {
+ TransitionValues startParentValues = getTransitionValues(startParent, true);
+ parentVisibilityInfo =
+ getVisibilityChangeInfo(startParentValues, endParentValues);
+ }
+ if (parentVisibilityInfo == null || !parentVisibilityInfo.visibilityChange) {
+ overlayView = copyViewImage(startView);
} else if (startParent.getParent() == null) {
int id = startParent.getId();
if (id != View.NO_ID && sceneRoot.findViewById(id) != null
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 5d6d998..6aa86c7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -94,7 +94,8 @@
void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
IRemoteCallback startedCallback, boolean scaleUp);
void overridePendingAppTransitionAspectScaledThumb(in Bitmap srcThumb, int startX,
- int startY, IRemoteCallback startedCallback, boolean scaleUp);
+ int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
+ boolean scaleUp);
void executeAppTransition();
void setAppStartingWindow(IBinder token, String pkg, int theme,
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index fa4a13a..debf45d 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -453,6 +453,12 @@
throw new IllegalStateException("Cannot clone this animator");
}
+ @Override
+ public void setAllowRunningAsynchronously(boolean mayRunAsync) {
+ checkMutable();
+ nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
+ }
+
private static native long nCreateAnimator(int property, float finalValue);
private static native long nCreateCanvasPropertyFloatAnimator(
long canvasProperty, float finalValue);
@@ -466,6 +472,7 @@
private static native long nGetDuration(long nativePtr);
private static native void nSetStartDelay(long nativePtr, long startDelay);
private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
+ private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
private static native void nStart(long animPtr, RenderNodeAnimator finishListener);
private static native void nEnd(long animPtr);
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 6c66eb0..a94f973 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -26,6 +26,7 @@
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.TypedValue;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
@@ -315,6 +316,7 @@
private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
private static final String REMOTE_COMMAND_DUMP = "DUMP";
+ private static final String REMOTE_COMMAND_DUMP_THEME = "DUMP_THEME";
private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
private static final String REMOTE_PROFILE = "PROFILE";
@@ -430,6 +432,8 @@
if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
dump(view, false, true, clientStream);
+ } else if (REMOTE_COMMAND_DUMP_THEME.equalsIgnoreCase(command)) {
+ dumpTheme(view, clientStream);
} else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
captureLayers(view, new DataOutputStream(clientStream));
} else {
@@ -820,6 +824,64 @@
}
}
+ /**
+ * Dumps the theme attributes from the given View.
+ * @hide
+ */
+ public static void dumpTheme(View view, OutputStream clientStream) throws IOException {
+ BufferedWriter out = null;
+ try {
+ out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
+ String[] attributes = getStyleAttributesDump(view.getContext().getResources(),
+ view.getContext().getTheme());
+ if (attributes != null) {
+ for (int i = 0; i < attributes.length; i += 2) {
+ if (attributes[i] != null) {
+ out.write(attributes[i] + "\n");
+ out.write(attributes[i + 1] + "\n");
+ }
+ }
+ }
+ out.write("DONE.");
+ out.newLine();
+ } catch (Exception e) {
+ android.util.Log.w("View", "Problem dumping View Theme:", e);
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+
+ /**
+ * Gets the style attributes from the {@link Resources.Theme}. For debugging only.
+ *
+ * @param resources Resources to resolve attributes from.
+ * @param theme Theme to dump.
+ * @return a String array containing pairs of adjacent Theme attribute data: name followed by
+ * its value.
+ *
+ * @hide
+ */
+ private static String[] getStyleAttributesDump(Resources resources, Resources.Theme theme) {
+ TypedValue outValue = new TypedValue();
+ String nullString = "null";
+ int i = 0;
+ int[] attributes = theme.getAllAttributes();
+ String[] data = new String[attributes.length * 2];
+ for (int attributeId : attributes) {
+ try {
+ data[i] = resources.getResourceName(attributeId);
+ data[i + 1] = theme.resolveAttribute(attributeId, outValue, true) ?
+ outValue.coerceToString().toString() : nullString;
+ i += 2;
+ } catch (Resources.NotFoundException e) {
+ // ignore resources we can't resolve
+ }
+ }
+ return data;
+ }
+
private static View findView(ViewGroup group, String className, int hashCode) {
if (isRequestedView(group, className, hashCode)) {
return group;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ac1b6a9..c1e66de 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5724,6 +5724,28 @@
}
@Override
+ public void drawableHotspotChanged(float x, float y) {
+ super.drawableHotspotChanged(x, y);
+
+ if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
+ if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
+ throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
+ + " child has duplicateParentState set to true");
+ }
+
+ final View[] children = mChildren;
+ final int count = mChildrenCount;
+
+ for (int i = 0; i < count; i++) {
+ final View child = children[i];
+ if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
+ child.drawableHotspotChanged(x, y);
+ }
+ }
+ }
+ }
+
+ @Override
protected int[] onCreateDrawableState(int extraSpace) {
if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
return super.onCreateDrawableState(extraSpace);
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index fe0f5b9..600fffe 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -35,9 +35,21 @@
* actually inserted.</p>
*/
public final class CursorAnchorInfo implements Parcelable {
+ /**
+ * The index of the first character of the selected text (inclusive). {@code -1} when there is
+ * no text selection.
+ */
private final int mSelectionStart;
+ /**
+ * The index of the first character of the selected text (exclusive). {@code -1} when there is
+ * no text selection.
+ */
private final int mSelectionEnd;
+ /**
+ * The index of the first character of the composing text (inclusive). {@code -1} when there is
+ * no composing text.
+ */
private final int mComposingTextStart;
/**
* The text, tracked as a composing region.
@@ -82,7 +94,7 @@
* Java chars, in the local coordinates that will be transformed with the transformation matrix
* when rendered on the screen.
*/
- private final SparseRectFArray mCharacterRects;
+ private final SparseRectFArray mCharacterBoundsArray;
/**
* Transformation matrix that is applied to any positional information of this class to
@@ -91,18 +103,24 @@
private final Matrix mMatrix;
/**
- * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterRectFlags(int)}: the
+ * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
* insertion marker or character bounds have at least one visible region.
*/
public static final int FLAG_HAS_VISIBLE_REGION = 0x01;
/**
- * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterRectFlags(int)}: the
+ * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
* insertion marker or character bounds have at least one invisible (clipped) region.
*/
public static final int FLAG_HAS_INVISIBLE_REGION = 0x02;
/**
+ * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
+ * insertion marker or character bounds is placed at right-to-left (RTL) character.
+ */
+ public static final int FLAG_IS_RTL = 0x04;
+
+ /**
* @removed
*/
public static final int CHARACTER_RECT_TYPE_MASK = 0x0f;
@@ -144,7 +162,7 @@
mInsertionMarkerTop = source.readFloat();
mInsertionMarkerBaseline = source.readFloat();
mInsertionMarkerBottom = source.readFloat();
- mCharacterRects = source.readParcelable(SparseRectFArray.class.getClassLoader());
+ mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader());
mMatrix = new Matrix();
mMatrix.setValues(source.createFloatArray());
}
@@ -166,7 +184,7 @@
dest.writeFloat(mInsertionMarkerTop);
dest.writeFloat(mInsertionMarkerBaseline);
dest.writeFloat(mInsertionMarkerBottom);
- dest.writeParcelable(mCharacterRects, flags);
+ dest.writeParcelable(mCharacterBoundsArray, flags);
final float[] matrixArray = new float[9];
mMatrix.getValues(matrixArray);
dest.writeFloatArray(matrixArray);
@@ -174,7 +192,6 @@
@Override
public int hashCode(){
- // TODO: Improve the hash function.
final float floatHash = mInsertionMarkerHorizontal + mInsertionMarkerTop
+ mInsertionMarkerBaseline + mInsertionMarkerBottom;
int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash);
@@ -185,7 +202,7 @@
hash *= 31;
hash += Objects.hashCode(mComposingText);
hash *= 31;
- hash += Objects.hashCode(mCharacterRects);
+ hash += Objects.hashCode(mCharacterBoundsArray);
hash *= 31;
hash += Objects.hashCode(mMatrix);
return hash;
@@ -231,7 +248,7 @@
|| !areSameFloatImpl(mInsertionMarkerBottom, that.mInsertionMarkerBottom)) {
return false;
}
- if (!Objects.equals(mCharacterRects, that.mCharacterRects)) {
+ if (!Objects.equals(mCharacterBoundsArray, that.mCharacterBoundsArray)) {
return false;
}
if (!Objects.equals(mMatrix, that.mMatrix)) {
@@ -250,7 +267,7 @@
+ " mInsertionMarkerTop=" + mInsertionMarkerTop
+ " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
+ " mInsertionMarkerBottom=" + mInsertionMarkerBottom
- + " mCharacterRects=" + Objects.toString(mCharacterRects)
+ + " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray)
+ " mMatrix=" + Objects.toString(mMatrix)
+ "}";
}
@@ -259,6 +276,19 @@
* Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe.
*/
public static final class Builder {
+ private int mSelectionStart = -1;
+ private int mSelectionEnd = -1;
+ private int mComposingTextStart = -1;
+ private CharSequence mComposingText = null;
+ private float mInsertionMarkerHorizontal = Float.NaN;
+ private float mInsertionMarkerTop = Float.NaN;
+ private float mInsertionMarkerBaseline = Float.NaN;
+ private float mInsertionMarkerBottom = Float.NaN;
+ private int mInsertionMarkerFlags = 0;
+ private SparseRectFArrayBuilder mCharacterBoundsArrayBuilder = null;
+ private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX);
+ private boolean mMatrixInitialized = false;
+
/**
* Sets the text range of the selection. Calling this can be skipped if there is no
* selection.
@@ -268,8 +298,6 @@
mSelectionEnd = newEnd;
return this;
}
- private int mSelectionStart = -1;
- private int mSelectionEnd = -1;
/**
* Sets the text range of the composing text. Calling this can be skipped if there is
@@ -288,8 +316,6 @@
}
return this;
}
- private int mComposingTextStart = -1;
- private CharSequence mComposingText = null;
/**
* @removed
@@ -335,11 +361,33 @@
mInsertionMarkerFlags = flags;
return this;
}
- private float mInsertionMarkerHorizontal = Float.NaN;
- private float mInsertionMarkerTop = Float.NaN;
- private float mInsertionMarkerBaseline = Float.NaN;
- private float mInsertionMarkerBottom = Float.NaN;
- private int mInsertionMarkerFlags = 0;
+
+ /**
+ * Adds the bounding box of the character specified with the index.
+ *
+ * @param index index of the character in Java chars units. Must be specified in
+ * ascending order across successive calls.
+ * @param left x coordinate of the left edge of the character in local coordinates.
+ * @param top y coordinate of the top edge of the character in local coordinates.
+ * @param right x coordinate of the right edge of the character in local coordinates.
+ * @param bottom y coordinate of the bottom edge of the character in local coordinates.
+ * @param flags flags for this character bounds. See {@link #FLAG_HAS_VISIBLE_REGION},
+ * {@link #FLAG_HAS_INVISIBLE_REGION} and {@link #FLAG_IS_RTL}. These flags must be
+ * specified when necessary.
+ * @throws IllegalArgumentException If the index is a negative value, or not greater than
+ * all of the previously called indices.
+ */
+ public Builder addCharacterBounds(final int index, final float left, final float top,
+ final float right, final float bottom, final int flags) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index must not be a negative integer.");
+ }
+ if (mCharacterBoundsArrayBuilder == null) {
+ mCharacterBoundsArrayBuilder = new SparseRectFArrayBuilder();
+ }
+ mCharacterBoundsArrayBuilder.append(index, left, top, right, bottom, flags);
+ return this;
+ }
/**
* Adds the bounding box of the character specified with the index.
@@ -358,21 +406,25 @@
* example.
* @throws IllegalArgumentException If the index is a negative value, or not greater than
* all of the previously called indices.
+ * @removed
*/
public Builder addCharacterRect(final int index, final float leadingEdgeX,
final float leadingEdgeY, final float trailingEdgeX, final float trailingEdgeY,
final int flags) {
- if (index < 0) {
- throw new IllegalArgumentException("index must not be a negative integer.");
+ final int newFlags;
+ final float left;
+ final float right;
+ if (leadingEdgeX <= trailingEdgeX) {
+ newFlags = flags;
+ left = leadingEdgeX;
+ right = trailingEdgeX;
+ } else {
+ newFlags = flags | FLAG_IS_RTL;
+ left = trailingEdgeX;
+ right = leadingEdgeX;
}
- if (mCharacterRectBuilder == null) {
- mCharacterRectBuilder = new SparseRectFArrayBuilder();
- }
- mCharacterRectBuilder.append(index, leadingEdgeX, leadingEdgeY, trailingEdgeX,
- trailingEdgeY, flags);
- return this;
+ return addCharacterBounds(index, left, leadingEdgeY, right, trailingEdgeY, newFlags);
}
- private SparseRectFArrayBuilder mCharacterRectBuilder = null;
/**
* Sets the matrix that transforms local coordinates into screen coordinates.
@@ -384,8 +436,6 @@
mMatrixInitialized = true;
return this;
}
- private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX);
- private boolean mMatrixInitialized = false;
/**
* @return {@link CursorAnchorInfo} using parameters in this {@link Builder}.
@@ -394,13 +444,15 @@
*/
public CursorAnchorInfo build() {
if (!mMatrixInitialized) {
- // Coordinate transformation matrix is mandatory when positional parameters are
- // specified.
- if ((mCharacterRectBuilder != null && !mCharacterRectBuilder.isEmpty()) ||
- !Float.isNaN(mInsertionMarkerHorizontal) ||
- !Float.isNaN(mInsertionMarkerTop) ||
- !Float.isNaN(mInsertionMarkerBaseline) ||
- !Float.isNaN(mInsertionMarkerBottom)) {
+ // Coordinate transformation matrix is mandatory when at least one positional
+ // parameter is specified.
+ final boolean hasCharacterBounds = (mCharacterBoundsArrayBuilder != null
+ && !mCharacterBoundsArrayBuilder.isEmpty());
+ if (hasCharacterBounds
+ || !Float.isNaN(mInsertionMarkerHorizontal)
+ || !Float.isNaN(mInsertionMarkerTop)
+ || !Float.isNaN(mInsertionMarkerBaseline)
+ || !Float.isNaN(mInsertionMarkerBottom)) {
throw new IllegalArgumentException("Coordinate transformation matrix is " +
"required when positional parameters are specified.");
}
@@ -424,8 +476,8 @@
mInsertionMarkerBottom = Float.NaN;
mMatrix.set(Matrix.IDENTITY_MATRIX);
mMatrixInitialized = false;
- if (mCharacterRectBuilder != null) {
- mCharacterRectBuilder.reset();
+ if (mCharacterBoundsArrayBuilder != null) {
+ mCharacterBoundsArrayBuilder.reset();
}
}
}
@@ -440,8 +492,8 @@
mInsertionMarkerTop = builder.mInsertionMarkerTop;
mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline;
mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
- mCharacterRects = builder.mCharacterRectBuilder != null ?
- builder.mCharacterRectBuilder.build() : null;
+ mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ?
+ builder.mCharacterBoundsArrayBuilder.build() : null;
mMatrix = new Matrix(builder.mMatrix);
}
@@ -539,6 +591,19 @@
/**
* Returns a new instance of {@link RectF} that indicates the location of the character
* specified with the index.
+ * @param index index of the character in a Java chars.
+ * @return the character bounds in local coordinates as a new instance of {@link RectF}.
+ */
+ public RectF getCharacterBounds(final int index) {
+ if (mCharacterBoundsArray == null) {
+ return null;
+ }
+ return mCharacterBoundsArray.get(index);
+ }
+
+ /**
+ * Returns a new instance of {@link RectF} that indicates the location of the character
+ * specified with the index.
* <p>
* Note that coordinates are not necessarily contiguous or even monotonous, especially when
* RTL text and LTR text are mixed.
@@ -549,28 +614,32 @@
* the location. Note that the {@code left} field can be greater than the {@code right} field
* if the character is in RTL text. Returns {@code null} if no location information is
* available.
+ * @removed
*/
- // TODO: Prepare a document about the expected behavior for surrogate pairs, combining
- // characters, and non-graphical chars.
public RectF getCharacterRect(final int index) {
- if (mCharacterRects == null) {
- return null;
+ return getCharacterBounds(index);
+ }
+
+ /**
+ * Returns the flags associated with the character bounds specified with the index.
+ * @param index index of the character in a Java chars.
+ * @return {@code 0} if no flag is specified.
+ */
+ public int getCharacterBoundsFlags(final int index) {
+ if (mCharacterBoundsArray == null) {
+ return 0;
}
- return mCharacterRects.get(index);
+ return mCharacterBoundsArray.getFlags(index, 0);
}
/**
* Returns the flags associated with the character rect specified with the index.
* @param index index of the character in a Java chars.
* @return {@code 0} if no flag is specified.
+ * @removed
*/
- // TODO: Prepare a document about the expected behavior for surrogate pairs, combining
- // characters, and non-graphical chars.
public int getCharacterRectFlags(final int index) {
- if (mCharacterRects == null) {
- return 0;
- }
- return mCharacterRects.getFlags(index, 0);
+ return getCharacterBoundsFlags(index);
}
/**
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 2729bd0..d77f0b2 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -125,6 +125,7 @@
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
defStyleAttr, defStyleRes);
final int mode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER);
+ final int firstDayOfWeek = a.getInt(R.styleable.DatePicker_firstDayOfWeek, 0);
a.recycle();
switch (mode) {
@@ -136,6 +137,10 @@
mDelegate = createSpinnerUIDelegate(context, attrs, defStyleAttr, defStyleRes);
break;
}
+
+ if (firstDayOfWeek != 0) {
+ setFirstDayOfWeek(firstDayOfWeek);
+ }
}
private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs,
@@ -300,6 +305,47 @@
}
/**
+ * Sets the first day of week.
+ *
+ * @param firstDayOfWeek The first day of the week conforming to the
+ * {@link CalendarView} APIs.
+ * @see Calendar#SUNDAY
+ * @see Calendar#MONDAY
+ * @see Calendar#TUESDAY
+ * @see Calendar#WEDNESDAY
+ * @see Calendar#THURSDAY
+ * @see Calendar#FRIDAY
+ * @see Calendar#SATURDAY
+ *
+ * @attr ref android.R.styleable#DatePicker_firstDayOfWeek
+ */
+ public void setFirstDayOfWeek(int firstDayOfWeek) {
+ if (firstDayOfWeek < Calendar.SUNDAY || firstDayOfWeek > Calendar.SATURDAY) {
+ throw new IllegalArgumentException("firstDayOfWeek must be between 1 and 7");
+ }
+ mDelegate.setFirstDayOfWeek(firstDayOfWeek);
+ }
+
+ /**
+ * Gets the first day of week.
+ *
+ * @return The first day of the week conforming to the {@link CalendarView}
+ * APIs.
+ * @see Calendar#SUNDAY
+ * @see Calendar#MONDAY
+ * @see Calendar#TUESDAY
+ * @see Calendar#WEDNESDAY
+ * @see Calendar#THURSDAY
+ * @see Calendar#FRIDAY
+ * @see Calendar#SATURDAY
+ *
+ * @attr ref android.R.styleable#DatePicker_firstDayOfWeek
+ */
+ public int getFirstDayOfWeek() {
+ return mDelegate.getFirstDayOfWeek();
+ }
+
+ /**
* Gets whether the {@link CalendarView} is shown.
*
* @return True if the calendar view is shown.
@@ -315,7 +361,7 @@
* @return The calendar view.
* @see #getCalendarViewShown()
*/
- public CalendarView getCalendarView () {
+ public CalendarView getCalendarView() {
return mDelegate.getCalendarView();
}
@@ -382,6 +428,9 @@
int getMonth();
int getDayOfMonth();
+ void setFirstDayOfWeek(int firstDayOfWeek);
+ int getFirstDayOfWeek();
+
void setMinDate(long minDate);
Calendar getMinDate();
@@ -699,6 +748,16 @@
}
@Override
+ public void setFirstDayOfWeek(int firstDayOfWeek) {
+ mCalendarView.setFirstDayOfWeek(firstDayOfWeek);
+ }
+
+ @Override
+ public int getFirstDayOfWeek() {
+ return mCalendarView.getFirstDayOfWeek();
+ }
+
+ @Override
public void setMinDate(long minDate) {
mTempDate.setTimeInMillis(minDate);
if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index eed49bf..b962962 100644
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -49,6 +49,7 @@
*/
class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements
View.OnClickListener, DatePickerController {
+ private static final int USE_LOCALE = 0;
private static final int UNINITIALIZED = -1;
private static final int MONTH_AND_DAY_VIEW = 0;
@@ -99,6 +100,8 @@
private Calendar mMinDate;
private Calendar mMaxDate;
+ private int mFirstDayOfWeek = USE_LOCALE;
+
private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs,
@@ -438,7 +441,15 @@
}
@Override
+ public void setFirstDayOfWeek(int firstDayOfWeek) {
+ mFirstDayOfWeek = firstDayOfWeek;
+ }
+
+ @Override
public int getFirstDayOfWeek() {
+ if (mFirstDayOfWeek != USE_LOCALE) {
+ return mFirstDayOfWeek;
+ }
return mCurrentDate.getFirstDayOfWeek();
}
diff --git a/core/java/android/widget/DatePickerController.java b/core/java/android/widget/DatePickerController.java
index 6a074da..059709d 100644
--- a/core/java/android/widget/DatePickerController.java
+++ b/core/java/android/widget/DatePickerController.java
@@ -35,6 +35,7 @@
Calendar getSelectedDay();
+ void setFirstDayOfWeek(int firstDayOfWeek);
int getFirstDayOfWeek();
int getMinYear();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 22138d0..3f168e8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3060,47 +3060,69 @@
final CharSequence composingText = text.subSequence(composingTextStart,
composingTextEnd);
builder.setComposingText(composingTextStart, composingText);
- }
- // TODO: Optimize this loop by caching the result.
- for (int offset = composingTextStart; offset < composingTextEnd; offset++) {
- if (offset < 0) {
- continue;
+
+ final int minLine = layout.getLineForOffset(composingTextStart);
+ final int maxLine = layout.getLineForOffset(composingTextEnd - 1);
+ for (int line = minLine; line <= maxLine; ++line) {
+ final int lineStart = layout.getLineStart(line);
+ final int lineEnd = layout.getLineEnd(line);
+ final int offsetStart = Math.max(lineStart, composingTextStart);
+ final int offsetEnd = Math.min(lineEnd, composingTextEnd);
+ final boolean ltrLine =
+ layout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT;
+ final float[] widths = new float[offsetEnd - offsetStart];
+ layout.getPaint().getTextWidths(text, offsetStart, offsetEnd, widths);
+ final float top = layout.getLineTop(line);
+ final float bottom = layout.getLineBottom(line);
+ for (int offset = offsetStart; offset < offsetEnd; ++offset) {
+ final float charWidth = widths[offset - offsetStart];
+ final boolean isRtl = layout.isRtlCharAt(offset);
+ final float primary = layout.getPrimaryHorizontal(offset);
+ final float secondary = layout.getSecondaryHorizontal(offset);
+ // TODO: This doesn't work perfectly for text with custom styles and
+ // TAB chars.
+ final float left;
+ final float right;
+ if (ltrLine) {
+ if (isRtl) {
+ left = secondary - charWidth;
+ right = secondary;
+ } else {
+ left = primary;
+ right = primary + charWidth;
+ }
+ } else {
+ if (!isRtl) {
+ left = secondary;
+ right = secondary + charWidth;
+ } else {
+ left = primary - charWidth;
+ right = primary;
+ }
+ }
+ // TODO: Check top-right and bottom-left as well.
+ final float localLeft = left + viewportToContentHorizontalOffset;
+ final float localRight = right + viewportToContentHorizontalOffset;
+ final float localTop = top + viewportToContentVerticalOffset;
+ final float localBottom = bottom + viewportToContentVerticalOffset;
+ final boolean isTopLeftVisible = isPositionVisible(localLeft, localTop);
+ final boolean isBottomRightVisible =
+ isPositionVisible(localRight, localBottom);
+ int characterBoundsFlags = 0;
+ if (isTopLeftVisible || isBottomRightVisible) {
+ characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+ }
+ if (!isTopLeftVisible || !isTopLeftVisible) {
+ characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+ }
+ if (isRtl) {
+ characterBoundsFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+ }
+ // Here offset is the index in Java chars.
+ builder.addCharacterBounds(offset, localLeft, localTop, localRight,
+ localBottom, characterBoundsFlags);
+ }
}
- final boolean isRtl = layout.isRtlCharAt(offset);
- final int line = layout.getLineForOffset(offset);
- final int nextCharIndex = offset + 1;
- final float localLeadingEdgeX = layout.getPrimaryHorizontal(offset);
- final float localTrailingEdgeX;
- if (nextCharIndex != layout.getLineEnd(line)) {
- localTrailingEdgeX = layout.getPrimaryHorizontal(nextCharIndex);
- } else if (isRtl) {
- localTrailingEdgeX = layout.getLineLeft(line);
- } else {
- localTrailingEdgeX = layout.getLineRight(line);
- }
- final float leadingEdgeX = localLeadingEdgeX
- + viewportToContentHorizontalOffset;
- final float trailingEdgeX = localTrailingEdgeX
- + viewportToContentHorizontalOffset;
- final float top = layout.getLineTop(line) + viewportToContentVerticalOffset;
- final float bottom = layout.getLineBottom(line)
- + viewportToContentVerticalOffset;
- // TODO: Check right-top and left-bottom as well.
- final boolean isLeadingEdgeTopVisible = isPositionVisible(leadingEdgeX, top);
- final boolean isTrailingEdgeBottomVisible =
- isPositionVisible(trailingEdgeX, bottom);
- int characterRectFlags = 0;
- if (isLeadingEdgeTopVisible || isTrailingEdgeBottomVisible) {
- characterRectFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
- }
- if (!isLeadingEdgeTopVisible || !isTrailingEdgeBottomVisible) {
- characterRectFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
- }
- // Here offset is the index in Java chars.
- // TODO: We must have a well-defined specification. For example, how
- // surrogate pairs and composition letters are handled must be documented.
- builder.addCharacterRect(offset, leadingEdgeX, top, trailingEdgeX, bottom,
- characterRectFlags);
}
}
@@ -3127,6 +3149,9 @@
if (!isTopVisible || !isBottomVisible) {
insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
}
+ if (layout.isRtlCharAt(offset)) {
+ insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+ }
builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 69cdbff..45a9dde 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -94,7 +94,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 113 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 114 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -385,10 +385,9 @@
String mLastWakeupReason = null;
long mLastWakeupUptimeMs = 0;
- private final HashMap<String, LongSamplingCounter> mWakeupReasonStats =
- new HashMap<String, LongSamplingCounter>();
+ private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
- public Map<String, ? extends LongCounter> getWakeupReasonStats() {
+ public Map<String, ? extends Timer> getWakeupReasonStats() {
return mWakeupReasonStats;
}
@@ -1131,6 +1130,10 @@
mCurrentReportedCount = count;
}
+ public void addCurrentReportedCount(int delta) {
+ updateCurrentReportedCount(mCurrentReportedCount + delta);
+ }
+
public void updateCurrentReportedTotalTime(long totalTime) {
if (mTimeBaseRunning && mUnpluggedReportedTotalTime == 0) {
// Updating the reported value for the first time.
@@ -1141,6 +1144,10 @@
mCurrentReportedTotalTime = totalTime;
}
+ public void addCurrentReportedTotalTime(long delta) {
+ updateCurrentReportedTotalTime(mCurrentReportedTotalTime + delta);
+ }
+
public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
super.onTimeStarted(elapsedRealtime, baseUptime, baseRealtime);
if (mTrackingReportedValues) {
@@ -1688,13 +1695,13 @@
* Get the wakeup reason counter, and create a new one if one
* doesn't already exist.
*/
- public LongSamplingCounter getWakeupReasonCounterLocked(String name) {
- LongSamplingCounter counter = mWakeupReasonStats.get(name);
- if (counter == null) {
- counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
- mWakeupReasonStats.put(name, counter);
+ public SamplingTimer getWakeupReasonTimerLocked(String name) {
+ SamplingTimer timer = mWakeupReasonStats.get(name);
+ if (timer == null) {
+ timer = new SamplingTimer(mOnBatteryTimeBase, true);
+ mWakeupReasonStats.put(name, timer);
}
- return counter;
+ return timer;
}
private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
@@ -2753,8 +2760,9 @@
void aggregateLastWakeupUptimeLocked(long uptimeMs) {
if (mLastWakeupReason != null) {
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
- LongSamplingCounter timer = getWakeupReasonCounterLocked(mLastWakeupReason);
- timer.addCountLocked(deltaUptime);
+ SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
+ timer.addCurrentReportedCount(1);
+ timer.addCurrentReportedTotalTime(deltaUptime * 1000); // time is in microseconds
mLastWakeupReason = null;
}
}
@@ -2762,7 +2770,7 @@
public void noteWakeupReasonLocked(String reason) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason reason \"" + reason +"\": "
+ if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason \"" + reason +"\": "
+ Integer.toHexString(mHistoryCur.states));
aggregateLastWakeupUptimeLocked(uptime);
mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
@@ -6193,7 +6201,7 @@
}
public void noteStartJobLocked(String name, long elapsedRealtimeMs) {
- StopwatchTimer t = mJobStats.stopObject(name);
+ StopwatchTimer t = mJobStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
}
@@ -6636,8 +6644,8 @@
}
if (mWakeupReasonStats.size() > 0) {
- for (LongSamplingCounter timer : mWakeupReasonStats.values()) {
- mOnBatteryScreenOffTimeBase.remove(timer);
+ for (SamplingTimer timer : mWakeupReasonStats.values()) {
+ mOnBatteryTimeBase.remove(timer);
}
mWakeupReasonStats.clear();
}
@@ -7848,7 +7856,7 @@
for (int iwr = 0; iwr < NWR; iwr++) {
if (in.readInt() != 0) {
String reasonName = in.readString();
- getWakeupReasonCounterLocked(reasonName).readSummaryFromParcelLocked(in);
+ getWakeupReasonTimerLocked(reasonName).readSummaryFromParcelLocked(in);
}
}
@@ -8122,12 +8130,12 @@
}
out.writeInt(mWakeupReasonStats.size());
- for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
- LongSamplingCounter counter = ent.getValue();
- if (counter != null) {
+ for (Map.Entry<String, SamplingTimer> ent : mWakeupReasonStats.entrySet()) {
+ SamplingTimer timer = ent.getValue();
+ if (timer != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- counter.writeSummaryFromParcelLocked(out);
+ timer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
@@ -8438,7 +8446,7 @@
for (int ikw = 0; ikw < NKW; ikw++) {
if (in.readInt() != 0) {
String wakelockName = in.readString();
- SamplingTimer kwlt = new SamplingTimer(mOnBatteryTimeBase, in);
+ SamplingTimer kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase, in);
mKernelWakelockStats.put(wakelockName, kwlt);
}
}
@@ -8448,9 +8456,8 @@
for (int iwr = 0; iwr < NWR; iwr++) {
if (in.readInt() != 0) {
String reasonName = in.readString();
- LongSamplingCounter counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase,
- in);
- mWakeupReasonStats.put(reasonName, counter);
+ SamplingTimer timer = new SamplingTimer(mOnBatteryTimeBase, in);
+ mWakeupReasonStats.put(reasonName, timer);
}
}
@@ -8585,12 +8592,12 @@
}
}
out.writeInt(mWakeupReasonStats.size());
- for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
- LongSamplingCounter counter = ent.getValue();
- if (counter != null) {
+ for (Map.Entry<String, SamplingTimer> ent : mWakeupReasonStats.entrySet()) {
+ SamplingTimer timer = ent.getValue();
+ if (timer != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- counter.writeToParcel(out);
+ timer.writeToParcel(out, uSecRealtime);
} else {
out.writeInt(0);
}
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index f0d7a35..7b33bc2 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -211,7 +211,7 @@
}
jobject jAudioFormat = NULL;
- if (event->trigger_in_data) {
+ if (event->trigger_in_data || event->capture_available) {
jAudioFormat = env->NewObject(gAudioFormatClass,
gAudioFormatCstor,
audioFormatFromNative(event->audio_config.format),
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 5faf03d..396f3ec 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1888,6 +1888,37 @@
return array;
}
+static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz,
+ jint styleId)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+ const ResTable& res(am->getResources());
+
+ const ResTable::bag_entry* startOfBag;
+ const ssize_t N = res.lockBag(styleId, &startOfBag);
+ if (N < 0) {
+ return NULL;
+ }
+
+ jintArray array = env->NewIntArray(N);
+ if (array == NULL) {
+ res.unlockBag(startOfBag);
+ return NULL;
+ }
+
+ Res_value value;
+ const ResTable::bag_entry* bag = startOfBag;
+ for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
+ int resourceId = bag->map.name.ident;
+ env->SetIntArrayRegion(array, i, 1, &resourceId);
+ }
+ res.unlockBag(startOfBag);
+ return array;
+}
+
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
if (isSystem) {
@@ -2038,6 +2069,8 @@
(void*) android_content_AssetManager_getArrayStringInfo },
{ "getArrayIntResource","(I)[I",
(void*) android_content_AssetManager_getArrayIntResource },
+ { "getStyleAttributes","(I)[I",
+ (void*) android_content_AssetManager_getStyleAttributes },
// Bookkeeping.
{ "init", "(Z)V",
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index 85c2a09..84b7913 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -159,6 +159,11 @@
animator->setInterpolator(interpolator);
}
+static void setAllowRunningAsync(JNIEnv* env, jobject clazz, jlong animatorPtr, jboolean mayRunAsync) {
+ BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+ animator->setAllowRunningAsync(mayRunAsync);
+}
+
static void start(JNIEnv* env, jobject clazz, jlong animatorPtr, jobject finishListener) {
BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
if (finishListener) {
@@ -191,6 +196,7 @@
{ "nGetDuration", "(J)J", (void*) getDuration },
{ "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
{ "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
+ { "nSetAllowRunningAsync", "(JZ)V", (void*) setAllowRunningAsync },
{ "nStart", "(JLandroid/view/RenderNodeAnimator;)V", (void*) start },
{ "nEnd", "(J)V", (void*) end },
#endif
diff --git a/core/res/res/values-mcc310-mnc260/config.xml b/core/res/res/values-mcc310-mnc260/config.xml
index 2f9394a..f8fff3b 100644
--- a/core/res/res/values-mcc310-mnc260/config.xml
+++ b/core/res/res/values-mcc310-mnc260/config.xml
@@ -25,6 +25,11 @@
-->
<integer name="config_mobile_mtu">1440</integer>
+ <!-- Flag specifying whether VoLTE & VT should be available for carrier: independent of
+ carrier provisioning. If false: hard disabled. If true: then depends on carrier
+ provisioning, availability etc -->
+ <bool name="config_carrier_volte_vt_available">true</bool>
+
<!-- Values for GPS configuration (T-Mobile) -->
<string-array translatable="false" name="config_gpsParameters">
<item>CAPABILITEIS=0x33</item>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index cd5d55b..57ecd31 100644
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -38,10 +38,10 @@
be disabled) but individual Features can be disabled using ImsConfig.setFeatureValue() -->
<bool name="imsServiceAllowTurnOff">false</bool>
- <!-- Flag specifying whether VoLTE & VT should be allowed on device: independent of the
+ <!-- Flag specifying whether VoLTE & VT should be available for carrier: independent of
carrier provisioning. If false: hard disabled. If true: then depends on carrier
provisioning, availability etc -->
- <bool name="config_mobile_allow_volte_vt">false</bool>
+ <bool name="config_carrier_volte_vt_available">false</bool>
<bool name="config_auto_attach_data_on_creation">false</bool>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d1cc1fd..cf4064f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4341,6 +4341,8 @@
<attr name="minDate" format="string" />
<!-- The maximal date shown by this calendar view in mm/dd/yyyy format. -->
<attr name="maxDate" format="string" />
+ <!-- The first day of week according to {@link java.util.Calendar}. -->
+ <attr name="firstDayOfWeek" />
<!-- @hide The layout of the date picker. -->
<attr name="internalLayout" format="reference" />
<!-- @hide The layout of the legacy DatePicker. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e905a3a..10c2518e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -939,7 +939,7 @@
{@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. If the value of
documentLaunchModes is <code>never</code> then any use of
-.........{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
Intent.FLAG_ACTIVITY_NEW_DOCUMENT} to launch this activity will be ignored. -->
<attr name="documentLaunchMode">
<!-- The default mode, which will create a new task only when
@@ -994,6 +994,15 @@
TaskDescription to change labels, colors and icons in the recent task list. -->
<attr name="relinquishTaskIdentity" format="boolean" />
+ <!-- Indicate that it is okay for this activity be resumed while the previous
+ activity is in the process of pausing, without waiting for the previous pause
+ to complete. Use this with caution: your activity can not acquire any exclusive
+ resources (such as opening the camera or recording audio) when it launches, or it
+ may conflict with the previous activity and fail.
+
+ <p>The default value of this attribute is <code>false</code>. -->
+ <attr name="resumeWhilePausing" format="boolean" />
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -1678,6 +1687,7 @@
<attr name="maxRecents" />
<attr name="autoRemoveFromRecents" />
<attr name="relinquishTaskIdentity" />
+ <attr name="resumeWhilePausing" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index d8e14a0..939cbf1 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -19,8 +19,8 @@
<color name="background_material_dark">#ff212121</color>
<color name="background_material_light">#fffafafa</color>
- <color name="ripple_material_light">#20444444</color>
- <color name="ripple_material_dark">#20ffffff</color>
+ <color name="ripple_material_light">#40000000</color>
+ <color name="ripple_material_dark">#40ffffff</color>
<color name="button_material_dark">#ff5a595b</color>
<color name="button_material_light">#ffd6d7d7</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c3d430b..f3b3077 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -771,6 +771,9 @@
specified -->
<string name="default_wallpaper_component" translatable="false">@null</string>
+ <!-- Component name of the built in wallpaper used to display bitmap wallpapers. This must not be null. -->
+ <string name="image_wallpaper_component" translatable="false">com.android.systemui/com.android.systemui.ImageWallpaper</string>
+
<!-- True if WallpaperService is enabled -->
<bool name="config_enableWallpaperService">true</bool>
@@ -1671,10 +1674,13 @@
be disabled) but individual Features can be disabled using ImsConfig.setFeatureValue() -->
<bool name="imsServiceAllowTurnOff">true</bool>
- <!-- Flag specifying whether VoLTE & VT should be allowed on device: independent of the
+ <!-- Flag specifying whether VoLTE & VT is availasble on device -->
+ <bool name="config_device_volte_vt_available">false</bool>
+
+ <!-- Flag specifying whether VoLTE & VT should be available for carrier: independent of
carrier provisioning. If false: hard disabled. If true: then depends on carrier
provisioning, availability etc -->
- <bool name="config_mobile_allow_volte_vt">true</bool>
+ <bool name="config_carrier_volte_vt_available">false</bool>
<bool name="config_networkSamplingWakesDevice">true</bool>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a4c3474..5b047f7 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2266,7 +2266,7 @@
<public type="attr" name="windowReenterTransition" />
<public type="attr" name="windowSharedElementReturnTransition" />
<public type="attr" name="windowSharedElementReenterTransition" />
- <public type="attr" name="__removed1" />
+ <public type="attr" name="resumeWhilePausing" />
<public type="attr" name="datePickerMode"/>
<public type="attr" name="timePickerMode"/>
<public type="attr" name="inset" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1b0f9c4..01e6e76 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1634,6 +1634,7 @@
<java-symbol type="string" name="hardware" />
<java-symbol type="string" name="heavy_weight_notification" />
<java-symbol type="string" name="heavy_weight_notification_detail" />
+ <java-symbol type="string" name="image_wallpaper_component" />
<java-symbol type="string" name="input_method_binding_label" />
<java-symbol type="string" name="launch_warning_original" />
<java-symbol type="string" name="launch_warning_replace" />
@@ -1969,7 +1970,8 @@
<java-symbol type="attr" name="preferenceFragmentStyle" />
<java-symbol type="bool" name="skipHoldBeforeMerge" />
<java-symbol type="bool" name="imsServiceAllowTurnOff" />
- <java-symbol type="bool" name="config_mobile_allow_volte_vt" />
+ <java-symbol type="bool" name="config_device_volte_vt_available" />
+ <java-symbol type="bool" name="config_carrier_volte_vt_available" />
<java-symbol type="bool" name="useImsAlwaysForEmergencyCall" />
<java-symbol type="attr" name="touchscreenBlocksFocus" />
<java-symbol type="layout" name="resolver_list_with_default" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
index a3c5351..77e1c530 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
@@ -54,7 +54,7 @@
protected static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
protected static final int WIFI_SCAN_TIMEOUT = 50 * 1000; // 50 seconds
protected static final int SHORT_TIMEOUT = 5 * 1000; // 5 seconds
- protected static final long LONG_TIMEOUT = 50 * 1000; // 50 seconds
+ protected static final long LONG_TIMEOUT = 2 * 60 * 1000; // 2 minutes
protected static final long WIFI_CONNECTION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
// 2 minutes timer between wifi stop and start
protected static final long WIFI_STOP_START_INTERVAL = 2 * 60 * 1000; // 2 minutes
diff --git a/core/tests/inputmethodtests/run_core_inputmethod_test.sh b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
index ed8b7f7..a11e49b 100755
--- a/core/tests/inputmethodtests/run_core_inputmethod_test.sh
+++ b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
@@ -15,7 +15,7 @@
if [[ $rebuild == true ]]; then
make -j4 FrameworksCoreInputMethodTests
- TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreInputMethodTests.apk
+ TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreInputMethodTests/FrameworksCoreInputMethodTests.apk
COMMAND="adb install -r $TESTAPP"
echo $COMMAND
$COMMAND
diff --git a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
index cc4a7c4..b6a03d9 100644
--- a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
+++ b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
@@ -26,14 +26,12 @@
import java.util.Objects;
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_FULLY_VISIBLE;
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_INVISIBLE;
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_NOT_FEASIBLE;
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_PARTIALLY_VISIBLE;
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_UNSPECIFIED;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL;
public class CursorAnchorInfoTest extends InstrumentationTestCase {
- private static final RectF[] MANY_RECTS = new RectF[] {
+ private static final RectF[] MANY_BOUNDS = new RectF[] {
new RectF(101.0f, 201.0f, 301.0f, 401.0f),
new RectF(102.0f, 202.0f, 302.0f, 402.0f),
new RectF(103.0f, 203.0f, 303.0f, 403.0f),
@@ -55,25 +53,25 @@
new RectF(119.0f, 219.0f, 319.0f, 419.0f),
};
private static final int[] MANY_FLAGS_ARRAY = new int[] {
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_INVISIBLE,
- CHARACTER_RECT_TYPE_PARTIALLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_NOT_FEASIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE,
- CHARACTER_RECT_TYPE_NOT_FEASIBLE,
- CHARACTER_RECT_TYPE_NOT_FEASIBLE,
+ FLAG_HAS_INVISIBLE_REGION,
+ FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+ FLAG_HAS_VISIBLE_REGION,
+ FLAG_HAS_INVISIBLE_REGION,
+ FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
};
@SmallTest
@@ -82,11 +80,13 @@
final int SELECTION_END = 40;
final int COMPOSING_TEXT_START = 32;
final String COMPOSING_TEXT = "test";
- final boolean INSERTION_MARKER_CLIPPED = true;
+ final int INSERTION_MARKER_FLAGS =
+ FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
final float INSERTION_MARKER_HORIZONTAL = 10.5f;
final float INSERTION_MARKER_TOP = 100.1f;
final float INSERTION_MARKER_BASELINE = 110.4f;
final float INSERTION_MARKER_BOTOM = 111.0f;
+
Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
@@ -94,13 +94,13 @@
builder.setSelectionRange(SELECTION_START, SELECTION_END)
.setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT)
.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
- INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM,
- INSERTION_MARKER_CLIPPED)
+ INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS)
.setMatrix(TRANSFORM_MATRIX);
- for (int i = 0; i < MANY_RECTS.length; i++) {
- final RectF rect = MANY_RECTS[i];
+ for (int i = 0; i < MANY_BOUNDS.length; i++) {
+ final RectF bounds = MANY_BOUNDS[i];
final int flags = MANY_FLAGS_ARRAY[i];
- builder.addCharacterRect(i, rect.left, rect.top, rect.right, rect.bottom, flags);
+ builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom,
+ flags);
}
final CursorAnchorInfo info = builder.build();
@@ -108,26 +108,24 @@
assertEquals(SELECTION_END, info.getSelectionEnd());
assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart());
assertTrue(TextUtils.equals(COMPOSING_TEXT, info.getComposingText()));
- assertTrue(info.isInsertionMarkerClipped());
+ assertEquals(INSERTION_MARKER_FLAGS, info.getInsertionMarkerFlags());
assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal());
assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop());
assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline());
assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom());
assertEquals(TRANSFORM_MATRIX, info.getMatrix());
- for (int i = 0; i < MANY_RECTS.length; i++) {
- final RectF expectedRect = MANY_RECTS[i];
- assertEquals(expectedRect, info.getCharacterRect(i));
+ for (int i = 0; i < MANY_BOUNDS.length; i++) {
+ final RectF expectedBounds = MANY_BOUNDS[i];
+ assertEquals(expectedBounds, info.getCharacterRect(i));
}
assertNull(info.getCharacterRect(-1));
- assertNull(info.getCharacterRect(MANY_RECTS.length + 1));
+ assertNull(info.getCharacterRect(MANY_BOUNDS.length + 1));
for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
final int expectedFlags = MANY_FLAGS_ARRAY[i];
assertEquals(expectedFlags, info.getCharacterRectFlags(i));
}
- assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED,
- info.getCharacterRectFlags(-1));
- assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED,
- info.getCharacterRectFlags(MANY_RECTS.length + 1));
+ assertEquals(0, info.getCharacterRectFlags(-1));
+ assertEquals(0, info.getCharacterRectFlags(MANY_BOUNDS.length + 1));
// Make sure that the builder can reproduce the same object.
final CursorAnchorInfo info2 = builder.build();
@@ -135,25 +133,24 @@
assertEquals(SELECTION_END, info2.getSelectionEnd());
assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart());
assertTrue(TextUtils.equals(COMPOSING_TEXT, info2.getComposingText()));
- assertTrue(info2.isInsertionMarkerClipped());
+ assertEquals(INSERTION_MARKER_FLAGS, info2.getInsertionMarkerFlags());
assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal());
assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop());
assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline());
assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom());
assertEquals(TRANSFORM_MATRIX, info2.getMatrix());
- for (int i = 0; i < MANY_RECTS.length; i++) {
- final RectF expectedRect = MANY_RECTS[i];
- assertEquals(expectedRect, info2.getCharacterRect(i));
+ for (int i = 0; i < MANY_BOUNDS.length; i++) {
+ final RectF expectedBounds = MANY_BOUNDS[i];
+ assertEquals(expectedBounds, info2.getCharacterRect(i));
}
assertNull(info2.getCharacterRect(-1));
- assertNull(info2.getCharacterRect(MANY_RECTS.length + 1));
+ assertNull(info2.getCharacterRect(MANY_BOUNDS.length + 1));
for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
final int expectedFlags = MANY_FLAGS_ARRAY[i];
assertEquals(expectedFlags, info2.getCharacterRectFlags(i));
}
- assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED, info2.getCharacterRectFlags(-1));
- assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED,
- info2.getCharacterRectFlags(MANY_RECTS.length + 1));
+ assertEquals(0, info2.getCharacterRectFlags(-1));
+ assertEquals(0, info2.getCharacterRectFlags(MANY_BOUNDS.length + 1));
assertEquals(info, info2);
assertEquals(info.hashCode(), info2.hashCode());
@@ -163,25 +160,24 @@
assertEquals(SELECTION_END, info3.getSelectionEnd());
assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart());
assertTrue(TextUtils.equals(COMPOSING_TEXT, info3.getComposingText()));
- assertTrue(info3.isInsertionMarkerClipped());
+ assertEquals(INSERTION_MARKER_FLAGS, info3.getInsertionMarkerFlags());
assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal());
assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop());
assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline());
assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom());
assertEquals(TRANSFORM_MATRIX, info3.getMatrix());
- for (int i = 0; i < MANY_RECTS.length; i++) {
- final RectF expectedRect = MANY_RECTS[i];
- assertEquals(expectedRect, info3.getCharacterRect(i));
+ for (int i = 0; i < MANY_BOUNDS.length; i++) {
+ final RectF expectedBounds = MANY_BOUNDS[i];
+ assertEquals(expectedBounds, info3.getCharacterRect(i));
}
assertNull(info3.getCharacterRect(-1));
- assertNull(info3.getCharacterRect(MANY_RECTS.length + 1));
+ assertNull(info3.getCharacterRect(MANY_BOUNDS.length + 1));
for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
final int expectedFlags = MANY_FLAGS_ARRAY[i];
assertEquals(expectedFlags, info3.getCharacterRectFlags(i));
}
- assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED, info3.getCharacterRectFlags(-1));
- assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED,
- info3.getCharacterRectFlags(MANY_RECTS.length + 1));
+ assertEquals(0, info3.getCharacterRectFlags(-1));
+ assertEquals(0, info3.getCharacterRectFlags(MANY_BOUNDS.length + 1));
assertEquals(info.hashCode(), info3.hashCode());
builder.reset();
@@ -190,7 +186,7 @@
assertEquals(-1, uninitializedInfo.getSelectionEnd());
assertEquals(-1, uninitializedInfo.getComposingTextStart());
assertNull(uninitializedInfo.getComposingText());
- assertFalse(uninitializedInfo.isInsertionMarkerClipped());
+ assertEquals(0, uninitializedInfo.getInsertionMarkerFlags());
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal());
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop());
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline());
@@ -218,7 +214,7 @@
final int SELECTION_END1 = 7;
final String COMPOSING_TEXT1 = "0123456789";
final int COMPOSING_TEXT_START1 = 0;
- final boolean INSERTION_MARKER_CLIPPED1 = true;
+ final int INSERTION_MARKER_FLAGS1 = FLAG_HAS_VISIBLE_REGION;
final float INSERTION_MARKER_HORIZONTAL1 = 10.5f;
final float INSERTION_MARKER_TOP1 = 100.1f;
final float INSERTION_MARKER_BASELINE1 = 110.4f;
@@ -227,7 +223,8 @@
final int SELECTION_END2 = 8;
final String COMPOSING_TEXT2 = "9876543210";
final int COMPOSING_TEXT_START2 = 3;
- final boolean INSERTION_MARKER_CLIPPED2 = false;
+ final int INSERTION_MARKER_FLAGS2 =
+ FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
final float INSERTION_MARKER_HORIZONTAL2 = 14.5f;
final float INSERTION_MARKER_TOP2 = 200.1f;
final float INSERTION_MARKER_BASELINE2 = 210.4f;
@@ -265,10 +262,10 @@
assertEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
Float.NaN, Float.NaN, Float.NaN, Float.NaN,
- INSERTION_MARKER_CLIPPED1).build(),
+ INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
Float.NaN, Float.NaN, Float.NaN, Float.NaN,
- INSERTION_MARKER_CLIPPED1).build());
+ INSERTION_MARKER_FLAGS1).build());
// Check Matrix.
assertEquals(
@@ -290,74 +287,74 @@
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build(),
+ INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build());
+ INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
Float.NaN, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build(),
+ INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build());
+ INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build(),
+ INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build());
+ INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build(),
+ INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP2,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build());
+ INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build(),
+ INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE2, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build());
+ INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build(),
+ INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build());
+ INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build(),
+ INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM2,
- INSERTION_MARKER_CLIPPED1).build());
+ INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED1).build(),
+ INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_CLIPPED2).build());
+ INSERTION_MARKER_FLAGS2).build());
}
@SmallTest
@@ -394,7 +391,7 @@
final int SELECTION_END = 40;
final int COMPOSING_TEXT_START = 32;
final String COMPOSING_TEXT = "test";
- final boolean INSERTION_MARKER_CLIPPED = true;
+ final int INSERTION_MARKER_FLAGS = FLAG_HAS_VISIBLE_REGION;
final float INSERTION_MARKER_HORIZONTAL = 10.5f;
final float INSERTION_MARKER_TOP = 100.1f;
final float INSERTION_MARKER_BASELINE = 110.4f;
@@ -416,7 +413,7 @@
}
builder.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
- INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_CLIPPED);
+ INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS);
try {
// Coordinate transformation matrix is required if no positional information is
// specified.
@@ -438,19 +435,10 @@
}
@SmallTest
- public void testBuilderAddCharacterRect() throws Exception {
+ public void testBuilderAddCharacterBounds() throws Exception {
// A negative index should be rejected.
try {
- new Builder().addCharacterRect(-1, 0.0f, 0.0f, 0.0f, 0.0f,
- CHARACTER_RECT_TYPE_FULLY_VISIBLE);
- assertTrue(false);
- } catch (IllegalArgumentException ex) {
- }
-
- // CHARACTER_RECT_TYPE_UNSPECIFIED is not allowed.
- try {
- new Builder().addCharacterRect(0, 0.0f, 0.0f, 0.0f, 0.0f,
- CHARACTER_RECT_TYPE_UNSPECIFIED);
+ new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION);
assertTrue(false);
} catch (IllegalArgumentException ex) {
}
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 43922b8..6f95f91 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -22,6 +22,7 @@
import android.animation.TimeInterpolator;
import android.graphics.Canvas;
import android.graphics.CanvasProperty;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
@@ -58,7 +59,7 @@
private final Rect mBounds;
/** Full-opacity color for drawing this ripple. */
- private int mColor;
+ private int mColorOpaque;
/** Maximum ripple radius. */
private float mOuterRadius;
@@ -120,7 +121,7 @@
}
public void setup(int maxRadius, int color, float density) {
- mColor = color | 0xFF000000;
+ mColorOpaque = color | 0xFF000000;
if (maxRadius != RippleDrawable.RADIUS_AUTO) {
mHasMaxRadius = true;
@@ -236,6 +237,9 @@
if (N > 0) {
cancelHardwareAnimations(false);
+ // We canceled old animations, but we're about to run new ones.
+ mHardwareAnimating = true;
+
for (int i = 0; i < N; i++) {
pendingAnimations.get(i).setTarget(c);
pendingAnimations.get(i).start();
@@ -253,9 +257,8 @@
private boolean drawSoftware(Canvas c, Paint p) {
boolean hasContent = false;
- // Cache the paint alpha so we can restore it later.
- final int paintAlpha = p.getAlpha();
- final int alpha = (int) (paintAlpha * mOpacity + 0.5f);
+ p.setColor(mColorOpaque);
+ final int alpha = (int) (255 * mOpacity + 0.5f);
final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
if (alpha > 0 && radius > 0) {
final float x = MathUtils.lerp(
@@ -268,8 +271,6 @@
hasContent = true;
}
- p.setAlpha(paintAlpha);
-
return hasContent;
}
@@ -369,7 +370,7 @@
final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
final Paint paint = getTempPaint();
paint.setAntiAlias(true);
- paint.setColor(mColor);
+ paint.setColor(mColorOpaque);
paint.setAlpha((int) (255 * mOpacity + 0.5f));
paint.setStyle(Style.FILL);
mPropPaint = CanvasProperty.createPaint(paint);
@@ -402,6 +403,12 @@
mHardwareAnimating = true;
+ // Set up the software values to match the hardware end values.
+ mOpacity = 0;
+ mTweenX = 1;
+ mTweenY = 1;
+ mTweenRadius = 1;
+
invalidateSelf();
}
@@ -412,7 +419,7 @@
public void jump() {
mCanceled = true;
endSoftwareAnimations();
- endHardwareAnimations();
+ cancelHardwareAnimations(true);
mCanceled = false;
}
@@ -438,24 +445,6 @@
}
}
- private void endHardwareAnimations() {
- final ArrayList<RenderNodeAnimator> runningAnimations = mRunningAnimations;
- final int N = runningAnimations.size();
- for (int i = 0; i < N; i++) {
- runningAnimations.get(i).end();
- }
- runningAnimations.clear();
-
- // Abort any pending animations. Since we always have a completion
- // listener on a pending animation, we also need to remove ourselves.
- if (!mPendingAnimations.isEmpty()) {
- mPendingAnimations.clear();
- removeSelf();
- }
-
- mHardwareAnimating = false;
- }
-
private Paint getTempPaint() {
if (mTempPaint == null) {
mTempPaint = new Paint();
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 80ecea3..bc6f5fb 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -22,6 +22,7 @@
import android.animation.TimeInterpolator;
import android.graphics.Canvas;
import android.graphics.CanvasProperty;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
@@ -46,8 +47,6 @@
private static final float WAVE_OUTER_SIZE_INFLUENCE_MAX = 200f;
private static final float WAVE_OUTER_SIZE_INFLUENCE_MIN = 40f;
- private static final long RIPPLE_ENTER_DELAY = 80;
-
// Hardware animators.
private final ArrayList<RenderNodeAnimator> mRunningAnimations =
new ArrayList<RenderNodeAnimator>();
@@ -60,7 +59,10 @@
private final Rect mBounds;
/** Full-opacity color for drawing this ripple. */
- private int mColor;
+ private int mColorOpaque;
+
+ /** Maximum alpha value for drawing this ripple. */
+ private int mColorAlpha;
/** Maximum ripple radius. */
private float mOuterRadius;
@@ -103,7 +105,8 @@
}
public void setup(int maxRadius, int color, float density) {
- mColor = color | 0xFF000000;
+ mColorOpaque = color | 0xFF000000;
+ mColorAlpha = Color.alpha(color);
if (maxRadius != RippleDrawable.RADIUS_AUTO) {
mHasMaxRadius = true;
@@ -159,6 +162,11 @@
return hasContent;
}
+ public boolean shouldDraw() {
+ final int outerAlpha = (int) (mColorAlpha * mOuterOpacity + 0.5f);
+ return mCanUseHardware && mHardwareAnimating || outerAlpha > 0 && mOuterRadius > 0;
+ }
+
private boolean drawHardware(HardwareCanvas c) {
// If we have any pending hardware animations, cancel any running
// animations and start those now.
@@ -167,6 +175,9 @@
if (N > 0) {
cancelHardwareAnimations(false);
+ // We canceled old animations, but we're about to run new ones.
+ mHardwareAnimating = true;
+
for (int i = 0; i < N; i++) {
pendingAnimations.get(i).setTarget(c);
pendingAnimations.get(i).start();
@@ -184,10 +195,8 @@
private boolean drawSoftware(Canvas c, Paint p) {
boolean hasContent = false;
- // Cache the paint alpha so we can restore it later.
- final int paintAlpha = p.getAlpha();
-
- final int outerAlpha = (int) (paintAlpha * mOuterOpacity + 0.5f);
+ p.setColor(mColorOpaque);
+ final int outerAlpha = (int) (mColorAlpha * mOuterOpacity + 0.5f);
if (outerAlpha > 0 && mOuterRadius > 0) {
p.setAlpha(outerAlpha);
p.setStyle(Style.FILL);
@@ -195,8 +204,6 @@
hasContent = true;
}
- p.setAlpha(paintAlpha);
-
return hasContent;
}
@@ -248,25 +255,25 @@
// Determine at what time the inner and outer opacity intersect.
// inner(t) = mOpacity - t * WAVE_OPACITY_DECAY_VELOCITY / 1000
// outer(t) = mOuterOpacity + t * WAVE_OUTER_OPACITY_VELOCITY / 1000
- final int outerInflection = Math.max(0, (int) (1000 * (1 - mOuterOpacity)
+ final int inflectionDuration = Math.max(0, (int) (1000 * (1 - mOuterOpacity)
/ (WAVE_OPACITY_DECAY_VELOCITY + outerOpacityVelocity) + 0.5f));
- final int inflectionOpacity = (int) (255 * (mOuterOpacity + outerInflection
- * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
+ final int inflectionOpacity = (int) (mColorAlpha * (mOuterOpacity
+ + inflectionDuration * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
if (mCanUseHardware) {
- exitHardware(opacityDuration, outerInflection, inflectionOpacity);
+ exitHardware(opacityDuration, inflectionDuration, inflectionOpacity);
} else {
- exitSoftware(opacityDuration, outerInflection, inflectionOpacity);
+ exitSoftware(opacityDuration, inflectionDuration, inflectionOpacity);
}
}
- private void exitHardware(int opacityDuration, int outerInflection, int inflectionOpacity) {
+ private void exitHardware(int opacityDuration, int inflectionDuration, int inflectionOpacity) {
mPendingAnimations.clear();
final Paint outerPaint = getTempPaint();
outerPaint.setAntiAlias(true);
- outerPaint.setColor(mColor);
- outerPaint.setAlpha((int) (255 * mOuterOpacity + 0.5f));
+ outerPaint.setColor(mColorOpaque);
+ outerPaint.setAlpha((int) (mColorAlpha * mOuterOpacity + 0.5f));
outerPaint.setStyle(Style.FILL);
mPropOuterPaint = CanvasProperty.createPaint(outerPaint);
mPropOuterRadius = CanvasProperty.createFloat(mOuterRadius);
@@ -274,21 +281,21 @@
mPropOuterY = CanvasProperty.createFloat(mOuterY);
final RenderNodeAnimator outerOpacityAnim;
- if (outerInflection > 0) {
+ if (inflectionDuration > 0) {
// Outer opacity continues to increase for a bit.
- outerOpacityAnim = new RenderNodeAnimator(
- mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, inflectionOpacity);
- outerOpacityAnim.setDuration(outerInflection);
+ outerOpacityAnim = new RenderNodeAnimator(mPropOuterPaint,
+ RenderNodeAnimator.PAINT_ALPHA, inflectionOpacity);
+ outerOpacityAnim.setDuration(inflectionDuration);
outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
// Chain the outer opacity exit animation.
- final int outerDuration = opacityDuration - outerInflection;
+ final int outerDuration = opacityDuration - inflectionDuration;
if (outerDuration > 0) {
final RenderNodeAnimator outerFadeOutAnim = new RenderNodeAnimator(
mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
outerFadeOutAnim.setDuration(outerDuration);
outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
- outerFadeOutAnim.setStartDelay(outerInflection);
+ outerFadeOutAnim.setStartDelay(inflectionDuration);
outerFadeOutAnim.setStartValue(inflectionOpacity);
outerFadeOutAnim.addListener(mAnimationListener);
@@ -320,7 +327,7 @@
*/
public void jump() {
endSoftwareAnimations();
- endHardwareAnimations();
+ cancelHardwareAnimations(true);
}
private void endSoftwareAnimations() {
@@ -330,23 +337,6 @@
}
}
- private void endHardwareAnimations() {
- final ArrayList<RenderNodeAnimator> runningAnimations = mRunningAnimations;
- final int N = runningAnimations.size();
- for (int i = 0; i < N; i++) {
- runningAnimations.get(i).end();
- }
- runningAnimations.clear();
-
- // Abort any pending animations. Since we always have a completion
- // listener on a pending animation, we also need to remove ourselves.
- if (!mPendingAnimations.isEmpty()) {
- mPendingAnimations.clear();
- }
-
- mHardwareAnimating = false;
- }
-
private Paint getTempPaint() {
if (mTempPaint == null) {
mTempPaint = new Paint();
@@ -354,18 +344,18 @@
return mTempPaint;
}
- private void exitSoftware(int opacityDuration, int outerInflection, int inflectionOpacity) {
+ private void exitSoftware(int opacityDuration, int inflectionDuration, int inflectionOpacity) {
final ObjectAnimator outerOpacityAnim;
- if (outerInflection > 0) {
+ if (inflectionDuration > 0) {
// Outer opacity continues to increase for a bit.
outerOpacityAnim = ObjectAnimator.ofFloat(this,
"outerOpacity", inflectionOpacity / 255.0f);
outerOpacityAnim.setAutoCancel(true);
- outerOpacityAnim.setDuration(outerInflection);
+ outerOpacityAnim.setDuration(inflectionDuration);
outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
// Chain the outer opacity exit animation.
- final int outerDuration = opacityDuration - outerInflection;
+ final int outerDuration = opacityDuration - inflectionDuration;
if (outerDuration > 0) {
outerOpacityAnim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -446,14 +436,4 @@
mHardwareAnimating = false;
}
};
-
- /**
- * Interpolator with a smooth log deceleration
- */
- private static final class LogInterpolator implements TimeInterpolator {
- @Override
- public float getInterpolation(float input) {
- return 1 - (float) Math.pow(400, -input * 1.4);
- }
- }
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index fa762b7..b05fb61 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -242,7 +242,7 @@
@Override
protected boolean onStateChange(int[] stateSet) {
- super.onStateChange(stateSet);
+ final boolean changed = super.onStateChange(stateSet);
boolean enabled = false;
boolean pressed = false;
@@ -263,19 +263,7 @@
setRippleActive(enabled && pressed);
setBackgroundActive(focused || (enabled && pressed));
- // Update the paint color. Only applicable when animated in software.
- if (mRipplePaint != null && mState.mColor != null) {
- final ColorStateList stateList = mState.mColor;
- final int newColor = stateList.getColorForState(stateSet, 0);
- final int oldColor = mRipplePaint.getColor();
- if (oldColor != newColor) {
- mRipplePaint.setColor(newColor);
- invalidateSelf();
- return true;
- }
- }
-
- return false;
+ return changed;
}
private void setRippleActive(boolean active) {
@@ -587,6 +575,10 @@
ripples[i].onHotspotBoundsChanged();
}
+ if (mRipple != null) {
+ mRipple.onHotspotBoundsChanged();
+ }
+
if (mBackground != null) {
mBackground.onHotspotBoundsChanged();
}
@@ -617,17 +609,23 @@
final boolean drawNonMaskContent = mLayerState.mNum > (hasMask ? 1 : 0);
final boolean drawMask = hasMask && mMask.getOpacity() != PixelFormat.OPAQUE;
final Rect bounds = getDirtyBounds();
+ final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipRect(bounds);
// If we have content, draw it into a layer first.
- final int contentLayer = drawNonMaskContent ?
- drawContentLayer(canvas, bounds, SRC_OVER) : -1;
+ final int contentLayer;
+ if (drawNonMaskContent) {
+ contentLayer = drawContentLayer(canvas, bounds, SRC_OVER);
+ } else {
+ contentLayer = -1;
+ }
// Next, try to draw the ripples (into a layer if necessary). If we need
// to mask against the underlying content, set the xfermode to SRC_ATOP.
final PorterDuffXfermode xfermode = (hasMask || !drawNonMaskContent) ? SRC_OVER : SRC_ATOP;
// If we have a background and a non-opaque mask, draw the masking layer.
- final int backgroundLayer = drawBackgroundLayer(canvas, bounds, xfermode);
+ final int backgroundLayer = drawBackgroundLayer(canvas, bounds, xfermode, drawMask);
if (backgroundLayer >= 0) {
if (drawMask) {
drawMaskingLayer(canvas, bounds, DST_IN);
@@ -644,10 +642,13 @@
canvas.restoreToCount(rippleLayer);
}
- // Composite the layers if needed.
- if (contentLayer >= 0) {
- canvas.restoreToCount(contentLayer);
+ // If we failed to draw anything, at least draw a color so that
+ // invalidation works correctly.
+ if (contentLayer < 0 && backgroundLayer < 0 && rippleLayer < 0) {
+ canvas.drawColor(Color.TRANSPARENT);
}
+
+ canvas.restoreToCount(saveCount);
}
/**
@@ -711,81 +712,29 @@
return restoreToCount;
}
- private int drawBackgroundLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
- // Separate the ripple color and alpha channel. The alpha will be
- // applied when we merge the ripples down to the canvas.
- final int rippleARGB;
- if (mState.mColor != null) {
- rippleARGB = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
- } else {
- rippleARGB = Color.TRANSPARENT;
+ private int drawBackgroundLayer(
+ Canvas canvas, Rect bounds, PorterDuffXfermode mode, boolean drawMask) {
+ int saveCount = -1;
+
+ if (mBackground != null && mBackground.shouldDraw()) {
+ // TODO: We can avoid saveLayer here if we push the xfermode into
+ // the background's render thread animator at exit() time.
+ if (drawMask || mode != SRC_OVER) {
+ saveCount = canvas.saveLayer(bounds.left, bounds.top, bounds.right,
+ bounds.bottom, getMaskingPaint(mode));
+ }
+
+ final float x = mHotspotBounds.exactCenterX();
+ final float y = mHotspotBounds.exactCenterY();
+ canvas.translate(x, y);
+ mBackground.draw(canvas, getRipplePaint());
+ canvas.translate(-x, -y);
}
- if (mRipplePaint == null) {
- mRipplePaint = new Paint();
- mRipplePaint.setAntiAlias(true);
- }
-
- final int rippleAlpha = Color.alpha(rippleARGB);
- final Paint ripplePaint = mRipplePaint;
- ripplePaint.setColor(rippleARGB);
- ripplePaint.setAlpha(0xFF);
-
- boolean drewRipples = false;
- int restoreToCount = -1;
- int restoreTranslate = -1;
-
- // Draw background.
- final RippleBackground background = mBackground;
- if (background != null) {
- // If we're masking the ripple layer, make sure we have a layer
- // first. This will merge SRC_OVER (directly) onto the canvas.
- final Paint maskingPaint = getMaskingPaint(mode);
- maskingPaint.setAlpha(rippleAlpha);
- restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
- bounds.right, bounds.bottom, maskingPaint);
-
- restoreTranslate = canvas.save();
- // Translate the canvas to the current hotspot bounds.
- canvas.translate(mHotspotBounds.exactCenterX(), mHotspotBounds.exactCenterY());
-
- drewRipples = background.draw(canvas, ripplePaint);
- }
-
- // Always restore the translation.
- if (restoreTranslate >= 0) {
- canvas.restoreToCount(restoreTranslate);
- }
-
- // If we created a layer with no content, merge it immediately.
- if (restoreToCount >= 0 && !drewRipples) {
- canvas.restoreToCount(restoreToCount);
- restoreToCount = -1;
- }
-
- return restoreToCount;
+ return saveCount;
}
private int drawRippleLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
- // Separate the ripple color and alpha channel. The alpha will be
- // applied when we merge the ripples down to the canvas.
- final int rippleARGB;
- if (mState.mColor != null) {
- rippleARGB = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
- } else {
- rippleARGB = Color.TRANSPARENT;
- }
-
- if (mRipplePaint == null) {
- mRipplePaint = new Paint();
- mRipplePaint.setAntiAlias(true);
- }
-
- final int rippleAlpha = Color.alpha(rippleARGB);
- final Paint ripplePaint = mRipplePaint;
- ripplePaint.setColor(rippleARGB);
- ripplePaint.setAlpha(0xFF);
-
boolean drewRipples = false;
int restoreToCount = -1;
int restoreTranslate = -1;
@@ -807,16 +756,21 @@
// first. This will merge SRC_OVER (directly) onto the canvas.
if (restoreToCount < 0) {
final Paint maskingPaint = getMaskingPaint(mode);
- maskingPaint.setAlpha(rippleAlpha);
+ final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
+ final int alpha = Color.alpha(color);
+ maskingPaint.setAlpha(alpha / 2);
+
+ // TODO: We can avoid saveLayer here if we're only drawing one
+ // ripple and we don't have content or a translucent mask.
restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
bounds.right, bounds.bottom, maskingPaint);
- restoreTranslate = canvas.save();
// Translate the canvas to the current hotspot bounds.
+ restoreTranslate = canvas.save();
canvas.translate(mHotspotBounds.exactCenterX(), mHotspotBounds.exactCenterY());
}
- drewRipples |= ripple.draw(canvas, ripplePaint);
+ drewRipples |= ripple.draw(canvas, getRipplePaint());
}
// Always restore the translation.
@@ -845,6 +799,14 @@
return restoreToCount;
}
+ private Paint getRipplePaint() {
+ if (mRipplePaint == null) {
+ mRipplePaint = new Paint();
+ mRipplePaint.setAntiAlias(true);
+ }
+ return mRipplePaint;
+ }
+
private Paint getMaskingPaint(PorterDuffXfermode xfermode) {
if (mMaskingPaint == null) {
mMaskingPaint = new Paint();
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 1ab6235b..35a4a09 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -53,6 +53,10 @@
mListener = listener;
}
AnimationListener* listener() { return mListener.get(); }
+ ANDROID_API void setAllowRunningAsync(bool mayRunAsync) {
+ mMayRunAsync = mayRunAsync;
+ }
+ bool mayRunAsync() { return mMayRunAsync; }
ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); }
ANDROID_API void end() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); }
@@ -101,6 +105,7 @@
nsecs_t mStartTime;
nsecs_t mDuration;
nsecs_t mStartDelay;
+ bool mMayRunAsync;
sp<AnimationListener> mListener;
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index e06d800..c28fb88 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -90,6 +90,9 @@
if (animator->isRunning()) {
mInfo.out.hasAnimations = true;
}
+ if (CC_UNLIKELY(!animator->mayRunAsync())) {
+ mInfo.out.requiresUiRedraw = true;
+ }
}
return remove;
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 308f783..9b644f4 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -530,7 +530,6 @@
}
}
- /** @hide */
public static final Parcelable.Creator<AudioAttributes> CREATOR
= new Parcelable.Creator<AudioAttributes>() {
/**
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9ee7027..e3b8985 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -243,7 +243,7 @@
* or unplugged.
* An integer value of 1 indicates a plugged-in state, 0 is unplugged.
*/
- public static final String EXTRA_AUDIO_PLUG_STATE = "android.media.extra.audio_plug_state";
+ public static final String EXTRA_AUDIO_PLUG_STATE = "android.media.extra.AUDIO_PLUG_STATE";
/**
* Extra used in {@link #ACTION_HDMI_AUDIO_PLUG} to define the maximum number of channels
@@ -251,7 +251,7 @@
* The corresponding integer value is only available when the device is plugged in (as expressed
* by {@link #EXTRA_AUDIO_PLUG_STATE}).
*/
- public static final String EXTRA_MAX_CHANNEL_COUNT = "android.media.extra.max_channel_count";
+ public static final String EXTRA_MAX_CHANNEL_COUNT = "android.media.extra.MAX_CHANNEL_COUNT";
/**
* Extra used in {@link #ACTION_HDMI_AUDIO_PLUG} to define the audio encodings supported by
@@ -261,7 +261,7 @@
* {@link AudioFormat} (for instance see {@link AudioFormat#ENCODING_PCM_16BIT}). Use
* {@link android.content.Intent#getIntArrayExtra(String)} to retrieve the encoding values.
*/
- public static final String EXTRA_ENCODINGS = "android.media.extra.encodings";
+ public static final String EXTRA_ENCODINGS = "android.media.extra.ENCODINGS";
/**
* Broadcast Action: An analog audio speaker/headset plugged in or unplugged.
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 72a6f88..317d472 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -389,8 +389,11 @@
}
/**
- * Informs the application that video is available and the playback of the TV stream has
- * been started.
+ * Informs the application that the video is now available for watching. This is primarily
+ * used to signal the application to unblock the screen. The TV input service must call this
+ * method as soon as the content rendered onto its surface gets ready for viewing.
+ *
+ * @see #notifyVideoUnavailable
*/
public void notifyVideoAvailable() {
runOnMainThread(new Runnable() {
@@ -407,16 +410,18 @@
}
/**
- * Informs the application that video is not available, so the TV input cannot continue
- * playing the TV stream.
+ * Informs the application that the video became unavailable for some reason. This is
+ * primarily used to signal the application to block the screen not to show any intermittent
+ * video artifacts.
*
- * @param reason The reason that the TV input stopped the playback:
- * <ul>
- * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
- * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
- * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL}
- * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING}
- * </ul>
+ * @param reason The reason why the video became unavailable:
+ * <ul>
+ * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
+ * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
+ * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL}
+ * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING}
+ * </ul>
+ * @see #notifyVideoAvailable
*/
public void notifyVideoUnavailable(final int reason) {
if (reason < TvInputManager.VIDEO_UNAVAILABLE_REASON_START
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d4ebb01..b94a258 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -176,6 +176,7 @@
android:theme="@style/RecentsStyle"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
+ android:resumeWhilePausing="true"
android:exported="true">
<intent-filter>
<action android:name="com.android.systemui.TOGGLE_RECENTS" />
@@ -196,6 +197,8 @@
android:label="@string/accessibility_desc_recent_apps"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
+ android:stateNotNeeded="true"
+ android:resumeWhilePausing="true"
android:theme="@style/RecentsTheme">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5399a39..0e18979 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -137,7 +137,7 @@
<!-- The min animation duration for animating the task in when transitioning from home. -->
<integer name="recents_animate_task_enter_from_home_duration">275</integer>
<!-- The animation stagger to apply to each task animation when transitioning from home. -->
- <integer name="recents_animate_task_enter_from_home_delay">150</integer>
+ <integer name="recents_animate_task_enter_from_home_delay">10</integer>
<!-- The short duration when animating in/out the lock to app button. -->
<integer name="recents_animate_lock_to_app_button_short_duration">150</integer>
<!-- The long duration when animating in/out the lock to app button. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index b7434fd..34430d9 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -203,8 +203,7 @@
Drawable icon = getFullResIcon(td.resolveInfo, pm);
if (td.userId != UserHandle.myUserId()) {
// Need to badge the icon
- icon = mContext.getPackageManager().getUserBadgedDrawableForDensity(icon,
- new UserHandle(td.userId), null, 0);
+ icon = mContext.getPackageManager().getUserBadgedIcon(icon, new UserHandle(td.userId));
}
if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
+ td + ": " + thumbnail);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 2d114c0..5fa9fa4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -16,10 +16,12 @@
package com.android.systemui.recents;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -385,9 +387,9 @@
toTask);
if (toTransform != null && toTask.key != null) {
Rect toTaskRect = toTransform.rect;
-
- // XXX: Reduce the memory usage the to the task bar height
- Bitmap thumbnail = Bitmap.createBitmap(toTaskRect.width(), toTaskRect.height(),
+ 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);
@@ -401,7 +403,8 @@
mStartAnimationTriggered = false;
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mStatusBarView,
- thumbnail, toTaskRect.left, toTaskRect.top, this);
+ thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
+ toTaskRect.height(), this);
}
// If both the screenshot and thumbnail fails, then just fall back to the default transition
@@ -562,11 +565,34 @@
public void onAnimationStarted() {
// Notify recents to start the enter animation
if (!mStartAnimationTriggered) {
+ // There can be a race condition between the start animation callback and
+ // the start of the new activity (where we register the receiver that listens
+ // to this broadcast, so we add our own receiver and if that gets called, then
+ // we know the activity has not yet started and we can retry sending the broadcast.
+ BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() == Activity.RESULT_OK) {
+ mStartAnimationTriggered = true;
+ return;
+ }
+
+ // Schedule for the broadcast to be sent again after some time
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ onAnimationStarted();
+ }
+ }, 75);
+ }
+ };
+
+ // Send the broadcast to notify Recents that the animation has started
Intent intent = new Intent(ACTION_START_ENTER_ANIMATION);
intent.setPackage(mContext.getPackageName());
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- mStartAnimationTriggered = true;
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
+ fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index ec7799a..8f92027 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -146,6 +146,9 @@
ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
onEnterAnimationTriggered();
+ // Notify the fallback receiver that we have successfully got the broadcast
+ // See AlternateRecentsComponent.onAnimationStarted()
+ setResultCode(Activity.RESULT_OK);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 07a42bd..887cbac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -356,7 +356,7 @@
*/
public Drawable getBadgedIcon(Drawable icon, int userId) {
if (userId != UserHandle.myUserId()) {
- icon = mPm.getUserBadgedDrawableForDensity(icon, new UserHandle(userId), null, 0);
+ icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
}
return icon;
}
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 0c6e7b6..1bfb41f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -413,10 +413,8 @@
final SystemServicesProxy ssp =
RecentsTaskLoader.getInstance().getSystemServicesProxy();
ActivityOptions opts = null;
- int thumbnailWidth = transform.rect.width();
- int thumbnailHeight = transform.rect.height();
- if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
- task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
+ 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
@@ -424,7 +422,11 @@
tv.unsetFocusedTask();
}
- b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888);
+ 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 {
@@ -435,7 +437,7 @@
}
} else {
// Notify the system to skip the thumbnail layer by using an ALPHA_8 bitmap
- b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ALPHA_8);
+ b = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
}
ActivityOptions.OnAnimationStartedListener animStartedListener = null;
if (lockToTask) {
@@ -456,7 +458,8 @@
};
}
opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
- b, offsetX, offsetY, animStartedListener);
+ b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
+ animStartedListener);
}
final ActivityOptions launchOpts = opts;
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 eecc170..49aa52b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -839,25 +839,28 @@
@Override
public void onClick(final View v) {
- // We purposely post the handler delayed to allow for the touch feedback to draw
final TaskView tv = this;
- postDelayed(new Runnable() {
- @Override
- public void run() {
- if (Constants.DebugFlags.App.EnableTaskFiltering && v == mHeaderView.mApplicationIcon) {
- mCb.onTaskViewAppIconClicked(tv);
- } else if (v == mHeaderView.mDismissButton) {
- dismissTask();
- } else {
- if (v == mActionButtonView) {
- // Reset the translation of the action button before we animate it out
- mActionButtonView.setTranslationZ(0f);
+ final boolean delayViewClick = (v != this);
+ if (delayViewClick) {
+ // We purposely post the handler delayed to allow for the touch feedback to draw
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (Constants.DebugFlags.App.EnableTaskFiltering && v == mHeaderView.mApplicationIcon) {
+ mCb.onTaskViewAppIconClicked(tv);
+ } else if (v == mHeaderView.mDismissButton) {
+ dismissTask();
}
- mCb.onTaskViewClicked(tv, tv.getTask(),
- (v == mFooterView || v == mActionButtonView));
}
+ }, 125);
+ } else {
+ if (v == mActionButtonView) {
+ // Reset the translation of the action button before we animate it out
+ mActionButtonView.setTranslationZ(0f);
}
- }, 125);
+ mCb.onTaskViewClicked(tv, tv.getTask(),
+ (v == mFooterView || v == mActionButtonView));
+ }
}
/**** View.OnLongClickListener Implementation ****/
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6ddad41..1397ea4 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6278,12 +6278,12 @@
}
@Override
- public final void activityPaused(IBinder token, PersistableBundle persistentState) {
+ public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
- stack.activityPausedLocked(token, false, persistentState);
+ stack.activityPausedLocked(token, false);
}
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 77b4cc9..e043f03 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -698,12 +698,12 @@
case ActivityOptions.ANIM_SCALE_UP:
service.mWindowManager.overridePendingAppTransitionScaleUp(
pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getStartWidth(), pendingOptions.getStartHeight());
+ pendingOptions.getWidth(), pendingOptions.getHeight());
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
- pendingOptions.getStartX()+pendingOptions.getStartWidth(),
- pendingOptions.getStartY()+pendingOptions.getStartHeight()));
+ pendingOptions.getStartX()+pendingOptions.getWidth(),
+ pendingOptions.getStartY()+pendingOptions.getHeight()));
}
break;
case ActivityOptions.ANIM_THUMBNAIL_SCALE_UP:
@@ -728,15 +728,14 @@
service.mWindowManager.overridePendingAppTransitionAspectScaledThumb(
pendingOptions.getThumbnail(),
pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight(),
pendingOptions.getOnAnimationStartListener(),
(animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP));
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
- pendingOptions.getStartX()
- + pendingOptions.getThumbnail().getWidth(),
- pendingOptions.getStartY()
- + pendingOptions.getThumbnail().getHeight()));
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
}
break;
default:
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 81c379a..6168546 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -284,7 +284,7 @@
if (r.app != null) {
mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
}
- activityPausedLocked(r.appToken, true, r.persistentState);
+ activityPausedLocked(r.appToken, true);
}
} break;
case LAUNCH_TICK_MSG: {
@@ -712,7 +712,7 @@
// Still have something resumed; can't sleep until it is paused.
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause " + mResumedActivity);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
- startPausingLocked(false, true);
+ startPausingLocked(false, true, false, false);
return true;
}
if (mPausingActivity != null) {
@@ -790,22 +790,38 @@
return null;
}
- final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+ /**
+ * Start pausing the currently resumed activity. It is an error to call this if there
+ * is already an activity being paused or there is no resumed activity.
+ *
+ * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+ * @param uiSleeping True if this is happening with the user interface going to sleep (the
+ * screen turning off).
+ * @param resuming True if this is being called as part of resuming the top activity, so
+ * we shouldn't try to instigate a resume here.
+ * @param dontWait True if the caller does not want to wait for the pause to complete. If
+ * set to true, we will immediately complete the pause here before returning.
+ * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+ * it to tell us when it is done.
+ */
+ final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
+ boolean dontWait) {
if (mPausingActivity != null) {
- Slog.e(TAG, "Trying to pause when pause is already pending for "
- + mPausingActivity, new RuntimeException("here").fillInStackTrace());
+ Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity);
+ completePauseLocked(false);
}
ActivityRecord prev = mResumedActivity;
if (prev == null) {
- Slog.e(TAG, "Trying to pause when nothing is resumed",
- new RuntimeException("here").fillInStackTrace());
- mStackSupervisor.resumeTopActivitiesLocked();
- return;
+ if (!resuming) {
+ Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+ mStackSupervisor.resumeTopActivitiesLocked();
+ }
+ return false;
}
if (mActivityContainer.mParentActivity == null) {
// Top level stack, not a child. Look for child stacks.
- mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping);
+ mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);
}
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
@@ -834,7 +850,7 @@
prev.shortComponentName);
mService.updateUsageStats(prev, false);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
- userLeaving, prev.configChangeFlags);
+ userLeaving, prev.configChangeFlags, dontWait);
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
@@ -865,39 +881,46 @@
if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
}
- // Schedule a pause timeout in case the app doesn't respond.
- // We don't give it much time because this directly impacts the
- // responsiveness seen by the user.
- Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
- msg.obj = prev;
- prev.pauseTime = SystemClock.uptimeMillis();
- mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
- if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+ if (dontWait) {
+ // If the caller said they don't want to wait for the pause, then complete
+ // the pause now.
+ completePauseLocked(false);
+ return false;
+
+ } else {
+ // Schedule a pause timeout in case the app doesn't respond.
+ // We don't give it much time because this directly impacts the
+ // responsiveness seen by the user.
+ Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
+ msg.obj = prev;
+ prev.pauseTime = SystemClock.uptimeMillis();
+ mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+ if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+ return true;
+ }
+
} else {
// This activity failed to schedule the
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ if (!resuming) {
+ mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ }
+ return false;
}
}
- final void activityPausedLocked(IBinder token, boolean timeout,
- PersistableBundle persistentState) {
+ final void activityPausedLocked(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(
TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- if (persistentState != null) {
- r.persistentState = persistentState;
- mService.notifyTaskPersisterLocked(r.task, false);
- }
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
- r.state = ActivityState.PAUSED;
- completePauseLocked();
+ completePauseLocked(true);
} else {
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
r.userId, System.identityHashCode(r), r.shortComponentName,
@@ -948,11 +971,12 @@
}
}
- private void completePauseLocked() {
+ private void completePauseLocked(boolean resumeNext) {
ActivityRecord prev = mPausingActivity;
if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
if (prev != null) {
+ prev.state = ActivityState.PAUSED;
if (prev.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
@@ -995,19 +1019,21 @@
mPausingActivity = null;
}
- final ActivityStack topStack = mStackSupervisor.getFocusedStack();
- if (!mService.isSleepingOrShuttingDown()) {
- mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
- } else {
- mStackSupervisor.checkReadyForSleepLocked();
- ActivityRecord top = topStack.topRunningActivityLocked(null);
- if (top == null || (prev != null && top != prev)) {
- // If there are no more activities available to run,
- // do resume anyway to start something. Also if the top
- // activity on the stack is not the just paused activity,
- // we need to go ahead and resume it to ensure we complete
- // an in-flight app switch.
- mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+ if (resumeNext) {
+ final ActivityStack topStack = mStackSupervisor.getFocusedStack();
+ if (!mService.isSleepingOrShuttingDown()) {
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
+ } else {
+ mStackSupervisor.checkReadyForSleepLocked();
+ ActivityRecord top = topStack.topRunningActivityLocked(null);
+ if (top == null || (prev != null && top != prev)) {
+ // If there are no more activities available to run,
+ // do resume anyway to start something. Also if the top
+ // activity on the stack is not the just paused activity,
+ // we need to go ahead and resume it to ensure we complete
+ // an in-flight app switch.
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+ }
}
}
@@ -1607,10 +1633,10 @@
// We need to start pausing the current activity so the top one
// can be resumed...
- boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving);
+ boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;
+ boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
if (mResumedActivity != null) {
- pausing = true;
- startPausingLocked(userLeaving, false);
+ pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity);
}
if (pausing) {
@@ -2720,7 +2746,7 @@
if (mPausingActivity == null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
- startPausingLocked(false, false);
+ startPausingLocked(false, false, false, false);
}
if (endTask) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 780efa1..f821daf 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -592,7 +592,7 @@
* @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
* @return true if any activity was paused as a result of this call.
*/
- boolean pauseBackStacks(boolean userLeaving) {
+ boolean pauseBackStacks(boolean userLeaving, boolean resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -601,8 +601,8 @@
if (!isFrontStack(stack) && stack.mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.mResumedActivity);
- stack.startPausingLocked(userLeaving, false);
- someActivityPaused = true;
+ someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
+ dontWait);
}
}
}
@@ -631,7 +631,8 @@
return pausing;
}
- void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping) {
+ void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping,
+ boolean resuming, boolean dontWait) {
// TODO: Put all stacks in supervisor and iterate through them instead.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -639,7 +640,7 @@
final ActivityStack stack = stacks.get(stackNdx);
if (stack.mResumedActivity != null &&
stack.mActivityContainer.mParentActivity == parent) {
- stack.startPausingLocked(userLeaving, uiSleeping);
+ stack.startPausingLocked(userLeaving, uiSleeping, resuming, dontWait);
}
}
}
@@ -1640,57 +1641,8 @@
}
}
- if (sourceRecord == null) {
- // This activity is not being started from another... in this
- // case we -always- start a new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
- "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // The original activity who is starting us is running as a single
- // instance... this new activity it is starting must go on its
- // own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- } else if (launchSingleInstance || launchSingleTask) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
-
- ActivityInfo newTaskInfo = null;
- Intent newTaskIntent = null;
- ActivityStack sourceStack;
- if (sourceRecord != null) {
- if (sourceRecord.finishing) {
- // If the source is finishing, we can't further count it as our source. This
- // is because the task it is associated with may now be empty and on its way out,
- // so we don't want to blindly throw it in to that task. Instead we will take
- // the NEW_TASK flow and try to find a task for it. But save the task information
- // so it can be used when creating the new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
- Slog.w(TAG, "startActivity called from finishing " + sourceRecord
- + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- newTaskInfo = sourceRecord.info;
- newTaskIntent = sourceRecord.task.intent;
- }
- sourceRecord = null;
- sourceStack = null;
- } else {
- sourceStack = sourceRecord.task.stack;
- }
- } else {
- sourceStack = null;
- }
-
boolean addingToTask = false;
- boolean movedHome = false;
TaskRecord reuseTask = null;
- ActivityStack targetStack;
-
- intent.setFlags(launchFlags);
// If the caller is not coming from another activity, but has given us an
// explicit task into which they would like us to launch the new activity,
@@ -1746,6 +1698,58 @@
inTask = null;
}
+ if (inTask == null) {
+ if (sourceRecord == null) {
+ // This activity is not being started from another... in this
+ // case we -always- start a new task.
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
+ Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
+ "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // The original activity who is starting us is running as a single
+ // instance... this new activity it is starting must go on its
+ // own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ } else if (launchSingleInstance || launchSingleTask) {
+ // The activity being started is a single instance... it always
+ // gets launched into its own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ }
+
+ ActivityInfo newTaskInfo = null;
+ Intent newTaskIntent = null;
+ ActivityStack sourceStack;
+ if (sourceRecord != null) {
+ if (sourceRecord.finishing) {
+ // If the source is finishing, we can't further count it as our source. This
+ // is because the task it is associated with may now be empty and on its way out,
+ // so we don't want to blindly throw it in to that task. Instead we will take
+ // the NEW_TASK flow and try to find a task for it. But save the task information
+ // so it can be used when creating the new task.
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+ + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ newTaskInfo = sourceRecord.info;
+ newTaskIntent = sourceRecord.task.intent;
+ }
+ sourceRecord = null;
+ sourceStack = null;
+ } else {
+ sourceStack = sourceRecord.task.stack;
+ }
+ } else {
+ sourceStack = null;
+ }
+
+ boolean movedHome = false;
+ ActivityStack targetStack;
+
+ intent.setFlags(launchFlags);
+
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
// this if NEW_TASK has been requested, and there is not an additional qualifier telling
@@ -3840,7 +3844,7 @@
mContainerState = CONTAINER_STATE_NO_SURFACE;
((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
- mStack.startPausingLocked(false, true);
+ mStack.startPausingLocked(false, true, false, false);
}
}
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 2ef9828..0cb8eb8 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -453,11 +453,10 @@
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
+ if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
if (action.equals(ALARM_WAKEUP)) {
- if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
startNavigating(false);
} else if (action.equals(ALARM_TIMEOUT)) {
- if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
hibernate();
} else if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) {
checkSmsSuplInit(intent);
@@ -691,7 +690,6 @@
intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- intentFilter = new IntentFilter();
intentFilter.addAction(SIM_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3eb2d5f..802df95 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -85,6 +85,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
+import com.android.internal.R;
public class WallpaperManagerService extends IWallpaperManager.Stub {
static final String TAG = "WallpaperManagerService";
@@ -99,12 +100,6 @@
static final long MIN_WALLPAPER_CRASH_TIME = 10000;
static final String WALLPAPER = "wallpaper";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
- /**
- * Name of the component used to display bitmap wallpapers from either the gallery or
- * built-in wallpapers.
- */
- static final ComponentName IMAGE_WALLPAPER = new ComponentName("com.android.systemui",
- "com.android.systemui.ImageWallpaper");
/**
* Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
@@ -146,7 +141,7 @@
if (event == CLOSE_WRITE) {
mWallpaper.imageWallpaperPending = false;
}
- bindWallpaperComponentLocked(IMAGE_WALLPAPER, true,
+ bindWallpaperComponentLocked(mImageWallpaper, true,
false, mWallpaper, null);
saveSettingsLocked(mWallpaper);
}
@@ -161,6 +156,12 @@
final MyPackageMonitor mMonitor;
WallpaperData mLastWallpaper;
+ /**
+ * Name of the component used to display bitmap wallpapers from either the gallery or
+ * built-in wallpapers.
+ */
+ final ComponentName mImageWallpaper;
+
SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
int mCurrentUserId;
@@ -447,6 +448,8 @@
public WallpaperManagerService(Context context) {
if (DEBUG) Slog.v(TAG, "WallpaperService startup");
mContext = context;
+ mImageWallpaper = ComponentName.unflattenFromString(
+ context.getResources().getString(R.string.image_wallpaper_component));
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mIPackageManager = AppGlobals.getPackageManager();
@@ -599,33 +602,35 @@
f.delete();
}
final long ident = Binder.clearCallingIdentity();
- RuntimeException e = null;
try {
- wallpaper.imageWallpaperPending = false;
- if (userId != mCurrentUserId) return;
- if (bindWallpaperComponentLocked(defaultFailed
- ? IMAGE_WALLPAPER
- : null, true, false, wallpaper, reply)) {
- return;
+ RuntimeException e = null;
+ try {
+ wallpaper.imageWallpaperPending = false;
+ if (userId != mCurrentUserId) return;
+ if (bindWallpaperComponentLocked(defaultFailed
+ ? mImageWallpaper
+ : null, true, false, wallpaper, reply)) {
+ return;
+ }
+ } catch (IllegalArgumentException e1) {
+ e = e1;
}
- } catch (IllegalArgumentException e1) {
- e = e1;
+
+ // This can happen if the default wallpaper component doesn't
+ // exist. This should be a system configuration problem, but
+ // let's not let it crash the system and just live with no
+ // wallpaper.
+ Slog.e(TAG, "Default wallpaper component not found!", e);
+ clearWallpaperComponentLocked(wallpaper);
+ if (reply != null) {
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e1) {
+ }
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
-
- // This can happen if the default wallpaper component doesn't
- // exist. This should be a system configuration problem, but
- // let's not let it crash the system and just live with no
- // wallpaper.
- Slog.e(TAG, "Default wallpaper component not found!", e);
- clearWallpaperComponentLocked(wallpaper);
- if (reply != null) {
- try {
- reply.sendResult(null);
- } catch (RemoteException e1) {
- }
- }
}
public boolean hasNamedWallpaper(String name) {
@@ -848,7 +853,7 @@
componentName = WallpaperManager.getDefaultWallpaperComponent(mContext);
if (componentName == null) {
// Fall back to static image wallpaper
- componentName = IMAGE_WALLPAPER;
+ componentName = mImageWallpaper;
//clearWallpaperComponentLocked();
//return;
if (DEBUG) Slog.v(TAG, "Using image wallpaper");
@@ -876,7 +881,7 @@
WallpaperInfo wi = null;
Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
- if (componentName != null && !componentName.equals(IMAGE_WALLPAPER)) {
+ if (componentName != null && !componentName.equals(mImageWallpaper)) {
// Make sure the selected service is actually a wallpaper service.
List<ResolveInfo> ris =
mIPackageManager.queryIntentServices(intent,
@@ -1052,7 +1057,7 @@
out.attribute(null, "height", Integer.toString(wallpaper.height));
out.attribute(null, "name", wallpaper.name);
if (wallpaper.wallpaperComponent != null
- && !wallpaper.wallpaperComponent.equals(IMAGE_WALLPAPER)) {
+ && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
out.attribute(null, "component",
wallpaper.wallpaperComponent.flattenToShortString());
}
@@ -1124,7 +1129,7 @@
if (wallpaper.nextWallpaperComponent == null
|| "android".equals(wallpaper.nextWallpaperComponent
.getPackageName())) {
- wallpaper.nextWallpaperComponent = IMAGE_WALLPAPER;
+ wallpaper.nextWallpaperComponent = mImageWallpaper;
}
if (DEBUG) {
@@ -1196,7 +1201,7 @@
loadSettingsLocked(0);
wallpaper = mWallpaperMap.get(0);
if (wallpaper.nextWallpaperComponent != null
- && !wallpaper.nextWallpaperComponent.equals(IMAGE_WALLPAPER)) {
+ && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
wallpaper, null)) {
// No such live wallpaper or other failure; fall back to the default
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index aabb8f7..bfc7659 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -569,9 +569,9 @@
int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
Rect contentInsets, boolean isFullScreen) {
Animation a;
- final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ final int thumbWidthI = mNextAppTransitionStartWidth;
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final int thumbHeightI = mNextAppTransitionStartHeight;
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
// Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
@@ -993,7 +993,7 @@
}
void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
- IRemoteCallback startedCallback, boolean scaleUp) {
+ int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
if (isTransitionSet()) {
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1002,6 +1002,8 @@
mNextAppTransitionScaleUp = scaleUp;
mNextAppTransitionStartX = startX;
mNextAppTransitionStartY = startY;
+ mNextAppTransitionStartWidth = targetWidth;
+ mNextAppTransitionStartHeight = targetHeight;
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
} else {
@@ -1138,6 +1140,10 @@
pw.print(mNextAppTransitionStartX);
pw.print(" mNextAppTransitionStartY=");
pw.println(mNextAppTransitionStartY);
+ pw.print(" mNextAppTransitionStartWidth=");
+ pw.print(mNextAppTransitionStartWidth);
+ pw.print(" mNextAppTransitionStartHeight=");
+ pw.println(mNextAppTransitionStartHeight);
pw.print(" mNextAppTransitionScaleUp="); pw.println(mNextAppTransitionScaleUp);
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a1afe29..55c6d81 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4031,10 +4031,11 @@
@Override
public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX,
- int startY, IRemoteCallback startedCallback, boolean scaleUp) {
+ int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
+ boolean scaleUp) {
synchronized(mWindowMap) {
mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX,
- startY, startedCallback, scaleUp);
+ startY, targetWidth, targetHeight, startedCallback, scaleUp);
}
}
@@ -4303,20 +4304,28 @@
}
public void setAppFullscreen(IBinder token, boolean toOpaque) {
- AppWindowToken atoken = findAppWindowToken(token);
- if (atoken != null) {
- atoken.appFullscreen = toOpaque;
- setWindowOpaque(token, toOpaque);
- requestTraversal();
+ synchronized (mWindowMap) {
+ AppWindowToken atoken = findAppWindowToken(token);
+ if (atoken != null) {
+ atoken.appFullscreen = toOpaque;
+ setWindowOpaqueLocked(token, toOpaque);
+ requestTraversalLocked();
+ }
}
}
public void setWindowOpaque(IBinder token, boolean isOpaque) {
+ synchronized (mWindowMap) {
+ setWindowOpaqueLocked(token, isOpaque);
+ }
+ }
+
+ public void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
AppWindowToken wtoken = findAppWindowToken(token);
if (wtoken != null) {
WindowState win = wtoken.findMainWindow();
if (win != null) {
- win.mWinAnimator.setOpaque(isOpaque);
+ win.mWinAnimator.setOpaqueLocked(isOpaque);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 3d4be12..a871522 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1556,11 +1556,11 @@
}
}
- void setOpaque(boolean isOpaque) {
+ void setOpaqueLocked(boolean isOpaque) {
if (mSurfaceControl == null) {
return;
}
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaque");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaqueLocked");
SurfaceControl.openTransaction();
try {
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "isOpaque=" + isOpaque,
@@ -1568,7 +1568,7 @@
mSurfaceControl.setOpaque(isOpaque);
} finally {
SurfaceControl.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaque");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
}
}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 6c80a65..5f639ab 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -23,13 +23,11 @@
import android.util.ArrayMap;
import android.util.ArraySet;
-import java.util.ArrayList;
-
class IntervalStats {
public long beginTime;
public long endTime;
public long lastTimeSaved;
- public final ArrayMap<String, UsageStats> stats = new ArrayMap<>();
+ public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
public Configuration activeConfiguration;
public TimeSparseArray<UsageEvents.Event> events;
@@ -44,13 +42,13 @@
* Gets the UsageStats object for the given package, or creates one and adds it internally.
*/
UsageStats getOrCreateUsageStats(String packageName) {
- UsageStats usageStats = stats.get(packageName);
+ UsageStats usageStats = packageStats.get(packageName);
if (usageStats == null) {
usageStats = new UsageStats();
usageStats.mPackageName = getCachedStringRef(packageName);
usageStats.mBeginTimeStamp = beginTime;
usageStats.mEndTimeStamp = endTime;
- stats.put(usageStats.mPackageName, usageStats);
+ packageStats.put(usageStats.mPackageName, usageStats);
}
return usageStats;
}
diff --git a/services/usage/java/com/android/server/usage/UnixCalendar.java b/services/usage/java/com/android/server/usage/UnixCalendar.java
new file mode 100644
index 0000000..ce06a91
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UnixCalendar.java
@@ -0,0 +1,99 @@
+/**
+ * Copyright (C) 2014 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 com.android.server.usage;
+
+import android.app.usage.UsageStatsManager;
+
+/**
+ * A handy calendar object that knows nothing of Locale's or TimeZones. This simplifies
+ * interval book-keeping. It is *NOT* meant to be used as a user-facing calendar, as it has
+ * no concept of Locale or TimeZone.
+ */
+public class UnixCalendar {
+ private static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
+ private static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
+ private static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
+ private static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
+ private long mTime;
+
+ public UnixCalendar(long time) {
+ mTime = time;
+ }
+
+ public void truncateToDay() {
+ mTime -= mTime % DAY_IN_MILLIS;
+ }
+
+ public void truncateToWeek() {
+ mTime -= mTime % WEEK_IN_MILLIS;
+ }
+
+ public void truncateToMonth() {
+ mTime -= mTime % MONTH_IN_MILLIS;
+ }
+
+ public void truncateToYear() {
+ mTime -= mTime % YEAR_IN_MILLIS;
+ }
+
+ public void addDays(int val) {
+ mTime += val * DAY_IN_MILLIS;
+ }
+
+ public void addWeeks(int val) {
+ mTime += val * WEEK_IN_MILLIS;
+ }
+
+ public void addMonths(int val) {
+ mTime += val * MONTH_IN_MILLIS;
+ }
+
+ public void addYears(int val) {
+ mTime += val * YEAR_IN_MILLIS;
+ }
+
+ public void setTimeInMillis(long time) {
+ mTime = time;
+ }
+
+ public long getTimeInMillis() {
+ return mTime;
+ }
+
+ public static void truncateTo(UnixCalendar calendar, int intervalType) {
+ switch (intervalType) {
+ case UsageStatsManager.INTERVAL_YEARLY:
+ calendar.truncateToYear();
+ break;
+
+ case UsageStatsManager.INTERVAL_MONTHLY:
+ calendar.truncateToMonth();
+ break;
+
+ case UsageStatsManager.INTERVAL_WEEKLY:
+ calendar.truncateToWeek();
+ break;
+
+ case UsageStatsManager.INTERVAL_DAILY:
+ calendar.truncateToDay();
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Can't truncate date to interval " +
+ intervalType);
+ }
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 37340a4..62a7ec0 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -21,24 +21,30 @@
import android.util.AtomicFile;
import android.util.Slog;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Calendar;
import java.util.List;
/**
* Provides an interface to query for UsageStat data from an XML database.
*/
class UsageStatsDatabase {
+ private static final int CURRENT_VERSION = 2;
+
private static final String TAG = "UsageStatsDatabase";
private static final boolean DEBUG = UsageStatsService.DEBUG;
private final Object mLock = new Object();
private final File[] mIntervalDirs;
private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
- private final Calendar mCal;
+ private final UnixCalendar mCal;
+ private final File mVersionFile;
public UsageStatsDatabase(File dir) {
mIntervalDirs = new File[] {
@@ -47,8 +53,9 @@
new File(dir, "monthly"),
new File(dir, "yearly"),
};
+ mVersionFile = new File(dir, "version");
mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
- mCal = Calendar.getInstance();
+ mCal = new UnixCalendar(0);
}
/**
@@ -64,6 +71,8 @@
}
}
+ checkVersionLocked();
+
final FilenameFilter backupFileFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
@@ -81,7 +90,45 @@
}
for (File f : files) {
- mSortedStatFiles[i].put(Long.parseLong(f.getName()), new AtomicFile(f));
+ final AtomicFile af = new AtomicFile(f);
+ mSortedStatFiles[i].put(UsageStatsXml.parseBeginTime(af), af);
+ }
+ }
+ }
+ }
+ }
+
+ private void checkVersionLocked() {
+ int version;
+ try (BufferedReader reader = new BufferedReader(new FileReader(mVersionFile))) {
+ version = Integer.parseInt(reader.readLine());
+ } catch (NumberFormatException | IOException e) {
+ version = 0;
+ }
+
+ if (version != CURRENT_VERSION) {
+ Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION);
+ doUpgradeLocked(version);
+
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) {
+ writer.write(Integer.toString(CURRENT_VERSION));
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write new version");
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private void doUpgradeLocked(int thisVersion) {
+ if (thisVersion < 2) {
+ // Delete all files if we are version 0. This is a pre-release version,
+ // so this is fine.
+ Slog.i(TAG, "Deleting all usage stats files");
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ File[] files = mIntervalDirs[i].listFiles();
+ if (files != null) {
+ for (File f : files) {
+ f.delete();
}
}
}
@@ -161,25 +208,48 @@
throw new IllegalArgumentException("Bad interval type " + intervalType);
}
- if (endTime < beginTime) {
+ final TimeSparseArray<AtomicFile> intervalStats = mSortedStatFiles[intervalType];
+
+ if (endTime <= beginTime) {
+ if (DEBUG) {
+ Slog.d(TAG, "endTime(" + endTime + ") <= beginTime(" + beginTime + ")");
+ }
return null;
}
- final int startIndex = mSortedStatFiles[intervalType].closestIndexOnOrBefore(beginTime);
+ int startIndex = intervalStats.closestIndexOnOrBefore(beginTime);
if (startIndex < 0) {
+ // All the stats available have timestamps after beginTime, which means they all
+ // match.
+ startIndex = 0;
+ }
+
+ int endIndex = intervalStats.closestIndexOnOrBefore(endTime);
+ if (endIndex < 0) {
+ // All the stats start after this range ends, so nothing matches.
+ if (DEBUG) {
+ Slog.d(TAG, "No results for this range. All stats start after.");
+ }
return null;
}
- int endIndex = mSortedStatFiles[intervalType].closestIndexOnOrAfter(endTime);
- if (endIndex < 0) {
- endIndex = mSortedStatFiles[intervalType].size() - 1;
+ if (intervalStats.keyAt(endIndex) == endTime) {
+ // The endTime is exclusive, so if we matched exactly take the one before.
+ endIndex--;
+ if (endIndex < 0) {
+ // All the stats start after this range ends, so nothing matches.
+ if (DEBUG) {
+ Slog.d(TAG, "No results for this range. All stats start after.");
+ }
+ return null;
+ }
}
try {
IntervalStats stats = new IntervalStats();
ArrayList<T> results = new ArrayList<>();
for (int i = startIndex; i <= endIndex; i++) {
- final AtomicFile f = mSortedStatFiles[intervalType].valueAt(i);
+ final AtomicFile f = intervalStats.valueAt(i);
if (DEBUG) {
Slog.d(TAG, "Reading stat file " + f.getBaseFile().getAbsolutePath());
@@ -230,22 +300,22 @@
synchronized (mLock) {
long timeNow = System.currentTimeMillis();
mCal.setTimeInMillis(timeNow);
- mCal.add(Calendar.YEAR, -3);
+ mCal.addYears(-3);
pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_YEARLY],
mCal.getTimeInMillis());
mCal.setTimeInMillis(timeNow);
- mCal.add(Calendar.MONTH, -6);
+ mCal.addMonths(-6);
pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_MONTHLY],
mCal.getTimeInMillis());
mCal.setTimeInMillis(timeNow);
- mCal.add(Calendar.WEEK_OF_YEAR, -4);
+ mCal.addWeeks(-4);
pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_WEEKLY],
mCal.getTimeInMillis());
mCal.setTimeInMillis(timeNow);
- mCal.add(Calendar.DAY_OF_YEAR, -7);
+ mCal.addDays(-7);
pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_DAILY],
mCal.getTimeInMillis());
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index e77bf86..2dcdcc4 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -38,6 +38,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
@@ -62,9 +63,7 @@
static final boolean DEBUG = false;
private static final long TEN_SECONDS = 10 * 1000;
private static final long TWENTY_MINUTES = 20 * 60 * 1000;
- private static final long TWO_MINUTES = 2 * 60 * 1000;
private static final long FLUSH_INTERVAL = DEBUG ? TEN_SECONDS : TWENTY_MINUTES;
- private static final long END_TIME_DELAY = DEBUG ? 0 : TWO_MINUTES;
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
@@ -78,6 +77,8 @@
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
private File mUsageStatsDir;
+ long mRealTimeSnapshot;
+ long mSystemTimeSnapshot;
public UsageStatsService(Context context) {
super(context);
@@ -104,6 +105,9 @@
cleanUpRemovedUsersLocked();
}
+ mRealTimeSnapshot = SystemClock.elapsedRealtime();
+ mSystemTimeSnapshot = System.currentTimeMillis();
+
publishLocalService(UsageStatsManagerInternal.class, new LocalService());
publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
}
@@ -178,7 +182,7 @@
}
/**
- * Called by the Bunder stub
+ * Called by the Binder stub
*/
void shutdown() {
synchronized (mLock) {
@@ -382,6 +386,14 @@
*/
private class LocalService extends UsageStatsManagerInternal {
+ /**
+ * The system may have its time change, so at least make sure the events
+ * are monotonic in order.
+ */
+ private long computeMonotonicSystemTime(long realTime) {
+ return (realTime - mRealTimeSnapshot) + mSystemTimeSnapshot;
+ }
+
@Override
public void reportEvent(ComponentName component, int userId, int eventType) {
if (component == null) {
@@ -392,7 +404,7 @@
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = component.getPackageName();
event.mClass = component.getClassName();
- event.mTimeStamp = System.currentTimeMillis();
+ event.mTimeStamp = computeMonotonicSystemTime(SystemClock.elapsedRealtime());
event.mEventType = eventType;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -406,7 +418,7 @@
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = "android";
- event.mTimeStamp = System.currentTimeMillis();
+ event.mTimeStamp = computeMonotonicSystemTime(SystemClock.elapsedRealtime());
event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
event.mConfiguration = new Configuration(config);
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsUtils.java b/services/usage/java/com/android/server/usage/UsageStatsUtils.java
deleted file mode 100644
index dd5f3b9..0000000
--- a/services/usage/java/com/android/server/usage/UsageStatsUtils.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (C) 2014 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 com.android.server.usage;
-
-import android.app.usage.UsageStatsManager;
-
-import java.util.Calendar;
-
-/**
- * A collection of utility methods used by the UsageStatsService and accompanying classes.
- */
-final class UsageStatsUtils {
- private UsageStatsUtils() {}
-
- /**
- * Truncates the date to the given UsageStats bucket. For example, if the bucket is
- * {@link UsageStatsManager#INTERVAL_YEARLY}, the date is truncated to the 1st day of the year,
- * with the time set to 00:00:00.
- *
- * @param bucket The UsageStats bucket to truncate to.
- * @param cal The date to truncate.
- */
- public static void truncateDateTo(int bucket, Calendar cal) {
- cal.set(Calendar.HOUR_OF_DAY, 0);
- cal.set(Calendar.MINUTE, 0);
- cal.set(Calendar.SECOND, 0);
- cal.set(Calendar.MILLISECOND, 0);
-
- switch (bucket) {
- case UsageStatsManager.INTERVAL_YEARLY:
- cal.set(Calendar.DAY_OF_YEAR, 0);
- break;
-
- case UsageStatsManager.INTERVAL_MONTHLY:
- cal.set(Calendar.DAY_OF_MONTH, 0);
- break;
-
- case UsageStatsManager.INTERVAL_WEEKLY:
- cal.set(Calendar.DAY_OF_WEEK, 0);
- break;
-
- case UsageStatsManager.INTERVAL_DAILY:
- break;
-
- default:
- throw new UnsupportedOperationException("Can't truncate date to bucket " + bucket);
- }
- }
-}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXml.java b/services/usage/java/com/android/server/usage/UsageStatsXml.java
index 48881d0..9ce6d63 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXml.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXml.java
@@ -37,10 +37,15 @@
private static final String USAGESTATS_TAG = "usagestats";
private static final String VERSION_ATTR = "version";
+ public static long parseBeginTime(AtomicFile file) {
+ return Long.parseLong(file.getBaseFile().getName());
+ }
+
public static void read(AtomicFile file, IntervalStats statsOut) throws IOException {
try {
FileInputStream in = file.openRead();
try {
+ statsOut.beginTime = parseBeginTime(file);
read(in, statsOut);
statsOut.lastTimeSaved = file.getLastModifiedTime();
} finally {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 6529950..ef95a7b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -15,7 +15,6 @@
*/
package com.android.server.usage;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -26,44 +25,52 @@
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
-import android.content.ComponentName;
import android.content.res.Configuration;
import java.io.IOException;
import java.net.ProtocolException;
-import java.util.Locale;
/**
* UsageStats reader/writer for version 1 of the XML format.
*/
final class UsageStatsXmlV1 {
+ private static final String PACKAGES_TAG = "packages";
private static final String PACKAGE_TAG = "package";
- private static final String CONFIGURATION_TAG = "config";
- private static final String EVENT_LOG_TAG = "event-log";
- private static final String BEGIN_TIME_ATTR = "beginTime";
- private static final String END_TIME_ATTR = "endTime";
- private static final String NAME_ATTR = "name";
+ private static final String CONFIGURATIONS_TAG = "configurations";
+ private static final String CONFIG_TAG = "config";
+
+ private static final String EVENT_LOG_TAG = "event-log";
+ private static final String EVENT_TAG = "event";
+
+ // Attributes
private static final String PACKAGE_ATTR = "package";
private static final String CLASS_ATTR = "class";
- private static final String TOTAL_TIME_ACTIVE_ATTR = "totalTimeActive";
- private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+ private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive";
private static final String COUNT_ATTR = "count";
private static final String ACTIVE_ATTR = "active";
private static final String LAST_EVENT_ATTR = "lastEvent";
private static final String TYPE_ATTR = "type";
+
+ // Time attributes stored as an offset of the beginTime.
+ private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+ private static final String END_TIME_ATTR = "endTime";
private static final String TIME_ATTR = "time";
private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- final String name = parser.getAttributeValue(null, NAME_ATTR);
- if (name == null) {
- throw new ProtocolException("no " + NAME_ATTR + " attribute present");
+ final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
+ if (pkg == null) {
+ throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
}
- UsageStats stats = statsOut.getOrCreateUsageStats(name);
+ final UsageStats stats = statsOut.getOrCreateUsageStats(pkg);
+
+ // Apply the offset to the beginTime to find the absolute time.
+ stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_ACTIVE_ATTR);
+
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
- stats.mLastTimeUsed = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
}
@@ -72,8 +79,12 @@
final Configuration config = new Configuration();
Configuration.readXmlAttrs(parser, config);
- ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
- configStats.mLastTimeActive = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
+ final ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
+
+ // Apply the offset to the beginTime to find the absolute time.
+ configStats.mLastTimeActive = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_ACTIVE_ATTR);
+
configStats.mTotalTimeActive = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
configStats.mActivationCount = XmlUtils.readIntAttribute(parser, COUNT_ATTR);
if (XmlUtils.readBooleanAttribute(parser, ACTIVE_ATTR)) {
@@ -83,30 +94,19 @@
private static void loadEvent(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
- String className;
+ final String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
if (packageName == null) {
- // Try getting the component name if it exists.
- final String componentName = XmlUtils.readStringAttribute(parser, NAME_ATTR);
- if (componentName == null) {
- throw new ProtocolException("no " + NAME_ATTR + " or " + PACKAGE_ATTR +
- " attribute present");
- }
- ComponentName component = ComponentName.unflattenFromString(componentName);
- if (component == null) {
- throw new ProtocolException("ComponentName " + componentName + " is invalid");
- }
-
- packageName = component.getPackageName();
- className = component.getClassName();
- } else {
- className = XmlUtils.readStringAttribute(parser, CLASS_ATTR);
+ throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
}
- UsageEvents.Event event = statsOut.buildEvent(packageName, className);
- event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
- event.mTimeStamp = XmlUtils.readLongAttribute(parser, TIME_ATTR);
+ final String className = XmlUtils.readStringAttribute(parser, CLASS_ATTR);
+ final UsageEvents.Event event = statsOut.buildEvent(packageName, className);
+
+ // Apply the offset to the beginTime to find the absolute time of this event.
+ event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR);
+
+ event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
event.mConfiguration = new Configuration();
Configuration.readXmlAttrs(parser, event.mConfiguration);
@@ -118,48 +118,60 @@
statsOut.events.put(event.mTimeStamp, event);
}
- private static void writeUsageStats(XmlSerializer xml, final UsageStats stats)
- throws IOException {
+ private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
+ final UsageStats usageStats) throws IOException {
xml.startTag(null, PACKAGE_TAG);
- XmlUtils.writeStringAttribute(xml, NAME_ATTR, stats.mPackageName);
- XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeInForeground);
- XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeUsed);
- XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, stats.mLastEvent);
+
+ // Write the time offset.
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
+ usageStats.mLastTimeUsed - stats.beginTime);
+
+ XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
+ XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
+
xml.endTag(null, PACKAGE_TAG);
}
- private static void writeConfigStats(XmlSerializer xml, final ConfigurationStats stats,
- boolean isActive) throws IOException {
- xml.startTag(null, CONFIGURATION_TAG);
- XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeActive);
- XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeActive);
- XmlUtils.writeIntAttribute(xml, COUNT_ATTR, stats.mActivationCount);
+ private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats,
+ final ConfigurationStats configStats, boolean isActive) throws IOException {
+ xml.startTag(null, CONFIG_TAG);
+
+ // Write the time offset.
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
+ configStats.mLastTimeActive - stats.beginTime);
+
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, configStats.mTotalTimeActive);
+ XmlUtils.writeIntAttribute(xml, COUNT_ATTR, configStats.mActivationCount);
if (isActive) {
XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true);
}
// Now write the attributes representing the configuration object.
- Configuration.writeXmlAttrs(xml, stats.mConfiguration);
+ Configuration.writeXmlAttrs(xml, configStats.mConfiguration);
- xml.endTag(null, CONFIGURATION_TAG);
+ xml.endTag(null, CONFIG_TAG);
}
- private static void writeEvent(XmlSerializer xml, final UsageEvents.Event event)
- throws IOException {
- xml.startTag(null, EVENT_LOG_TAG);
+ private static void writeEvent(XmlSerializer xml, final IntervalStats stats,
+ final UsageEvents.Event event) throws IOException {
+ xml.startTag(null, EVENT_TAG);
+
+ // Store the time offset.
+ XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp - stats.beginTime);
+
XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage);
if (event.mClass != null) {
XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass);
}
XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
- XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp);
if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE
&& event.mConfiguration != null) {
Configuration.writeXmlAttrs(xml, event.mConfiguration);
}
- xml.endTag(null, EVENT_LOG_TAG);
+ xml.endTag(null, EVENT_TAG);
}
/**
@@ -171,7 +183,7 @@
*/
public static void read(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- statsOut.stats.clear();
+ statsOut.packageStats.clear();
statsOut.configurations.clear();
statsOut.activeConfiguration = null;
@@ -179,7 +191,6 @@
statsOut.events.clear();
}
- statsOut.beginTime = XmlUtils.readLongAttribute(parser, BEGIN_TIME_ATTR);
statsOut.endTime = XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
int eventCode;
@@ -196,11 +207,11 @@
loadUsageStats(parser, statsOut);
break;
- case CONFIGURATION_TAG:
+ case CONFIG_TAG:
loadConfigStats(parser, statsOut);
break;
- case EVENT_LOG_TAG:
+ case EVENT_TAG:
loadEvent(parser, statsOut);
break;
}
@@ -208,32 +219,38 @@
}
/**
- * Writes the stats object to an XML file. The {@link FastXmlSerializer}
+ * Writes the stats object to an XML file. The {@link XmlSerializer}
* has already written the <code><usagestats></code> tag, but attributes may still
* be added.
*
- * @param serializer The serializer to which to write the stats data.
+ * @param xml The serializer to which to write the packageStats data.
* @param stats The stats object to write to the XML file.
*/
- public static void write(FastXmlSerializer serializer, IntervalStats stats) throws IOException {
- serializer.attribute(null, BEGIN_TIME_ATTR, Long.toString(stats.beginTime));
- serializer.attribute(null, END_TIME_ATTR, Long.toString(stats.endTime));
+ public static void write(XmlSerializer xml, IntervalStats stats) throws IOException {
+ XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime);
- final int statsCount = stats.stats.size();
+ xml.startTag(null, PACKAGES_TAG);
+ final int statsCount = stats.packageStats.size();
for (int i = 0; i < statsCount; i++) {
- writeUsageStats(serializer, stats.stats.valueAt(i));
+ writeUsageStats(xml, stats, stats.packageStats.valueAt(i));
}
+ xml.endTag(null, PACKAGES_TAG);
+
+ xml.startTag(null, CONFIGURATIONS_TAG);
final int configCount = stats.configurations.size();
for (int i = 0; i < configCount; i++) {
boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
- writeConfigStats(serializer, stats.configurations.valueAt(i), active);
+ writeConfigStats(xml, stats, stats.configurations.valueAt(i), active);
}
+ xml.endTag(null, CONFIGURATIONS_TAG);
+ xml.startTag(null, EVENT_LOG_TAG);
final int eventCount = stats.events != null ? stats.events.size() : 0;
for (int i = 0; i < eventCount; i++) {
- writeEvent(serializer, stats.events.valueAt(i));
+ writeEvent(xml, stats, stats.events.valueAt(i));
}
+ xml.endTag(null, EVENT_LOG_TAG);
}
private UsageStatsXmlV1() {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 7142a99..2769666 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -32,7 +32,6 @@
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.List;
/**
@@ -47,7 +46,7 @@
private final UsageStatsDatabase mDatabase;
private final IntervalStats[] mCurrentStats;
private boolean mStatsChanged = false;
- private final Calendar mDailyExpiryDate;
+ private final UnixCalendar mDailyExpiryDate;
private final StatsUpdatedListener mListener;
private final String mLogPrefix;
@@ -56,7 +55,7 @@
}
UserUsageStatsService(int userId, File usageStatsDir, StatsUpdatedListener listener) {
- mDailyExpiryDate = Calendar.getInstance();
+ mDailyExpiryDate = new UnixCalendar(0);
mDatabase = new UsageStatsDatabase(usageStatsDir);
mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
mListener = listener;
@@ -66,6 +65,7 @@
void init() {
mDatabase.init();
+ final long timeNow = System.currentTimeMillis();
int nullCount = 0;
for (int i = 0; i < mCurrentStats.length; i++) {
mCurrentStats[i] = mDatabase.getLatestUsageStats(i);
@@ -73,6 +73,11 @@
// Find out how many intervals we don't have data for.
// Ideally it should be all or none.
nullCount++;
+ } else if (mCurrentStats[i].beginTime > timeNow) {
+ Slog.e(TAG, mLogPrefix + "Interval " + i + " has stat in the future " +
+ mCurrentStats[i].beginTime);
+ mCurrentStats[i] = null;
+ nullCount++;
}
}
@@ -94,17 +99,18 @@
// that is reported.
mDailyExpiryDate.setTimeInMillis(
mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
- mDailyExpiryDate.add(Calendar.DAY_OF_YEAR, 1);
- UsageStatsUtils.truncateDateTo(UsageStatsManager.INTERVAL_DAILY, mDailyExpiryDate);
- Slog.i(TAG, mLogPrefix + "Rollover scheduled for "
- + sDateFormat.format(mDailyExpiryDate.getTime()));
+ mDailyExpiryDate.addDays(1);
+ mDailyExpiryDate.truncateToDay();
+ Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
+ sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) +
+ "(" + mDailyExpiryDate.getTimeInMillis() + ")");
}
// Now close off any events that were open at the time this was saved.
for (IntervalStats stat : mCurrentStats) {
- final int pkgCount = stat.stats.size();
+ final int pkgCount = stat.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
- UsageStats pkgStats = stat.stats.valueAt(i);
+ UsageStats pkgStats = stat.packageStats.valueAt(i);
if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
stat.update(pkgStats.mPackageName, stat.lastTimeSaved,
@@ -162,13 +168,13 @@
public void combine(IntervalStats stats, boolean mutable,
List<UsageStats> accResult) {
if (!mutable) {
- accResult.addAll(stats.stats.values());
+ accResult.addAll(stats.packageStats.values());
return;
}
- final int statCount = stats.stats.size();
+ final int statCount = stats.packageStats.size();
for (int i = 0; i < statCount; i++) {
- accResult.add(new UsageStats(stats.stats.valueAt(i)));
+ accResult.add(new UsageStats(stats.packageStats.valueAt(i)));
}
}
};
@@ -195,49 +201,68 @@
* and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
* provided to select the stats to use from the IntervalStats object.
*/
- private <T> List<T> queryStats(int bucketType, long beginTime, long endTime,
+ private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
StatCombiner<T> combiner) {
- if (bucketType == UsageStatsManager.INTERVAL_BEST) {
- bucketType = mDatabase.findBestFitBucket(beginTime, endTime);
+ if (intervalType == UsageStatsManager.INTERVAL_BEST) {
+ intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
+ if (intervalType < 0) {
+ // Nothing saved to disk yet, so every stat is just as equal (no rollover has
+ // occurred.
+ intervalType = UsageStatsManager.INTERVAL_DAILY;
+ }
}
- if (bucketType < 0 || bucketType >= mCurrentStats.length) {
+ if (intervalType < 0 || intervalType >= mCurrentStats.length) {
if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "Bad bucketType used " + bucketType);
+ Slog.d(TAG, mLogPrefix + "Bad intervalType used " + intervalType);
}
return null;
}
- if (beginTime >= mCurrentStats[bucketType].endTime) {
- if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
- + mCurrentStats[bucketType].endTime);
- }
- // Nothing newer available.
- return null;
-
- } else if (beginTime >= mCurrentStats[bucketType].beginTime) {
- if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "Returning in-memory stats for bucket " + bucketType);
- }
- // Fast path for retrieving in-memory state.
- ArrayList<T> results = new ArrayList<>();
- combiner.combine(mCurrentStats[bucketType], true, results);
- return results;
- }
-
- // Flush any changes that were made to disk before we do a disk query.
- // If we're not grabbing the ongoing stats, no need to persist.
- persistActiveStats();
+ final IntervalStats currentStats = mCurrentStats[intervalType];
if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "SELECT * FROM " + bucketType + " WHERE beginTime >= "
+ Slog.d(TAG, mLogPrefix + "SELECT * FROM " + intervalType + " WHERE beginTime >= "
+ beginTime + " AND endTime < " + endTime);
}
- final List<T> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime, combiner);
+ if (beginTime >= currentStats.endTime) {
+ if (DEBUG) {
+ Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
+ + currentStats.endTime);
+ }
+ // Nothing newer available.
+ return null;
+ }
+
+ // Truncate the endTime to just before the in-memory stats. Then, we'll append the
+ // in-memory stats to the results (if necessary) so as to avoid writing to disk too
+ // often.
+ final long truncatedEndTime = Math.min(currentStats.beginTime, endTime);
+
+ // Get the stats from disk.
+ List<T> results = mDatabase.queryUsageStats(intervalType, beginTime,
+ truncatedEndTime, combiner);
if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "Results: " + (results == null ? 0 : results.size()));
+ Slog.d(TAG, "Got " + (results != null ? results.size() : 0) + " results from disk");
+ Slog.d(TAG, "Current stats beginTime=" + currentStats.beginTime +
+ " endTime=" + currentStats.endTime);
+ }
+
+ // Now check if the in-memory stats match the range and add them if they do.
+ if (beginTime < currentStats.endTime && endTime > currentStats.beginTime) {
+ if (DEBUG) {
+ Slog.d(TAG, mLogPrefix + "Returning in-memory stats");
+ }
+
+ if (results == null) {
+ results = new ArrayList<>();
+ }
+ combiner.combine(currentStats, true, results);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, mLogPrefix + "Results: " + (results != null ? results.size() : 0));
}
return results;
}
@@ -250,44 +275,45 @@
return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
}
- UsageEvents queryEvents(long beginTime, long endTime) {
- if (endTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime) {
- if (beginTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].endTime) {
- return null;
- }
+ UsageEvents queryEvents(final long beginTime, final long endTime) {
+ final ArraySet<String> names = new ArraySet<>();
+ List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
+ beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<UsageEvents.Event> accumulatedResult) {
+ if (stats.events == null) {
+ return;
+ }
- TimeSparseArray<UsageEvents.Event> events =
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events;
- if (events == null) {
- return null;
- }
+ final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
+ if (startIndex < 0) {
+ return;
+ }
- final int startIndex = events.closestIndexOnOrAfter(beginTime);
- if (startIndex < 0) {
- return null;
- }
+ final int size = stats.events.size();
+ for (int i = startIndex; i < size; i++) {
+ if (stats.events.keyAt(i) >= endTime) {
+ return;
+ }
- ArraySet<String> names = new ArraySet<>();
- ArrayList<UsageEvents.Event> results = new ArrayList<>();
- final int size = events.size();
- for (int i = startIndex; i < size; i++) {
- if (events.keyAt(i) >= endTime) {
- break;
- }
- final UsageEvents.Event event = events.valueAt(i);
- names.add(event.mPackage);
- if (event.mClass != null) {
- names.add(event.mClass);
- }
- results.add(event);
- }
- String[] table = names.toArray(new String[names.size()]);
- Arrays.sort(table);
- return new UsageEvents(results, table);
+ final UsageEvents.Event event = stats.events.valueAt(i);
+ names.add(event.mPackage);
+ if (event.mClass != null) {
+ names.add(event.mClass);
+ }
+ accumulatedResult.add(event);
+ }
+ }
+ });
+
+ if (results == null || results.isEmpty()) {
+ return null;
}
- // TODO(adamlesinski): Query the previous days.
- return null;
+ String[] table = names.toArray(new String[names.size()]);
+ Arrays.sort(table);
+ return new UsageEvents(results, table);
}
void persistActiveStats() {
@@ -314,9 +340,9 @@
mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
ArraySet<String> continuePreviousDay = new ArraySet<>();
for (IntervalStats stat : mCurrentStats) {
- final int pkgCount = stat.stats.size();
+ final int pkgCount = stat.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
- UsageStats pkgStats = stat.stats.valueAt(i);
+ UsageStats pkgStats = stat.packageStats.valueAt(i);
if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
continuePreviousDay.add(pkgStats.mPackageName);
@@ -360,54 +386,53 @@
private void loadActiveStats() {
final long timeNow = System.currentTimeMillis();
- Calendar tempCal = mDailyExpiryDate;
- for (int bucketType = 0; bucketType < mCurrentStats.length; bucketType++) {
+ final UnixCalendar tempCal = mDailyExpiryDate;
+ for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
tempCal.setTimeInMillis(timeNow);
- UsageStatsUtils.truncateDateTo(bucketType, tempCal);
+ UnixCalendar.truncateTo(tempCal, intervalType);
- if (mCurrentStats[bucketType] != null &&
- mCurrentStats[bucketType].beginTime == tempCal.getTimeInMillis()) {
+ if (mCurrentStats[intervalType] != null &&
+ mCurrentStats[intervalType].beginTime == tempCal.getTimeInMillis()) {
// These are the same, no need to load them (in memory stats are always newer
// than persisted stats).
continue;
}
- final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(bucketType);
- if (lastBeginTime >= tempCal.getTimeInMillis()) {
+ final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(intervalType);
+ if (lastBeginTime > timeNow) {
+ Slog.e(TAG, mLogPrefix + "Latest usage stats for interval " +
+ intervalType + " begins in the future");
+ mCurrentStats[intervalType] = null;
+ } else if (lastBeginTime >= tempCal.getTimeInMillis()) {
if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "Loading existing stats (" + lastBeginTime +
- ") for bucket " + bucketType);
+ Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
+ sDateFormat.format(lastBeginTime) + "(" + lastBeginTime +
+ ") for interval " + intervalType);
}
- mCurrentStats[bucketType] = mDatabase.getLatestUsageStats(bucketType);
- if (DEBUG) {
- if (mCurrentStats[bucketType] != null) {
- Slog.d(TAG, mLogPrefix + "Found " +
- (mCurrentStats[bucketType].events == null ?
- 0 : mCurrentStats[bucketType].events.size()) +
- " events");
- }
- }
+ mCurrentStats[intervalType] = mDatabase.getLatestUsageStats(intervalType);
} else {
- mCurrentStats[bucketType] = null;
+ mCurrentStats[intervalType] = null;
}
- if (mCurrentStats[bucketType] == null) {
+ if (mCurrentStats[intervalType] == null) {
if (DEBUG) {
- Slog.d(TAG, "Creating new stats (" + tempCal.getTimeInMillis() +
- ") for bucket " + bucketType);
+ Slog.d(TAG, "Creating new stats @ " +
+ sDateFormat.format(tempCal.getTimeInMillis()) + "(" +
+ tempCal.getTimeInMillis() + ") for interval " + intervalType);
}
- mCurrentStats[bucketType] = new IntervalStats();
- mCurrentStats[bucketType].beginTime = tempCal.getTimeInMillis();
- mCurrentStats[bucketType].endTime = timeNow;
+ mCurrentStats[intervalType] = new IntervalStats();
+ mCurrentStats[intervalType].beginTime = tempCal.getTimeInMillis();
+ mCurrentStats[intervalType].endTime = timeNow;
}
}
mStatsChanged = false;
mDailyExpiryDate.setTimeInMillis(timeNow);
- mDailyExpiryDate.add(Calendar.DAY_OF_YEAR, 1);
- UsageStatsUtils.truncateDateTo(UsageStatsManager.INTERVAL_DAILY, mDailyExpiryDate);
- Slog.i(TAG, mLogPrefix + "Rollover scheduled for "
- + sDateFormat.format(mDailyExpiryDate.getTime()));
+ mDailyExpiryDate.addDays(1);
+ mDailyExpiryDate.truncateToDay();
+ Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
+ sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
+ tempCal.getTimeInMillis() + ")");
}
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index 5bf0855..c3aa2a0 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -517,6 +517,20 @@
}
/**
+ * Merges the calls within this conference. See {@link PhoneCapabilities#MERGE_CONFERENCE}.
+ */
+ public void mergeConference() {
+ mInCallAdapter.mergeConference(mTelecommCallId);
+ }
+
+ /**
+ * Swaps the calls within this conference. See {@link PhoneCapabilities#SWAP_CONFERENCE}.
+ */
+ public void swapConference() {
+ mInCallAdapter.swapConference(mTelecommCallId);
+ }
+
+ /**
* Obtains the parent of this {@code Call} in a conference, if any.
*
* @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
diff --git a/telecomm/java/android/telecomm/Conference.java b/telecomm/java/android/telecomm/Conference.java
index 879ff66..c838b48 100644
--- a/telecomm/java/android/telecomm/Conference.java
+++ b/telecomm/java/android/telecomm/Conference.java
@@ -93,6 +93,18 @@
public void onUnhold() {}
/**
+ * Invoked when the child calls should be merged. Only invoked if the conference contains the
+ * capability {@link PhoneCapabilities#MERGE_CONFERENCE}.
+ */
+ public void onMerge() {}
+
+ /**
+ * Invoked when the child calls should be swapped. Only invoked if the conference contains the
+ * capability {@link PhoneCapabilities#SWAP_CONFERENCE}.
+ */
+ public void onSwap() {}
+
+ /**
* Sets state to be on hold.
*/
public final void setOnHold() {
@@ -141,7 +153,7 @@
* @param connection The connection to add.
* @return True if the connection was successfully added.
*/
- public boolean addConnection(Connection connection) {
+ public final boolean addConnection(Connection connection) {
if (connection != null && !mChildConnections.contains(connection)) {
if (connection.setConference(this)) {
mChildConnections.add(connection);
@@ -160,7 +172,7 @@
* @param connection The connection to remove.
* @return True if the connection was successfully removed.
*/
- public void removeConnection(Connection connection) {
+ public final void removeConnection(Connection connection) {
Log.d(this, "removing %s from %s", connection, mChildConnections);
if (connection != null && mChildConnections.remove(connection)) {
connection.resetConference();
@@ -173,7 +185,7 @@
/**
* Tears down the conference object and any of it's current connections.
*/
- public void destroy() {
+ public final void destroy() {
Log.d(this, "destroying conference : %s", this);
// Tear down the children.
for (Connection connection : mChildConnections) {
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 4d6e267..6df117e 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -1068,10 +1068,10 @@
if (mState != state) {
Log.d(this, "setState: %s", stateToString(state));
mState = state;
+ onSetState(state);
for (Listener l : mListeners) {
l.onStateChanged(this, state);
}
- onSetState(state);
}
}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 2dc6910..833aa26 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -72,6 +72,8 @@
private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 15;
private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16;
private static final int MSG_ANSWER_VIDEO = 17;
+ private static final int MSG_MERGE_CONFERENCE = 18;
+ private static final int MSG_SWAP_CONFERENCE = 19;
private static Connection sNullConnection;
@@ -182,6 +184,16 @@
}
@Override
+ public void mergeConference(String callId) {
+ mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget();
+ }
+
+ @Override
+ public void swapConference(String callId) {
+ mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget();
+ }
+
+ @Override
public void onPostDialContinue(String callId, boolean proceed) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
@@ -298,6 +310,12 @@
case MSG_SPLIT_FROM_CONFERENCE:
splitFromConference((String) msg.obj);
break;
+ case MSG_MERGE_CONFERENCE:
+ mergeConference((String) msg.obj);
+ break;
+ case MSG_SWAP_CONFERENCE:
+ swapConference((String) msg.obj);
+ break;
case MSG_ON_POST_DIAL_CONTINUE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -639,6 +657,22 @@
}
}
+ private void mergeConference(String callId) {
+ Log.d(this, "mergeConference(%s)", callId);
+ Conference conference = findConferenceForAction(callId, "mergeConference");
+ if (conference != null) {
+ conference.onMerge();
+ }
+ }
+
+ private void swapConference(String callId) {
+ Log.d(this, "swapConference(%s)", callId);
+ Conference conference = findConferenceForAction(callId, "swapConference");
+ if (conference != null) {
+ conference.onSwap();
+ }
+ }
+
private void onPostDialContinue(String callId, boolean proceed) {
Log.d(this, "onPostDialContinue(%s)", callId);
findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java
index 80f7b57..96ea5a6 100644
--- a/telecomm/java/android/telecomm/InCallAdapter.java
+++ b/telecomm/java/android/telecomm/InCallAdapter.java
@@ -241,6 +241,26 @@
}
/**
+ * Instructs Telecomm to merge child calls of the specified conference call.
+ */
+ public void mergeConference(String callId) {
+ try {
+ mAdapter.mergeConference(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecomm to swap the child calls of the specified conference call.
+ */
+ public void swapConference(String callId) {
+ try {
+ mAdapter.swapConference(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
* Instructs Telecomm to turn the proximity sensor on.
*/
public void turnProximitySensorOn() {
diff --git a/telecomm/java/android/telecomm/PhoneCapabilities.java b/telecomm/java/android/telecomm/PhoneCapabilities.java
index 3d76608..7a338b4 100644
--- a/telecomm/java/android/telecomm/PhoneCapabilities.java
+++ b/telecomm/java/android/telecomm/PhoneCapabilities.java
@@ -27,11 +27,17 @@
/** Call supports the hold feature. */
public static final int SUPPORT_HOLD = 0x00000002;
- /** Call can currently be merged. */
- public static final int MERGE_CALLS = 0x00000004;
+ /**
+ * Calls within a conference can be merged. Some connection services create a conference call
+ * only after two calls have been merged. However, a conference call can also be added the
+ * moment there are more than one call. CDMA calls are implemented in this way because the call
+ * actions are more limited when more than one call exists. This flag allows merge to be exposed
+ * as a capability on the conference call instead of individual calls.
+ */
+ public static final int MERGE_CONFERENCE = 0x00000004;
- /** Call can currently be swapped with another call. */
- public static final int SWAP_CALLS = 0x00000008;
+ /** Calls withing a conference can be swapped between foreground and background. */
+ public static final int SWAP_CONFERENCE = 0x00000008;
/** Call currently supports adding another call to this one. */
public static final int ADD_CALL = 0x00000010;
@@ -42,8 +48,11 @@
/** Call can be muted. */
public static final int MUTE = 0x00000040;
- /** Call supports generic conference mode. */
- public static final int GENERIC_CONFERENCE = 0x00000080;
+ /**
+ * Call supports conference call management. This capability only applies to conference calls
+ * which can have other calls as children.
+ */
+ public static final int MANAGE_CONFERENCE = 0x00000080;
/**
* Local device supports video telephony.
@@ -69,8 +78,8 @@
*/
public static final int VoWIFI = 0x00000800;
- public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL
- | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE;
+ public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CONFERENCE | SWAP_CONFERENCE | ADD_CALL
+ | RESPOND_VIA_TEXT | MUTE | MANAGE_CONFERENCE;
public static String toString(int capabilities) {
StringBuilder builder = new StringBuilder();
@@ -81,11 +90,11 @@
if ((capabilities & SUPPORT_HOLD) != 0) {
builder.append(" SUPPORT_HOLD");
}
- if ((capabilities & MERGE_CALLS) != 0) {
- builder.append(" MERGE_CALLS");
+ if ((capabilities & MERGE_CONFERENCE) != 0) {
+ builder.append(" MERGE_CONFERENCE");
}
- if ((capabilities & SWAP_CALLS) != 0) {
- builder.append(" SWAP_CALLS");
+ if ((capabilities & SWAP_CONFERENCE) != 0) {
+ builder.append(" SWAP_CONFERENCE");
}
if ((capabilities & ADD_CALL) != 0) {
builder.append(" ADD_CALL");
@@ -96,8 +105,8 @@
if ((capabilities & MUTE) != 0) {
builder.append(" MUTE");
}
- if ((capabilities & GENERIC_CONFERENCE) != 0) {
- builder.append(" GENERIC_CONFERENCE");
+ if ((capabilities & MANAGE_CONFERENCE) != 0) {
+ builder.append(" MANAGE_CONFERENCE");
}
if ((capabilities & SUPPORTS_VT_LOCAL) != 0) {
builder.append(" SUPPORTS_VT_LOCAL");
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
index 32b877d..a673733 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
@@ -65,6 +65,10 @@
void splitFromConference(String callId);
+ void mergeConference(String conferenceCallId);
+
+ void swapConference(String conferenceCallId);
+
void onPostDialContinue(String callId, boolean proceed);
void onPhoneAccountClicked(String callId);
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
index 2ce5c6b..626bb91 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
@@ -54,6 +54,10 @@
void splitFromConference(String callId);
+ void mergeConference(String callId);
+
+ void swapConference(String callId);
+
void turnOnProximitySensor();
void turnOffProximitySensor(boolean screenOnImmediately);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index cdee3de..93c49ba 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2511,9 +2511,9 @@
* @param content String containing SAT/USAT response in hexadecimal
* format starting with command tag. See TS 102 223 for
* details.
- * @return The APDU response from the ICC card, with the last 4 bytes
- * being the status word. If the command fails, returns an empty
- * string.
+ * @return The APDU response from the ICC card in hexadecimal format
+ * with the last 4 bytes being the status word. If the command fails,
+ * returns an empty string.
*/
public String sendEnvelopeWithStatus(String content) {
try {
@@ -2903,27 +2903,6 @@
}
/**
- * Get the calculated preferred network type.
- * Used for debugging incorrect network type.
- * <p>
- * Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
- *
- * @return the preferred network type, defined in RILConstants.java or -1 if
- * none available.
- */
- public int getCalculatedPreferredNetworkType() {
- try {
- return getITelephony().getCalculatedPreferredNetworkType();
- } catch (RemoteException ex) {
- Rlog.e(TAG, "getCalculatedPreferredNetworkType RemoteException", ex);
- } catch (NullPointerException ex) {
- Rlog.e(TAG, "getCalculatedPreferredNetworkType NPE", ex);
- }
- return -1;
- }
-
- /**
* Get the preferred network type.
* Used for device configuration by some CDMA operators.
* <p>
@@ -2932,6 +2911,7 @@
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
*
* @return the preferred network type, defined in RILConstants.java.
+ * @hide
*/
public int getPreferredNetworkType() {
try {
@@ -2954,6 +2934,7 @@
*
* @param networkType the preferred network type, defined in RILConstants.java.
* @return true on success; false on any failure.
+ * @hide
*/
public boolean setPreferredNetworkType(int networkType) {
try {
@@ -2967,25 +2948,17 @@
}
/**
- * Set the CDMA subscription source.
- * Used for device supporting both NV and RUIM for CDMA.
+ * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
+ *
* <p>
* Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
*
- * @param subscriptionType the subscription type, 0 for RUIM, 1 for NV.
* @return true on success; false on any failure.
*/
- public boolean setCdmaSubscription(int subscriptionType) {
- try {
- return getITelephony().setCdmaSubscription(subscriptionType);
- } catch (RemoteException ex) {
- Rlog.e(TAG, "setCdmaSubscription RemoteException", ex);
- } catch (NullPointerException ex) {
- Rlog.e(TAG, "setCdmaSubscription NPE", ex);
- }
- return false;
+ public boolean setGlobalPreferredNetworkType() {
+ return setPreferredNetworkType(RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
}
/**
@@ -3023,9 +2996,9 @@
}
/**
- * Override the branding for the input ICCID.
+ * Override the branding for the current ICCID.
*
- * Once set, whenever the ICCID is inserted into the device, the service
+ * Once set, whenever the SIM is present in the device, the service
* provider name (SPN) and the operator name will both be replaced by the
* brand value input. To unset the value, the same function should be
* called with a null brand value.
@@ -3034,14 +3007,12 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* or has to be carrier app - see #hasCarrierPrivileges.
*
- * @param iccId The ICCID of that the branding applies to.
* @param brand The brand name to display/set.
* @return true if the operation was executed correctly.
*/
- public boolean setOperatorBrandOverride(String iccId, String brand) {
- // TODO: Validate ICCID format.
+ public boolean setOperatorBrandOverride(String brand) {
try {
- return getITelephony().setOperatorBrandOverride(iccId, brand);
+ return getITelephony().setOperatorBrandOverride(brand);
} catch (RemoteException ex) {
Rlog.e(TAG, "setOperatorBrandOverride RemoteException", ex);
} catch (NullPointerException ex) {
@@ -3393,6 +3364,7 @@
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
*
* @param enable true means enabling the simplified UI.
+ * @hide
*/
public void enableSimplifiedNetworkSettings(boolean enable) {
enableSimplifiedNetworkSettings(getDefaultSubscription(), enable);
@@ -3408,6 +3380,7 @@
*
* @param subId for which the simplified UI should be enabled or disabled.
* @param enable true means enabling the simplified UI.
+ * @hide
*/
public void enableSimplifiedNetworkSettings(long subId, boolean enable) {
try {
@@ -3424,6 +3397,7 @@
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*
* @return true if the simplified UI is enabled.
+ * @hide
*/
public boolean getSimplifiedNetworkSettingsEnabled() {
return getSimplifiedNetworkSettingsEnabled(getDefaultSubscription());
@@ -3437,6 +3411,7 @@
*
* @param subId for which the simplified UI should be enabled or disabled.
* @return true if the simplified UI is enabled.
+ * @hide
*/
public boolean getSimplifiedNetworkSettingsEnabled(long subId) {
try {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5c3dcdb..79c72b88 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -643,15 +643,6 @@
boolean setPreferredNetworkType(int networkType);
/**
- * Set the CDMA subscription source.
- * Used for device supporting both NV and RUIM for CDMA.
- *
- * @param subscriptionType the subscription type, 0 for RUIM, 1 for NV.
- * @return true on success; false on any failure.
- */
- boolean setCdmaSubscription(int subscriptionType);
-
- /**
* User enable/disable Mobile Data.
*
* @param enable true to turn on, else false
@@ -766,9 +757,9 @@
String getLine1AlphaTagForDisplay(long subId);
/**
- * Override the operator branding for the input ICCID.
+ * Override the operator branding for the current ICCID.
*
- * Once set, whenever the ICCID is inserted into the device, the service
+ * Once set, whenever the SIM is present in the device, the service
* provider name (SPN) and the operator name will both be replaced by the
* brand value input. To unset the value, the same function should be
* called with a null brand value.
@@ -777,11 +768,10 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* or has to be carrier app - see #hasCarrierPrivileges.
*
- * @param iccid The ICCID of that the branding applies to.
* @param brand The brand name to display/set.
* @return true if the operation was executed correctly.
*/
- boolean setOperatorBrandOverride(String iccId, String brand);
+ boolean setOperatorBrandOverride(String brand);
/**
* Returns the result and response from RIL for oem request
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index e03b9c8..44787e7 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -30,7 +30,6 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.BitmapFactory;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -391,6 +390,17 @@
}
@Override
+ protected void onPause() {
+ super.onPause();
+ Log.i(TAG, "I'm such a slooow poor loser");
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ }
+ Log.i(TAG, "See?");
+ }
+
+ @Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mOverrideConfig != null) {
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RevealActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RevealActivity.java
index 3360e30..256a1d4 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/RevealActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RevealActivity.java
@@ -17,6 +17,7 @@
package com.android.test.hwui;
import android.animation.Animator;
+import android.animation.AnimatorSet;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
@@ -34,6 +35,7 @@
private static final int DURATION = 800;
private boolean mShouldBlock;
+ private int mIteration = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -57,10 +59,17 @@
Animator animator = ViewAnimationUtils.createCircularReveal(view,
view.getWidth() / 2, view.getHeight() / 2,
0, Math.max(view.getWidth(), view.getHeight()));
- animator.setDuration(DURATION);
- animator.setAllowRunningAsynchronously(true);
- animator.start();
+ if (mIteration < 2) {
+ animator.setDuration(DURATION);
+ animator.start();
+ } else {
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(animator);
+ set.setDuration(DURATION);
+ set.start();
+ }
+ mIteration = (mIteration + 1) % 4;
mShouldBlock = !mShouldBlock;
if (mShouldBlock) {
view.post(sBlockThread);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 2604e97..c403ce6 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -222,7 +222,8 @@
@Override
public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX,
- int startY, IRemoteCallback startedCallback, boolean scaleUp) {
+ int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
+ boolean scaleUp) {
// TODO Auto-generated method stub
}