diff options
Diffstat (limited to 'core/java')
26 files changed, 633 insertions, 317 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index a285932..7a636db 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -213,6 +213,13 @@ public class ActivityManager { public static final int BROADCAST_STICKY_CANT_HAVE_PERMISSION = -1; /** + * Result for IActivityManager.broadcastIntent: trying to send a broadcast + * to a stopped user. Fail. + * @hide + */ + public static final int BROADCAST_FAILED_USER_STOPPED = -2; + + /** * Type for IActivityManaqer.getIntentSender: this PendingIntent is * for a sendBroadcast operation. * @hide diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 0092ee7..2784d44 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -38,6 +38,7 @@ import android.view.ViewTreeObserver; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; final class BackStackState implements Parcelable { final int[] mOps; @@ -1055,7 +1056,7 @@ final class BackStackRecord extends FragmentTransaction implements } private static ArrayList<View> captureExitingViews(Transition exitTransition, - Fragment outFragment, ArrayMap<String, View> namedViews) { + Fragment outFragment, ArrayMap<String, View> namedViews, View nonExistentView) { ArrayList<View> viewList = null; if (exitTransition != null) { viewList = new ArrayList<View>(); @@ -1064,7 +1065,10 @@ final class BackStackRecord extends FragmentTransaction implements if (namedViews != null) { viewList.removeAll(namedViews.values()); } - addTargets(exitTransition, viewList); + if (!viewList.isEmpty()) { + viewList.add(nonExistentView); + addTargets(exitTransition, viewList); + } } return viewList; } @@ -1132,11 +1136,8 @@ final class BackStackRecord extends FragmentTransaction implements namedViews = mapSharedElementsIn(state, isBack, inFragment); removeTargets(sharedElementTransition, sharedElementTargets); sharedElementTargets.clear(); - if (namedViews.isEmpty()) { - sharedElementTargets.add(state.nonExistentView); - } else { - sharedElementTargets.addAll(namedViews.values()); - } + sharedElementTargets.add(state.nonExistentView); + sharedElementTargets.addAll(namedViews.values()); addTargets(sharedElementTransition, sharedElementTargets); @@ -1153,6 +1154,9 @@ final class BackStackRecord extends FragmentTransaction implements if (namedViews != null) { enteringViews.removeAll(namedViews.values()); } + enteringViews.add(state.nonExistentView); + // We added this earlier to prevent any views being targeted. + enterTransition.removeTarget(state.nonExistentView); addTargets(enterTransition, enteringViews); } setSharedElementEpicenter(enterTransition, state); @@ -1293,11 +1297,8 @@ final class BackStackRecord extends FragmentTransaction implements ArrayList<View> sharedElementTargets = new ArrayList<View>(); if (sharedElementTransition != null) { namedViews = remapSharedElements(state, outFragment, isBack); - if (namedViews.isEmpty()) { - sharedElementTargets.add(state.nonExistentView); - } else { - sharedElementTargets.addAll(namedViews.values()); - } + sharedElementTargets.add(state.nonExistentView); + sharedElementTargets.addAll(namedViews.values()); addTargets(sharedElementTransition, sharedElementTargets); // Notify the start of the transition. @@ -1310,7 +1311,7 @@ final class BackStackRecord extends FragmentTransaction implements } ArrayList<View> exitingViews = captureExitingViews(exitTransition, outFragment, - namedViews); + namedViews, state.nonExistentView); if (exitingViews == null || exitingViews.isEmpty()) { exitTransition = null; } @@ -1388,20 +1389,69 @@ final class BackStackRecord extends FragmentTransaction implements } } - private static void removeTargets(Transition transition, ArrayList<View> views) { - int numViews = views.size(); - for (int i = 0; i < numViews; i++) { - transition.removeTarget(views.get(i)); + /** + * This method removes the views from transitions that target ONLY those views. + * The views list should match those added in addTargets and should contain + * one view that is not in the view hierarchy (state.nonExistentView). + */ + public static void removeTargets(Transition transition, ArrayList<View> views) { + if (transition instanceof TransitionSet) { + TransitionSet set = (TransitionSet) transition; + int numTransitions = set.getTransitionCount(); + for (int i = 0; i < numTransitions; i++) { + Transition child = set.getTransitionAt(i); + removeTargets(child, views); + } + } else if (!hasSimpleTarget(transition)) { + List<View> targets = transition.getTargets(); + if (targets != null && targets.size() == views.size() && + targets.containsAll(views)) { + // We have an exact match. We must have added these earlier in addTargets + for (int i = views.size() - 1; i >= 0; i--) { + transition.removeTarget(views.get(i)); + } + } } } - private static void addTargets(Transition transition, ArrayList<View> views) { - int numViews = views.size(); - for (int i = 0; i < numViews; i++) { - transition.addTarget(views.get(i)); + /** + * This method adds views as targets to the transition, but only if the transition + * doesn't already have a target. It is best for views to contain one View object + * that does not exist in the view hierarchy (state.nonExistentView) so that + * when they are removed later, a list match will suffice to remove the targets. + * Otherwise, if you happened to have targeted the exact views for the transition, + * the removeTargets call will remove them unexpectedly. + */ + public static void addTargets(Transition transition, ArrayList<View> views) { + if (transition instanceof TransitionSet) { + TransitionSet set = (TransitionSet) transition; + int numTransitions = set.getTransitionCount(); + for (int i = 0; i < numTransitions; i++) { + Transition child = set.getTransitionAt(i); + addTargets(child, views); + } + } else if (!hasSimpleTarget(transition)) { + List<View> targets = transition.getTargets(); + if (isNullOrEmpty(targets)) { + // We can just add the target views + int numViews = views.size(); + for (int i = 0; i < numViews; i++) { + transition.addTarget(views.get(i)); + } + } } } + private static boolean hasSimpleTarget(Transition transition) { + return !isNullOrEmpty(transition.getTargetIds()) || + !isNullOrEmpty(transition.getTargetNames()) || + !isNullOrEmpty(transition.getTargetTypes()); + } + + private static boolean isNullOrEmpty(List list) { + return list == null || list.isEmpty(); + } + /** * Remaps a name-to-View map, substituting different names for keys. * diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index c65f017..dfe5cf5 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1113,7 +1113,11 @@ public class Notification implements Parcelable /** Notification action extra which contains wearable extensions */ private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; + // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options. private static final String KEY_FLAGS = "flags"; + private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel"; + private static final String KEY_CONFIRM_LABEL = "confirmLabel"; + private static final String KEY_CANCEL_LABEL = "cancelLabel"; // Flags bitwise-ored to mFlags private static final int FLAG_AVAILABLE_OFFLINE = 0x1; @@ -1123,6 +1127,10 @@ public class Notification implements Parcelable private int mFlags = DEFAULT_FLAGS; + private CharSequence mInProgressLabel; + private CharSequence mConfirmLabel; + private CharSequence mCancelLabel; + /** * Create a {@link android.app.Notification.Action.WearableExtender} with default * options. @@ -1139,6 +1147,9 @@ public class Notification implements Parcelable Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS); if (wearableBundle != null) { mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS); + mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL); + mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL); + mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL); } } @@ -1154,6 +1165,15 @@ public class Notification implements Parcelable if (mFlags != DEFAULT_FLAGS) { wearableBundle.putInt(KEY_FLAGS, mFlags); } + if (mInProgressLabel != null) { + wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel); + } + if (mConfirmLabel != null) { + wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel); + } + if (mCancelLabel != null) { + wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel); + } builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle); return builder; @@ -1163,6 +1183,9 @@ public class Notification implements Parcelable public WearableExtender clone() { WearableExtender that = new WearableExtender(); that.mFlags = this.mFlags; + that.mInProgressLabel = this.mInProgressLabel; + that.mConfirmLabel = this.mConfirmLabel; + that.mCancelLabel = this.mCancelLabel; return that; } @@ -1194,6 +1217,72 @@ public class Notification implements Parcelable mFlags &= ~mask; } } + + /** + * Set a label to display while the wearable is preparing to automatically execute the + * action. This is usually a 'ing' verb ending in ellipsis like "Sending..." + * + * @param label the label to display while the action is being prepared to execute + * @return this object for method chaining + */ + public WearableExtender setInProgressLabel(CharSequence label) { + mInProgressLabel = label; + return this; + } + + /** + * Get the label to display while the wearable is preparing to automatically execute + * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..." + * + * @return the label to display while the action is being prepared to execute + */ + public CharSequence getInProgressLabel() { + return mInProgressLabel; + } + + /** + * Set a label to display to confirm that the action should be executed. + * This is usually an imperative verb like "Send". + * + * @param label the label to confirm the action should be executed + * @return this object for method chaining + */ + public WearableExtender setConfirmLabel(CharSequence label) { + mConfirmLabel = label; + return this; + } + + /** + * Get the label to display to confirm that the action should be executed. + * This is usually an imperative verb like "Send". + * + * @return the label to confirm the action should be executed + */ + public CharSequence getConfirmLabel() { + return mConfirmLabel; + } + + /** + * Set a label to display to cancel the action. + * This is usually an imperative verb, like "Cancel". + * + * @param label the label to display to cancel the action + * @return this object for method chaining + */ + public WearableExtender setCancelLabel(CharSequence label) { + mCancelLabel = label; + return this; + } + + /** + * Get the label to display to cancel the action. + * This is usually an imperative verb like "Cancel". + * + * @return the label to display to cancel the action + */ + public CharSequence getCancelLabel() { + return mCancelLabel; + } } } @@ -4356,7 +4445,7 @@ public class Notification implements Parcelable /** Notification extra which contains wearable extensions */ private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; - // Keys within EXTRA_WEARABLE_OPTIONS for wearable options. + // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options. private static final String KEY_ACTIONS = "actions"; private static final String KEY_FLAGS = "flags"; private static final String KEY_DISPLAY_INTENT = "displayIntent"; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index d3ff79d..9157b1b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -62,13 +62,16 @@ import java.util.Collections; import java.util.List; /** - * Public interface for managing policies enforced on a device. Most clients - * of this class must have published a {@link DeviceAdminReceiver} that the user - * has currently enabled. + * Public interface for managing policies enforced on a device. Most clients of this class must be + * registered with the system as a + * <a href={@docRoot}guide/topics/admin/device-admin.html">device administrator</a>. Additionally, + * a device administrator may be registered as either a profile or device owner. A given method is + * accessible to all device administrators unless the documentation for that method specifies that + * it is restricted to either device or profile owners. * * <div class="special reference"> * <h3>Developer Guides</h3> - * <p>For more information about managing policies for device adminstration, read the + * <p>For more information about managing policies for device administration, read the * <a href="{@docRoot}guide/topics/admin/device-admin.html">Device Administration</a> * developer guide.</p> * </div> @@ -2586,6 +2589,10 @@ public class DevicePolicyManager { * <p>The application restrictions are only made visible to the target application and the * profile or device owner. * + * <p>If the restrictions are not available yet, but may be applied in the near future, + * the admin can notify the target application of that by adding + * {@link UserManager#KEY_RESTRICTIONS_PENDING} to the settings parameter. + * * <p>The calling device admin must be a profile or device owner; if it is not, a security * exception will be thrown. * @@ -2593,6 +2600,8 @@ public class DevicePolicyManager { * @param packageName The name of the package to update restricted settings for. * @param settings A {@link Bundle} to be parsed by the receiving application, conveying a new * set of active restrictions. + * + * @see UserManager#KEY_RESTRICTIONS_PENDING */ public void setApplicationRestrictions(ComponentName admin, String packageName, Bundle settings) { diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index 8a44c8e..0a2d4f5 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -291,4 +291,16 @@ interface IBackupManager { * {@hide} */ void opComplete(int token); + + /** + * Make the device's backup and restore machinery (in)active. When it is inactive, + * the device will not perform any backup operations, nor will it deliver data for + * restore, although clients can still safely call BackupManager methods. + * + * @param whichUser User handle of the defined user whose backup active state + * is to be adjusted. + * @param makeActive {@code true} when backup services are to be made active; + * {@code false} otherwise. + */ + void setBackupServiceActive(int whichUser, boolean makeActive); } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 992f601..cd4535a 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -98,4 +98,7 @@ interface IBluetooth boolean isActivityAndEnergyReportingSupported(); void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); + + // for dumpsys support + String dump(); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 3234e77..bd6eeea 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -367,10 +367,27 @@ public class UserManager { * <p/>Type: Boolean * @see #setUserRestrictions(Bundle) * @see #getUserRestrictions() - * @hide */ public static final String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam"; + /** + * Application restriction key that is used to indicate the pending arrival + * of real restrictions for the app. + * + * <p> + * Applications that support restrictions should check for the presence of this key. + * A <code>true</code> value indicates that restrictions may be applied in the near + * future but are not available yet. It is the responsibility of any + * management application that sets this flag to update it when the final + * restrictions are enforced. + * + * <p/>Key for application restrictions. + * <p/>Type: Boolean + * @see android.app.admin.DevicePolicyManager#addApplicationRestriction() + * @see android.app.admin.DevicePolicyManager#getApplicationRestriction() + */ + public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending"; + /** @hide */ public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3; /** @hide */ diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index 00d60c0..d6c997f 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -94,31 +94,6 @@ public class TrustAgentService extends Service { private static final int MSG_TRUST_TIMEOUT = 3; /** - * Container class for a list of configuration options and helper methods - */ - public static final class Configuration { - public final List<PersistableBundle> options; - public Configuration(List<PersistableBundle> opts) { - options = opts; - } - - /** - * Very basic method to determine if all bundles have the given feature, regardless - * of type. - * @param option String to search for. - * @return true if found in all bundles. - */ - public boolean hasOption(String option) { - if (options == null || options.size() == 0) return false; - final int N = options.size(); - for (int i = 0; i < N; i++) { - if (!options.get(i).containsKey(option)) return false; - } - return true; - } - } - - /** * Class containing raw data for a given configuration request. */ private static final class ConfigurationData { @@ -147,7 +122,7 @@ public class TrustAgentService extends Service { break; case MSG_CONFIGURE: ConfigurationData data = (ConfigurationData) msg.obj; - boolean result = onConfigure(new Configuration(data.options)); + boolean result = onConfigure(data.options); try { synchronized (mLock) { mCallback.onConfigureCompleted(result, data.token); @@ -212,7 +187,7 @@ public class TrustAgentService extends Service { * @param options bundle containing all options or null if none. * @return true if the {@link TrustAgentService} supports configuration options. */ - public boolean onConfigure(Configuration options) { + public boolean onConfigure(List<PersistableBundle> options) { return false; } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 26e9a30..ceaf5f8 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -675,7 +675,8 @@ public abstract class WallpaperService extends Service { com.android.internal.R.style.Animation_Wallpaper; mInputChannel = new InputChannel(); if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, - Display.DEFAULT_DISPLAY, mContentInsets, mInputChannel) < 0) { + Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, + mInputChannel) < 0) { Log.w(TAG, "Failed to add window while updating wallpaper surface."); return; } diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java index 3fd28a6..a159b40 100644 --- a/core/java/android/transition/ChangeTransform.java +++ b/core/java/android/transition/ChangeTransform.java @@ -17,11 +17,14 @@ package android.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.FloatArrayEvaluator; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; +import android.animation.PropertyValuesHolder; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Matrix; +import android.graphics.Path; +import android.graphics.PointF; import android.util.AttributeSet; import android.util.Property; import android.view.GhostView; @@ -56,16 +59,35 @@ public class ChangeTransform extends Transition { PROPNAME_PARENT_MATRIX, }; - private static final Property<View, Matrix> ANIMATION_MATRIX_PROPERTY = - new Property<View, Matrix>(Matrix.class, "animationMatrix") { + /** + * This property sets the animation matrix properties that are not translations. + */ + private static final Property<PathAnimatorMatrix, float[]> NON_TRANSLATIONS_PROPERTY = + new Property<PathAnimatorMatrix, float[]>(float[].class, "nonTranslations") { @Override - public Matrix get(View object) { + public float[] get(PathAnimatorMatrix object) { return null; } @Override - public void set(View object, Matrix value) { - object.setAnimationMatrix(value); + public void set(PathAnimatorMatrix object, float[] value) { + object.setValues(value); + } + }; + + /** + * This property sets the translation animation matrix properties. + */ + private static final Property<PathAnimatorMatrix, PointF> TRANSLATIONS_PROPERTY = + new Property<PathAnimatorMatrix, PointF>(PointF.class, "translations") { + @Override + public PointF get(PathAnimatorMatrix object) { + return null; + } + + @Override + public void set(PathAnimatorMatrix object, PointF value) { + object.setTranslation(value); } }; @@ -261,8 +283,23 @@ public class ChangeTransform extends Transition { final View view = endValues.view; setIdentityTransforms(view); - ObjectAnimator animator = ObjectAnimator.ofObject(view, ANIMATION_MATRIX_PROPERTY, - new TransitionUtils.MatrixEvaluator(), startMatrix, endMatrix); + final float[] startMatrixValues = new float[9]; + startMatrix.getValues(startMatrixValues); + final float[] endMatrixValues = new float[9]; + endMatrix.getValues(endMatrixValues); + final PathAnimatorMatrix pathAnimatorMatrix = + new PathAnimatorMatrix(view, startMatrixValues); + + PropertyValuesHolder valuesProperty = PropertyValuesHolder.ofObject( + NON_TRANSLATIONS_PROPERTY, new FloatArrayEvaluator(new float[9]), + startMatrixValues, endMatrixValues); + Path path = getPathMotion().getPath(startMatrixValues[Matrix.MTRANS_X], + startMatrixValues[Matrix.MTRANS_Y], endMatrixValues[Matrix.MTRANS_X], + endMatrixValues[Matrix.MTRANS_Y]); + PropertyValuesHolder translationProperty = PropertyValuesHolder.ofObject( + TRANSLATIONS_PROPERTY, null, path); + ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(pathAnimatorMatrix, + valuesProperty, translationProperty); final Matrix finalEndMatrix = endMatrix; @@ -285,14 +322,13 @@ public class ChangeTransform extends Transition { view.setTagInternal(R.id.parentMatrix, null); } } - ANIMATION_MATRIX_PROPERTY.set(view, null); + view.setAnimationMatrix(null); transforms.restore(view); } @Override public void onAnimationPause(Animator animation) { - ValueAnimator animator = (ValueAnimator) animation; - Matrix currentMatrix = (Matrix) animator.getAnimatedValue(); + Matrix currentMatrix = pathAnimatorMatrix.getMatrix(); setCurrentMatrix(currentMatrix); } @@ -457,4 +493,47 @@ public class ChangeTransform extends Transition { mGhostView.setVisibility(View.VISIBLE); } } + + /** + * PathAnimatorMatrix allows the translations and the rest of the matrix to be set + * separately. This allows the PathMotion to affect the translations while scale + * and rotation are evaluated separately. + */ + private static class PathAnimatorMatrix { + private final Matrix mMatrix = new Matrix(); + private final View mView; + private final float[] mValues; + private float mTranslationX; + private float mTranslationY; + + public PathAnimatorMatrix(View view, float[] values) { + mView = view; + mValues = values.clone(); + mTranslationX = mValues[Matrix.MTRANS_X]; + mTranslationY = mValues[Matrix.MTRANS_Y]; + setAnimationMatrix(); + } + + public void setValues(float[] values) { + System.arraycopy(values, 0, mValues, 0, values.length); + setAnimationMatrix(); + } + + public void setTranslation(PointF translation) { + mTranslationX = translation.x; + mTranslationY = translation.y; + setAnimationMatrix(); + } + + private void setAnimationMatrix() { + mValues[Matrix.MTRANS_X] = mTranslationX; + mValues[Matrix.MTRANS_Y] = mTranslationY; + mMatrix.setValues(mValues); + mView.setAnimationMatrix(mMatrix); + } + + public Matrix getMatrix() { + return mMatrix; + } + } } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 037ed28..7b13e84 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -36,15 +36,16 @@ import android.view.Surface; */ interface IWindowSession { int add(IWindow window, int seq, in WindowManager.LayoutParams attrs, - in int viewVisibility, out Rect outContentInsets, + in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets, out InputChannel outInputChannel); int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outContentInsets, - out InputChannel outInputChannel); + out Rect outStableInsets, out InputChannel outInputChannel); int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, - in int viewVisibility, out Rect outContentInsets); + in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets); int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, - in int viewVisibility, in int layerStackId, out Rect outContentInsets); + in int viewVisibility, in int layerStackId, out Rect outContentInsets, + out Rect outStableInsets); void remove(IWindow window); /** diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index afc804c..49be57d 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -496,7 +496,8 @@ public class SurfaceView extends View { mLayout.type = mWindowType; mLayout.gravity = Gravity.START|Gravity.TOP; mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout, - mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets); + mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets, + mStableInsets); } boolean realSizeChanged; diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 00a8884..5579c13 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -66,6 +66,8 @@ public class ThreadedRenderer extends HardwareRenderer { private static final int SYNC_OK = 0; // Needs a ViewRoot invalidate private static final int SYNC_INVALIDATE_REQUIRED = 1 << 0; + // Spoiler: the reward is GPU-accelerated drawing, better find that Surface! + private static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1; private static final String[] VISUALIZERS = { PROFILE_PROPERTY_VISUALIZE_BARS, @@ -336,6 +338,12 @@ public class ThreadedRenderer extends HardwareRenderer { int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos, recordDuration, view.getResources().getDisplayMetrics().density); + if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { + setEnabled(false); + // Invalidate since we failed to draw. This should fetch a Surface + // if it is still needed or do nothing if we are no longer drawing + attachInfo.mViewRootImpl.invalidate(); + } if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) { attachInfo.mViewRootImpl.invalidate(); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index ea0a077..5d2a24b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -526,7 +526,7 @@ public final class ViewRootImpl implements ViewParent, collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), - mAttachInfo.mContentInsets, mInputChannel); + mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 673f075..b8e94ee 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -865,12 +865,15 @@ public interface WindowManagerPolicy { * Return the insets for the areas covered by system windows. These values * are computed on the most recent layout, so they are not guaranteed to * be correct. - * + * * @param attrs The LayoutParams of the window. - * @param contentInset The areas covered by system windows, expressed as positive insets - * + * @param outContentInsets The areas covered by system windows, expressed as positive insets. + * @param outStableInsets The areas covered by stable system windows irrespective of their + * current visibility. Expressed as positive insets. + * */ - public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset); + public void getInsetHintLw(WindowManager.LayoutParams attrs, Rect outContentInsets, + Rect outStableInsets); /** * Called when layout of the windows is finished. After this function has diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index 64c81e0..cf3dbab 100644 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -183,8 +183,11 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i mHeaderYearTextView.getTextColors(), R.attr.state_selected, headerSelectedTextColor)); - mDayPickerView = new DayPickerView(mContext, this); + mDayPickerView = new DayPickerView(mContext); + mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek); mDayPickerView.setRange(mMinDate, mMaxDate); + mDayPickerView.setDay(mCurrentDate); + mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener); mYearPickerView = new YearPickerView(mContext); mYearPickerView.init(this); @@ -333,7 +336,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i switch (viewIndex) { case MONTH_AND_DAY_VIEW: - mDayPickerView.onDateChanged(); + mDayPickerView.setDay(getSelectedDay()); if (mCurrentView != viewIndex) { mMonthAndDayLayout.setSelected(true); mHeaderYearTextView.setSelected(false); @@ -445,6 +448,8 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i @Override public void setFirstDayOfWeek(int firstDayOfWeek) { mFirstDayOfWeek = firstDayOfWeek; + + mDayPickerView.setFirstDayOfWeek(firstDayOfWeek); } @Override @@ -606,19 +611,12 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i } } - @Override - public void onDayOfMonthSelected(int year, int month, int day) { - mCurrentDate.set(Calendar.YEAR, year); - mCurrentDate.set(Calendar.MONTH, month); - mCurrentDate.set(Calendar.DAY_OF_MONTH, day); - updatePickers(); - updateDisplay(true); - } - private void updatePickers() { for (OnDateChangedListener listener : mListeners) { listener.onDateChanged(); } + + mDayPickerView.setDay(getSelectedDay()); } @Override @@ -627,11 +625,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i } @Override - public void unregisterOnDateChangedListener(OnDateChangedListener listener) { - mListeners.remove(listener); - } - - @Override public Calendar getSelectedDay() { return mCurrentDate; } @@ -652,6 +645,22 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i } /** + * Listener called when the user selects a day in the day picker view. + */ + private final DayPickerView.OnDaySelectedListener + mOnDaySelectedListener = new DayPickerView.OnDaySelectedListener() { + @Override + public void onDaySelected(DayPickerView view, Calendar day) { + mCurrentDate.setTimeInMillis(day.getTimeInMillis()); + + updatePickers(); + updateDisplay(true); + + tryVibrate(); + } + }; + + /** * Class for managing state storing/restoring. */ private static class SavedState extends View.BaseSavedState { diff --git a/core/java/android/widget/DatePickerController.java b/core/java/android/widget/DatePickerController.java index ea6ec61..8f809ba 100644 --- a/core/java/android/widget/DatePickerController.java +++ b/core/java/android/widget/DatePickerController.java @@ -27,16 +27,9 @@ interface DatePickerController { void onYearSelected(int year); - void onDayOfMonthSelected(int year, int month, int day); - void registerOnDateChangedListener(OnDateChangedListener listener); - void unregisterOnDateChangedListener(OnDateChangedListener listener); - Calendar getSelectedDay(); - void setFirstDayOfWeek(int firstDayOfWeek); - int getFirstDayOfWeek(); - void tryVibrate(); } diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java index 45d1403..443884a 100644 --- a/core/java/android/widget/DateTimeView.java +++ b/core/java/android/widget/DateTimeView.java @@ -32,6 +32,7 @@ import android.widget.RemoteViews.RemoteView; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; // @@ -62,8 +63,8 @@ public class DateTimeView extends TextView { int mLastDisplay = -1; DateFormat mLastFormat; - private boolean mAttachedToWindow; private long mUpdateTimeMillis; + private static final ThreadLocal<ReceiverInfo> sReceiverInfo = new ThreadLocal<ReceiverInfo>(); public DateTimeView(Context context) { super(context); @@ -76,15 +77,21 @@ public class DateTimeView extends TextView { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - registerReceivers(); - mAttachedToWindow = true; + ReceiverInfo ri = sReceiverInfo.get(); + if (ri == null) { + ri = new ReceiverInfo(); + sReceiverInfo.set(ri); + } + ri.addView(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - unregisterReceivers(); - mAttachedToWindow = false; + final ReceiverInfo ri = sReceiverInfo.get(); + if (ri != null) { + ri.removeView(this); + } } @android.view.RemotableViewMethod @@ -204,49 +211,86 @@ public class DateTimeView extends TextView { } } - private void registerReceivers() { - Context context = getContext(); + void clearFormatAndUpdate() { + mLastFormat = null; + update(); + } - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_TIME_TICK); - filter.addAction(Intent.ACTION_TIME_CHANGED); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - context.registerReceiver(mBroadcastReceiver, filter); + private static class ReceiverInfo { + private final ArrayList<DateTimeView> mAttachedViews = new ArrayList<DateTimeView>(); + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_TIME_TICK.equals(action)) { + if (System.currentTimeMillis() < getSoonestUpdateTime()) { + // The update() function takes a few milliseconds to run because of + // all of the time conversions it needs to do, so we can't do that + // every minute. + return; + } + } + // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format. + updateAll(); + } + }; - Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT); - context.getContentResolver().registerContentObserver(uri, true, mContentObserver); - } + private final ContentObserver mObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + updateAll(); + } + }; - private void unregisterReceivers() { - Context context = getContext(); - context.unregisterReceiver(mBroadcastReceiver); - context.getContentResolver().unregisterContentObserver(mContentObserver); - } + public void addView(DateTimeView v) { + final boolean register = mAttachedViews.isEmpty(); + mAttachedViews.add(v); + if (register) { + register(v.getContext().getApplicationContext()); + } + } - private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_TIME_TICK.equals(action)) { - if (System.currentTimeMillis() < mUpdateTimeMillis) { - // The update() function takes a few milliseconds to run because of - // all of the time conversions it needs to do, so we can't do that - // every minute. - return; + public void removeView(DateTimeView v) { + mAttachedViews.remove(v); + if (mAttachedViews.isEmpty()) { + unregister(v.getContext().getApplicationContext()); + } + } + + void updateAll() { + final int count = mAttachedViews.size(); + for (int i = 0; i < count; i++) { + mAttachedViews.get(i).clearFormatAndUpdate(); + } + } + + long getSoonestUpdateTime() { + long result = Long.MAX_VALUE; + final int count = mAttachedViews.size(); + for (int i = 0; i < count; i++) { + final long time = mAttachedViews.get(i).mUpdateTimeMillis; + if (time < result) { + result = time; } } - // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format. - mLastFormat = null; - update(); + return result; } - }; - private ContentObserver mContentObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - mLastFormat = null; - update(); + void register(Context context) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_TIME_TICK); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + context.registerReceiver(mReceiver, filter); + + final Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT); + context.getContentResolver().registerContentObserver(uri, true, mObserver); } - }; + + void unregister(Context context) { + context.unregisterReceiver(mReceiver); + context.getContentResolver().unregisterContentObserver(mObserver); + } + } } diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index f9544d0..6cb1c9d 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -16,14 +16,10 @@ package android.widget; -import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; -import android.os.Build; import android.os.Bundle; -import android.os.Handler; -import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; import android.view.View; @@ -38,9 +34,7 @@ import java.util.Locale; /** * This displays a list of months in a calendar format with selectable days. */ -class DayPickerView extends ListView implements AbsListView.OnScrollListener, - OnDateChangedListener { - +class DayPickerView extends ListView implements AbsListView.OnScrollListener { private static final String TAG = "DayPickerView"; // How long the GoTo fling animation should last @@ -49,12 +43,14 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, // How long to wait after receiving an onScrollStateChanged notification before acting on it private static final int SCROLL_CHANGE_DELAY = 40; - private static int LIST_TOP_OFFSET = -1; // so that the top line will be under the separator + // so that the top line will be under the separator + private static final int LIST_TOP_OFFSET = -1; - private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault()); + private final SimpleMonthAdapter mAdapter = new SimpleMonthAdapter(getContext()); + + private final ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this); - // These affect the scroll speed and feel - private float mFriction = 1.0f; + private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault()); // highlighted time private Calendar mSelectedDay = Calendar.getInstance(); @@ -62,7 +58,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, private Calendar mMinDate = Calendar.getInstance(); private Calendar mMaxDate = Calendar.getInstance(); - private SimpleMonthAdapter mAdapter; + private OnDaySelectedListener mOnDaySelectedListener; // which month should be displayed/highlighted [0-11] private int mCurrentMonthDisplayed; @@ -71,34 +67,27 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, // used for tracking what state listview is in private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE; - private DatePickerController mController; private boolean mPerformingScroll; - private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this); - - public DayPickerView(Context context, DatePickerController controller) { + public DayPickerView(Context context) { super(context); - init(); - setController(controller); - } - - public void setController(DatePickerController controller) { - if (mController != null) { - mController.unregisterOnDateChangedListener(this); - } - mController = controller; - mController.registerOnDateChangedListener(this); - setUpAdapter(); setAdapter(mAdapter); - onDateChanged(); - } - - public void init() { setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); setDrawSelectorOnTop(false); - setUpListView(); + + goTo(mSelectedDay, false, true, true); + + mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener); + } + + public void setDay(Calendar day) { + goTo(day, false, true, true); + } + + public void setFirstDayOfWeek(int firstDayOfWeek) { + mAdapter.setFirstDayOfWeek(firstDayOfWeek); } public void setRange(Calendar minDate, Calendar maxDate) { @@ -113,54 +102,19 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, } /** - * Constrains the supplied calendar to stay within the min and max - * calendars, returning <code>true</code> if the supplied calendar - * was modified. + * Sets the listener to call when the user selects a day. * - * @param value The calendar to constrain - * @param min The minimum calendar - * @param max The maximum calendar - * @return True if <code>value</code> was modified - */ - private boolean constrainCalendar(Calendar value, Calendar min, Calendar max) { - if (value.compareTo(min) < 0) { - value.setTimeInMillis(min.getTimeInMillis()); - return true; - } - - if (value.compareTo(max) > 0) { - value.setTimeInMillis(max.getTimeInMillis()); - return true; - } - - return false; - } - - public void onChange() { - setUpAdapter(); - setAdapter(mAdapter); - } - - /** - * Creates a new adapter if necessary and sets up its parameters. Override - * this method to provide a custom adapter. + * @param listener The listener to call. */ - protected void setUpAdapter() { - if (mAdapter == null) { - mAdapter = new SimpleMonthAdapter(getContext(), mController); - } else { - mAdapter.setSelectedDay(mSelectedDay); - mAdapter.notifyDataSetChanged(); - } - // refresh the view with the new parameters - mAdapter.notifyDataSetChanged(); + public void setOnDaySelectedListener(OnDaySelectedListener listener) { + mOnDaySelectedListener = listener; } /* * Sets all the required fields for the list view. Override this method to * set a different list view behavior. */ - protected void setUpListView() { + private void setUpListView() { // Transparent background on scroll setCacheColorHint(0); // No dividers @@ -173,7 +127,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, setOnScrollListener(this); setFadingEdgeLength(0); // Make the scrolling behavior nicer - setFriction(ViewConfiguration.getScrollFriction() * mFriction); + setFriction(ViewConfiguration.getScrollFriction()); } private int getDiffMonths(Calendar start, Calendar end) { @@ -203,7 +157,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, * visible * @return Whether or not the view animated to the new location */ - public boolean goTo(Calendar day, boolean animate, boolean setSelected, boolean forceScroll) { + private boolean goTo(Calendar day, boolean animate, boolean setSelected, boolean forceScroll) { // Set the selected day if (setSelected) { @@ -392,11 +346,6 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, return firstPosition + mostVisibleIndex; } - @Override - public void onDateChanged() { - goTo(mController.getSelectedDay(), false, true, true); - } - /** * Attempts to return the date that has accessibility focus. * @@ -529,4 +478,18 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, mPerformingScroll = true; return true; } + + public interface OnDaySelectedListener { + public void onDaySelected(DayPickerView view, Calendar day); + } + + private final SimpleMonthAdapter.OnDaySelectedListener + mProxyOnDaySelectedListener = new SimpleMonthAdapter.OnDaySelectedListener() { + @Override + public void onDaySelected(SimpleMonthAdapter adapter, Calendar day) { + if (mOnDaySelectedListener != null) { + mOnDaySelectedListener.onDaySelected(DayPickerView.this, day); + } + } + }; } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 54a7940..396c0b9 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -198,54 +198,17 @@ public class PopupWindow { mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); final TypedArray a = context.obtainStyledAttributes( - attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes); - - mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground); + attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes); + final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground); mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0); mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false); final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1); - mAnimationStyle = animStyle == com.android.internal.R.style.Animation_PopupWindow ? -1 : - animStyle; + mAnimationStyle = animStyle == R.style.Animation_PopupWindow ? -1 : animStyle; - // If this is a StateListDrawable, try to find and store the drawable to be - // used when the drop-down is placed above its anchor view, and the one to be - // used when the drop-down is placed below its anchor view. We extract - // the drawables ourselves to work around a problem with using refreshDrawableState - // that it will take into account the padding of all drawables specified in a - // StateListDrawable, thus adding superfluous padding to drop-down views. - // - // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and - // at least one other drawable, intended for the 'below-anchor state'. - if (mBackground instanceof StateListDrawable) { - StateListDrawable background = (StateListDrawable) mBackground; - - // Find the above-anchor view - this one's easy, it should be labeled as such. - int aboveAnchorStateIndex = background.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET); - - // Now, for the below-anchor view, look for any other drawable specified in the - // StateListDrawable which is not for the above-anchor state and use that. - int count = background.getStateCount(); - int belowAnchorStateIndex = -1; - for (int i = 0; i < count; i++) { - if (i != aboveAnchorStateIndex) { - belowAnchorStateIndex = i; - break; - } - } - - // Store the drawables we found, if we found them. Otherwise, set them both - // to null so that we'll just use refreshDrawableState. - if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) { - mAboveAnchorBackgroundDrawable = background.getStateDrawable(aboveAnchorStateIndex); - mBelowAnchorBackgroundDrawable = background.getStateDrawable(belowAnchorStateIndex); - } else { - mBelowAnchorBackgroundDrawable = null; - mAboveAnchorBackgroundDrawable = null; - } - } - a.recycle(); + + setBackgroundDrawable(bg); } /** @@ -346,6 +309,43 @@ public class PopupWindow { */ public void setBackgroundDrawable(Drawable background) { mBackground = background; + + // If this is a StateListDrawable, try to find and store the drawable to be + // used when the drop-down is placed above its anchor view, and the one to be + // used when the drop-down is placed below its anchor view. We extract + // the drawables ourselves to work around a problem with using refreshDrawableState + // that it will take into account the padding of all drawables specified in a + // StateListDrawable, thus adding superfluous padding to drop-down views. + // + // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and + // at least one other drawable, intended for the 'below-anchor state'. + if (mBackground instanceof StateListDrawable) { + StateListDrawable stateList = (StateListDrawable) mBackground; + + // Find the above-anchor view - this one's easy, it should be labeled as such. + int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET); + + // Now, for the below-anchor view, look for any other drawable specified in the + // StateListDrawable which is not for the above-anchor state and use that. + int count = stateList.getStateCount(); + int belowAnchorStateIndex = -1; + for (int i = 0; i < count; i++) { + if (i != aboveAnchorStateIndex) { + belowAnchorStateIndex = i; + break; + } + } + + // Store the drawables we found, if we found them. Otherwise, set them both + // to null so that we'll just use refreshDrawableState. + if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) { + mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex); + mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex); + } else { + mBelowAnchorBackgroundDrawable = null; + mAboveAnchorBackgroundDrawable = null; + } + } } /** diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java index 5aa78c8..ecd2912 100644 --- a/core/java/android/widget/SimpleMonthAdapter.java +++ b/core/java/android/widget/SimpleMonthAdapter.java @@ -20,29 +20,28 @@ import android.content.Context; import android.content.res.ColorStateList; import android.view.View; import android.view.ViewGroup; +import android.widget.SimpleMonthView.OnDayClickListener; import java.util.Calendar; -import java.util.HashMap; /** * An adapter for a list of {@link android.widget.SimpleMonthView} items. */ -class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayClickListener { +class SimpleMonthAdapter extends BaseAdapter { private final Calendar mMinDate = Calendar.getInstance(); private final Calendar mMaxDate = Calendar.getInstance(); private final Context mContext; - private final DatePickerController mController; private Calendar mSelectedDay; private ColorStateList mCalendarTextColors; + private OnDaySelectedListener mOnDaySelectedListener; - public SimpleMonthAdapter(Context context, DatePickerController controller) { - mContext = context; - mController = controller; + private int mFirstDayOfWeek; - init(); - setSelectedDay(mController.getSelectedDay()); + public SimpleMonthAdapter(Context context) { + mContext = context; + mSelectedDay = Calendar.getInstance(); } public void setRange(Calendar min, Calendar max) { @@ -52,27 +51,34 @@ class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayCli notifyDataSetInvalidated(); } + public void setFirstDayOfWeek(int firstDayOfWeek) { + mFirstDayOfWeek = firstDayOfWeek; + + notifyDataSetInvalidated(); + } + /** * Updates the selected day and related parameters. * * @param day The day to highlight */ public void setSelectedDay(Calendar day) { - if (mSelectedDay != day) { - mSelectedDay = day; - notifyDataSetChanged(); - } - } + mSelectedDay = day; - void setCalendarTextColor(ColorStateList colors) { - mCalendarTextColors = colors; + notifyDataSetChanged(); } /** - * Set up the gesture detector and selected time + * Sets the listener to call when the user selects a day. + * + * @param listener The listener to call. */ - protected void init() { - mSelectedDay = Calendar.getInstance(); + public void setOnDaySelectedListener(OnDaySelectedListener listener) { + mOnDaySelectedListener = listener; + } + + void setCalendarTextColor(ColorStateList colors) { + mCalendarTextColors = colors; } @Override @@ -111,7 +117,7 @@ class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayCli AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.MATCH_PARENT); v.setLayoutParams(params); v.setClickable(true); - v.setOnDayClickListener(this); + v.setOnDayClickListener(mOnDayClickListener); if (mCalendarTextColors != null) { v.setTextColor(mCalendarTextColors); @@ -148,7 +154,7 @@ class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayCli enabledDayRangeEnd = 31; } - v.setMonthParams(selectedDay, month, year, mController.getFirstDayOfWeek(), + v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek, enabledDayRangeStart, enabledDayRangeEnd); v.invalidate(); @@ -159,27 +165,24 @@ class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayCli return mSelectedDay.get(Calendar.YEAR) == year && mSelectedDay.get(Calendar.MONTH) == month; } - @Override - public void onDayClick(SimpleMonthView view, Calendar day) { - if (day != null && isCalendarInRange(day)) { - onDaySelected(day); - } - } - private boolean isCalendarInRange(Calendar value) { return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0; } - /** - * Maintains the same hour/min/sec but moves the day to the tapped day. - * - * @param day The day that was tapped - */ - private void onDaySelected(Calendar day) { - mController.tryVibrate(); - mController.onDayOfMonthSelected(day.get(Calendar.YEAR), day.get(Calendar.MONTH), - day.get(Calendar.DAY_OF_MONTH)); + private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() { + @Override + public void onDayClick(SimpleMonthView view, Calendar day) { + if (day != null && isCalendarInRange(day)) { + setSelectedDay(day); + + if (mOnDaySelectedListener != null) { + mOnDaySelectedListener.onDaySelected(SimpleMonthAdapter.this, day); + } + } + } + }; - setSelectedDay(day); + public interface OnDaySelectedListener { + public void onDaySelected(SimpleMonthAdapter view, Calendar day); } } diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index 86f580d..b5338df 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -23,8 +23,11 @@ import android.os.Process; import android.os.StrictMode; import android.os.SystemClock; import android.util.Slog; + import com.android.internal.util.FastPrintWriter; +import libcore.io.IoUtils; + import java.io.File; import java.io.FileInputStream; import java.io.PrintWriter; @@ -325,7 +328,12 @@ public class ProcessCpuTracker { mBaseIdleTime = idletime; } - mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats); + final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); + try { + mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats); + } finally { + StrictMode.setThreadPolicy(savedPolicy); + } final float[] loadAverages = mLoadAverageData; if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT, @@ -847,12 +855,7 @@ public class ProcessCpuTracker { } catch (java.io.FileNotFoundException e) { } catch (java.io.IOException e) { } finally { - if (is != null) { - try { - is.close(); - } catch (java.io.IOException e) { - } - } + IoUtils.closeQuietly(is); StrictMode.setThreadPolicy(savedPolicy); } return null; diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index d26f79e..7ad3470 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -1940,13 +1940,19 @@ public class StateMachine { * @param args */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(getName() + ":"); - pw.println(" total records=" + getLogRecCount()); + pw.println(this.toString()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getName() + ":\n"); + sb.append(" total records=" + getLogRecCount() + "\n"); for (int i = 0; i < getLogRecSize(); i++) { - pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString()); - pw.flush(); + sb.append(" rec[" + i + "]: " + getLogRec(i).toString() + "\n"); } - pw.println("curState=" + getCurrentState().getName()); + sb.append("curState=" + getCurrentState().getName()); + return sb.toString(); } /** diff --git a/core/java/com/android/internal/widget/ILockSettingsObserver.aidl b/core/java/com/android/internal/widget/ILockSettingsObserver.aidl index 6c354d8..edf8f0e 100644 --- a/core/java/com/android/internal/widget/ILockSettingsObserver.aidl +++ b/core/java/com/android/internal/widget/ILockSettingsObserver.aidl @@ -18,5 +18,14 @@ package com.android.internal.widget; /** {@hide} */ oneway interface ILockSettingsObserver { + /** + * Called when a lock setting has changed. + * + * Note: Impementations of this should do as little work as possible, because this may be + * called synchronously while writing a setting. + * + * @param key the key of the setting that has changed or {@code null} if any may have changed. + * @param userId the user whose setting has changed. + */ void onLockSettingChanged(in String key, in int userId); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index d6885da..a4b8380 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -65,6 +65,13 @@ public class LockPatternUtils { private static final boolean DEBUG = false; /** + * If true, LockPatternUtils will cache its values in-process. While this leads to faster reads, + * it can cause problems because writes to to the settings are no longer synchronous + * across all processes. + */ + private static final boolean ENABLE_CLIENT_CACHE = false; + + /** * The maximum number of incorrect attempts before the user is prevented * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}. */ @@ -207,8 +214,13 @@ public class LockPatternUtils { private ILockSettings getLockSettings() { if (mLockSettingsService == null) { - mLockSettingsService = LockPatternUtilsCache.getInstance( - ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"))); + ILockSettings service = ILockSettings.Stub.asInterface( + ServiceManager.getService("lock_settings")); + if (ENABLE_CLIENT_CACHE) { + mLockSettingsService = LockPatternUtilsCache.getInstance(service); + } else { + mLockSettingsService = service; + } } return mLockSettingsService; } diff --git a/core/java/com/android/internal/widget/LockPatternUtilsCache.java b/core/java/com/android/internal/widget/LockPatternUtilsCache.java index 624f67c..a9524ff 100644 --- a/core/java/com/android/internal/widget/LockPatternUtilsCache.java +++ b/core/java/com/android/internal/widget/LockPatternUtilsCache.java @@ -18,7 +18,9 @@ package com.android.internal.widget; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.ArrayMap; +import android.util.Log; /** * A decorator for {@link ILockSettings} that caches the key-value responses in memory. @@ -28,9 +30,11 @@ import android.util.ArrayMap; */ public class LockPatternUtilsCache implements ILockSettings { - private static final String HAS_LOCK_PATTERN_CACHE_KEY + private static final String TAG = "LockPatternUtilsCache"; + + public static final String HAS_LOCK_PATTERN_CACHE_KEY = "LockPatternUtils.Cache.HasLockPatternCacheKey"; - private static final String HAS_LOCK_PASSWORD_CACHE_KEY + public static final String HAS_LOCK_PASSWORD_CACHE_KEY = "LockPatternUtils.Cache.HasLockPasswordCacheKey"; private static LockPatternUtilsCache sInstance; @@ -53,7 +57,7 @@ public class LockPatternUtilsCache implements ILockSettings { // ILockSettings - private LockPatternUtilsCache(ILockSettings service) { + public LockPatternUtilsCache(ILockSettings service) { mService = service; try { service.registerObserver(mObserver); @@ -186,6 +190,7 @@ public class LockPatternUtilsCache implements ILockSettings { // Caching private Object peekCache(String key, int userId) { + if (!validateUserId(userId)) return null; synchronized (mCache) { // Safe to reuse mCacheKey, because it is not stored in the map. return mCache.get(mCacheKey.set(key, userId)); @@ -193,6 +198,7 @@ public class LockPatternUtilsCache implements ILockSettings { } private void putCache(String key, int userId, Object value) { + if (!validateUserId(userId)) return; synchronized (mCache) { // Create a new key, because this will be stored in the map. mCache.put(new CacheKey().set(key, userId), value); @@ -200,9 +206,14 @@ public class LockPatternUtilsCache implements ILockSettings { } private void invalidateCache(String key, int userId) { + if (!validateUserId(userId)) return; synchronized (mCache) { - // Safe to reuse mCacheKey, because it is not stored in the map. - mCache.remove(mCacheKey.set(key, userId)); + if (key != null) { + // Safe to reuse mCacheKey, because it is not stored in the map. + mCache.remove(mCacheKey.set(key, userId)); + } else { + mCache.clear(); + } } } @@ -213,6 +224,14 @@ public class LockPatternUtilsCache implements ILockSettings { } }; + private final boolean validateUserId(int userId) { + if (userId < UserHandle.USER_OWNER) { + Log.e(TAG, "User " + userId + " not supported: Must be a concrete user."); + return false; + } + return true; + } + private static final class CacheKey { String key; int userId; |