diff options
260 files changed, 6455 insertions, 2556 deletions
diff --git a/api/current.txt b/api/current.txt index 0cbf46ede410..c3a4a0c8919d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4016,6 +4016,23 @@ package android.app { field public java.lang.String serviceDetails; } + public class AutomaticZenRule implements android.os.Parcelable { + ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean); + ctor public AutomaticZenRule(android.os.Parcel); + method public int describeContents(); + method public android.net.Uri getConditionId(); + method public int getInterruptionFilter(); + method public java.lang.String getName(); + method public android.content.ComponentName getOwner(); + method public boolean isEnabled(); + method public void setConditionId(android.net.Uri); + method public void setEnabled(boolean); + method public void setInterruptionFilter(int); + method public void setName(java.lang.String); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR; + } + public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int); ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int); @@ -5061,15 +5078,20 @@ package android.app { } public class NotificationManager { + method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule); method public void cancel(int); method public void cancel(java.lang.String, int); method public void cancelAll(); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); + method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String); + method public java.util.List<android.app.AutomaticZenRule> getAutomaticZenRules(); method public final int getCurrentInterruptionFilter(); method public android.app.NotificationManager.Policy getNotificationPolicy(); method public boolean isNotificationPolicyAccessGranted(); method public void notify(int, android.app.Notification); method public void notify(java.lang.String, int, android.app.Notification); + method public boolean removeAutomaticZenRule(java.lang.String); + method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; @@ -9213,6 +9235,7 @@ package android.content.pm { method public void setAppLabel(java.lang.CharSequence); method public void setAppPackageName(java.lang.String); method public void setInstallLocation(int); + method public void setOriginatingUid(int); method public void setOriginatingUri(android.net.Uri); method public void setReferrerUri(android.net.Uri); method public void setSize(long); @@ -23567,7 +23590,7 @@ package android.os { method public static final int myPid(); method public static final int myTid(); method public static final int myUid(); - method public static final android.os.UserHandle myUserHandle(); + method public static android.os.UserHandle myUserHandle(); method public static final void sendSignal(int, int); method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException; method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException; @@ -45363,7 +45386,10 @@ package java.lang.reflect { } public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member { + method public boolean equals(java.lang.Object); method public A getAnnotation(java.lang.Class<A>); + method public java.lang.annotation.Annotation[] getAnnotations(); + method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); method public java.lang.Class<T> getDeclaringClass(); method public java.lang.Class<?>[] getExceptionTypes(); method public java.lang.reflect.Type[] getGenericExceptionTypes(); @@ -45373,6 +45399,7 @@ package java.lang.reflect { method public java.lang.annotation.Annotation[][] getParameterAnnotations(); method public java.lang.Class<?>[] getParameterTypes(); method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters(); + method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>); method public boolean isSynthetic(); method public boolean isVarArgs(); method public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException; @@ -45446,7 +45473,10 @@ package java.lang.reflect { } public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member { + method public boolean equals(java.lang.Object); method public A getAnnotation(java.lang.Class<A>); + method public java.lang.annotation.Annotation[] getAnnotations(); + method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); method public java.lang.Class<?> getDeclaringClass(); method public java.lang.Object getDefaultValue(); method public java.lang.Class<?>[] getExceptionTypes(); @@ -45460,6 +45490,7 @@ package java.lang.reflect { method public java.lang.Class<?> getReturnType(); method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters(); method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException; + method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>); method public boolean isBridge(); method public boolean isSynthetic(); method public boolean isVarArgs(); diff --git a/api/system-current.txt b/api/system-current.txt index 51febfde484c..325aa28bef05 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4127,6 +4127,23 @@ package android.app { field public java.lang.String serviceDetails; } + public class AutomaticZenRule implements android.os.Parcelable { + ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean); + ctor public AutomaticZenRule(android.os.Parcel); + method public int describeContents(); + method public android.net.Uri getConditionId(); + method public int getInterruptionFilter(); + method public java.lang.String getName(); + method public android.content.ComponentName getOwner(); + method public boolean isEnabled(); + method public void setConditionId(android.net.Uri); + method public void setEnabled(boolean); + method public void setInterruptionFilter(int); + method public void setName(java.lang.String); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR; + } + public class BroadcastOptions { method public static android.app.BroadcastOptions makeBasic(); method public void setTemporaryAppWhitelistDuration(long); @@ -5178,15 +5195,20 @@ package android.app { } public class NotificationManager { + method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule); method public void cancel(int); method public void cancel(java.lang.String, int); method public void cancelAll(); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); + method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String); + method public java.util.List<android.app.AutomaticZenRule> getAutomaticZenRules(); method public final int getCurrentInterruptionFilter(); method public android.app.NotificationManager.Policy getNotificationPolicy(); method public boolean isNotificationPolicyAccessGranted(); method public void notify(int, android.app.Notification); method public void notify(java.lang.String, int, android.app.Notification); + method public boolean removeAutomaticZenRule(java.lang.String); + method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; @@ -9496,6 +9518,7 @@ package android.content.pm { method public void setAppPackageName(java.lang.String); method public void setGrantedRuntimePermissions(java.lang.String[]); method public void setInstallLocation(int); + method public void setOriginatingUid(int); method public void setOriginatingUri(android.net.Uri); method public void setReferrerUri(android.net.Uri); method public void setSize(long); @@ -25519,7 +25542,7 @@ package android.os { method public static final int myPid(); method public static final int myTid(); method public static final int myUid(); - method public static final android.os.UserHandle myUserHandle(); + method public static android.os.UserHandle myUserHandle(); method public static final void sendSignal(int, int); method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException; method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException; @@ -47972,7 +47995,10 @@ package java.lang.reflect { } public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member { + method public boolean equals(java.lang.Object); method public A getAnnotation(java.lang.Class<A>); + method public java.lang.annotation.Annotation[] getAnnotations(); + method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); method public java.lang.Class<T> getDeclaringClass(); method public java.lang.Class<?>[] getExceptionTypes(); method public java.lang.reflect.Type[] getGenericExceptionTypes(); @@ -47982,6 +48008,7 @@ package java.lang.reflect { method public java.lang.annotation.Annotation[][] getParameterAnnotations(); method public java.lang.Class<?>[] getParameterTypes(); method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters(); + method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>); method public boolean isSynthetic(); method public boolean isVarArgs(); method public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException; @@ -48055,7 +48082,10 @@ package java.lang.reflect { } public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member { + method public boolean equals(java.lang.Object); method public A getAnnotation(java.lang.Class<A>); + method public java.lang.annotation.Annotation[] getAnnotations(); + method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); method public java.lang.Class<?> getDeclaringClass(); method public java.lang.Object getDefaultValue(); method public java.lang.Class<?>[] getExceptionTypes(); @@ -48069,6 +48099,7 @@ package java.lang.reflect { method public java.lang.Class<?> getReturnType(); method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters(); method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException; + method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>); method public boolean isBridge(); method public boolean isSynthetic(); method public boolean isVarArgs(); diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 16f825da6f31..d444638c715a 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -603,7 +603,17 @@ public final class AnimatorSet extends Animator { createDependencyGraph(); // Now that all dependencies are set up, start the animations that should be started. - start(mRootNode); + boolean setIsEmpty = false; + if (mStartDelay > 0) { + start(mRootNode); + } else if (mNodes.size() > 1) { + // No delay, but there are other animators in the set + onChildAnimatorEnded(mDelayAnim); + } else { + // Set is empty, no delay, no other animation. Skip to end in this case + setIsEmpty = true; + } + if (mListeners != null) { ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); @@ -612,18 +622,9 @@ public final class AnimatorSet extends Animator { tmpListeners.get(i).onAnimationStart(this); } } - if (mNodes.size() == 0 && mStartDelay == 0) { - // Handle unusual case where empty AnimatorSet is started - should send out - // end event immediately since the event will not be sent out at all otherwise - mStarted = false; - if (mListeners != null) { - ArrayList<AnimatorListener> tmpListeners = - (ArrayList<AnimatorListener>) mListeners.clone(); - int numListeners = tmpListeners.size(); - for (int i = 0; i < numListeners; ++i) { - tmpListeners.get(i).onAnimationEnd(this); - } - } + if (setIsEmpty) { + // In the case of empty AnimatorSet, we will trigger the onAnimationEnd() right away. + onChildAnimatorEnded(mDelayAnim); } } @@ -751,44 +752,7 @@ public final class AnimatorSet extends Animator { public void onAnimationEnd(Animator animation) { animation.removeListener(this); mAnimatorSet.mPlayingSet.remove(animation); - Node animNode = mAnimatorSet.mNodeMap.get(animation); - animNode.mEnded = true; - - if (!mAnimatorSet.mTerminated) { - List<Node> children = animNode.mChildNodes; - // Start children animations, if any. - int childrenSize = children == null ? 0 : children.size(); - for (int i = 0; i < childrenSize; i++) { - if (children.get(i).mLatestParent == animNode) { - mAnimatorSet.start(children.get(i)); - } - } - // Listeners are already notified of the AnimatorSet ending in cancel() or - // end(); the logic below only kicks in when animations end normally - boolean allDone = true; - // Traverse the tree and find if there's any unfinished node - int size = mAnimatorSet.mNodes.size(); - for (int i = 0; i < size; i++) { - if (!mAnimatorSet.mNodes.get(i).mEnded) { - allDone = false; - break; - } - } - if (allDone) { - // If this was the last child animation to end, then notify listeners that this - // AnimatorSet has ended - if (mAnimatorSet.mListeners != null) { - ArrayList<AnimatorListener> tmpListeners = - (ArrayList<AnimatorListener>) mAnimatorSet.mListeners.clone(); - int numListeners = tmpListeners.size(); - for (int i = 0; i < numListeners; ++i) { - tmpListeners.get(i).onAnimationEnd(mAnimatorSet); - } - } - mAnimatorSet.mStarted = false; - mAnimatorSet.mPaused = false; - } - } + mAnimatorSet.onChildAnimatorEnded(animation); } // Nothing to do @@ -801,6 +765,47 @@ public final class AnimatorSet extends Animator { } + private void onChildAnimatorEnded(Animator animation) { + Node animNode = mNodeMap.get(animation); + animNode.mEnded = true; + + if (!mTerminated) { + List<Node> children = animNode.mChildNodes; + // Start children animations, if any. + int childrenSize = children == null ? 0 : children.size(); + for (int i = 0; i < childrenSize; i++) { + if (children.get(i).mLatestParent == animNode) { + start(children.get(i)); + } + } + // Listeners are already notified of the AnimatorSet ending in cancel() or + // end(); the logic below only kicks in when animations end normally + boolean allDone = true; + // Traverse the tree and find if there's any unfinished node + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + if (!mNodes.get(i).mEnded) { + allDone = false; + break; + } + } + if (allDone) { + // If this was the last child animation to end, then notify listeners that this + // AnimatorSet has ended + if (mListeners != null) { + ArrayList<AnimatorListener> tmpListeners = + (ArrayList<AnimatorListener>) mListeners.clone(); + int numListeners = tmpListeners.size(); + for (int i = 0; i < numListeners; ++i) { + tmpListeners.get(i).onAnimationEnd(this); + } + } + mStarted = false; + mPaused = false; + } + } + } + /** * @hide */ diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index da9a8c8d8900..1995ef58e499 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -669,6 +669,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * The frame delay may be ignored when the animation system uses an external timing * source, such as the display refresh rate (vsync), to govern animations. * + * Note that this method should be called from the same thread that {@link #start()} is + * called in order to check the frame delay for that animation. A runtime exception will be + * thrown if the calling thread does not have a Looper. + * * @return the requested time between frames, in milliseconds */ public static long getFrameDelay() { @@ -685,6 +689,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * The frame delay may be ignored when the animation system uses an external timing * source, such as the display refresh rate (vsync), to govern animations. * + * Note that this method should be called from the same thread that {@link #start()} is + * called in order to have the new frame delay take effect on that animation. A runtime + * exception will be thrown if the calling thread does not have a Looper. + * * @param frameDelay the requested time between frames, in milliseconds */ public static void setFrameDelay(long frameDelay) { @@ -924,6 +932,13 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio updateScaledDuration(); // in case the scale factor has changed since creation time AnimationHandler animationHandler = AnimationHandler.getInstance(); animationHandler.addAnimationFrameCallback(this, mStartDelay); + + if (mStartDelay == 0) { + // If there's no start delay, init the animation and notify start listeners right away + // Otherwise, postpone this until the first frame after the start delay. + startAnimation(); + setCurrentFraction(mSeekFraction == -1 ? 0 : mSeekFraction); + } } @Override @@ -1067,6 +1082,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio mStartListenersCalled = false; mPlayingBackwards = false; mReversing = false; + mLastFrameTime = 0; mCurrentIteration = 0; if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(), @@ -1176,12 +1192,13 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * @hide */ public final void doAnimationFrame(long frameTime) { - mLastFrameTime = frameTime; AnimationHandler handler = AnimationHandler.getInstance(); - if (!mRunning) { + if (mLastFrameTime == 0) { // First frame handler.addOneShotCommitCallback(this); - startAnimation(); + if (mStartDelay > 0) { + startAnimation(); + } if (mSeekFraction < 0) { mStartTime = frameTime; } else { @@ -1191,6 +1208,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio } mStartTimeCommitted = false; // allow start time to be compensated for jank } + mLastFrameTime = frameTime; if (mPaused) { if (mPauseTime < 0) { mPauseTime = frameTime; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4997dc751082..f60250cc6eb1 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3742,6 +3742,12 @@ public class Activity extends ContextThemeWrapper } @Override + public void setTheme(int resid) { + super.setTheme(resid); + mWindow.setTheme(resid); + } + + @Override protected void onApplyThemeResource(Resources.Theme theme, @StyleRes int resid, boolean first) { if (mParent == null) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 6606f9be2e79..61a9a84a5965 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -468,36 +468,51 @@ public class ActivityManager { */ public static final int DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT = 1; - /** * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates - * that the resize is from the window manager (instead of the user). + * that the resize doesn't need to preserve the window, and can be skipped if bounds + * is unchanged. This mode is used by window manager in most cases. * @hide */ public static final int RESIZE_MODE_SYSTEM = 0; /** * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates - * that the resize is from the window manager (instead of the user) due to a screen - * rotation change. + * that the resize should preserve the window if possible. * @hide */ - public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = 1; + public static final int RESIZE_MODE_PRESERVE_WINDOW = (0x1 << 0); /** * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates - * that the resize is initiated by the user (most likely via a drag action on the - * window's edge or corner). + * that the resize should be performed even if the bounds appears unchanged. + * @hide + */ + public static final int RESIZE_MODE_FORCED = (0x1 << 1); + + /** + * Input parameter to {@link android.app.IActivityManager#resizeTask} used by window + * manager during a screen rotation. * @hide */ - public static final int RESIZE_MODE_USER = 2; + public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = RESIZE_MODE_PRESERVE_WINDOW; + + /** + * Input parameter to {@link android.app.IActivityManager#resizeTask} used when the + * resize is due to a drag action. + * @hide + */ + public static final int RESIZE_MODE_USER = RESIZE_MODE_PRESERVE_WINDOW; /** * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates - * that the resize should be performed even if the bounds appears unchanged. + * that the resize should preserve the window if possible, and should not be skipped + * even if the bounds is unchanged. Usually used to force a resizing when a drag action + * is ending. * @hide */ - public static final int RESIZE_MODE_FORCED = 3; + public static final int RESIZE_MODE_USER_FORCED = + RESIZE_MODE_PRESERVE_WINDOW | RESIZE_MODE_FORCED; /** @hide */ public int getFrontActivityScreenCompatMode() { diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 40eb799f26b9..373a23fa7422 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -55,4 +55,13 @@ public abstract class ActivityManagerInternal { * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL} */ public abstract ComponentName getHomeActivityForUser(int userId); + + /** + * Called when a user has been deleted. This can happen during normal device usage + * or just at startup, when partially removed users are purged. Any state persisted by the + * ActivityManager should be purged now. + * + * @param userId The user being cleaned up. + */ + public abstract void onUserRemoved(int userId); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0d53dbe950ea..f29dba263a5c 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1344,7 +1344,7 @@ public class ApplicationPackageManager extends PackageManager { final VerificationParams verificationParams = new VerificationParams(null, null, null, VerificationParams.NO_UID, null); installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags, - installerPackageName, verificationParams, null, UserHandle.myUserId()); + installerPackageName, verificationParams, null, mContext.getUserId()); } @Override @@ -1354,7 +1354,7 @@ public class ApplicationPackageManager extends PackageManager { final VerificationParams verificationParams = new VerificationParams(verificationURI, null, null, VerificationParams.NO_UID, manifestDigest); installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags, - installerPackageName, verificationParams, encryptionParams, UserHandle.myUserId()); + installerPackageName, verificationParams, encryptionParams, mContext.getUserId()); } @Override @@ -1362,14 +1362,14 @@ public class ApplicationPackageManager extends PackageManager { IPackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags, - installerPackageName, verificationParams, encryptionParams, UserHandle.myUserId()); + installerPackageName, verificationParams, encryptionParams, mContext.getUserId()); } @Override public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName) { installPackageAsUser(packageURI, observer, flags, installerPackageName, - UserHandle.myUserId()); + mContext.getUserId()); } @Override @@ -1389,7 +1389,7 @@ public class ApplicationPackageManager extends PackageManager { final VerificationParams verificationParams = new VerificationParams(verificationURI, null, null, VerificationParams.NO_UID, manifestDigest); installCommon(packageURI, observer, flags, installerPackageName, verificationParams, - encryptionParams, UserHandle.myUserId()); + encryptionParams, mContext.getUserId()); } @Override @@ -1397,7 +1397,7 @@ public class ApplicationPackageManager extends PackageManager { PackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { installCommon(packageURI, observer, flags, installerPackageName, verificationParams, - encryptionParams, UserHandle.myUserId()); + encryptionParams, mContext.getUserId()); } private void installCommon(Uri packageURI, @@ -1421,7 +1421,7 @@ public class ApplicationPackageManager extends PackageManager { @Override public int installExistingPackage(String packageName) throws NameNotFoundException { - return installExistingPackageAsUser(packageName, UserHandle.myUserId()); + return installExistingPackageAsUser(packageName, mContext.getUserId()); } @Override @@ -1726,7 +1726,7 @@ public class ApplicationPackageManager extends PackageManager { @Override public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) { - deletePackageAsUser(packageName, observer, flags, UserHandle.myUserId()); + deletePackageAsUser(packageName, observer, flags, mContext.getUserId()); } @Override @@ -1838,7 +1838,7 @@ public class ApplicationPackageManager extends PackageManager { public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { try { - mPM.replacePreferredActivity(filter, match, set, activity, UserHandle.myUserId()); + mPM.replacePreferredActivity(filter, match, set, activity, mContext.getUserId()); } catch (RemoteException e) { // Should never happen! } @@ -2169,7 +2169,7 @@ public class ApplicationPackageManager extends PackageManager { } private UserInfo getUserIfProfile(int userHandle) { - List<UserInfo> userProfiles = getUserManager().getProfiles(UserHandle.myUserId()); + List<UserInfo> userProfiles = getUserManager().getProfiles(mContext.getUserId()); for (UserInfo user : userProfiles) { if (user.id == userHandle) { return user; diff --git a/core/java/android/app/AutomaticZenRule.aidl b/core/java/android/app/AutomaticZenRule.aidl new file mode 100644 index 000000000000..feb21d657c6a --- /dev/null +++ b/core/java/android/app/AutomaticZenRule.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +parcelable AutomaticZenRule;
\ No newline at end of file diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java new file mode 100644 index 000000000000..fea5624411ad --- /dev/null +++ b/core/java/android/app/AutomaticZenRule.java @@ -0,0 +1,190 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.content.ComponentName; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Rule instance information for zen mode. + */ +public class AutomaticZenRule implements Parcelable { + + private boolean enabled = false; + private String name; + private int interruptionFilter; + private Uri conditionId; + private ComponentName owner; + + /** + * Creates an automatic zen rule. + * + * @param name The name of the rule. + * @param owner The Condition Provider service that owns this rule. + * @param conditionId A representation of the state that should cause the Condition Provider + * service to apply the interruption filter. + * @param interruptionFilter The interruption filter defines which notifications are allowed to + * interrupt the user (e.g. via sound & vibration) while this rule + * is active. + * @param enabled Whether the rule is enabled. + */ + public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, + int interruptionFilter, boolean enabled) { + this.name = name; + this.owner = owner; + this.conditionId = conditionId; + this.interruptionFilter = interruptionFilter; + this.enabled = enabled; + } + + public AutomaticZenRule(Parcel source) { + enabled = source.readInt() == 1; + if (source.readInt() == 1) { + name = source.readString(); + } + interruptionFilter = source.readInt(); + conditionId = source.readParcelable(null); + owner = source.readParcelable(null); + } + + /** + * Returns the {@link ComponentName} of the condition provider service that owns this rule. + */ + public ComponentName getOwner() { + return owner; + } + + /** + * Returns the representation of the state that causes this rule to become active. + */ + public Uri getConditionId() { + return conditionId; + } + + /** + * Returns the interruption filter that is applied when this rule is active. + */ + public int getInterruptionFilter() { + return interruptionFilter; + } + + /** + * Returns the name of this rule. + */ + public String getName() { + return name; + } + + /** + * Returns whether this rule is enabled. + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Sets the representation of the state that causes this rule to become active. + */ + public void setConditionId(Uri conditionId) { + this.conditionId = conditionId; + } + + /** + * Sets the interruption filter that is applied when this rule is active. + * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants in NotificationManager. + */ + public void setInterruptionFilter(int interruptionFilter) { + this.interruptionFilter = interruptionFilter; + } + + /** + * Sets the name of this rule. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Enables this rule. + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(enabled ? 1 : 0); + if (name != null) { + dest.writeInt(1); + dest.writeString(name); + } else { + dest.writeInt(0); + } + dest.writeInt(interruptionFilter); + dest.writeParcelable(conditionId, 0); + dest.writeParcelable(owner, 0); + } + + @Override + public String toString() { + return new StringBuilder(AutomaticZenRule.class.getSimpleName()).append('[') + .append("enabled=").append(enabled) + .append(",name=").append(name) + .append(",interruptionFilter=").append(interruptionFilter) + .append(",conditionId=").append(conditionId) + .append(",owner=").append(owner) + .append(']').toString(); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof AutomaticZenRule)) return false; + if (o == this) return true; + final AutomaticZenRule other = (AutomaticZenRule) o; + return other.enabled == enabled + && Objects.equals(other.name, name) + && other.interruptionFilter == interruptionFilter + && Objects.equals(other.conditionId, conditionId) + && Objects.equals(other.owner, owner); + } + + @Override + public int hashCode() { + return Objects.hash(enabled, name, interruptionFilter, conditionId, owner); + } + + public static final Parcelable.Creator<AutomaticZenRule> CREATOR + = new Parcelable.Creator<AutomaticZenRule>() { + @Override + public AutomaticZenRule createFromParcel(Parcel source) { + return new AutomaticZenRule(source); + } + @Override + public AutomaticZenRule[] newArray(int size) { + return new AutomaticZenRule[size]; + } + }; +}
\ No newline at end of file diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index f78fb47e5f5e..920fbe9cfc1c 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -30,6 +30,7 @@ import android.service.notification.IConditionListener; import android.service.notification.IConditionProvider; import android.service.notification.INotificationListener; import android.service.notification.StatusBarNotification; +import android.app.AutomaticZenRule; import android.service.notification.ZenModeConfig; /** {@hide} */ @@ -92,6 +93,11 @@ interface INotificationManager String[] getPackagesRequestingNotificationPolicyAccess(); boolean isNotificationPolicyAccessGrantedForPackage(String pkg); void setNotificationPolicyAccessGranted(String pkg, boolean granted); + AutomaticZenRule getAutomaticZenRule(String name); + List<AutomaticZenRule> getAutomaticZenRules(); + boolean addOrUpdateAutomaticZenRule(in AutomaticZenRule automaticZenRule); + boolean renameAutomaticZenRule(String oldName, String newName); + boolean removeAutomaticZenRule(String name); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 605c00613087..cbf198bb9415 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -420,6 +420,106 @@ public class NotificationManager } /** + * Returns AutomaticZenRules owned by the caller. + * + * <p> + * Only available if policy access is granted to this package. + * See {@link #isNotificationPolicyAccessGranted}. + */ + public List<AutomaticZenRule> getAutomaticZenRules() { + INotificationManager service = getService(); + try { + return service.getAutomaticZenRules(); + } catch (RemoteException e) { + } + return null; + } + + /** + * Returns the AutomaticZenRule with the given name, if it exists and the caller has access. + * + * <p> + * Only available if policy access is granted to this package. + * See {@link #isNotificationPolicyAccessGranted}. + * + * <p> + * Returns null if there are no zen rules that match the given name, or if the calling package + * doesn't own the matching rule. See {@link AutomaticZenRule#getOwner}. + */ + public AutomaticZenRule getAutomaticZenRule(String name) { + INotificationManager service = getService(); + try { + return service.getAutomaticZenRule(name); + } catch (RemoteException e) { + } + return null; + } + + /** + * Creates or updates the given zen rule. + * + * <p> + * Only available if policy access is granted to this package. + * See {@link #isNotificationPolicyAccessGranted}. + * + * <p> + * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}. + * @param automaticZenRule the rule to create or update. + * @return Whether the rule was successfully created or updated. + */ + public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule) { + INotificationManager service = getService(); + try { + return service.addOrUpdateAutomaticZenRule(automaticZenRule); + } catch (RemoteException e) { + } + return false; + } + + /** + * Renames a zen rule. + * + * <p> + * Only available if policy access is granted to this package. + * See {@link #isNotificationPolicyAccessGranted}. + * + * <p> + * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}. + * @param oldName The name of the rule to update. + * @param newName The new name for the rule. + * @return Whether the rule was successfully updated. + */ + public boolean renameAutomaticZenRule(String oldName, String newName) { + INotificationManager service = getService(); + try { + return service.renameAutomaticZenRule(oldName, newName); + } catch (RemoteException e) { + } + return false; + } + + /** + * Deletes the automatic zen rule with the given name. + * + * <p> + * Only available if policy access is granted to this package. + * See {@link #isNotificationPolicyAccessGranted}. + * + * <p> + * Callers can only delete rules that they own. See {@link AutomaticZenRule#getOwner}. + * @param name the name of the rule to delete. + * @return Whether the rule was successfully deleted. + */ + public boolean removeAutomaticZenRule(String name) { + INotificationManager service = getService(); + try { + return service.removeAutomaticZenRule(name); + } catch (RemoteException e) { + } + return false; + } + + /** * Checks the ability to read/modify notification policy for the calling package. * * <p> diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e6484e965ee0..a118f167ae5c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2438,6 +2438,17 @@ public class DevicePolicyManager { } /** + * Determine whether or not creating a guest user has been disabled for the device + * + * @hide + */ + public boolean getGuestUserDisabled(@Nullable ComponentName admin) { + // Currently guest users can always be created if multi-user is enabled + // TODO introduce a policy for guest user creation + return false; + } + + /** * Called by a device/profile owner to set whether the screen capture is disabled. Disabling * screen capture also prevents the content from being shown on display devices that do not have * a secure video output. See {@link android.view.Display#FLAG_SECURE} for more details about diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 9341be16c28f..32830053fae2 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -868,6 +868,9 @@ public class PackageInstaller { public static final int MODE_INHERIT_EXISTING = 2; /** {@hide} */ + public static final int UID_UNKNOWN = -1; + + /** {@hide} */ public int mode = MODE_INVALID; /** {@hide} */ public int installFlags; @@ -886,6 +889,8 @@ public class PackageInstaller { /** {@hide} */ public Uri originatingUri; /** {@hide} */ + public int originatingUid = UID_UNKNOWN; + /** {@hide} */ public Uri referrerUri; /** {@hide} */ public String abiOverride; @@ -915,6 +920,7 @@ public class PackageInstaller { appIcon = source.readParcelable(null); appLabel = source.readString(); originatingUri = source.readParcelable(null); + originatingUid = source.readInt(); referrerUri = source.readParcelable(null); abiOverride = source.readString(); volumeUuid = source.readString(); @@ -983,6 +989,15 @@ public class PackageInstaller { } /** + * Sets the UID that initiated package installation. Used for verification purposes. + * + * @see PackageManager#EXTRA_VERIFICATION_INSTALLER_UID + */ + public void setOriginatingUid(int originatingUid) { + this.originatingUid = originatingUid; + } + + /** * Optionally set the URI that referred you to install this package. Used * for verification purposes. * @@ -1022,6 +1037,11 @@ public class PackageInstaller { } /** {@hide} */ + public void setInstallFlagsForcePermissionPrompt() { + installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT; + } + + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); pw.printHexPair("installFlags", installFlags); @@ -1031,6 +1051,7 @@ public class PackageInstaller { pw.printPair("appIcon", (appIcon != null)); pw.printPair("appLabel", appLabel); pw.printPair("originatingUri", originatingUri); + pw.printPair("originatingUid", originatingUid); pw.printPair("referrerUri", referrerUri); pw.printPair("abiOverride", abiOverride); pw.printPair("volumeUuid", volumeUuid); @@ -1053,6 +1074,7 @@ public class PackageInstaller { dest.writeParcelable(appIcon, flags); dest.writeString(appLabel); dest.writeParcelable(originatingUri, flags); + dest.writeInt(originatingUid); dest.writeParcelable(referrerUri, flags); dest.writeString(abiOverride); dest.writeString(volumeUuid); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 697b946eec73..82cbbbeb39fc 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -422,6 +422,15 @@ public abstract class PackageManager { public static final int INSTALL_FORCE_VOLUME_UUID = 0x00000200; /** + * Flag parameter for {@link #installPackage} to indicate that we always want to force + * the prompt for permission approval. This overrides any special behaviour for internal + * components. + * + * @hide + */ + public static final int INSTALL_FORCE_PERMISSION_PROMPT = 0x00000400; + + /** * Flag parameter for * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate * that you don't want to kill the app containing the component. Be careful when you set this diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e29bd2cc53e0..0606e35a081e 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -16,24 +16,14 @@ package android.content.res; -import android.annotation.AttrRes; -import android.annotation.ColorInt; -import android.annotation.StyleRes; -import android.annotation.StyleableRes; -import android.graphics.drawable.DrawableInflater; -import android.icu.text.PluralRules; -import com.android.internal.util.GrowingArrayUtils; -import com.android.internal.util.XmlUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - import android.animation.Animator; import android.animation.StateListAnimator; import android.annotation.AnimRes; import android.annotation.AnyRes; import android.annotation.ArrayRes; +import android.annotation.AttrRes; import android.annotation.BoolRes; +import android.annotation.ColorInt; import android.annotation.ColorRes; import android.annotation.DimenRes; import android.annotation.DrawableRes; @@ -45,18 +35,23 @@ import android.annotation.Nullable; import android.annotation.PluralsRes; import android.annotation.RawRes; import android.annotation.StringRes; +import android.annotation.StyleRes; +import android.annotation.StyleableRes; import android.annotation.XmlRes; import android.content.pm.ActivityInfo; import android.graphics.Movie; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable.ConstantState; +import android.graphics.drawable.DrawableInflater; +import android.icu.text.PluralRules; import android.os.Build; import android.os.Bundle; import android.os.Trace; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.LocaleList; import android.util.Log; import android.util.LongSparseArray; import android.util.Pools.SynchronizedPool; @@ -65,6 +60,12 @@ import android.util.TypedValue; import android.view.ViewDebug; import android.view.ViewHierarchyEncoder; +import com.android.internal.util.GrowingArrayUtils; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -372,7 +373,7 @@ public class Resources { private PluralRules getPluralRule() { synchronized (sSync) { if (mPluralRule == null) { - mPluralRule = PluralRules.forLocale(mConfiguration.locale); + mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary()); } return mPluralRule; } @@ -435,7 +436,7 @@ public class Resources { @NonNull public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException { final String raw = getString(id); - return String.format(mConfiguration.locale, raw, formatArgs); + return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs); } /** @@ -466,7 +467,7 @@ public class Resources { public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs) throws NotFoundException { String raw = getQuantityText(id, quantity).toString(); - return String.format(mConfiguration.locale, raw, formatArgs); + return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs); } /** @@ -1971,9 +1972,10 @@ public class Resources { mCompatibilityInfo.applyToDisplayMetrics(mMetrics); final int configChanges = calcConfigChanges(config); - if (mConfiguration.locale == null) { - mConfiguration.locale = Locale.getDefault(); - mConfiguration.setLayoutDirection(mConfiguration.locale); + LocaleList locales = mConfiguration.getLocales(); + if (locales.isEmpty()) { + locales = LocaleList.getDefault(); + mConfiguration.setLocales(locales); } if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) { mMetrics.densityDpi = mConfiguration.densityDpi; @@ -1981,11 +1983,6 @@ public class Resources { } mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale; - String locale = null; - if (mConfiguration.locale != null) { - locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag()); - } - final int width, height; if (mMetrics.widthPixels >= mMetrics.heightPixels) { width = mMetrics.widthPixels; @@ -2005,8 +2002,10 @@ public class Resources { keyboardHidden = mConfiguration.keyboardHidden; } + // TODO: Pass the whole locale list to setConfiguration() mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, - locale, mConfiguration.orientation, + adjustLanguageTag(locales.getPrimary().toLanguageTag()), + mConfiguration.orientation, mConfiguration.touchscreen, mConfiguration.densityDpi, mConfiguration.keyboard, keyboardHidden, mConfiguration.navigation, width, height, @@ -2030,7 +2029,7 @@ public class Resources { } synchronized (sSync) { if (mPluralRule != null) { - mPluralRule = PluralRules.forLocale(config.locale); + mPluralRule = PluralRules.forLocale(config.getLocales().getPrimary()); } } } @@ -2049,9 +2048,8 @@ public class Resources { mCompatibilityInfo.applyToConfiguration(density, mTmpConfig); - if (mTmpConfig.locale == null) { - mTmpConfig.locale = Locale.getDefault(); - mTmpConfig.setLayoutDirection(mTmpConfig.locale); + if (mTmpConfig.getLocales().isEmpty()) { + mTmpConfig.setLocales(LocaleList.getDefault()); } configChanges = mConfiguration.updateFrom(mTmpConfig); configChanges = ActivityInfo.activityInfoConfigToNative(configChanges); diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index e965d654fcc7..3f566eb92756 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1545,7 +1545,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Whether video stabilization is * active.</p> - * <p>Video stabilization automatically translates and scales images from + * <p>Video stabilization automatically warps images from * the camera in order to stabilize motion between consecutive frames.</p> * <p>If enabled, video stabilization can modify the * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream stabilized.</p> @@ -1555,6 +1555,15 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * the video stabilization modes in the first several capture results may * still be "OFF", and it will become "ON" when the initialization is * done.</p> + * <p>In addition, not all recording sizes or frame rates may be supported for + * stabilization by a device that reports stabilization support. It is guaranteed + * that an output targeting a MediaRecorder or MediaCodec will be stabilized if + * the recording resolution is less than or equal to 1920 x 1080 (width less than + * or equal to 1920, height less than or equal to 1080), and the recording + * frame rate is less than or equal to 30fps. At other sizes, the CaptureResult + * {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return + * OFF if the recording output is not stabilized, or if there are no output + * Surface types that can be stabilized.</p> * <p>If a camera device supports both this mode and OIS * ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may * produce undesirable interaction, so it is recommended not to enable @@ -1566,6 +1575,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * </ul></p> * <p>This key is available on all devices.</p> * + * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE * @see CaptureRequest#SCALER_CROP_REGION * @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 46eddb3f6651..b3acf2b09784 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2056,7 +2056,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Whether video stabilization is * active.</p> - * <p>Video stabilization automatically translates and scales images from + * <p>Video stabilization automatically warps images from * the camera in order to stabilize motion between consecutive frames.</p> * <p>If enabled, video stabilization can modify the * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream stabilized.</p> @@ -2066,6 +2066,15 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * the video stabilization modes in the first several capture results may * still be "OFF", and it will become "ON" when the initialization is * done.</p> + * <p>In addition, not all recording sizes or frame rates may be supported for + * stabilization by a device that reports stabilization support. It is guaranteed + * that an output targeting a MediaRecorder or MediaCodec will be stabilized if + * the recording resolution is less than or equal to 1920 x 1080 (width less than + * or equal to 1920, height less than or equal to 1080), and the recording + * frame rate is less than or equal to 30fps. At other sizes, the CaptureResult + * {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return + * OFF if the recording output is not stabilized, or if there are no output + * Surface types that can be stabilized.</p> * <p>If a camera device supports both this mode and OIS * ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may * produce undesirable interaction, so it is recommended not to enable @@ -2077,6 +2086,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * </ul></p> * <p>This key is available on all devices.</p> * + * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE * @see CaptureRequest#SCALER_CROP_REGION * @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index c3733085caf6..cd483b104489 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -19,6 +19,7 @@ package android.os; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.Log; +import android.util.MathUtils; import java.io.Serializable; import java.util.ArrayList; @@ -1345,18 +1346,19 @@ public class BaseBundle { */ void readFromParcelInner(Parcel parcel) { int length = parcel.readInt(); - if (length < 0) { - throw new RuntimeException("Bad length in parcel: " + length); - } readFromParcelInner(parcel, length); } private void readFromParcelInner(Parcel parcel, int length) { - if (length == 0) { + if (length < 0) { + throw new RuntimeException("Bad length in parcel: " + length); + + } else if (length == 0) { // Empty Bundle or end of data. mParcelledData = EMPTY_PARCEL; return; } + int magic = parcel.readInt(); if (magic != BUNDLE_MAGIC) { //noinspection ThrowableInstanceNeverThrown @@ -1366,7 +1368,7 @@ public class BaseBundle { // Advance within this Parcel int offset = parcel.dataPosition(); - parcel.setDataPosition(offset + length); + parcel.setDataPosition(MathUtils.addOrThrow(offset, length)); Parcel p = Parcel.obtain(); p.setDataPosition(0); diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index c4f62baab4a1..9fdbec39b2bd 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -23,8 +23,11 @@ import android.os.WorkSource; interface IPowerManager { - // WARNING: The first five methods must remain the first five methods because their - // transaction numbers must not change unless IPowerManager.cpp is also updated. + // WARNING: When methods are inserted or deleted, the transaction IDs in + // frameworks/native/include/powermanager/IPowerManager.h must be updated to match the order in this file. + // + // When a method's argument list is changed, BnPowerManager's corresponding serialization code (if any) in + // frameworks/native/services/powermanager/IPowerManager.cpp must be updated. void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws, String historyTag); void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName, diff --git a/core/java/android/os/ParcelableParcel.java b/core/java/android/os/ParcelableParcel.java index 11785f1072fd..5bbe6488d514 100644 --- a/core/java/android/os/ParcelableParcel.java +++ b/core/java/android/os/ParcelableParcel.java @@ -16,6 +16,8 @@ package android.os; +import android.util.MathUtils; + /** * Parcelable containing a raw Parcel of data. * @hide @@ -33,9 +35,13 @@ public class ParcelableParcel implements Parcelable { mParcel = Parcel.obtain(); mClassLoader = loader; int size = src.readInt(); + if (size < 0) { + throw new IllegalArgumentException("Negative size read from parcel"); + } + int pos = src.dataPosition(); - mParcel.appendFrom(src, src.dataPosition(), size); - src.setDataPosition(pos + size); + src.setDataPosition(MathUtils.addOrThrow(pos, size)); + mParcel.appendFrom(src, pos, size); } public Parcel getParcel() { diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 37467caf8bfc..d178d20ac14c 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.ActivityManager; import android.app.ActivityManagerNative; +import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.pm.UserInfo; import android.content.res.Resources; @@ -1337,12 +1338,15 @@ public class UserManager { } /** - * Returns true if the user switcher should be shown, this will be if there - * are multiple users that aren't managed profiles. + * Returns true if the user switcher should be shown, this will be if device supports multi-user + * and there are at least 2 users available that are not managed profiles. * @hide * @return true if user switcher should be shown. */ public boolean isUserSwitcherEnabled() { + if (!supportsMultipleUsers()) { + return false; + } List<UserInfo> users = getUsers(true); if (users == null) { return false; @@ -1353,8 +1357,8 @@ public class UserManager { ++switchableUserCount; } } - final boolean guestEnabled = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.GUEST_USER_ENABLED, 0) == 1; + final boolean guestEnabled = !mContext.getSystemService(DevicePolicyManager.class) + .getGuestUserDisabled(null); return switchableUserCount > 1 || guestEnabled; } diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java index 67f64090db16..5414f0007970 100644 --- a/core/java/android/preference/SeekBarPreference.java +++ b/core/java/android/preference/SeekBarPreference.java @@ -96,18 +96,15 @@ public class SeekBarPreference extends Preference @Override public boolean onKey(View v, int keyCode, KeyEvent event) { - if (event.getAction() != KeyEvent.ACTION_UP) { - if (keyCode == KeyEvent.KEYCODE_PLUS - || keyCode == KeyEvent.KEYCODE_EQUALS) { - setProgress(getProgress() + 1); - return true; - } - if (keyCode == KeyEvent.KEYCODE_MINUS) { - setProgress(getProgress() - 1); - return true; - } + if (event.getAction() != KeyEvent.ACTION_DOWN) { + return false; + } + + SeekBar seekBar = (SeekBar) v.findViewById(com.android.internal.R.id.seekbar); + if (seekBar == null) { + return false; } - return false; + return seekBar.onKeyDown(keyCode, event); } public void setMax(int max) { diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 1a83cd567dc0..241e6db91756 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -19,6 +19,7 @@ package android.provider; import static android.net.TrafficStats.KB_IN_BYTES; import static android.system.OsConstants.SEEK_SET; +import android.annotation.Nullable; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; @@ -136,6 +137,9 @@ public final class DocumentsContract { */ private static final int THUMBNAIL_BUFFER_SIZE = (int) (128 * KB_IN_BYTES); + /** {@hide} */ + public static final String PACKAGE_DOCUMENTS_UI = "com.android.documentsui"; + /** * Constants related to a document, including {@link Cursor} column names * and flags. @@ -761,19 +765,33 @@ public final class DocumentsContract { * @see #buildDocumentUri(String, String) * @see #buildDocumentUriUsingTree(Uri, String) */ - public static boolean isDocumentUri(Context context, Uri uri) { - final List<String> paths = uri.getPathSegments(); - if (paths.size() == 2 && PATH_DOCUMENT.equals(paths.get(0))) { - return isDocumentsProvider(context, uri.getAuthority()); + public static boolean isDocumentUri(Context context, @Nullable Uri uri) { + if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) { + final List<String> paths = uri.getPathSegments(); + if (paths.size() == 2) { + return PATH_DOCUMENT.equals(paths.get(0)); + } else if (paths.size() == 4) { + return PATH_TREE.equals(paths.get(0)) && PATH_DOCUMENT.equals(paths.get(2)); + } } - if (paths.size() == 4 && PATH_TREE.equals(paths.get(0)) - && PATH_DOCUMENT.equals(paths.get(2))) { - return isDocumentsProvider(context, uri.getAuthority()); + return false; + } + + /** {@hide} */ + public static boolean isRootUri(Context context, @Nullable Uri uri) { + if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) { + final List<String> paths = uri.getPathSegments(); + return (paths.size() == 2 && PATH_ROOT.equals(paths.get(0))); } return false; } /** {@hide} */ + public static boolean isContentUri(@Nullable Uri uri) { + return uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()); + } + + /** {@hide} */ public static boolean isTreeUri(Uri uri) { final List<String> paths = uri.getPathSegments(); return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 225f0cf97c2e..d601831b863c 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7689,14 +7689,6 @@ public final class Settings { public static final String DEVICE_NAME = "device_name"; /** - * Whether it should be possible to create a guest user on the device. - * <p> - * Type: int (0 for disabled, 1 for enabled) - * @hide - */ - public static final String GUEST_USER_ENABLED = "guest_user_enabled"; - - /** * Whether the NetworkScoringService has been first initialized. * <p> * Type: int (0 for false, 1 for true) diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 631057013e50..4de903efff18 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -755,6 +755,10 @@ public class ZenModeConfig implements Parcelable { return rt; } + public static ComponentName getScheduleConditionProvider() { + return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider"); + } + public static class ScheduleInfo { public int[] days; public int startHour; @@ -827,6 +831,10 @@ public class ZenModeConfig implements Parcelable { return rt; } + public static ComponentName getEventConditionProvider() { + return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider"); + } + public static class EventInfo { public static final int REPLY_ANY_EXCEPT_NO = 0; public static final int REPLY_YES_OR_MAYBE = 1; diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java index 1665c752752f..f2b6041457de 100644 --- a/core/java/android/text/Hyphenator.java +++ b/core/java/android/text/Hyphenator.java @@ -16,15 +16,17 @@ package android.text; -import com.android.internal.annotations.GuardedBy; - import android.annotation.Nullable; import android.util.Log; -import libcore.io.IoUtils; +import com.android.internal.annotations.GuardedBy; import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.Locale; @@ -45,12 +47,18 @@ public class Hyphenator { @GuardedBy("sLock") final static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>(); - final static Hyphenator sEmptyHyphenator = new Hyphenator(StaticLayout.nLoadHyphenator("")); + final static Hyphenator sEmptyHyphenator = + new Hyphenator(StaticLayout.nLoadHyphenator(null, 0), null); final private long mNativePtr; - private Hyphenator(long nativePtr) { + // We retain a reference to the buffer to keep the memory mapping valid + @SuppressWarnings("unused") + final private ByteBuffer mBuffer; + + private Hyphenator(long nativePtr, ByteBuffer b) { mNativePtr = nativePtr; + mBuffer = b; } public long getNativePtr() { @@ -94,12 +102,18 @@ public class Hyphenator { } private static Hyphenator loadHyphenator(String languageTag) { - String patternFilename = "hyph-"+languageTag.toLowerCase(Locale.US)+".pat.txt"; + String patternFilename = "hyph-" + languageTag.toLowerCase(Locale.US) + ".hyb"; File patternFile = new File(getSystemHyphenatorLocation(), patternFilename); try { - String patternData = IoUtils.readFileAsString(patternFile.getAbsolutePath()); - long nativePtr = StaticLayout.nLoadHyphenator(patternData); - return new Hyphenator(nativePtr); + RandomAccessFile f = new RandomAccessFile(patternFile, "r"); + try { + FileChannel fc = f.getChannel(); + MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + long nativePtr = StaticLayout.nLoadHyphenator(buf, 0); + return new Hyphenator(nativePtr, buf); + } finally { + f.close(); + } } catch (IOException e) { Log.e(TAG, "error loading hyphenation " + patternFile, e); return null; @@ -152,7 +166,7 @@ public class Hyphenator { sMap.put(null, null); // TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data - String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "sa", "und-Ethi"}; + String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "und-Ethi"}; for (int i = 0; i < availableLanguages.length; i++) { String languageTag = availableLanguages[i]; Hyphenator h = loadHyphenator(languageTag); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 79c4a55a737c..6ece09131a26 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -29,6 +29,7 @@ import android.util.Pools.SynchronizedPool; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Locale; @@ -1244,7 +1245,7 @@ public class StaticLayout extends Layout { private static native void nFreeBuilder(long nativePtr); private static native void nFinishBuilder(long nativePtr); - /* package */ static native long nLoadHyphenator(String patternData); + /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset); private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator); diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java index 8b57d3d3d6c4..acca3edc98cf 100644 --- a/core/java/android/util/MathUtils.java +++ b/core/java/android/util/MathUtils.java @@ -185,4 +185,24 @@ public final class MathUtils { public static void randomSeed(long seed) { sRandom.setSeed(seed); } + + /** + * Returns the sum of the two parameters, or throws an exception if the resulting sum would + * cause an overflow or underflow. + * @throws IllegalArgumentException when overflow or underflow would occur. + */ + public static int addOrThrow(int a, int b) throws IllegalArgumentException { + if (b == 0) { + return a; + } + + if (b > 0 && a <= (Integer.MAX_VALUE - b)) { + return a + b; + } + + if (b < 0 && a >= (Integer.MIN_VALUE - b)) { + return a + b; + } + throw new IllegalArgumentException("Addition overflow: " + a + " + " + b); + } } diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index dcef14267b7f..304e9c048b60 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -349,7 +349,7 @@ public class ThreadedRenderer extends HardwareRenderer { * @param right The right side of the protected bounds. * @param bottom The bottom side of the protected bounds. */ - public void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) { + public void setContentDrawBounds(int left, int top, int right, int bottom) { mStagedContentBounds.set(left, top, right, bottom); } @@ -370,9 +370,9 @@ public class ThreadedRenderer extends HardwareRenderer { // renderer. if (!mCurrentContentBounds.equals(mStagedContentBounds)) { mCurrentContentBounds.set(mStagedContentBounds); - nSetContentOverdrawProtectionBounds(mNativeProxy, mCurrentContentBounds.left, - mCurrentContentBounds.top, mCurrentContentBounds.right, - mCurrentContentBounds.bottom); + nSetContentDrawBounds(mNativeProxy, mCurrentContentBounds.left, + mCurrentContentBounds.top, mCurrentContentBounds.right, + mCurrentContentBounds.bottom); } attachInfo.mIgnoreDirtyState = false; @@ -600,6 +600,6 @@ public class ThreadedRenderer extends HardwareRenderer { boolean placeFront); private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode); private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode); - private static native void nSetContentOverdrawProtectionBounds(long nativeProxy, int left, + private static native void nSetContentDrawBounds(long nativeProxy, int left, int top, int right, int bottom); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 263ec7d18f08..2c7a43648f2c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6649,12 +6649,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Sets a delegate for implementing accessibility support via composition as - * opposed to inheritance. The delegate's primary use is for implementing - * backwards compatible widgets. For more details see {@link AccessibilityDelegate}. - * - * @param delegate The delegate instance. + * Sets a delegate for implementing accessibility support via composition + * (as opposed to inheritance). For more details, see + * {@link AccessibilityDelegate}. + * <p> + * <strong>Note:</strong> On platform versions prior to + * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on + * views in the {@code android.widget.*} package are called <i>before</i> + * host methods. This prevents certain properties such as class name from + * being modified by overriding + * {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)}, + * as any changes will be overwritten by the host class. + * <p> + * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate + * methods are called <i>after</i> host methods, which all properties to be + * modified without being overwritten by the host class. * + * @param delegate the object to which accessibility method calls should be + * delegated * @see AccessibilityDelegate */ public void setAccessibilityDelegate(@Nullable AccessibilityDelegate delegate) { @@ -22277,6 +22289,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * corresponding delegate method without altering the behavior of the rest * accessibility related methods of the host view. * </p> + * <p> + * <strong>Note:</strong> On platform versions prior to + * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on + * views in the {@code android.widget.*} package are called <i>before</i> + * host methods. This prevents certain properties such as class name from + * being modified by overriding + * {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)}, + * as any changes will be overwritten by the host class. + * <p> + * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate + * methods are called <i>after</i> host methods, which all properties to be + * modified without being overwritten by the host class. */ public static class AccessibilityDelegate { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f6c60ed688a5..7cf23e717f6e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -73,6 +73,7 @@ import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import android.view.WindowCallbacks; import android.widget.Scroller; import com.android.internal.R; @@ -115,6 +116,12 @@ public final class ViewRootImpl implements ViewParent, private static final boolean DEBUG_INPUT_STAGES = false || LOCAL_LOGV; /** + * Set to false if we do not want to use the multi threaded renderer. Note that by disabling + * this, WindowCallbacks will not fire. + */ + private static final boolean USE_MT_RENDERER = true; + + /** * Set this system property to true to force the view hierarchy to render * at 60 Hz. This can be used to measure the potential framerate. */ @@ -132,11 +139,11 @@ public final class ViewRootImpl implements ViewParent, static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); - static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>(); + static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList(); static boolean sFirstDrawComplete = false; + static final ArrayList<WindowCallbacks> sWindowCallbacks = new ArrayList(); - static final ArrayList<ComponentCallbacks> sConfigCallbacks - = new ArrayList<ComponentCallbacks>(); + static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList(); final Context mContext; final IWindowSession mWindowSession; @@ -417,6 +424,22 @@ public final class ViewRootImpl implements ViewParent, } } + public static void addWindowCallbacks(WindowCallbacks callback) { + if (USE_MT_RENDERER) { + synchronized (sWindowCallbacks) { + sWindowCallbacks.add(callback); + } + } + } + + public static void removeWindowCallbacks(WindowCallbacks callback) { + if (USE_MT_RENDERER) { + synchronized (sWindowCallbacks) { + sWindowCallbacks.remove(callback); + } + } + } + // FIXME for perf testing only private boolean mProfile = false; @@ -1378,6 +1401,7 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mWindowVisibility = viewVisibility; host.dispatchWindowVisibilityChanged(viewVisibility); if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { + endDragResizing(); destroyHardwareResources(); } if (viewVisibility == View.GONE) { @@ -1701,14 +1725,20 @@ public final class ViewRootImpl implements ViewParent, final boolean dragResizing = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0; if (mDragResizing != dragResizing) { - mDragResizing = dragResizing; - mFullRedrawNeeded = true; + if (dragResizing) { + startDragResizing(frame); + } else { + // We shouldn't come here, but if we come we should end the resize. + endDragResizing(); + } } - if (dragResizing) { - mCanvasOffsetX = mWinFrame.left; - mCanvasOffsetY = mWinFrame.top; - } else { - mCanvasOffsetX = mCanvasOffsetY = 0; + if (!USE_MT_RENDERER) { + if (dragResizing) { + mCanvasOffsetX = mWinFrame.left; + mCanvasOffsetY = mWinFrame.top; + } else { + mCanvasOffsetX = mCanvasOffsetY = 0; + } } } catch (RemoteException e) { } @@ -6635,6 +6665,15 @@ public final class ViewRootImpl implements ViewParent, Configuration newConfig) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { + // Tell all listeners that we are resizing the window so that the chrome can get + // updated as fast as possible on a separate thread, + if (mViewAncestor.get().mDragResizing) { + synchronized (sWindowCallbacks) { + for (int i = sWindowCallbacks.size() - 1; i >= 0; i--) { + sWindowCallbacks.get(i).onWindowSizeIsChanging(frame); + } + } + } viewAncestor.dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets, reportDraw, newConfig); } @@ -6804,6 +6843,36 @@ public final class ViewRootImpl implements ViewParent, } /** + * Start a drag resizing which will inform all listeners that a window resize is taking place. + */ + private void startDragResizing(Rect initialBounds) { + if (!mDragResizing) { + mDragResizing = true; + synchronized (sWindowCallbacks) { + for (int i = sWindowCallbacks.size() - 1; i >= 0; i--) { + sWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds); + } + } + mFullRedrawNeeded = true; + } + } + + /** + * End a drag resize which will inform all listeners that a window resize has ended. + */ + private void endDragResizing() { + if (mDragResizing) { + mDragResizing = false; + synchronized (sWindowCallbacks) { + for (int i = sWindowCallbacks.size() - 1; i >= 0; i--) { + sWindowCallbacks.get(i).onWindowDragResizeEnd(); + } + } + mFullRedrawNeeded = true; + } + } + + /** * Class for managing the accessibility interaction connection * based on the global accessibility state. */ diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 0e7089ff7a8c..5f4e7af9390e 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -2016,4 +2016,8 @@ public abstract class Window { public boolean hasNonClientDecorView() { return false; } + + /** @hide */ + public void setTheme(int resId) { + } } diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java new file mode 100644 index 000000000000..cb6e98353a59 --- /dev/null +++ b/core/java/android/view/WindowCallbacks.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Rect; + +/** + * These callbacks are used to communicate window configuration changes while the user is performing + * window changes. + * @hide + */ +public interface WindowCallbacks { + /** + * Called by the system when the window got changed by the user, before the layouter got called. + * It can be used to perform a "quick and dirty" resize which should never take more then 4ms to + * complete. + * + * <p>At the time the layouting has not happened yet. + * + * @param newBounds The new window frame bounds. + */ + void onWindowSizeIsChanging(Rect newBounds); + + /** + * Called when a drag resize starts. + * @param initialBounds The initial bounds where the window will be. + */ + void onWindowDragResizeStart(Rect initialBounds); + + /** + * Called when a drag resize ends. + */ + void onWindowDragResizeEnd(); +} diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 72971e85dd24..81ec3306c4c7 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -88,6 +88,9 @@ public final class AccessibilityManager { /** @hide */ public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12; + /** @hide */ + public static final int AUTOCLICK_DELAY_DEFAULT = 600; + static final Object sInstanceSync = new Object(); private static AccessibilityManager sInstance; diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 68855ff72531..10aefe4eb714 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -666,7 +666,7 @@ public abstract class AbsSeekBar extends ProgressBar { progress += scale * max; setHotspot(x, (int) event.getY()); - setProgress((int) progress, true); + setProgressInternal((int) progress, true, false); } /** @@ -706,9 +706,12 @@ public abstract class AbsSeekBar extends ProgressBar { int increment = mKeyProgressIncrement; switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_MINUS: increment = -increment; // fallthrough case KeyEvent.KEYCODE_DPAD_RIGHT: + case KeyEvent.KEYCODE_PLUS: + case KeyEvent.KEYCODE_EQUALS: increment = isLayoutRtl() ? -increment : increment; if (setProgressInternal(getProgress() + increment, true, true)) { diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index fddc40fc77b7..f53aa38a4c7a 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -2645,6 +2645,8 @@ public class Editor { private SuggestionAdapter mSuggestionsAdapter; private final Comparator<SuggestionSpan> mSuggestionSpanComparator; private final HashMap<SuggestionSpan, Integer> mSpansLengths; + private final TextAppearanceSpan mHighlightSpan = new TextAppearanceSpan( + mTextView.getContext(), android.R.style.TextAppearance_SuggestionHighlight); private class CustomPopupWindow extends PopupWindow { public CustomPopupWindow(Context context, int defStyleAttr) { @@ -2710,8 +2712,6 @@ public class Editor { SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents int suggestionIndex; // the index of this suggestion inside suggestionSpan SpannableStringBuilder text = new SpannableStringBuilder(); - TextAppearanceSpan highlightSpan = new TextAppearanceSpan(mTextView.getContext(), - android.R.style.TextAppearance_SuggestionHighlight); } private class SuggestionAdapter extends BaseAdapter { @@ -2948,7 +2948,7 @@ public class Editor { suggestionInfo.suggestionIndex = ADD_TO_DICTIONARY; suggestionInfo.text.replace(0, suggestionInfo.text.length(), mTextView. getContext().getString(com.android.internal.R.string.addToDictionary)); - suggestionInfo.text.setSpan(suggestionInfo.highlightSpan, 0, 0, + suggestionInfo.text.setSpan(mHighlightSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mNumberOfSuggestions++; @@ -2961,8 +2961,7 @@ public class Editor { suggestionInfo.suggestionIndex = DELETE_TEXT; suggestionInfo.text.replace(0, suggestionInfo.text.length(), mTextView.getContext().getString(com.android.internal.R.string.deleteText)); - suggestionInfo.text.setSpan(suggestionInfo.highlightSpan, 0, 0, - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + suggestionInfo.text.setSpan(mHighlightSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mNumberOfSuggestions++; if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan(); @@ -2993,8 +2992,8 @@ public class Editor { suggestionInfo.suggestionEnd = suggestionInfo.suggestionStart + suggestionInfo.text.length(); - suggestionInfo.text.setSpan(suggestionInfo.highlightSpan, 0, - suggestionInfo.text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + suggestionInfo.text.setSpan(mHighlightSpan, 0, suggestionInfo.text.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // Add the text before and after the span. final String textAsString = text.toString(); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 2172b5c57893..f0e216fc7873 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -40,6 +40,7 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.os.UserManager; +import android.provider.DocumentsContract; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; @@ -269,7 +270,20 @@ public class ChooserActivity extends ResolverActivity { } @Override - boolean shouldAutoLaunchSingleChoice() { + boolean shouldAutoLaunchSingleChoice(TargetInfo target) { + final Intent intent = target.getResolvedIntent(); + final ResolveInfo resolve = target.getResolveInfo(); + + // When GET_CONTENT is handled by the DocumentsUI system component, + // we're okay automatically launching it, since it offers it's own + // intent disambiguation UI. + if (intent != null && Intent.ACTION_GET_CONTENT.equals(intent.getAction()) + && resolve != null && resolve.priority > 0 + && resolve.activityInfo != null && DocumentsContract.PACKAGE_DOCUMENTS_UI + .equals(resolve.activityInfo.packageName)) { + return true; + } + return false; } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index ef9d1cebff57..17104894310e 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -796,7 +796,7 @@ public class ResolverActivity extends Activity { return false; } - boolean shouldAutoLaunchSingleChoice() { + boolean shouldAutoLaunchSingleChoice(TargetInfo target) { return true; } @@ -837,18 +837,21 @@ public class ResolverActivity extends Activity { mAlwaysUseOption = alwaysUseOption; int count = mAdapter.getUnfilteredCount(); - if ((!shouldAutoLaunchSingleChoice() && count > 0) - || count > 1 - || (count == 1 && mAdapter.getOtherProfile() != null)) { + if (count == 1 && mAdapter.getOtherProfile() == null) { + // Only one target, so we're a candidate to auto-launch! + final TargetInfo target = mAdapter.targetInfoForPosition(0, false); + if (shouldAutoLaunchSingleChoice(target)) { + safelyStartActivity(target); + mPackageMonitor.unregister(); + mRegistered = false; + finish(); + return true; + } + } + if (count > 0) { setContentView(layoutId); mAdapterView = (AbsListView) findViewById(R.id.resolver_list); onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption); - } else if (count == 1) { - safelyStartActivity(mAdapter.targetInfoForPosition(0, false)); - mPackageMonitor.unregister(); - mRegistered = false; - finish(); - return true; } else { setContentView(R.layout.resolver_list); diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index b6240e48a1fd..c25db65b342b 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -46,6 +46,9 @@ public class MetricsLogger implements MetricsConstants { public static final int ACTION_FINGERPRINT_RENAME = 254; public static final int ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255; public static final int ACTION_WIGGLE_CAMERA_GESTURE = 256; + public static final int QS_LOCK_TILE = 257; + public static final int QS_USER_TILE = 258; + public static final int QS_BATTERY_TILE = 259; public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 8cf2dabdb549..64b7768c8337 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -5704,6 +5704,8 @@ public final class BatteryStatsImpl extends BatteryStats { cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in); } } + } else { + mCpuClusterSpeed[cluster] = null; } } } else { @@ -9382,13 +9384,14 @@ public final class BatteryStatsImpl extends BatteryStats { u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][]; for (int cluster = 0; cluster < numClusters; cluster++) { - int NSB = in.readInt(); - if (mPowerProfile != null && - mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) { - throw new ParcelFormatException("File corrupt: too many speed bins " + NSB); - } - if (in.readInt() != 0) { + final int NSB = in.readInt(); + if (mPowerProfile != null && + mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) { + throw new ParcelFormatException("File corrupt: too many speed bins " + + NSB); + } + u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB]; for (int speed = 0; speed < NSB; speed++) { if (in.readInt() != 0) { @@ -9397,6 +9400,8 @@ public final class BatteryStatsImpl extends BatteryStats { u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in); } } + } else { + u.mCpuClusterSpeed[cluster] = null; } } } else { diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java new file mode 100644 index 000000000000..4f17c39ddf81 --- /dev/null +++ b/core/java/com/android/internal/policy/DecorContext.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.policy; + +import android.content.Context; +import android.view.ContextThemeWrapper; +import android.view.WindowManager; +import android.view.WindowManagerImpl; + +/** + * Context for decor views which can be seeded with pure application context and not depend on the + * activity, but still provide some of the facilities that Activity has, e.g. themes. + * + * @hide + */ +class DecorContext extends ContextThemeWrapper { + private PhoneWindow mPhoneWindow; + private WindowManager mWindowManager; + + public DecorContext(Context context) { + super(context, null); + } + + void setPhoneWindow(PhoneWindow phoneWindow) { + mPhoneWindow = phoneWindow; + mWindowManager = null; + } + + @Override + public Object getSystemService(String name) { + if (Context.WINDOW_SERVICE.equals(name)) { + if (mWindowManager == null) { + WindowManagerImpl wm = + (WindowManagerImpl) super.getSystemService(Context.WINDOW_SERVICE); + mWindowManager = wm.createLocalWindowManager(mPhoneWindow); + } + return mWindowManager; + } + return super.getSystemService(name); + } +} diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 41c92576ffea..c9b81190ce38 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -32,6 +32,8 @@ import android.animation.Animator; import android.animation.ObjectAnimator; import android.app.ActivityManagerNative; import android.app.SearchManager; +import android.content.ContextWrapper; +import android.content.res.Resources; import android.os.Build; import android.os.UserHandle; @@ -317,6 +319,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private Rect mOutsets = new Rect(); private boolean mIsStartingWindow; + private int mTheme = -1; static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( @@ -2302,7 +2305,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - private final class DecorView extends FrameLayout implements RootViewSurfaceTaker { + private static final class DecorView extends FrameLayout implements RootViewSurfaceTaker { /* package */int mDefaultOpacity = PixelFormat.OPAQUE; @@ -2372,6 +2375,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private int mRootScrollY = 0; + private PhoneWindow mWindow; + public DecorView(Context context, int featureId) { super(context); mFeatureId = featureId; @@ -2393,7 +2398,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void onDraw(Canvas c) { super.onDraw(c); - mBackgroundFallback.draw(mContentRoot, c, mContentParent); + mBackgroundFallback.draw(mWindow.mContentRoot, c, mWindow.mContentParent); } @Override @@ -2405,7 +2410,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (isDown && (event.getRepeatCount() == 0)) { // First handle chording of panel key: if a panel key is held // but not released, try to execute a shortcut in it. - if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) { + if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) { boolean handled = dispatchKeyShortcutEvent(event); if (handled) { return true; @@ -2414,15 +2419,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // If a panel is open, perform a shortcut on it without the // chorded panel key - if ((mPreparedPanel != null) && mPreparedPanel.isOpen) { - if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) { + if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) { + if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) { return true; } } } - if (!isDestroyed()) { - final Callback cb = getCallback(); + if (!mWindow.isDestroyed()) { + final Callback cb = mWindow.getCallback(); final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event) : super.dispatchKeyEvent(event); if (handled) { @@ -2430,28 +2435,28 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event) - : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event); + return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event) + : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event); } @Override public boolean dispatchKeyShortcutEvent(KeyEvent ev) { // If the panel is already prepared, then perform the shortcut using it. boolean handled; - if (mPreparedPanel != null) { - handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev, + if (mWindow.mPreparedPanel != null) { + handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev, Menu.FLAG_PERFORM_NO_CLOSE); if (handled) { - if (mPreparedPanel != null) { - mPreparedPanel.isHandled = true; + if (mWindow.mPreparedPanel != null) { + mWindow.mPreparedPanel.isHandled = true; } return true; } } // Shortcut not handled by the panel. Dispatch to the view hierarchy. - final Callback cb = getCallback(); - handled = cb != null && !isDestroyed() && mFeatureId < 0 + final Callback cb = mWindow.getCallback(); + handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev); if (handled) { return true; @@ -2461,10 +2466,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // combination such as Control+C. Temporarily prepare the panel then mark it // unprepared again when finished to ensure that the panel will again be prepared // the next time it is shown for real. - PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); - if (st != null && mPreparedPanel == null) { - preparePanel(st, ev); - handled = performPanelShortcut(st, ev.getKeyCode(), ev, + PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false); + if (st != null && mWindow.mPreparedPanel == null) { + mWindow.preparePanel(st, ev); + handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev, Menu.FLAG_PERFORM_NO_CLOSE); st.isPrepared = false; if (handled) { @@ -2476,23 +2481,23 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public boolean dispatchTouchEvent(MotionEvent ev) { - final Callback cb = getCallback(); - return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) - : super.dispatchTouchEvent(ev); + final Callback cb = mWindow.getCallback(); + return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 + ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); } @Override public boolean dispatchTrackballEvent(MotionEvent ev) { - final Callback cb = getCallback(); - return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev) - : super.dispatchTrackballEvent(ev); + final Callback cb = mWindow.getCallback(); + return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 + ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev); } @Override public boolean dispatchGenericMotionEvent(MotionEvent ev) { - final Callback cb = getCallback(); - return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev) - : super.dispatchGenericMotionEvent(ev); + final Callback cb = mWindow.getCallback(); + return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 + ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev); } public boolean superDispatchKeyEvent(KeyEvent event) { @@ -2544,7 +2549,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public boolean onInterceptTouchEvent(MotionEvent event) { int action = event.getAction(); - if (mHasNonClientDecor && mNonClientDecorView.mVisible) { + if (mHasNonClientDecor && mWindow.mNonClientDecorView.mVisible) { // Don't dispatch ACTION_DOWN to the non client decor if the window is // resizable and the event was (starting) outside the window. // Window resizing events should be handled by WindowManager. @@ -2567,7 +2572,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { int x = (int)event.getX(); int y = (int)event.getY(); if (isOutOfBounds(x, y)) { - closePanel(mFeatureId); + mWindow.closePanel(mFeatureId); return true; } } @@ -2593,7 +2598,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (action == MotionEvent.ACTION_MOVE) { if (y > (mDownY+30)) { Log.i(TAG, "Closing!"); - closePanel(mFeatureId); + mWindow.closePanel(mFeatureId); mWatchingForMenu = false; return true; } @@ -2609,8 +2614,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (action == MotionEvent.ACTION_DOWN) { int y = (int)event.getY(); - if (y >= (getHeight()-5) && !hasChildren()) { - Log.i(TAG, "Watchiing!"); + if (y >= (getHeight()-5) && !mWindow.hasChildren()) { + Log.i(TAG, "Watching!"); mWatchingForMenu = true; } return false; @@ -2624,7 +2629,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (action == MotionEvent.ACTION_MOVE) { if (y < (getHeight()-30)) { Log.i(TAG, "Opening!"); - openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent( + mWindow.openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent( KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)); mWatchingForMenu = false; return true; @@ -2658,8 +2663,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { - final Callback cb = getCallback(); - if (cb != null && !isDestroyed()) { + final Callback cb = mWindow.getCallback(); + if (cb != null && !mWindow.isDestroyed()) { if (cb.dispatchPopulateAccessibilityEvent(event)) { return true; } @@ -2696,7 +2701,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (SWEEP_OPEN_MENU) { if (mMenuBackground == null && mFeatureId < 0 - && getAttributes().height + && mWindow.getAttributes().height == WindowManager.LayoutParams.MATCH_PARENT) { mMenuBackground = getContext().getDrawable( R.drawable.menu_background); @@ -2721,7 +2726,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { boolean fixedWidth = false; if (widthMode == AT_MOST) { - final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor; + final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor + : mWindow.mFixedWidthMajor; if (tvw != null && tvw.type != TypedValue.TYPE_NULL) { final int w; if (tvw.type == TypedValue.TYPE_DIMENSION) { @@ -2742,7 +2748,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } if (heightMode == AT_MOST) { - final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor; + final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor + : mWindow.mFixedHeightMinor; if (tvh != null && tvh.type != TypedValue.TYPE_NULL) { final int h; if (tvh.type == TypedValue.TYPE_DIMENSION) { @@ -2760,21 +2767,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - getOutsets(mOutsets); - if (mOutsets.top > 0 || mOutsets.bottom > 0) { + getOutsets(mWindow.mOutsets); + if (mWindow.mOutsets.top > 0 || mWindow.mOutsets.bottom > 0) { int mode = MeasureSpec.getMode(heightMeasureSpec); if (mode != MeasureSpec.UNSPECIFIED) { int height = MeasureSpec.getSize(heightMeasureSpec); heightMeasureSpec = MeasureSpec.makeMeasureSpec( - height + mOutsets.top + mOutsets.bottom, mode); + height + mWindow.mOutsets.top + mWindow.mOutsets.bottom, mode); } } - if (mOutsets.left > 0 || mOutsets.right > 0) { + if (mWindow.mOutsets.left > 0 || mWindow.mOutsets.right > 0) { int mode = MeasureSpec.getMode(widthMeasureSpec); if (mode != MeasureSpec.UNSPECIFIED) { int width = MeasureSpec.getSize(widthMeasureSpec); widthMeasureSpec = MeasureSpec.makeMeasureSpec( - width + mOutsets.left + mOutsets.right, mode); + width + mWindow.mOutsets.left + mWindow.mOutsets.right, mode); } } @@ -2786,7 +2793,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY); if (!fixedWidth && widthMode == AT_MOST) { - final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor; + final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor; if (tv.type != TypedValue.TYPE_NULL) { final int min; if (tv.type == TypedValue.TYPE_DIMENSION) { @@ -2814,12 +2821,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - getOutsets(mOutsets); - if (mOutsets.left > 0) { - offsetLeftAndRight(-mOutsets.left); + getOutsets(mWindow.mOutsets); + if (mWindow.mOutsets.left > 0) { + offsetLeftAndRight(-mWindow.mOutsets.left); } - if (mOutsets.top > 0) { - offsetTopAndBottom(-mOutsets.top); + if (mWindow.mOutsets.top > 0) { + offsetTopAndBottom(-mWindow.mOutsets.top); } } @@ -2835,46 +2842,46 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public boolean showContextMenuForChild(View originalView) { // Reuse the context menu builder - if (mContextMenu == null) { - mContextMenu = new ContextMenuBuilder(getContext()); - mContextMenu.setCallback(mContextMenuCallback); + if (mWindow.mContextMenu == null) { + mWindow.mContextMenu = new ContextMenuBuilder(getContext()); + mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback); } else { - mContextMenu.clearAll(); + mWindow.mContextMenu.clearAll(); } - final MenuDialogHelper helper = mContextMenu.show(originalView, + final MenuDialogHelper helper = mWindow.mContextMenu.show(originalView, originalView.getWindowToken()); if (helper != null) { - helper.setPresenterCallback(mContextMenuCallback); - } else if (mContextMenuHelper != null) { + helper.setPresenterCallback(mWindow.mContextMenuCallback); + } else if (mWindow.mContextMenuHelper != null) { // No menu to show, but if we have a menu currently showing it just became blank. // Close it. - mContextMenuHelper.dismiss(); + mWindow.mContextMenuHelper.dismiss(); } - mContextMenuHelper = helper; + mWindow.mContextMenuHelper = helper; return helper != null; } @Override public boolean showContextMenuForChild(View originalView, float x, float y) { // Reuse the context menu builder - if (mContextMenu == null) { - mContextMenu = new ContextMenuBuilder(getContext()); - mContextMenu.setCallback(mContextMenuCallback); + if (mWindow.mContextMenu == null) { + mWindow.mContextMenu = new ContextMenuBuilder(getContext()); + mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback); } else { - mContextMenu.clearAll(); + mWindow.mContextMenu.clearAll(); } - final MenuPopupHelper helper = mContextMenu.showPopup( + final MenuPopupHelper helper = mWindow.mContextMenu.showPopup( getContext(), originalView, x, y); if (helper != null) { - helper.setCallback(mContextMenuCallback); - } else if (mContextMenuPopupHelper != null) { + helper.setCallback(mWindow.mContextMenuCallback); + } else if (mWindow.mContextMenuPopupHelper != null) { // No menu to show, but if we have a menu currently showing it just became blank. // Close it. - mContextMenuPopupHelper.dismiss(); + mWindow.mContextMenuPopupHelper.dismiss(); } - mContextMenuPopupHelper = helper; + mWindow.mContextMenuPopupHelper = helper; return helper != null; } @@ -2904,14 +2911,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { View originatingView, ActionMode.Callback callback, int type) { ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback); ActionMode mode = null; - if (getCallback() != null && !isDestroyed()) { + if (mWindow.getCallback() != null && !mWindow.isDestroyed()) { try { - mode = getCallback().onWindowStartingActionMode(wrappedCallback, type); + mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type); } catch (AbstractMethodError ame) { // Older apps might not implement the typed version of this method. if (type == ActionMode.TYPE_PRIMARY) { try { - mode = getCallback().onWindowStartingActionMode(wrappedCallback); + mode = mWindow.getCallback().onWindowStartingActionMode( + wrappedCallback); } catch (AbstractMethodError ame2) { // Older apps might not implement this callback method at all. } @@ -2936,9 +2944,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mode = null; } } - if (mode != null && getCallback() != null && !isDestroyed()) { + if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) { try { - getCallback().onActionModeStarted(mode); + mWindow.getCallback().onActionModeStarted(mode); } catch (AbstractMethodError ame) { // Older apps might not implement this callback method. } @@ -3027,10 +3035,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } private WindowInsets updateColorViews(WindowInsets insets, boolean animate) { - WindowManager.LayoutParams attrs = getAttributes(); + WindowManager.LayoutParams attrs = mWindow.getAttributes(); int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility(); - if (!mIsFloating && ActivityManager.isHighEndGfx()) { + if (!mWindow.mIsFloating && ActivityManager.isHighEndGfx()) { boolean disallowAnimate = !isLaidOut(); disallowAnimate |= ((mLastWindowFlags ^ attrs.flags) & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; @@ -3062,14 +3070,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0; int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset; - updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor, - navBarSize, navBarToRightEdge, 0 /* rightInset */, - animate && !disallowAnimate); + updateColorViewInt(mNavigationColorViewState, sysUiVisibility, + mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge, + 0 /* rightInset */, animate && !disallowAnimate); boolean statusBarNeedsRightInset = navBarToRightEdge && mNavigationColorViewState.present; int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0; - updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor, + updateColorViewInt(mStatusColorViewState, sysUiVisibility, mWindow.mStatusBarColor, mLastTopInset, false /* matchVertical */, statusBarRightInset, animate && !disallowAnimate); } @@ -3086,13 +3094,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { int consumedRight = consumingNavBar ? mLastRightInset : 0; int consumedBottom = consumingNavBar ? mLastBottomInset : 0; - if (mContentRoot != null - && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) { - MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams(); + if (mWindow.mContentRoot != null + && mWindow.mContentRoot.getLayoutParams() instanceof MarginLayoutParams) { + MarginLayoutParams lp = (MarginLayoutParams) mWindow.mContentRoot.getLayoutParams(); if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) { lp.rightMargin = consumedRight; lp.bottomMargin = consumedBottom; - mContentRoot.setLayoutParams(lp); + mWindow.mContentRoot.setLayoutParams(lp); if (insets == null) { // The insets have changed, but we're not currently in the process @@ -3130,11 +3138,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color, int size, boolean verticalBar, int rightMargin, boolean animate) { state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0 - && (getAttributes().flags & state.hideWindowFlag) == 0 - && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; + && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0 + && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; boolean show = state.present && (color & Color.BLACK) != 0 - && (getAttributes().flags & state.translucentFlag) == 0; + && (mWindow.getAttributes().flags & state.translucentFlag) == 0; boolean visibilityChanged = false; View view = state.view; @@ -3226,14 +3234,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mPrimaryActionModeView.getLayoutParams(); boolean mlpChanged = false; if (mPrimaryActionModeView.isShown()) { - if (mTempRect == null) { - mTempRect = new Rect(); + if (mWindow.mTempRect == null) { + mWindow.mTempRect = new Rect(); } - final Rect rect = mTempRect; + final Rect rect = mWindow.mTempRect; // If the parent doesn't consume the insets, manually // apply the default system window insets. - mContentParent.computeSystemWindowInsets(insets, rect); + mWindow.mContentParent.computeSystemWindowInsets(insets, rect); final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0; if (mlp.topMargin != newMargin) { mlpChanged = true; @@ -3264,7 +3272,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // mode is overlaid on the app content (e.g. it's // sitting in a FrameLayout, see // screen_simple_overlay_action_mode.xml). - final boolean nonOverlay = (getLocalFeatures() + final boolean nonOverlay = (mWindow.getLocalFeatures() & (1 << FEATURE_ACTION_MODE_OVERLAY)) == 0; insets = insets.consumeSystemWindowInsets( false, nonOverlay && showStatusGuard /* top */, false, false); @@ -3288,14 +3296,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private void updateNavigationGuard(WindowInsets insets) { // IMEs lay out below the nav bar, but the content view must not (for back compat) - if (getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) { + if (mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) { // prevent the content view from including the nav bar height - if (mContentParent != null) { - if (mContentParent.getLayoutParams() instanceof MarginLayoutParams) { + if (mWindow.mContentParent != null) { + if (mWindow.mContentParent.getLayoutParams() instanceof MarginLayoutParams) { MarginLayoutParams mlp = - (MarginLayoutParams) mContentParent.getLayoutParams(); + (MarginLayoutParams) mWindow.mContentParent.getLayoutParams(); mlp.bottomMargin = insets.getSystemWindowInsetBottom(); - mContentParent.setLayoutParams(mlp); + mWindow.mContentParent.setLayoutParams(mlp); } } // position the navigation guard view, creating it if necessary @@ -3377,7 +3385,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mDefaultOpacity = opacity; if (mFeatureId < 0) { - setDefaultWindowFormat(opacity); + mWindow.setDefaultWindowFormat(opacity); } } @@ -3387,12 +3395,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // If the user is chording a menu shortcut, release the chord since // this window lost focus - if (hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus && mPanelChordingKey != 0) { - closePanel(FEATURE_OPTIONS_PANEL); + if (mWindow.hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus + && mWindow.mPanelChordingKey != 0) { + mWindow.closePanel(FEATURE_OPTIONS_PANEL); } - final Callback cb = getCallback(); - if (cb != null && !isDestroyed() && mFeatureId < 0) { + final Callback cb = mWindow.getCallback(); + if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) { cb.onWindowFocusChanged(hasWindowFocus); } @@ -3408,8 +3417,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { protected void onAttachedToWindow() { super.onAttachedToWindow(); - final Callback cb = getCallback(); - if (cb != null && !isDestroyed() && mFeatureId < 0) { + final Callback cb = mWindow.getCallback(); + if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) { cb.onAttachedToWindow(); } @@ -3421,7 +3430,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * menu was open. When the activity is recreated, the menu * should be shown again. */ - openPanelsAfterRestore(); + mWindow.openPanelsAfterRestore(); } } @@ -3429,13 +3438,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - final Callback cb = getCallback(); + final Callback cb = mWindow.getCallback(); if (cb != null && mFeatureId < 0) { cb.onDetachedFromWindow(); } - if (mDecorContentParent != null) { - mDecorContentParent.dismissPopups(); + if (mWindow.mDecorContentParent != null) { + mWindow.mDecorContentParent.dismissPopups(); } if (mPrimaryActionModePopup != null) { @@ -3450,7 +3459,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mFloatingToolbar = null; } - PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); + PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false); if (st != null && st.menu != null && mFeatureId < 0) { st.menu.close(); } @@ -3459,29 +3468,29 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void onCloseSystemDialogs(String reason) { if (mFeatureId >= 0) { - closeAllPanels(); + mWindow.closeAllPanels(); } } public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() { - return mFeatureId < 0 ? mTakeSurfaceCallback : null; + return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null; } public InputQueue.Callback willYouTakeTheInputQueue() { - return mFeatureId < 0 ? mTakeInputQueueCallback : null; + return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null; } public void setSurfaceType(int type) { - PhoneWindow.this.setType(type); + mWindow.setType(type); } public void setSurfaceFormat(int format) { - PhoneWindow.this.setFormat(format); + mWindow.setFormat(format); } public void setSurfaceKeepScreenOn(boolean keepOn) { - if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } @Override @@ -3513,7 +3522,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { endOnGoingFadeAnimation(); cleanupPrimaryActionMode(); if (mPrimaryActionModeView == null) { - if (isFloating()) { + if (mWindow.isFloating()) { // Use the action bar theme. final TypedValue outValue = new TypedValue(); final Theme baseTheme = mContext.getTheme(); @@ -3660,7 +3669,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private void setHandledFloatingActionMode(ActionMode mode) { mFloatingActionMode = mode; - mFloatingToolbar = new FloatingToolbar(mContext, PhoneWindow.this); + mFloatingToolbar = new FloatingToolbar(mContext, mWindow); ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar); mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary. mFloatingActionModeOriginatingView.getViewTreeObserver() @@ -3699,6 +3708,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return windowHasNonClientDecor() && getElevation() > 0; } + void setWindow(PhoneWindow phoneWindow) { + mWindow = phoneWindow; + Context context = getContext(); + if (context instanceof DecorContext) { + DecorContext decorContex = (DecorContext) context; + decorContex.setPhoneWindow(mWindow); + } + } + /** * Clears out internal references when the action mode is destroyed. */ @@ -3787,9 +3805,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { cleanupFloatingActionModeViews(); mFloatingActionMode = null; } - if (getCallback() != null && !isDestroyed()) { + if (mWindow.getCallback() != null && !mWindow.isDestroyed()) { try { - getCallback().onActionModeFinished(mode); + mWindow.getCallback().onActionModeFinished(mode); } catch (AbstractMethodError ame) { // Older apps might not implement this callback method. } @@ -3809,7 +3827,20 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } protected DecorView generateDecor(int featureId) { - return new DecorView(getContext(), featureId); + // System process doesn't have application context and in that case we need to directly use + // the context we have. Otherwise we want the application context, so we don't cling to the + // activity. + Context applicationContext = getContext().getApplicationContext(); + Context context; + if (applicationContext == null) { + context = getContext(); + } else { + context = new DecorContext(applicationContext); + if (mTheme != -1) { + context.setTheme(mTheme); + } + } + return new DecorView(context, featureId); } protected void setFeatureFromAttrs(int featureId, TypedArray attrs, @@ -4178,13 +4209,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // Dependent on the brightness of the used title we either use the // dark or the light button frame. if (nonClientDecorView == null) { + Context context = mDecor.getContext(); TypedValue value = new TypedValue(); - getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true); + context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true); + LayoutInflater inflater = mLayoutInflater.from(context); if (Color.luminance(value.data) < 0.5) { - nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate( + nonClientDecorView = (NonClientDecorView) inflater.inflate( R.layout.non_client_decor_dark, null); } else { - nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate( + nonClientDecorView = (NonClientDecorView) inflater.inflate( R.layout.non_client_decor_light, null); } } @@ -4206,11 +4239,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mForceDecorInstall = false; if (mDecor == null) { mDecor = generateDecor(-1); + mDecor.setWindow(this); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } + } else { + mDecor.setWindow(this); } if (mContentParent == null) { mContentParent = generateLayout(mDecor); @@ -5385,4 +5421,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public boolean hasNonClientDecorView() { return mNonClientDecorView != null; } + + @Override + public void setTheme(int resid) { + mTheme = resid; + if (mDecor != null) { + Context context = mDecor.getContext(); + if (context instanceof DecorContext) { + context.setTheme(resid); + } + } + } } diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java index 6ab306cec730..6960a3b29fad 100644 --- a/core/java/com/android/internal/widget/NonClientDecorView.java +++ b/core/java/com/android/internal/widget/NonClientDecorView.java @@ -17,15 +17,23 @@ package com.android.internal.widget; import android.content.Context; +import android.graphics.Color; import android.graphics.Rect; +import android.os.Looper; import android.os.RemoteException; import android.util.AttributeSet; +import android.view.Choreographer; +import android.view.DisplayListCanvas; import android.view.MotionEvent; +import android.view.RenderNode; +import android.view.ThreadedRenderer; import android.view.View; +import android.view.ViewRootImpl; import android.widget.LinearLayout; import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.Window; +import android.view.WindowCallbacks; import android.util.Log; import android.util.TypedValue; @@ -57,7 +65,8 @@ import com.android.internal.policy.PhoneWindow; * </ul> * This will be mitigated once b/22527834 will be addressed. */ -public class NonClientDecorView extends LinearLayout implements View.OnClickListener { +public class NonClientDecorView extends LinearLayout + implements View.OnClickListener, View.OnTouchListener, WindowCallbacks { private final static String TAG = "NonClientDecorView"; // The height of a window which has focus in DIP. private final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20; @@ -66,6 +75,8 @@ public class NonClientDecorView extends LinearLayout implements View.OnClickList private PhoneWindow mOwner = null; private boolean mWindowHasShadow = false; private boolean mShowDecor = false; + // True when this object is listening for window size changes. + private boolean mAttachedCallbacksToRootViewImpl = false; // True if the window is being dragged. private boolean mDragging = false; @@ -84,6 +95,9 @@ public class NonClientDecorView extends LinearLayout implements View.OnClickList // to max until the first layout command has been executed. private boolean mAllowUpdateElevation = false; + // The resize frame renderer. + private ResizeFrameThread mFrameRendererThread = null; + public NonClientDecorView(Context context) { super(context); } @@ -107,12 +121,24 @@ public class NonClientDecorView extends LinearLayout implements View.OnClickList // By changing the outline provider to BOUNDS, the window can remove its // background without removing the shadow. mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS); + + if (!mAttachedCallbacksToRootViewImpl) { + // If there is no window callback installed there was no window set before. Set it now. + // Note that our ViewRootImpl object will not change. + getViewRootImpl().addWindowCallbacks(this); + mAttachedCallbacksToRootViewImpl = true; + } else if (mFrameRendererThread != null) { + // We are resizing and this call happened due to a configuration change. Tell the + // renderer about it. + mFrameRendererThread.onConfigurationChange(); + } + findViewById(R.id.maximize_window).setOnClickListener(this); findViewById(R.id.close_window).setOnClickListener(this); } @Override - public boolean onTouchEvent(MotionEvent e) { + public boolean onTouch(View v, MotionEvent e) { // Note: There are no mixed events. When a new device gets used (e.g. 1. Mouse, 2. touch) // the old input device events get cancelled first. So no need to remember the kind of // input device we are listening to. @@ -224,6 +250,7 @@ public class NonClientDecorView extends LinearLayout implements View.OnClickList boolean invisible = isFillingScreen() || !mShowDecor; View caption = getChildAt(0); caption.setVisibility(invisible ? GONE : VISIBLE); + caption.setOnTouchListener(this); mVisible = !invisible; } @@ -249,7 +276,9 @@ public class NonClientDecorView extends LinearLayout implements View.OnClickList **/ private void updateElevation() { float elevation = 0; - if (mWindowHasShadow) { + // Do not use a shadow when we are in resizing mode (mRenderer not null) since the shadow + // is bound to the content size and not the target size. + if (mWindowHasShadow && mFrameRendererThread == null) { boolean fill = isFillingScreen(); elevation = fill ? 0 : (mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : @@ -291,4 +320,270 @@ public class NonClientDecorView extends LinearLayout implements View.OnClickList } } } + + @Override + public void onWindowDragResizeStart(Rect initialBounds) { + if (mOwner.isDestroyed()) { + // If the owner's window is gone, we should not be able to come here anymore. + releaseResources(); + return; + } + if (mFrameRendererThread != null) { + return; + } + final ThreadedRenderer renderer = + (ThreadedRenderer) mOwner.getDecorView().getHardwareRenderer(); + if (renderer != null) { + mFrameRendererThread = new ResizeFrameThread(renderer, initialBounds); + // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time. + // If we want to get the shadow shown while resizing, we would need to elevate a new + // element which owns the caption and has the elevation. + updateElevation(); + } + } + + @Override + public void onWindowDragResizeEnd() { + releaseThreadedRenderer(); + } + + @Override + public void onWindowSizeIsChanging(Rect newBounds) { + if (mFrameRendererThread != null) { + mFrameRendererThread.setTargetRect(newBounds); + } + } + + /** + * Release the renderer thread which is usually done when the user stops resizing. + */ + private void releaseThreadedRenderer() { + if (mFrameRendererThread != null) { + mFrameRendererThread.releaseRenderer(); + mFrameRendererThread = null; + // Bring the shadow back. + updateElevation(); + } + } + + /** + * Called when the parent window is destroyed to release all resources. Note that this will also + * destroy the renderer thread. + */ + private void releaseResources() { + releaseThreadedRenderer(); + if (mAttachedCallbacksToRootViewImpl) { + ViewRootImpl.removeWindowCallbacks(this); + mAttachedCallbacksToRootViewImpl = false; + } + } + + /** + * The thread which draws the chrome while we are resizing. + * It starts with the creation and it ends once someone calls destroy(). + * Any size changes can be passed by a call to setTargetRect will passed to the thread and + * executed via the Choreographer. + */ + private class ResizeFrameThread extends Thread implements Choreographer.FrameCallback { + // This is containing the last requested size by a resize command. Note that this size might + // or might not have been applied to the output already. + private final Rect mTargetRect = new Rect(); + + // The render nodes for the multi threaded renderer. + private ThreadedRenderer mRenderer; + private RenderNode mFrameNode; + private RenderNode mBackdropNode; + + private final Rect mOldTargetRect = new Rect(); + private final Rect mNewTargetRect = new Rect(); + private Choreographer mChoreographer; + + // Cached size values from the last render for the case that the view hierarchy is gone + // during a configuration change. + private int mLastContentWidth; + private int mLastContentHeight; + private int mLastCaptionHeight; + private int mLastXOffset; + private int mLastYOffset; + + ResizeFrameThread(ThreadedRenderer renderer, Rect initialBounds) { + mRenderer = renderer; + + // Create the render nodes for our frame and backdrop which can be resized independently + // from the content. + mFrameNode = RenderNode.create("FrameNode", null); + mBackdropNode = RenderNode.create("BackdropNode", null); + + mRenderer.addRenderNode(mFrameNode, false); + mRenderer.addRenderNode(mBackdropNode, true); + + // Set the initial bounds and draw once so that we do not get a broken frame. + mTargetRect.set(initialBounds); + changeWindowSize(initialBounds); + + // Kick off our draw thread. + start(); + } + + /** + * Call this function asynchronously when the window size has been changed. The change will + * be picked up once per frame and the frame will be re-rendered accordingly. + * @param newTargetBounds The new target bounds. + */ + public void setTargetRect(Rect newTargetBounds) { + synchronized (this) { + mTargetRect.set(newTargetBounds); + // Notify of a bounds change. + pingRenderLocked(); + } + } + + /** + * The window got replaced due to a configuration change. + */ + public void onConfigurationChange() { + if (mRenderer != null) { + // Enforce a window redraw. + mOldTargetRect.set(0, 0, 0, 0); + pingRenderLocked(); + } + } + + /** + * All resources of the renderer will be released. This function can be called from the + * the UI thread as well as the renderer thread. + */ + public void releaseRenderer() { + synchronized (this) { + if (mRenderer != null) { + // Invalidate the current content bounds. + mRenderer.setContentDrawBounds(0, 0, 0, 0); + + // Remove the render nodes again (see comment above - better to do that only once). + mRenderer.removeRenderNode(mFrameNode); + mRenderer.removeRenderNode(mBackdropNode); + + mRenderer = null; + + // Exit the renderer loop. + pingRenderLocked(); + } + } + } + + @Override + public void run() { + try { + Looper.prepare(); + mChoreographer = Choreographer.getInstance(); + Looper.loop(); + } finally { + releaseRenderer(); + } + synchronized (this) { + // Make sure no more messages are being sent. + mChoreographer = null; + } + } + + /** + * The implementation of the FrameCallback. + * @param frameTimeNanos The time in nanoseconds when the frame started being rendered, + * in the {@link System#nanoTime()} timebase. Divide this value by {@code 1000000} + */ + @Override + public void doFrame(long frameTimeNanos) { + if (mRenderer == null) { + // Tell the looper to stop. We are done. + Looper.myLooper().quit(); + return; + } + // Prevent someone from changing this while we are copying. + synchronized (this) { + mNewTargetRect.set(mTargetRect); + } + if (!mNewTargetRect.equals(mOldTargetRect)) { + mOldTargetRect.set(mNewTargetRect); + changeWindowSize(mNewTargetRect); + } + } + + /** + * Resizing the frame to fit the new window size. + * @param newBounds The window bounds which needs to be drawn. + */ + private void changeWindowSize(Rect newBounds) { + long startTime = System.currentTimeMillis(); + + // While a configuration change is taking place the view hierarchy might become + // inaccessible. For that case we remember the previous metrics to avoid flashes. + View caption = getChildAt(0); + View content = getChildAt(1); + if (content != null && caption != null) { + mLastContentWidth = content.getWidth(); + mLastContentHeight = content.getHeight(); + mLastCaptionHeight = caption.getHeight(); + + // Get the draw position within our surface. + int[] surfaceOrigin = new int[2]; + surfaceOrigin[0] = 0; + surfaceOrigin[1] = 0; + + // Get the shadow offsets. + getLocationInSurface(surfaceOrigin); + mLastXOffset = surfaceOrigin[0]; + mLastYOffset = surfaceOrigin[1]; + } + + // Since the surface is spanning the entire screen, we have to add the start offset of + // the bounds to get to the surface location. + final int left = mLastXOffset + newBounds.left; + final int top = mLastYOffset + newBounds.top; + final int width = newBounds.width(); + final int height = newBounds.height(); + + // Produce the draw calls. + // TODO(skuhne): Create a separate caption view which draws this. If the shadow should + // be resized while the window resizes, this hierarchy needs to have the elevation. + // That said - it is probably no good idea to draw the shadow every time since it costs + // a considerable time which we should rather spend for resizing the content and it does + // barely show while the entire screen is moving. + mFrameNode.setLeftTopRightBottom(left, top, left + width, top + mLastCaptionHeight); + DisplayListCanvas canvas = mFrameNode.start(width, height); + canvas.drawColor(Color.BLACK); + mFrameNode.end(canvas); + + mBackdropNode.setLeftTopRightBottom(left, top + mLastCaptionHeight, left + width, + top + height); + + // The backdrop: clear everything with the background. Clipping is done elsewhere. + canvas = mBackdropNode.start(width, height - mLastCaptionHeight); + // TODO(skuhne): mOwner.getDecorView().mBackgroundFallback.draw(..) - or similar. + // Note: This might not work (calculator for example uses a transparent background). + canvas.drawColor(0xff808080); + mBackdropNode.end(canvas); + + // The current content buffer is drawn here. + mRenderer.setContentDrawBounds( + mLastXOffset, + mLastYOffset + mLastCaptionHeight, + mLastXOffset + mLastContentWidth, + mLastYOffset + mLastCaptionHeight + mLastContentHeight); + + // We need to render both rendered nodes explicitly. + mRenderer.drawRenderNode(mFrameNode); + mRenderer.drawRenderNode(mBackdropNode); + } + + /** + * Sends a message to the renderer to wake up and perform the next action which can be + * either the next rendering or the self destruction if mRenderer is null. + * Note: This call must be synchronized. + */ + private void pingRenderLocked() { + if (mChoreographer != null) { + mChoreographer.postFrameCallback(this); + } + } + } } diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 223dae0404c5..314e4b6f984e 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -66,10 +66,6 @@ static JMetricsID gFontMetrics_fieldID; static jclass gFontMetricsInt_class; static JMetricsID gFontMetricsInt_fieldID; -static jclass gPaint_class; -static jfieldID gPaint_nativeInstanceID; -static jfieldID gPaint_nativeTypefaceID; - static void defaultSettingsForAndroid(Paint* paint) { // GlyphID encoding is required because we are using Harfbuzz shaping paint->setTextEncoding(Paint::kGlyphID_TextEncoding); @@ -82,37 +78,17 @@ struct LocaleCacheEntry { static thread_local LocaleCacheEntry sSingleEntryLocaleCache; -class PaintGlue { -public: +namespace PaintGlue { enum MoveOpt { AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT }; - static Paint* getNativePaint(JNIEnv* env, jobject paint) { - SkASSERT(env); - SkASSERT(paint); - SkASSERT(env->IsInstanceOf(paint, gPaint_class)); - jlong paintHandle = env->GetLongField(paint, gPaint_nativeInstanceID); - android::Paint* p = reinterpret_cast<android::Paint*>(paintHandle); - SkASSERT(p); - return p; - } - - static TypefaceImpl* getNativeTypeface(JNIEnv* env, jobject paint) { - SkASSERT(env); - SkASSERT(paint); - SkASSERT(env->IsInstanceOf(paint, gPaint_class)); - jlong typefaceHandle = env->GetLongField(paint, gPaint_nativeTypefaceID); - android::TypefaceImpl* p = reinterpret_cast<android::TypefaceImpl*>(typefaceHandle); - return p; - } - static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) { Paint* obj = reinterpret_cast<Paint*>(objHandle); delete obj; } - static jlong init(JNIEnv* env, jobject clazz) { + static jlong init(JNIEnv* env, jobject) { static_assert(1 << 0 == SkPaint::kAntiAlias_Flag, "paint_flags_mismatch"); static_assert(1 << 2 == SkPaint::kDither_Flag, "paint_flags_mismatch"); static_assert(1 << 3 == SkPaint::kUnderlineText_Flag, "paint_flags_mismatch"); @@ -149,9 +125,8 @@ public: // Equivalent to the Java Paint's FILTER_BITMAP_FLAG. static const uint32_t sFilterBitmapFlag = 0x02; - static jint getFlags(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); - Paint* nativePaint = getNativePaint(env, paint); + static jint getFlags(JNIEnv* env, jobject, jlong paintHandle) { + Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle); uint32_t result = nativePaint->getFlags(); result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away. if (nativePaint->getFilterQuality() != kNone_SkFilterQuality) { @@ -160,9 +135,8 @@ public: return static_cast<jint>(result); } - static void setFlags(JNIEnv* env, jobject paint, jint flags) { - NPE_CHECK_RETURN_VOID(env, paint); - Paint* nativePaint = getNativePaint(env, paint); + static void setFlags(JNIEnv* env, jobject, jlong paintHandle, jint flags) { + Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle); // Instead of modifying 0x02, change the filter level. nativePaint->setFilterQuality(flags & sFilterBitmapFlag ? kLow_SkFilterQuality @@ -175,57 +149,47 @@ public: nativePaint->setFlags(flags); } - static jint getHinting(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); - return getNativePaint(env, paint)->getHinting() + static jint getHinting(JNIEnv* env, jobject, jlong paintHandle) { + return reinterpret_cast<Paint*>(paintHandle)->getHinting() == Paint::kNo_Hinting ? 0 : 1; } - static void setHinting(JNIEnv* env, jobject paint, jint mode) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setHinting( + static void setHinting(JNIEnv* env, jobject, jlong paintHandle, jint mode) { + reinterpret_cast<Paint*>(paintHandle)->setHinting( mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting); } - static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setAntiAlias(aa); + static void setAntiAlias(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) { + reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa); } - static void setLinearText(JNIEnv* env, jobject paint, jboolean linearText) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setLinearText(linearText); + static void setLinearText(JNIEnv* env, jobject, jlong paintHandle, jboolean linearText) { + reinterpret_cast<Paint*>(paintHandle)->setLinearText(linearText); } - static void setSubpixelText(JNIEnv* env, jobject paint, jboolean subpixelText) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setSubpixelText(subpixelText); + static void setSubpixelText(JNIEnv* env, jobject, jlong paintHandle, jboolean subpixelText) { + reinterpret_cast<Paint*>(paintHandle)->setSubpixelText(subpixelText); } - static void setUnderlineText(JNIEnv* env, jobject paint, jboolean underlineText) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setUnderlineText(underlineText); + static void setUnderlineText(JNIEnv* env, jobject, jlong paintHandle, jboolean underlineText) { + reinterpret_cast<Paint*>(paintHandle)->setUnderlineText(underlineText); } - static void setStrikeThruText(JNIEnv* env, jobject paint, jboolean strikeThruText) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setStrikeThruText(strikeThruText); + static void setStrikeThruText(JNIEnv* env, jobject, jlong paintHandle, jboolean strikeThruText) { + reinterpret_cast<Paint*>(paintHandle)->setStrikeThruText(strikeThruText); } - static void setFakeBoldText(JNIEnv* env, jobject paint, jboolean fakeBoldText) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setFakeBoldText(fakeBoldText); + static void setFakeBoldText(JNIEnv* env, jobject, jlong paintHandle, jboolean fakeBoldText) { + reinterpret_cast<Paint*>(paintHandle)->setFakeBoldText(fakeBoldText); } - static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setFilterQuality( + static void setFilterBitmap(JNIEnv* env, jobject, jlong paintHandle, jboolean filterBitmap) { + reinterpret_cast<Paint*>(paintHandle)->setFilterQuality( filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality); } - static void setDither(JNIEnv* env, jobject paint, jboolean dither) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setDither(dither); + static void setDither(JNIEnv* env, jobject, jlong paintHandle, jboolean dither) { + reinterpret_cast<Paint*>(paintHandle)->setDither(dither); } static jint getStyle(JNIEnv* env, jobject clazz,jlong objHandle) { @@ -239,48 +203,40 @@ public: obj->setStyle(style); } - static jint getColor(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); + static jint getColor(JNIEnv* env, jobject, jlong paintHandle) { int color; - color = getNativePaint(env, paint)->getColor(); + color = reinterpret_cast<Paint*>(paintHandle)->getColor(); return static_cast<jint>(color); } - static jint getAlpha(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); + static jint getAlpha(JNIEnv* env, jobject, jlong paintHandle) { int alpha; - alpha = getNativePaint(env, paint)->getAlpha(); + alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha(); return static_cast<jint>(alpha); } - static void setColor(JNIEnv* env, jobject paint, jint color) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setColor(color); + static void setColor(JNIEnv* env, jobject, jlong paintHandle, jint color) { + reinterpret_cast<Paint*>(paintHandle)->setColor(color); } - static void setAlpha(JNIEnv* env, jobject paint, jint a) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setAlpha(a); + static void setAlpha(JNIEnv* env, jobject, jlong paintHandle, jint a) { + reinterpret_cast<Paint*>(paintHandle)->setAlpha(a); } - static jfloat getStrokeWidth(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); - return SkScalarToFloat(getNativePaint(env, paint)->getStrokeWidth()); + static jfloat getStrokeWidth(JNIEnv* env, jobject, jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth()); } - static void setStrokeWidth(JNIEnv* env, jobject paint, jfloat width) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setStrokeWidth(width); + static void setStrokeWidth(JNIEnv* env, jobject, jlong paintHandle, jfloat width) { + reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width); } - static jfloat getStrokeMiter(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); - return SkScalarToFloat(getNativePaint(env, paint)->getStrokeMiter()); + static jfloat getStrokeMiter(JNIEnv* env, jobject, jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter()); } - static void setStrokeMiter(JNIEnv* env, jobject paint, jfloat miter) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setStrokeMiter(miter); + static void setStrokeMiter(JNIEnv* env, jobject, jlong paintHandle, jfloat miter) { + reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter); } static jint getStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle) { @@ -417,46 +373,38 @@ public: obj->setTextLocale(sSingleEntryLocaleCache.languageTag); } - static jboolean isElegantTextHeight(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); - Paint* obj = getNativePaint(env, paint); + static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) { + Paint* obj = reinterpret_cast<Paint*>(paintHandle); return obj->getFontVariant() == VARIANT_ELEGANT; } - static void setElegantTextHeight(JNIEnv* env, jobject paint, jboolean aa) { - NPE_CHECK_RETURN_VOID(env, paint); - Paint* obj = getNativePaint(env, paint); + static void setElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) { + Paint* obj = reinterpret_cast<Paint*>(paintHandle); obj->setFontVariant(aa ? VARIANT_ELEGANT : VARIANT_DEFAULT); } - static jfloat getTextSize(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); - return SkScalarToFloat(getNativePaint(env, paint)->getTextSize()); + static jfloat getTextSize(JNIEnv* env, jobject, jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSize()); } - static void setTextSize(JNIEnv* env, jobject paint, jfloat textSize) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setTextSize(textSize); + static void setTextSize(JNIEnv* env, jobject, jlong paintHandle, jfloat textSize) { + reinterpret_cast<Paint*>(paintHandle)->setTextSize(textSize); } - static jfloat getTextScaleX(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); - return SkScalarToFloat(getNativePaint(env, paint)->getTextScaleX()); + static jfloat getTextScaleX(JNIEnv* env, jobject, jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextScaleX()); } - static void setTextScaleX(JNIEnv* env, jobject paint, jfloat scaleX) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setTextScaleX(scaleX); + static void setTextScaleX(JNIEnv* env, jobject, jlong paintHandle, jfloat scaleX) { + reinterpret_cast<Paint*>(paintHandle)->setTextScaleX(scaleX); } - static jfloat getTextSkewX(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); - return SkScalarToFloat(getNativePaint(env, paint)->getTextSkewX()); + static jfloat getTextSkewX(JNIEnv* env, jobject, jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSkewX()); } - static void setTextSkewX(JNIEnv* env, jobject paint, jfloat skewX) { - NPE_CHECK_RETURN_VOID(env, paint); - getNativePaint(env, paint)->setTextSkewX(skewX); + static void setTextSkewX(JNIEnv* env, jobject, jlong paintHandle, jfloat skewX) { + reinterpret_cast<Paint*>(paintHandle)->setTextSkewX(skewX); } static jfloat getLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle) { @@ -489,14 +437,15 @@ public: paint->setHyphenEdit((uint32_t)hyphen); } - static SkScalar getMetricsInternal(JNIEnv* env, jobject jpaint, Paint::FontMetrics *metrics) { + static SkScalar getMetricsInternal(jlong paintHandle, jlong typefaceHandle, + Paint::FontMetrics *metrics) { const int kElegantTop = 2500; const int kElegantBottom = -1000; const int kElegantAscent = 1900; const int kElegantDescent = -500; const int kElegantLeading = 0; - Paint* paint = getNativePaint(env, jpaint); - TypefaceImpl* typeface = getNativeTypeface(env, jpaint); + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); typeface = TypefaceImpl_resolveDefault(typeface); FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle); float saveSkewX = paint->getTextSkewX(); @@ -520,24 +469,22 @@ public: return spacing; } - static jfloat ascent(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); + static jfloat ascent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) { Paint::FontMetrics metrics; - getMetricsInternal(env, paint, &metrics); + getMetricsInternal(paintHandle, typefaceHandle, &metrics); return SkScalarToFloat(metrics.fAscent); } - static jfloat descent(JNIEnv* env, jobject paint) { - NPE_CHECK_RETURN_ZERO(env, paint); + static jfloat descent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) { Paint::FontMetrics metrics; - getMetricsInternal(env, paint, &metrics); + getMetricsInternal(paintHandle, typefaceHandle, &metrics); return SkScalarToFloat(metrics.fDescent); } - static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) { - NPE_CHECK_RETURN_ZERO(env, paint); + static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, + jlong typefaceHandle, jobject metricsObj) { Paint::FontMetrics metrics; - SkScalar spacing = getMetricsInternal(env, paint, &metrics); + SkScalar spacing = getMetricsInternal(paintHandle, typefaceHandle, &metrics); if (metricsObj) { SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class)); @@ -550,11 +497,11 @@ public: return SkScalarToFloat(spacing); } - static jint getFontMetricsInt(JNIEnv* env, jobject paint, jobject metricsObj) { - NPE_CHECK_RETURN_ZERO(env, paint); + static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, + jlong typefaceHandle, jobject metricsObj) { Paint::FontMetrics metrics; - getMetricsInternal(env, paint, &metrics); + getMetricsInternal(paintHandle, typefaceHandle, &metrics); int ascent = SkScalarRoundToInt(metrics.fAscent); int descent = SkScalarRoundToInt(metrics.fDescent); int leading = SkScalarRoundToInt(metrics.fLeading); @@ -573,7 +520,6 @@ public: static jfloat doTextAdvances(JNIEnv *env, Paint *paint, TypefaceImpl* typeface, const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags, jfloatArray advances, jint advancesIndex) { - NPE_CHECK_RETURN_ZERO(env, paint); NPE_CHECK_RETURN_ZERO(env, text); if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) { @@ -841,7 +787,7 @@ public: static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle, jstring text, jint start, jint end, jint bidiFlags, jobject bounds) { - const Paint* paint = reinterpret_cast<Paint*>(paintHandle);; + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); const jchar* textArray = env->GetStringChars(text, NULL); doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags); @@ -961,97 +907,97 @@ public: return result; } -}; +}; // namespace PaintGlue static const JNINativeMethod methods[] = { - {"finalizer", "(J)V", (void*) PaintGlue::finalizer}, - {"native_init","()J", (void*) PaintGlue::init}, - {"native_initWithPaint","(J)J", (void*) PaintGlue::initWithPaint}, - - {"native_reset","!(J)V", (void*) PaintGlue::reset}, - {"native_set","!(JJ)V", (void*) PaintGlue::assign}, - {"getFlags","!()I", (void*) PaintGlue::getFlags}, - {"setFlags","!(I)V", (void*) PaintGlue::setFlags}, - {"getHinting","!()I", (void*) PaintGlue::getHinting}, - {"setHinting","!(I)V", (void*) PaintGlue::setHinting}, - {"setAntiAlias","!(Z)V", (void*) PaintGlue::setAntiAlias}, - {"setSubpixelText","!(Z)V", (void*) PaintGlue::setSubpixelText}, - {"setLinearText","!(Z)V", (void*) PaintGlue::setLinearText}, - {"setUnderlineText","!(Z)V", (void*) PaintGlue::setUnderlineText}, - {"setStrikeThruText","!(Z)V", (void*) PaintGlue::setStrikeThruText}, - {"setFakeBoldText","!(Z)V", (void*) PaintGlue::setFakeBoldText}, - {"setFilterBitmap","!(Z)V", (void*) PaintGlue::setFilterBitmap}, - {"setDither","!(Z)V", (void*) PaintGlue::setDither}, - {"native_getStyle","!(J)I", (void*) PaintGlue::getStyle}, - {"native_setStyle","!(JI)V", (void*) PaintGlue::setStyle}, - {"getColor","!()I", (void*) PaintGlue::getColor}, - {"setColor","!(I)V", (void*) PaintGlue::setColor}, - {"getAlpha","!()I", (void*) PaintGlue::getAlpha}, - {"setAlpha","!(I)V", (void*) PaintGlue::setAlpha}, - {"getStrokeWidth","!()F", (void*) PaintGlue::getStrokeWidth}, - {"setStrokeWidth","!(F)V", (void*) PaintGlue::setStrokeWidth}, - {"getStrokeMiter","!()F", (void*) PaintGlue::getStrokeMiter}, - {"setStrokeMiter","!(F)V", (void*) PaintGlue::setStrokeMiter}, - {"native_getStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap}, - {"native_setStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap}, - {"native_getStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin}, - {"native_setStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin}, - {"native_getFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath}, - {"native_setShader","!(JJ)J", (void*) PaintGlue::setShader}, - {"native_setColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter}, - {"native_setXfermode","!(JJ)J", (void*) PaintGlue::setXfermode}, - {"native_setPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect}, - {"native_setMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter}, - {"native_setTypeface","!(JJ)J", (void*) PaintGlue::setTypeface}, - {"native_setRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer}, - {"native_getTextAlign","!(J)I", (void*) PaintGlue::getTextAlign}, - {"native_setTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign}, - {"native_setTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale}, - {"isElegantTextHeight","!()Z", (void*) PaintGlue::isElegantTextHeight}, - {"setElegantTextHeight","!(Z)V", (void*) PaintGlue::setElegantTextHeight}, - {"getTextSize","!()F", (void*) PaintGlue::getTextSize}, - {"setTextSize","!(F)V", (void*) PaintGlue::setTextSize}, - {"getTextScaleX","!()F", (void*) PaintGlue::getTextScaleX}, - {"setTextScaleX","!(F)V", (void*) PaintGlue::setTextScaleX}, - {"getTextSkewX","!()F", (void*) PaintGlue::getTextSkewX}, - {"setTextSkewX","!(F)V", (void*) PaintGlue::setTextSkewX}, - {"native_getLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing}, - {"native_setLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing}, - {"native_setFontFeatureSettings","(JLjava/lang/String;)V", + {"nFinalizer", "(J)V", (void*) PaintGlue::finalizer}, + {"nInit","()J", (void*) PaintGlue::init}, + {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint}, + + {"nReset","!(J)V", (void*) PaintGlue::reset}, + {"nSet","!(JJ)V", (void*) PaintGlue::assign}, + {"nGetFlags","!(J)I", (void*) PaintGlue::getFlags}, + {"nSetFlags","!(JI)V", (void*) PaintGlue::setFlags}, + {"nGetHinting","!(J)I", (void*) PaintGlue::getHinting}, + {"nSetHinting","!(JI)V", (void*) PaintGlue::setHinting}, + {"nSetAntiAlias","!(JZ)V", (void*) PaintGlue::setAntiAlias}, + {"nSetSubpixelText","!(JZ)V", (void*) PaintGlue::setSubpixelText}, + {"nSetLinearText","!(JZ)V", (void*) PaintGlue::setLinearText}, + {"nSetUnderlineText","!(JZ)V", (void*) PaintGlue::setUnderlineText}, + {"nSetStrikeThruText","!(JZ)V", (void*) PaintGlue::setStrikeThruText}, + {"nSetFakeBoldText","!(JZ)V", (void*) PaintGlue::setFakeBoldText}, + {"nSetFilterBitmap","!(JZ)V", (void*) PaintGlue::setFilterBitmap}, + {"nSetDither","!(JZ)V", (void*) PaintGlue::setDither}, + {"nGetStyle","!(J)I", (void*) PaintGlue::getStyle}, + {"nSetStyle","!(JI)V", (void*) PaintGlue::setStyle}, + {"nGetColor","!(J)I", (void*) PaintGlue::getColor}, + {"nSetColor","!(JI)V", (void*) PaintGlue::setColor}, + {"nGetAlpha","!(J)I", (void*) PaintGlue::getAlpha}, + {"nSetAlpha","!(JI)V", (void*) PaintGlue::setAlpha}, + {"nGetStrokeWidth","!(J)F", (void*) PaintGlue::getStrokeWidth}, + {"nSetStrokeWidth","!(JF)V", (void*) PaintGlue::setStrokeWidth}, + {"nGetStrokeMiter","!(J)F", (void*) PaintGlue::getStrokeMiter}, + {"nSetStrokeMiter","!(JF)V", (void*) PaintGlue::setStrokeMiter}, + {"nGetStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap}, + {"nSetStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap}, + {"nGetStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin}, + {"nSetStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin}, + {"nGetFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath}, + {"nSetShader","!(JJ)J", (void*) PaintGlue::setShader}, + {"nSetColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter}, + {"nSetXfermode","!(JJ)J", (void*) PaintGlue::setXfermode}, + {"nSetPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect}, + {"nSetMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter}, + {"nSetTypeface","!(JJ)J", (void*) PaintGlue::setTypeface}, + {"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer}, + {"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign}, + {"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign}, + {"nSetTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale}, + {"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight}, + {"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight}, + {"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize}, + {"nSetTextSize","!(JF)V", (void*) PaintGlue::setTextSize}, + {"nGetTextScaleX","!(J)F", (void*) PaintGlue::getTextScaleX}, + {"nSetTextScaleX","!(JF)V", (void*) PaintGlue::setTextScaleX}, + {"nGetTextSkewX","!(J)F", (void*) PaintGlue::getTextSkewX}, + {"nSetTextSkewX","!(JF)V", (void*) PaintGlue::setTextSkewX}, + {"nGetLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing}, + {"nSetLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing}, + {"nSetFontFeatureSettings","(JLjava/lang/String;)V", (void*) PaintGlue::setFontFeatureSettings}, - {"native_getHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit}, - {"native_setHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit}, - {"ascent","!()F", (void*) PaintGlue::ascent}, - {"descent","!()F", (void*) PaintGlue::descent}, + {"nGetHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit}, + {"nSetHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit}, + {"nAscent","!(JJ)F", (void*) PaintGlue::ascent}, + {"nDescent","!(JJ)F", (void*) PaintGlue::descent}, - {"getFontMetrics", "!(Landroid/graphics/Paint$FontMetrics;)F", + {"nGetFontMetrics", "!(JJLandroid/graphics/Paint$FontMetrics;)F", (void*)PaintGlue::getFontMetrics}, - {"getFontMetricsInt", "!(Landroid/graphics/Paint$FontMetricsInt;)I", + {"nGetFontMetricsInt", "!(JJLandroid/graphics/Paint$FontMetricsInt;)I", (void*)PaintGlue::getFontMetricsInt}, - {"native_breakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC}, - {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS}, - {"native_getTextAdvances","(JJ[CIIIII[FI)F", + {"nBreakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC}, + {"nBreakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS}, + {"nGetTextAdvances","(JJ[CIIIII[FI)F", (void*) PaintGlue::getTextAdvances___CIIIII_FI}, - {"native_getTextAdvances","(JJLjava/lang/String;IIIII[FI)F", + {"nGetTextAdvances","(JJLjava/lang/String;IIIII[FI)F", (void*) PaintGlue::getTextAdvances__StringIIIII_FI}, - {"native_getTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C}, - {"native_getTextRunCursor", "(JLjava/lang/String;IIIII)I", + {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C}, + {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I", (void*) PaintGlue::getTextRunCursor__String}, - {"native_getTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C}, - {"native_getTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String}, - {"nativeGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V", + {"nGetTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C}, + {"nGetTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String}, + {"nGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V", (void*) PaintGlue::getStringBounds }, - {"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V", + {"nGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V", (void*) PaintGlue::getCharArrayBounds }, - {"native_hasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph }, - {"native_getRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F}, - {"native_getOffsetForAdvance", "(JJ[CIIIIZF)I", + {"nHasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph }, + {"nGetRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F}, + {"nGetOffsetForAdvance", "(JJ[CIIIIZF)I", (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I}, - {"native_setShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer}, - {"native_hasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer} + {"nSetShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer}, + {"nHasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer} }; int register_android_graphics_Paint(JNIEnv* env) { @@ -1073,10 +1019,6 @@ int register_android_graphics_Paint(JNIEnv* env) { gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I"); gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I"); - gPaint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Paint")); - gPaint_nativeInstanceID = GetFieldIDOrDie(env, gPaint_class, "mNativePaint", "J"); - gPaint_nativeTypefaceID = GetFieldIDOrDie(env, gPaint_class, "mNativeTypeface", "J"); - return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods)); } diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index a151e004922d..83f76eab053d 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -117,9 +117,17 @@ static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) { b->finish(); } -static jlong nLoadHyphenator(JNIEnv* env, jclass, jstring patternData) { - ScopedStringChars str(env, patternData); - Hyphenator* hyphenator = Hyphenator::load(str.get(), str.size()); +static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset) { + const uint8_t* bytebuf = nullptr; + if (buffer != nullptr) { + void* rawbuf = env->GetDirectBufferAddress(buffer); + if (rawbuf != nullptr) { + bytebuf = reinterpret_cast<const uint8_t*>(rawbuf) + offset; + } else { + ALOGE("failed to get direct buffer address"); + } + } + Hyphenator* hyphenator = Hyphenator::loadBinary(bytebuf); return reinterpret_cast<jlong>(hyphenator); } @@ -177,7 +185,7 @@ static const JNINativeMethod gMethods[] = { {"nNewBuilder", "()J", (void*) nNewBuilder}, {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, {"nFinishBuilder", "(J)V", (void*) nFinishBuilder}, - {"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator}, + {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator}, {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale}, {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph}, {"nSetIndents", "(J[I)V", (void*) nSetIndents}, diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index c79f833b54a9..17eb876aebf3 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -461,10 +461,10 @@ static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject c proxy->drawRenderNode(renderNode); } -static void android_view_ThreadedRenderer_setContentOverdrawProtectionBounds(JNIEnv* env, +static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env, jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - proxy->setContentOverdrawProtectionBounds(left, top, right, bottom); + proxy->setContentDrawBounds(left, top, right, bottom); } // ---------------------------------------------------------------------------- @@ -522,8 +522,7 @@ static const JNINativeMethod gMethods[] = { { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode}, { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode}, { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode}, - { "nSetContentOverdrawProtectionBounds", "(JIIII)V", - (void*)android_view_ThreadedRenderer_setContentOverdrawProtectionBounds}, + { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds}, }; int register_android_view_ThreadedRenderer(JNIEnv* env) { diff --git a/core/res/res/color/btn_colored_text_material.xml b/core/res/res/color/btn_colored_text_material.xml index 23d05a9bafd6..c80fea62ef30 100644 --- a/core/res/res/color/btn_colored_text_material.xml +++ b/core/res/res/color/btn_colored_text_material.xml @@ -18,6 +18,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="false" android:alpha="?attr/disabledAlpha" - android:color="?attr/textColorSecondary" /> - <item android:color="?attr/textColorSecondaryInverse" /> + android:color="?attr/textColorPrimary" /> + <item android:color="?attr/textColorPrimaryInverse" /> </selector> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index a08f040ff716..2f1627b29c0f 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -227,23 +227,23 @@ <string name="user_owner_label" msgid="2804351898001038951">"Persoonlik"</string> <string name="managed_profile_label" msgid="6260850669674791528">"Werk"</string> <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakte"</string> - <string name="permgroupdesc_contacts" msgid="6951499528303668046">"gaan by jou kontakte in"</string> + <string name="permgroupdesc_contacts" msgid="6951499528303668046">"in te gaan by jou kontakte"</string> <string name="permgrouplab_location" msgid="7275582855722310164">"Ligging"</string> - <string name="permgroupdesc_location" msgid="1346617465127855033">"verkry toegang tot hierdie toestel se ligging"</string> + <string name="permgroupdesc_location" msgid="1346617465127855033">"toegang te verkry tot hierdie toestel se ligging"</string> <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string> - <string name="permgroupdesc_calendar" msgid="3889615280211184106">"gaan by jou kalender in"</string> + <string name="permgroupdesc_calendar" msgid="3889615280211184106">"by jou kalender in te gaan"</string> <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string> - <string name="permgroupdesc_sms" msgid="4656988620100940350">"stuur en bekyk SMS-boodskappe"</string> + <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS-boodskappe te stuur en te bekyk"</string> <string name="permgrouplab_storage" msgid="1971118770546336966">"Stoor"</string> - <string name="permgroupdesc_storage" msgid="637758554581589203">"verkry toegang tot foto\'s, media en lêers op jou toestel"</string> + <string name="permgroupdesc_storage" msgid="637758554581589203">"toegang te verkry tot foto\'s, media en lêers op jou toestel"</string> <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofoon"</string> - <string name="permgroupdesc_microphone" msgid="4988812113943554584">"neem oudio op"</string> + <string name="permgroupdesc_microphone" msgid="4988812113943554584">"oudio op te neem"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> - <string name="permgroupdesc_camera" msgid="3250611594678347720">"neem foto\'s en neem video op"</string> + <string name="permgroupdesc_camera" msgid="3250611594678347720">"foto\'s en video te neem"</string> <string name="permgrouplab_phone" msgid="5229115638567440675">"Foon"</string> - <string name="permgroupdesc_phone" msgid="6234224354060641055">"maak en bestuur foonoproepe"</string> + <string name="permgroupdesc_phone" msgid="6234224354060641055">"foonoproepe te maak en te bestuur"</string> <string name="permgrouplab_sensors" msgid="416037179223226722">"Liggaamsensors"</string> - <string name="permgroupdesc_sensors" msgid="7147968539346634043">"kry toegang tot sensordata oor jou lewenstekens"</string> + <string name="permgroupdesc_sensors" msgid="7147968539346634043">"toegang te verkry tot sensordata oor jou lewenstekens"</string> <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Haal venster-inhoud op"</string> <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Ondersoek die inhoud van \'n venster waarmee jy interaksie het."</string> <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Skakel Verken deur raak aan"</string> @@ -272,7 +272,7 @@ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Laat die program toe om seluitsending-boodskappe te lees wat deur jou toestel ontvang word. Seluitsending-waarskuwings word in sommige plekke afgelewer om jou van noodsituasies te waarsku. Kwaadwillige programme mag inmeng met die prestasie of die werking van jou toestel wanneer \'n noodgeval se seluitsending ontvang word."</string> <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lees ingetekende nuus"</string> <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Laat die program toe om details oor die tans gesinkroniseerde strome te kry."</string> - <string name="permlab_sendSms" msgid="7544599214260982981">"stuur en bekyk SMS-boodskappe"</string> + <string name="permlab_sendSms" msgid="7544599214260982981">"SMS-boodskappe te stuur en te bekyk"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Laat die program toe om SMS-boodskappe te stuur. Dit kan tot onverwagse heffings lei. Kwaadwillige programme kan jou geld kos deur boodskappe sonder jou bevestiging te stuur."</string> <string name="permlab_readSms" msgid="8745086572213270480">"lees jou teksboodskappe (SMS of MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Laat die program toe om SMS-boodskappe wat op jou tablet of SIM-kaart gestoor is, te lees. Dit laat die program toe om alle SMS-boodskappe te lees, ongeag van die inhoud of vertroulikheid daarvan."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index af80ed322c61..0621bbe1bd0a 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -915,7 +915,7 @@ <string name="anr_application_process" msgid="8941757607340481057">"Приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" не отвечает. Закрыть его?"</string> <string name="anr_process" msgid="6513209874880517125">"Приложение \"<xliff:g id="PROCESS">%1$s</xliff:g>\" не отвечает.\n\nЗакрыть его?"</string> <string name="force_close" msgid="8346072094521265605">"ОК"</string> - <string name="report" msgid="4060218260984795706">"Создать отчет"</string> + <string name="report" msgid="4060218260984795706">"Отправить отчет"</string> <string name="wait" msgid="7147118217226317732">"Подождать"</string> <string name="webpage_unresponsive" msgid="3272758351138122503">"Страница не отвечает.\n\nЗакрыть ее?"</string> <string name="launch_warning_title" msgid="1547997780506713581">"Приложение перенаправлено"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 488470f7c46a..29fd6a1be775 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1235,7 +1235,7 @@ <string name="activity_resolver_use_once" msgid="2404644797149173758">"Bara en gång"</string> <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s har inte stöd för arbetsprofil"</string> <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Surfplatta"</string> - <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Tv"</string> + <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string> <string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Mobil"</string> <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hörlurar"</string> <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dockningsstationens högtalare"</string> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 38a1693141dc..58640eb2ef55 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -307,9 +307,10 @@ please see styles_device_defaults.xml. <style name="TextAppearance.Material.Widget.PopupMenu"/> <style name="TextAppearance.Material.Widget.PopupMenu.Large" parent="TextAppearance.Material.Menu" /> <style name="TextAppearance.Material.Widget.PopupMenu.Small" parent="TextAppearance.Material.Menu" /> - <style name="TextAppearance.Material.Widget.PopupMenu.Header" parent="TextAppearance.Material.Subhead"> + <style name="TextAppearance.Material.Widget.PopupMenu.Header"> <item name="fontFamily">@string/font_family_title_material</item> <item name="textSize">@dimen/text_size_menu_header_material</item> + <item name="textColor">?attr/textColorSecondary</item> </style> <style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" /> diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 58de87af1a5d..11b4a9e5adf5 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -430,7 +430,7 @@ public class Paint { * @param flags initial flag bits, as if they were passed via setFlags(). */ public Paint(int flags) { - mNativePaint = native_init(); + mNativePaint = nInit(); setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS); // TODO: Turning off hinting has undesirable side effects, we need to // revisit hinting once we add support for subpixel positioning @@ -448,13 +448,14 @@ public class Paint { * new paint. */ public Paint(Paint paint) { - mNativePaint = native_initWithPaint(paint.getNativeInstance()); + mNativePaint = nInitWithPaint(paint.getNativeInstance()); setClassVariablesFrom(paint); } /** Restores the paint to its default settings. */ public void reset() { - native_reset(mNativePaint); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nReset(mNativePaint); setFlags(HIDDEN_DEFAULT_PAINT_FLAGS); // TODO: Turning off hinting has undesirable side effects, we need to @@ -488,9 +489,11 @@ public class Paint { * methods on this. */ public void set(Paint src) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + if (src.mNativePaint == 0) throw new NullPointerException("Source is already finalized!"); if (this != src) { // copy over the native settings - native_set(mNativePaint, src.mNativePaint); + nSet(mNativePaint, src.mNativePaint); setClassVariablesFrom(src); } } @@ -538,10 +541,11 @@ public class Paint { * @hide */ public long getNativeInstance() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance(); if (newNativeShader != mNativeShader) { mNativeShader = newNativeShader; - native_setShader(mNativePaint, mNativeShader); + nSetShader(mNativePaint, mNativeShader); } return mNativePaint; } @@ -574,26 +578,46 @@ public class Paint { * * @return the paint's flags (see enums ending in _Flag for bit masks) */ - public native int getFlags(); + public int getFlags() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetFlags(mNativePaint); + } + + private native int nGetFlags(long paintPtr); /** * Set the paint's flags. Use the Flag enum to specific flag values. * * @param flags The new flag bits for the paint */ - public native void setFlags(int flags); + public void setFlags(int flags) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetFlags(mNativePaint, flags); + } + + private native void nSetFlags(long paintPtr, int flags); /** * Return the paint's hinting mode. Returns either * {@link #HINTING_OFF} or {@link #HINTING_ON}. */ - public native int getHinting(); + public int getHinting() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetHinting(mNativePaint); + } + + private native int nGetHinting(long paintPtr); /** * Set the paint's hinting mode. May be either * {@link #HINTING_OFF} or {@link #HINTING_ON}. */ - public native void setHinting(int mode); + public void setHinting(int mode) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetHinting(mNativePaint, mode); + } + + private native void nSetHinting(long paintPtr, int mode); /** * Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set @@ -615,7 +639,12 @@ public class Paint { * * @param aa true to set the antialias bit in the flags, false to clear it */ - public native void setAntiAlias(boolean aa); + public void setAntiAlias(boolean aa) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetAntiAlias(mNativePaint, aa); + } + + private native void nSetAntiAlias(long paintPtr, boolean aa); /** * Helper for getFlags(), returning true if DITHER_FLAG bit is set @@ -641,7 +670,12 @@ public class Paint { * * @param dither true to set the dithering bit in flags, false to clear it */ - public native void setDither(boolean dither); + public void setDither(boolean dither) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetDither(mNativePaint, dither); + } + + private native void nSetDither(long paintPtr, boolean dither); /** * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set @@ -658,7 +692,12 @@ public class Paint { * @param linearText true to set the linearText bit in the paint's flags, * false to clear it. */ - public native void setLinearText(boolean linearText); + public void setLinearText(boolean linearText) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetLinearText(mNativePaint, linearText); + } + + private native void nSetLinearText(long paintPtr, boolean linearText); /** * Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set @@ -675,7 +714,12 @@ public class Paint { * @param subpixelText true to set the subpixelText bit in the paint's * flags, false to clear it. */ - public native void setSubpixelText(boolean subpixelText); + public void setSubpixelText(boolean subpixelText) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetSubpixelText(mNativePaint, subpixelText); + } + + private native void nSetSubpixelText(long paintPtr, boolean subpixelText); /** * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set @@ -692,7 +736,12 @@ public class Paint { * @param underlineText true to set the underlineText bit in the paint's * flags, false to clear it. */ - public native void setUnderlineText(boolean underlineText); + public void setUnderlineText(boolean underlineText) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetUnderlineText(mNativePaint, underlineText); + } + + private native void nSetUnderlineText(long paintPtr, boolean underlineText); /** * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set @@ -709,7 +758,12 @@ public class Paint { * @param strikeThruText true to set the strikeThruText bit in the paint's * flags, false to clear it. */ - public native void setStrikeThruText(boolean strikeThruText); + public void setStrikeThruText(boolean strikeThruText) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetStrikeThruText(mNativePaint, strikeThruText); + } + + private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText); /** * Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set @@ -726,7 +780,12 @@ public class Paint { * @param fakeBoldText true to set the fakeBoldText bit in the paint's * flags, false to clear it. */ - public native void setFakeBoldText(boolean fakeBoldText); + public void setFakeBoldText(boolean fakeBoldText) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetFakeBoldText(mNativePaint, fakeBoldText); + } + + private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText); /** * Whether or not the bitmap filter is activated. @@ -749,7 +808,12 @@ public class Paint { * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's * flags, false to clear it. */ - public native void setFilterBitmap(boolean filter); + public void setFilterBitmap(boolean filter) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetFilterBitmap(mNativePaint, filter); + } + + private native void nSetFilterBitmap(long paintPtr, boolean filter); /** * Return the paint's style, used for controlling how primitives' @@ -759,7 +823,8 @@ public class Paint { * @return the paint's style setting (Fill, Stroke, StrokeAndFill) */ public Style getStyle() { - return sStyleArray[native_getStyle(mNativePaint)]; + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return sStyleArray[nGetStyle(mNativePaint)]; } /** @@ -770,7 +835,8 @@ public class Paint { * @param style The new style to set in the paint */ public void setStyle(Style style) { - native_setStyle(mNativePaint, style.nativeInt); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetStyle(mNativePaint, style.nativeInt); } /** @@ -782,7 +848,12 @@ public class Paint { * @return the paint's color (and alpha). */ @ColorInt - public native int getColor(); + public int getColor() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetColor(mNativePaint); + } + + private native int nGetColor(long paintPtr); /** * Set the paint's color. Note that the color is an int containing alpha @@ -792,7 +863,12 @@ public class Paint { * * @param color The new color (including alpha) to set in the paint. */ - public native void setColor(@ColorInt int color); + public void setColor(@ColorInt int color) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetColor(mNativePaint, color); + } + + private native void nSetColor(long paintPtr, @ColorInt int color); /** * Helper to getColor() that just returns the color's alpha value. This is @@ -801,7 +877,12 @@ public class Paint { * * @return the alpha component of the paint's color. */ - public native int getAlpha(); + public int getAlpha() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetAlpha(mNativePaint); + } + + private native int nGetAlpha(long paintPtr); /** * Helper to setColor(), that only assigns the color's alpha value, @@ -810,7 +891,12 @@ public class Paint { * * @param a set the alpha component [0..255] of the paint's color. */ - public native void setAlpha(int a); + public void setAlpha(int a) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetAlpha(mNativePaint, a); + } + + private native void nSetAlpha(long paintPtr, int a); /** * Helper to setColor(), that takes a,r,g,b and constructs the color int @@ -833,7 +919,12 @@ public class Paint { * @return the paint's stroke width, used whenever the paint's style is * Stroke or StrokeAndFill. */ - public native float getStrokeWidth(); + public float getStrokeWidth() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetStrokeWidth(mNativePaint); + } + + private native float nGetStrokeWidth(long paintPtr); /** * Set the width for stroking. @@ -843,7 +934,12 @@ public class Paint { * @param width set the paint's stroke width, used whenever the paint's * style is Stroke or StrokeAndFill. */ - public native void setStrokeWidth(float width); + public void setStrokeWidth(float width) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetStrokeWidth(mNativePaint, width); + } + + private native void nSetStrokeWidth(long paintPtr, float width); /** * Return the paint's stroke miter value. Used to control the behavior @@ -852,7 +948,12 @@ public class Paint { * @return the paint's miter limit, used whenever the paint's style is * Stroke or StrokeAndFill. */ - public native float getStrokeMiter(); + public float getStrokeMiter() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetStrokeMiter(mNativePaint); + } + + private native float nGetStrokeMiter(long paintPtr); /** * Set the paint's stroke miter value. This is used to control the behavior @@ -861,7 +962,12 @@ public class Paint { * @param miter set the miter limit on the paint, used whenever the paint's * style is Stroke or StrokeAndFill. */ - public native void setStrokeMiter(float miter); + public void setStrokeMiter(float miter) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetStrokeMiter(mNativePaint, miter); + } + + private native void nSetStrokeMiter(long paintPtr, float miter); /** * Return the paint's Cap, controlling how the start and end of stroked @@ -871,7 +977,8 @@ public class Paint { * style is Stroke or StrokeAndFill. */ public Cap getStrokeCap() { - return sCapArray[native_getStrokeCap(mNativePaint)]; + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return sCapArray[nGetStrokeCap(mNativePaint)]; } /** @@ -881,7 +988,8 @@ public class Paint { * style is Stroke or StrokeAndFill. */ public void setStrokeCap(Cap cap) { - native_setStrokeCap(mNativePaint, cap.nativeInt); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetStrokeCap(mNativePaint, cap.nativeInt); } /** @@ -890,7 +998,8 @@ public class Paint { * @return the paint's Join. */ public Join getStrokeJoin() { - return sJoinArray[native_getStrokeJoin(mNativePaint)]; + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return sJoinArray[nGetStrokeJoin(mNativePaint)]; } /** @@ -900,7 +1009,8 @@ public class Paint { * Stroke or StrokeAndFill. */ public void setStrokeJoin(Join join) { - native_setStrokeJoin(mNativePaint, join.nativeInt); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetStrokeJoin(mNativePaint, join.nativeInt); } /** @@ -915,7 +1025,8 @@ public class Paint { * drawn with a hairline (width == 0) */ public boolean getFillPath(Path src, Path dst) { - return native_getFillPath(mNativePaint, src.ni(), dst.ni()); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetFillPath(mNativePaint, src.ni(), dst.ni()); } /** @@ -958,10 +1069,11 @@ public class Paint { * @return filter */ public ColorFilter setColorFilter(ColorFilter filter) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); long filterNative = 0; if (filter != null) filterNative = filter.native_instance; - native_setColorFilter(mNativePaint, filterNative); + nSetColorFilter(mNativePaint, filterNative); mColorFilter = filter; return filter; } @@ -985,10 +1097,11 @@ public class Paint { * @return xfermode */ public Xfermode setXfermode(Xfermode xfermode) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); long xfermodeNative = 0; if (xfermode != null) xfermodeNative = xfermode.native_instance; - native_setXfermode(mNativePaint, xfermodeNative); + nSetXfermode(mNativePaint, xfermodeNative); mXfermode = xfermode; return xfermode; } @@ -1012,11 +1125,12 @@ public class Paint { * @return effect */ public PathEffect setPathEffect(PathEffect effect) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); long effectNative = 0; if (effect != null) { effectNative = effect.native_instance; } - native_setPathEffect(mNativePaint, effectNative); + nSetPathEffect(mNativePaint, effectNative); mPathEffect = effect; return effect; } @@ -1041,11 +1155,12 @@ public class Paint { * @return maskfilter */ public MaskFilter setMaskFilter(MaskFilter maskfilter) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); long maskfilterNative = 0; if (maskfilter != null) { maskfilterNative = maskfilter.native_instance; } - native_setMaskFilter(mNativePaint, maskfilterNative); + nSetMaskFilter(mNativePaint, maskfilterNative); mMaskFilter = maskfilter; return maskfilter; } @@ -1072,11 +1187,12 @@ public class Paint { * @return typeface */ public Typeface setTypeface(Typeface typeface) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); long typefaceNative = 0; if (typeface != null) { typefaceNative = typeface.native_instance; } - native_setTypeface(mNativePaint, typefaceNative); + nSetTypeface(mNativePaint, typefaceNative); mTypeface = typeface; mNativeTypeface = typefaceNative; return typeface; @@ -1110,11 +1226,12 @@ public class Paint { */ @Deprecated public Rasterizer setRasterizer(Rasterizer rasterizer) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); long rasterizerNative = 0; if (rasterizer != null) { rasterizerNative = rasterizer.native_instance; } - native_setRasterizer(mNativePaint, rasterizerNative); + nSetRasterizer(mNativePaint, rasterizerNative); mRasterizer = rasterizer; return rasterizer; } @@ -1132,7 +1249,8 @@ public class Paint { * opaque, or the alpha from the shadow color if not. */ public void setShadowLayer(float radius, float dx, float dy, int shadowColor) { - native_setShadowLayer(mNativePaint, radius, dx, dy, shadowColor); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor); } /** @@ -1149,7 +1267,8 @@ public class Paint { * @hide */ public boolean hasShadowLayer() { - return native_hasShadowLayer(mNativePaint); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nHasShadowLayer(mNativePaint); } /** @@ -1161,7 +1280,8 @@ public class Paint { * @return the paint's Align value for drawing text. */ public Align getTextAlign() { - return sAlignArray[native_getTextAlign(mNativePaint)]; + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return sAlignArray[nGetTextAlign(mNativePaint)]; } /** @@ -1173,7 +1293,8 @@ public class Paint { * @param align set the paint's Align value for drawing text. */ public void setTextAlign(Align align) { - native_setTextAlign(mNativePaint, align.nativeInt); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetTextAlign(mNativePaint, align.nativeInt); } /** @@ -1206,6 +1327,7 @@ public class Paint { * @param locale the paint's locale value for drawing text, must not be null. */ public void setTextLocale(@NonNull Locale locale) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (locale == null) { throw new IllegalArgumentException("locale cannot be null"); } @@ -1213,7 +1335,7 @@ public class Paint { return; } mLocales = new LocaleList(locale); - native_setTextLocale(mNativePaint, locale.toString()); + nSetTextLocale(mNativePaint, locale.toString()); } /** @@ -1244,13 +1366,14 @@ public class Paint { * @param locales the paint's locale list for drawing text, must not be null or empty. */ public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (locales == null || locales.isEmpty()) { throw new IllegalArgumentException("locales cannot be null or empty"); } if (locales.equals(mLocales)) return; mLocales = locales; // TODO: Pass the whole LocaleList to native code - native_setTextLocale(mNativePaint, locales.getPrimary().toString()); + nSetTextLocale(mNativePaint, locales.getPrimary().toString()); } /** @@ -1258,7 +1381,12 @@ public class Paint { * * @return true if elegant metrics are enabled for text drawing. */ - public native boolean isElegantTextHeight(); + public boolean isElegantTextHeight() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nIsElegantTextHeight(mNativePaint); + } + + private native boolean nIsElegantTextHeight(long paintPtr); /** * Set the paint's elegant height metrics flag. This setting selects font @@ -1267,21 +1395,36 @@ public class Paint { * * @param elegant set the paint's elegant metrics flag for drawing text. */ - public native void setElegantTextHeight(boolean elegant); + public void setElegantTextHeight(boolean elegant) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetElegantTextHeight(mNativePaint, elegant); + } + + private native void nSetElegantTextHeight(long paintPtr, boolean elegant); /** * Return the paint's text size. * * @return the paint's text size. */ - public native float getTextSize(); + public float getTextSize() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetTextSize(mNativePaint); + } + + private native float nGetTextSize(long paintPtr); /** * Set the paint's text size. This value must be > 0 * * @param textSize set the paint's text size. */ - public native void setTextSize(float textSize); + public void setTextSize(float textSize) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetTextSize(mNativePaint, textSize); + } + + private native void nSetTextSize(long paintPtr, float textSize); /** * Return the paint's horizontal scale factor for text. The default value @@ -1289,7 +1432,12 @@ public class Paint { * * @return the paint's scale factor in X for drawing/measuring text */ - public native float getTextScaleX(); + public float getTextScaleX() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetTextScaleX(mNativePaint); + } + + private native float nGetTextScaleX(long paintPtr); /** * Set the paint's horizontal scale factor for text. The default value @@ -1298,7 +1446,12 @@ public class Paint { * * @param scaleX set the paint's scale in X for drawing/measuring text. */ - public native void setTextScaleX(float scaleX); + public void setTextScaleX(float scaleX) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetTextScaleX(mNativePaint, scaleX); + } + + private native void nSetTextScaleX(long paintPtr, float scaleX); /** * Return the paint's horizontal skew factor for text. The default value @@ -1306,7 +1459,12 @@ public class Paint { * * @return the paint's skew factor in X for drawing text. */ - public native float getTextSkewX(); + public float getTextSkewX() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetTextSkewX(mNativePaint); + } + + private native float nGetTextSkewX(long paintPtr); /** * Set the paint's horizontal skew factor for text. The default value @@ -1314,7 +1472,12 @@ public class Paint { * * @param skewX set the paint's skew factor in X for drawing text. */ - public native void setTextSkewX(float skewX); + public void setTextSkewX(float skewX) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetTextSkewX(mNativePaint, skewX); + } + + private native void nSetTextSkewX(long paintPtr, float skewX); /** * Return the paint's letter-spacing for text. The default value @@ -1323,7 +1486,8 @@ public class Paint { * @return the paint's letter-spacing for drawing text. */ public float getLetterSpacing() { - return native_getLetterSpacing(mNativePaint); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetLetterSpacing(mNativePaint); } /** @@ -1334,7 +1498,8 @@ public class Paint { * @param letterSpacing set the paint's letter-spacing for drawing text. */ public void setLetterSpacing(float letterSpacing) { - native_setLetterSpacing(mNativePaint, letterSpacing); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetLetterSpacing(mNativePaint, letterSpacing); } /** @@ -1355,6 +1520,7 @@ public class Paint { * @param settings the font feature settings string to use, may be null. */ public void setFontFeatureSettings(String settings) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (settings != null && settings.equals("")) { settings = null; } @@ -1363,7 +1529,7 @@ public class Paint { return; } mFontFeatureSettings = settings; - native_setFontFeatureSettings(mNativePaint, settings); + nSetFontFeatureSettings(mNativePaint, settings); } /** @@ -1374,7 +1540,8 @@ public class Paint { * @hide */ public int getHyphenEdit() { - return native_getHyphenEdit(mNativePaint); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetHyphenEdit(mNativePaint); } /** @@ -1386,7 +1553,8 @@ public class Paint { * @hide */ public void setHyphenEdit(int hyphen) { - native_setHyphenEdit(mNativePaint, hyphen); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + nSetHyphenEdit(mNativePaint, hyphen); } /** @@ -1396,7 +1564,12 @@ public class Paint { * @return the distance above (negative) the baseline (ascent) based on the * current typeface and text size. */ - public native float ascent(); + public float ascent() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nAscent(mNativePaint, mNativeTypeface); + } + + private native float nAscent(long paintPtr, long typefacePtr); /** * Return the distance below (positive) the baseline (descent) based on the @@ -1405,7 +1578,12 @@ public class Paint { * @return the distance below (positive) the baseline (descent) based on * the current typeface and text size. */ - public native float descent(); + public float descent() { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nDescent(mNativePaint, mNativeTypeface); + } + + private native float nDescent(long paintPtr, long typefacePtr); /** * Class that describes the various metrics for a font at a given text size. @@ -1447,7 +1625,13 @@ public class Paint { * the appropriate values given the paint's text attributes. * @return the font's recommended interline spacing. */ - public native float getFontMetrics(FontMetrics metrics); + public float getFontMetrics(FontMetrics metrics) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics); + } + + private native float nGetFontMetrics(long paintPtr, + long typefacePtr, FontMetrics metrics); /** * Allocates a new FontMetrics object, and then calls getFontMetrics(fm) @@ -1487,7 +1671,13 @@ public class Paint { * * @return the font's interline spacing. */ - public native int getFontMetricsInt(FontMetricsInt fmi); + public int getFontMetricsInt(FontMetricsInt fmi) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi); + } + + private native int nGetFontMetricsInt(long paintPtr, + long typefacePtr, FontMetricsInt fmi); public FontMetricsInt getFontMetricsInt() { FontMetricsInt fm = new FontMetricsInt(); @@ -1515,6 +1705,7 @@ public class Paint { * @return The width of the text */ public float measureText(char[] text, int index, int count) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -1526,13 +1717,13 @@ public class Paint { return 0f; } if (!mHasCompatScaling) { - return (float) Math.ceil(native_getTextAdvances(mNativePaint, mNativeTypeface, text, + return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count, mBidiFlags, null, 0)); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - float w = native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, + float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count, mBidiFlags, null, 0); setTextSize(oldSize); return (float) Math.ceil(w*mInvCompatScaling); @@ -1547,6 +1738,7 @@ public class Paint { * @return The width of the text */ public float measureText(String text, int start, int end) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -1558,12 +1750,12 @@ public class Paint { return 0f; } if (!mHasCompatScaling) { - return (float) Math.ceil(native_getTextAdvances(mNativePaint, mNativeTypeface, text, + return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end, mBidiFlags, null, 0)); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - float w = native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, + float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end, mBidiFlags, null, 0); setTextSize(oldSize); return (float) Math.ceil(w * mInvCompatScaling); @@ -1576,6 +1768,7 @@ public class Paint { * @return The width of the text */ public float measureText(String text) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -1636,6 +1829,7 @@ public class Paint { */ public int breakText(char[] text, int index, int count, float maxWidth, float[] measuredWidth) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -1647,20 +1841,20 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - return native_breakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth, + return nBreakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth, mBidiFlags, measuredWidth); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - int res = native_breakText(mNativePaint, mNativeTypeface, text, index, count, + int res = nBreakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth * mCompatScaling, mBidiFlags, measuredWidth); setTextSize(oldSize); if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling; return res; } - private static native int native_breakText(long native_object, long native_typeface, + private static native int nBreakText(long nObject, long nTypeface, char[] text, int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth); @@ -1683,6 +1877,7 @@ public class Paint { public int breakText(CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -1731,6 +1926,7 @@ public class Paint { */ public int breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -1739,20 +1935,20 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - return native_breakText(mNativePaint, mNativeTypeface, text, measureForwards, + return nBreakText(mNativePaint, mNativeTypeface, text, measureForwards, maxWidth, mBidiFlags, measuredWidth); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - int res = native_breakText(mNativePaint, mNativeTypeface, text, measureForwards, + int res = nBreakText(mNativePaint, mNativeTypeface, text, measureForwards, maxWidth*mCompatScaling, mBidiFlags, measuredWidth); setTextSize(oldSize); if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling; return res; } - private static native int native_breakText(long native_object, long native_typeface, + private static native int nBreakText(long nObject, long nTypeface, String text, boolean measureForwards, float maxWidth, int bidiFlags, float[] measuredWidth); @@ -1768,6 +1964,7 @@ public class Paint { */ public int getTextWidths(char[] text, int index, int count, float[] widths) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -1780,14 +1977,14 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count, + nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count, mBidiFlags, widths, 0); return count; } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count, + nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count, mBidiFlags, widths, 0); setTextSize(oldSize); for (int i = 0; i < count; i++) { @@ -1851,6 +2048,7 @@ public class Paint { * @return the number of code units in the specified text. */ public int getTextWidths(String text, int start, int end, float[] widths) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -1865,14 +2063,14 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end, + nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end, mBidiFlags, widths, 0); return end - start; } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end, + nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end, mBidiFlags, widths, 0); setTextSize(oldSize); for (int i = 0; i < end - start; i++) { @@ -1904,6 +2102,7 @@ public class Paint { int contextIndex, int contextCount, boolean isRtl, float[] advances, int advancesIndex) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (chars == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -1920,14 +2119,14 @@ public class Paint { return 0f; } if (!mHasCompatScaling) { - return native_getTextAdvances(mNativePaint, mNativeTypeface, chars, index, count, + return nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count, contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - float res = native_getTextAdvances(mNativePaint, mNativeTypeface, chars, index, count, + float res = nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count, contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex); setTextSize(oldSize); @@ -1950,7 +2149,7 @@ public class Paint { public float getTextRunAdvances(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float[] advances, int advancesIndex) { - + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -2032,6 +2231,7 @@ public class Paint { */ public float getTextRunAdvances(String text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float[] advances, int advancesIndex) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -2048,14 +2248,14 @@ public class Paint { } if (!mHasCompatScaling) { - return native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, + return nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - float totalAdvance = native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, + float totalAdvance = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex); setTextSize(oldSize); @@ -2096,6 +2296,7 @@ public class Paint { */ public int getTextRunCursor(char[] text, int contextStart, int contextLength, int dir, int offset, int cursorOpt) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); int contextEnd = contextStart + contextLength; if (((contextStart | contextEnd | offset | (contextEnd - contextStart) | (offset - contextStart) | (contextEnd - offset) @@ -2104,7 +2305,7 @@ public class Paint { throw new IndexOutOfBoundsException(); } - return native_getTextRunCursor(mNativePaint, text, + return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength, dir, offset, cursorOpt); } @@ -2183,6 +2384,7 @@ public class Paint { */ public int getTextRunCursor(String text, int contextStart, int contextEnd, int dir, int offset, int cursorOpt) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (((contextStart | contextEnd | offset | (contextEnd - contextStart) | (offset - contextStart) | (contextEnd - offset) | (text.length() - contextEnd) | cursorOpt) < 0) @@ -2190,7 +2392,7 @@ public class Paint { throw new IndexOutOfBoundsException(); } - return native_getTextRunCursor(mNativePaint, text, + return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd, dir, offset, cursorOpt); } @@ -2209,10 +2411,11 @@ public class Paint { */ public void getTextPath(char[] text, int index, int count, float x, float y, Path path) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if ((index | count) < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } - native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y, + nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y, path.ni()); } @@ -2231,10 +2434,11 @@ public class Paint { */ public void getTextPath(String text, int start, int end, float x, float y, Path path) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } - native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y, + nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y, path.ni()); } @@ -2249,13 +2453,14 @@ public class Paint { * allocated by the caller. */ public void getTextBounds(String text, int start, int end, Rect bounds) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } if (bounds == null) { throw new NullPointerException("need bounds Rect"); } - nativeGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds); + nGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds); } /** @@ -2269,13 +2474,14 @@ public class Paint { * allocated by the caller. */ public void getTextBounds(char[] text, int index, int count, Rect bounds) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if ((index | count) < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } if (bounds == null) { throw new NullPointerException("need bounds Rect"); } - nativeGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, + nGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, bounds); } @@ -2296,7 +2502,8 @@ public class Paint { * @return true if the typeface has a glyph for the string */ public boolean hasGlyph(String string) { - return native_hasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string); + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); + return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string); } /** @@ -2337,6 +2544,7 @@ public class Paint { */ public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -2349,7 +2557,7 @@ public class Paint { return 0.0f; } // TODO: take mCompatScaling into account (or eliminate compat scaling)? - return native_getRunAdvance(mNativePaint, mNativeTypeface, text, start, end, + return nGetRunAdvance(mNativePaint, mNativeTypeface, text, start, end, contextStart, contextEnd, isRtl, offset); } @@ -2367,6 +2575,7 @@ public class Paint { */ public float getRunAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -2417,6 +2626,7 @@ public class Paint { */ public int getOffsetForAdvance(char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -2426,7 +2636,7 @@ public class Paint { throw new IndexOutOfBoundsException(); } // TODO: take mCompatScaling into account (or eliminate compat scaling)? - return native_getOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end, + return nGetOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end, contextStart, contextEnd, isRtl, advance); } @@ -2444,6 +2654,7 @@ public class Paint { */ public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance) { + if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -2464,90 +2675,88 @@ public class Paint { @Override protected void finalize() throws Throwable { try { - finalizer(mNativePaint); - mNativePaint = 0; + if (mNativePaint != 0) { + nFinalizer(mNativePaint); + mNativePaint = 0; + } } finally { super.finalize(); } } - private static native long native_init(); - private static native long native_initWithPaint(long paint); - private static native void native_reset(long native_object); - private static native void native_set(long native_dst, long native_src); - private static native int native_getStyle(long native_object); - private static native void native_setStyle(long native_object, int style); - private static native int native_getStrokeCap(long native_object); - private static native void native_setStrokeCap(long native_object, int cap); - private static native int native_getStrokeJoin(long native_object); - private static native void native_setStrokeJoin(long native_object, + private static native long nInit(); + private static native long nInitWithPaint(long paint); + private static native void nReset(long paintPtr); + private static native void nSet(long paintPtrDest, long paintPtrSrc); + private static native int nGetStyle(long paintPtr); + private static native void nSetStyle(long paintPtr, int style); + private static native int nGetStrokeCap(long paintPtr); + private static native void nSetStrokeCap(long paintPtr, int cap); + private static native int nGetStrokeJoin(long paintPtr); + private static native void nSetStrokeJoin(long paintPtr, int join); - private static native boolean native_getFillPath(long native_object, + private static native boolean nGetFillPath(long paintPtr, long src, long dst); - private static native long native_setShader(long native_object, long shader); - private static native long native_setColorFilter(long native_object, + private static native long nSetShader(long paintPtr, long shader); + private static native long nSetColorFilter(long paintPtr, long filter); - private static native long native_setXfermode(long native_object, + private static native long nSetXfermode(long paintPtr, long xfermode); - private static native long native_setPathEffect(long native_object, + private static native long nSetPathEffect(long paintPtr, long effect); - private static native long native_setMaskFilter(long native_object, + private static native long nSetMaskFilter(long paintPtr, long maskfilter); - private static native long native_setTypeface(long native_object, + private static native long nSetTypeface(long paintPtr, long typeface); - private static native long native_setRasterizer(long native_object, + private static native long nSetRasterizer(long paintPtr, long rasterizer); - private static native int native_getTextAlign(long native_object); - private static native void native_setTextAlign(long native_object, + private static native int nGetTextAlign(long paintPtr); + private static native void nSetTextAlign(long paintPtr, int align); - private static native void native_setTextLocale(long native_object, + private static native void nSetTextLocale(long paintPtr, String locale); - private static native int native_getTextGlyphs(long native_object, - String text, int start, int end, int contextStart, int contextEnd, - int flags, char[] glyphs); - - private static native float native_getTextAdvances(long native_object, long native_typeface, + private static native float nGetTextAdvances(long paintPtr, long typefacePtr, char[] text, int index, int count, int contextIndex, int contextCount, int bidiFlags, float[] advances, int advancesIndex); - private static native float native_getTextAdvances(long native_object, long native_typeface, + private static native float nGetTextAdvances(long paintPtr, long typefacePtr, String text, int start, int end, int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex); - private native int native_getTextRunCursor(long native_object, char[] text, + private native int nGetTextRunCursor(long paintPtr, char[] text, int contextStart, int contextLength, int dir, int offset, int cursorOpt); - private native int native_getTextRunCursor(long native_object, String text, + private native int nGetTextRunCursor(long paintPtr, String text, int contextStart, int contextEnd, int dir, int offset, int cursorOpt); - private static native void native_getTextPath(long native_object, long native_typeface, + private static native void nGetTextPath(long paintPtr, long typefacePtr, int bidiFlags, char[] text, int index, int count, float x, float y, long path); - private static native void native_getTextPath(long native_object, long native_typeface, + private static native void nGetTextPath(long paintPtr, long typefacePtr, int bidiFlags, String text, int start, int end, float x, float y, long path); - private static native void nativeGetStringBounds(long nativePaint, long native_typeface, + private static native void nGetStringBounds(long nativePaint, long typefacePtr, String text, int start, int end, int bidiFlags, Rect bounds); - private static native void nativeGetCharArrayBounds(long nativePaint, long native_typeface, + private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr, char[] text, int index, int count, int bidiFlags, Rect bounds); - private static native void finalizer(long nativePaint); + private static native void nFinalizer(long nativePaint); - private static native void native_setShadowLayer(long native_object, + private static native void nSetShadowLayer(long paintPtr, float radius, float dx, float dy, int color); - private static native boolean native_hasShadowLayer(long native_object); + private static native boolean nHasShadowLayer(long paintPtr); - private static native float native_getLetterSpacing(long native_object); - private static native void native_setLetterSpacing(long native_object, + private static native float nGetLetterSpacing(long paintPtr); + private static native void nSetLetterSpacing(long paintPtr, float letterSpacing); - private static native void native_setFontFeatureSettings(long native_object, + private static native void nSetFontFeatureSettings(long paintPtr, String settings); - private static native int native_getHyphenEdit(long native_object); - private static native void native_setHyphenEdit(long native_object, int hyphen); - private static native boolean native_hasGlyph(long native_object, long native_typeface, + private static native int nGetHyphenEdit(long paintPtr); + private static native void nSetHyphenEdit(long paintPtr, int hyphen); + private static native boolean nHasGlyph(long paintPtr, long typefacePtr, int bidiFlags, String string); - private static native float native_getRunAdvance(long native_object, long native_typeface, + private static native float nGetRunAdvance(long paintPtr, long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset); - private static native int native_getOffsetForAdvance(long native_object, - long native_typeface, char[] text, int start, int end, int contextStart, int contextEnd, + private static native int nGetOffsetForAdvance(long paintPtr, + long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance); } diff --git a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java index b67aa7dca17b..6763dd1970ae 100644 --- a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java +++ b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java @@ -162,4 +162,60 @@ public class PaintTest extends AndroidTestCase { } catch (IndexOutOfBoundsException e) { } } + + public void testMeasureTextBidi() { + Paint p = new Paint(); + { + String bidiText = "abc \u0644\u063A\u0629 def"; + p.setBidiFlags(Paint.BIDI_LTR); + float width = p.measureText(bidiText, 0, 4); + p.setBidiFlags(Paint.BIDI_RTL); + width += p.measureText(bidiText, 4, 7); + p.setBidiFlags(Paint.BIDI_LTR); + width += p.measureText(bidiText, 7, bidiText.length()); + assertEquals(width, p.measureText(bidiText), 1.0f); + } + { + String bidiText = "abc \u0644\u063A\u0629 def"; + p.setBidiFlags(Paint.BIDI_DEFAULT_LTR); + float width = p.measureText(bidiText, 0, 4); + width += p.measureText(bidiText, 4, 7); + width += p.measureText(bidiText, 7, bidiText.length()); + assertEquals(width, p.measureText(bidiText), 1.0f); + } + { + String bidiText = "abc \u0644\u063A\u0629 def"; + p.setBidiFlags(Paint.BIDI_FORCE_LTR); + float width = p.measureText(bidiText, 0, 4); + width += p.measureText(bidiText, 4, 7); + width += p.measureText(bidiText, 7, bidiText.length()); + assertEquals(width, p.measureText(bidiText), 1.0f); + } + { + String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629"; + p.setBidiFlags(Paint.BIDI_RTL); + float width = p.measureText(bidiText, 0, 4); + p.setBidiFlags(Paint.BIDI_LTR); + width += p.measureText(bidiText, 4, 7); + p.setBidiFlags(Paint.BIDI_RTL); + width += p.measureText(bidiText, 7, bidiText.length()); + assertEquals(width, p.measureText(bidiText), 1.0f); + } + { + String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629"; + p.setBidiFlags(Paint.BIDI_DEFAULT_RTL); + float width = p.measureText(bidiText, 0, 4); + width += p.measureText(bidiText, 4, 7); + width += p.measureText(bidiText, 7, bidiText.length()); + assertEquals(width, p.measureText(bidiText), 1.0f); + } + { + String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629"; + p.setBidiFlags(Paint.BIDI_FORCE_RTL); + float width = p.measureText(bidiText, 0, 4); + width += p.measureText(bidiText, 4, 7); + width += p.measureText(bidiText, 7, bidiText.length()); + assertEquals(width, p.measureText(bidiText), 1.0f); + } + } } diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index a81ffb9f59fa..0c29a9e928a2 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -44,6 +44,12 @@ namespace uirenderer { #define DEBUG_COLOR_MERGEDBATCH 0x5f7f7fff #define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f +static bool avoidOverdraw() { + // Don't avoid overdraw when visualizing it, since that makes it harder to + // debug where it's coming from, and when the problem occurs. + return !Properties::debugOverdraw; +}; + ///////////////////////////////////////////////////////////////////////////////// // Operation Batches ///////////////////////////////////////////////////////////////////////////////// @@ -218,7 +224,10 @@ public: // if paints are equal, then modifiers + paint attribs don't need to be compared if (op->mPaint == mOps[0].op->mPaint) return true; - if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false; + if (PaintUtils::getAlphaDirect(op->mPaint) + != PaintUtils::getAlphaDirect(mOps[0].op->mPaint)) { + return false; + } if (op->mPaint && mOps[0].op->mPaint && op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) { @@ -495,7 +504,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { && mSaveStack.empty() && !state->mRoundRectClipState; - if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() && + if (CC_LIKELY(avoidOverdraw()) && mBatches.size() && state->mClipSideFlags != kClipSide_ConservativeFull && deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) { // avoid overdraw by resetting drawing state + discarding drawing ops @@ -533,7 +542,11 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { if (deferInfo.mergeable) { // Try to merge with any existing batch with same mergeId. - if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) { + std::unordered_map<mergeid_t, DrawBatch*>& mergingBatch + = mMergingBatches[deferInfo.batchId]; + auto getResult = mergingBatch.find(deferInfo.mergeId); + if (getResult != mergingBatch.end()) { + targetBatch = getResult->second; if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) { targetBatch = nullptr; } @@ -577,7 +590,8 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { if (deferInfo.mergeable) { targetBatch = new MergingDrawBatch(deferInfo, renderer.getViewportWidth(), renderer.getViewportHeight()); - mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch); + mMergingBatches[deferInfo.batchId].insert( + std::make_pair(deferInfo.mergeId, targetBatch)); } else { targetBatch = new DrawBatch(deferInfo); mBatchLookup[deferInfo.batchId] = targetBatch; @@ -642,7 +656,7 @@ void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { // save and restore so that reordering doesn't affect final state renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); - if (CC_LIKELY(mAvoidOverdraw)) { + if (CC_LIKELY(avoidOverdraw())) { for (unsigned int i = 1; i < mBatches.size(); i++) { if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) { discardDrawingBatches(i - 1); diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 4f2dca5f3ee1..7873fbdd342a 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -17,9 +17,10 @@ #ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H #define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H +#include <unordered_map> + #include <utils/Errors.h> #include <utils/LinearAllocator.h> -#include <utils/TinyHashMap.h> #include "Matrix.h" #include "OpenGLRenderer.h" @@ -82,8 +83,8 @@ public: class DeferredDisplayList { friend struct DeferStateStruct; // used to give access to allocator public: - DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) : - mBounds(bounds), mAvoidOverdraw(avoidOverdraw) { + DeferredDisplayList(const Rect& bounds) + : mBounds(bounds) { clear(); } ~DeferredDisplayList() { clear(); } @@ -151,7 +152,6 @@ private: // layer space bounds of rendering Rect mBounds; - const bool mAvoidOverdraw; /** * At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so @@ -177,7 +177,7 @@ private: * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not * collide, which avoids the need to resolve mergeid collisions. */ - TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count]; + std::unordered_map<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count]; LinearAllocator mAllocator; }; diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 38f2363f3532..70383340fc8d 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -47,7 +47,8 @@ DeferredLayerUpdater::~DeferredLayerUpdater() { } void DeferredLayerUpdater::setPaint(const SkPaint* paint) { - OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode); + mAlpha = PaintUtils::getAlphaDirect(paint); + mMode = PaintUtils::getXfermodeDirect(paint); SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr; SkRefCnt_SafeAssign(mColorFilter, colorFilter); } diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index dc5cb8b349f1..ddfc533f9d77 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -172,10 +172,6 @@ public: void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; } bool getQuickRejected() { return mQuickRejected; } - inline int getPaintAlpha() const { - return OpenGLRenderer::getAlphaDirect(mPaint); - } - virtual bool hasTextShadow() const { return false; } @@ -213,7 +209,7 @@ protected: if (state.mAlpha != 1.0f) return false; - SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint); + SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint); return (mode == SkXfermode::kSrcOver_Mode || mode == SkXfermode::kSrc_Mode); @@ -249,8 +245,8 @@ public: virtual bool getLocalBounds(Rect& localBounds) override { localBounds.set(mLocalBounds); - OpenGLRenderer::TextShadow textShadow; - if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) { + PaintUtils::TextShadow textShadow; + if (PaintUtils::getTextShadow(mPaint, &textShadow)) { Rect shadow(mLocalBounds); shadow.translate(textShadow.dx, textShadow.dx); shadow.outset(textShadow.radius); @@ -372,8 +368,8 @@ public: private: bool isSaveLayerAlpha() const { - SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint); - int alpha = OpenGLRenderer::getAlphaDirect(mPaint); + SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint); + int alpha = PaintUtils::getAlphaDirect(mPaint); return alpha < 255 && mode == SkXfermode::kSrcOver_Mode; } @@ -691,7 +687,7 @@ public: // TODO: support clipped bitmaps by handling them in SET_TEXTURE deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() && !state.mClipSideFlags && - OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode && + PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode && (mBitmap->colorType() != kAlpha_8_SkColorType); } @@ -895,7 +891,7 @@ public: deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch; deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap; deferInfo.mergeable = state.mMatrix.isPureTranslate() && - OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; + PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque(); } @@ -1241,7 +1237,7 @@ public: } virtual bool hasTextShadow() const override { - return OpenGLRenderer::hasTextShadow(mPaint); + return PaintUtils::hasTextShadow(mPaint); } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, @@ -1330,7 +1326,7 @@ public: deferInfo.mergeable = state.mMatrix.isPureTranslate() && !hasDecorations - && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; + && PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; } virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h index fa20b0807a88..4785ea48cddc 100644 --- a/libs/hwui/Glop.h +++ b/libs/hwui/Glop.h @@ -135,10 +135,6 @@ struct Glop { } fill; struct Transform { - // Orthographic projection matrix for current FBO - // TODO: move out of Glop, since this is static per FBO - Matrix4 ortho; - // modelView transform, accounting for delta between mesh transform and content of the mesh // often represents x/y offsets within command, or scaling for mesh unit size Matrix4 modelView; diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index 69559a77c3a0..fa166ae5ca5a 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -461,11 +461,10 @@ GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) { // Transform //////////////////////////////////////////////////////////////////////////////// -void GlopBuilder::setTransform(const Matrix4& ortho, const Matrix4& canvas, +void GlopBuilder::setTransform(const Matrix4& canvas, const int transformFlags) { TRIGGER_STAGE(kTransformStage); - mOutGlop->transform.ortho = ortho; mOutGlop->transform.canvas = canvas; mOutGlop->transform.transformFlags = transformFlags; } diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h index 549bb21e5f8d..8d05570dd206 100644 --- a/libs/hwui/GlopBuilder.h +++ b/libs/hwui/GlopBuilder.h @@ -71,7 +71,7 @@ public: GlopBuilder& setFillTextureLayer(Layer& layer, float alpha); GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) { - setTransform(snapshot.getOrthoMatrix(), *snapshot.transform, transformFlags); + setTransform(*snapshot.transform, transformFlags); return *this; } @@ -102,8 +102,7 @@ private: void setFill(int color, float alphaScale, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage, const SkShader* shader, const SkColorFilter* colorFilter); - void setTransform(const Matrix4& ortho, const Matrix4& canvas, - const int transformFlags); + void setTransform(const Matrix4& canvas, const int transformFlags); enum StageFlags { kInitialStage = 0, diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 8d8528961794..f99d92b89420 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -170,7 +170,8 @@ void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, } void Layer::setPaint(const SkPaint* paint) { - OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); + alpha = PaintUtils::getAlphaDirect(paint); + mode = PaintUtils::getXfermodeDirect(paint); setColorFilter((paint) ? paint->getColorFilter() : nullptr); } diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index a401ce119021..9f24e379e418 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -540,7 +540,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float Rect bounds(left, top, right, bottom); Rect clip; calculateLayerBoundsAndClip(bounds, clip, true); - updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint)); + updateSnapshotIgnoreForLayer(bounds, clip, true, PaintUtils::getAlphaDirect(paint)); if (!mState.currentlyIgnored()) { writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f); @@ -615,7 +615,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto Rect clip; Rect bounds(left, top, right, bottom); calculateLayerBoundsAndClip(bounds, clip, fboLayer); - updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint)); + updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, PaintUtils::getAlphaDirect(paint)); // Bail out if we won't draw in this snapshot if (mState.currentlyIgnored()) { @@ -1405,7 +1405,7 @@ void OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type) { setStencilFromClip(); } - mRenderState.render(glop); + mRenderState.render(glop, currentSnapshot()->getOrthoMatrix()); if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) { // TODO: specify more clearly when a draw should dirty the layer. // is writing to the stencil the only time we should ignore this? @@ -1431,10 +1431,7 @@ void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t return; } - // Don't avoid overdraw when visualizing, since that makes it harder to - // debug where it's coming from, and when the problem occurs. - bool avoidOverdraw = !Properties::debugOverdraw; - DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw); + DeferredDisplayList deferredList(mState.currentClipRect()); DeferStateStruct deferStruct(deferredList, *this, replayFlags); renderNode->defer(deferStruct, 0); @@ -1958,8 +1955,8 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, FontRenderer& fontRenderer, int alpha, float x, float y) { mCaches.textureState().activateTexture(0); - TextShadow textShadow; - if (!getTextShadow(paint, &textShadow)) { + PaintUtils::TextShadow textShadow; + if (!PaintUtils::getTextShadow(paint, &textShadow)) { LOG_ALWAYS_FATAL("failed to query shadow attributes"); } @@ -1987,8 +1984,10 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, renderGlop(glop); } +// TODO: remove this, once mState.currentlyIgnored captures snapshot alpha bool OpenGLRenderer::canSkipText(const SkPaint* paint) const { - float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha; + float alpha = (PaintUtils::hasTextShadow(paint) + ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha; return MathUtils::isZero(alpha) && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode; } @@ -2017,11 +2016,10 @@ void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count, FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(); fontRenderer.setFont(paint, SkMatrix::I()); - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); + int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha; + SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint); - if (CC_UNLIKELY(hasTextShadow(paint))) { + if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) { drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, 0.0f, 0.0f); } @@ -2162,13 +2160,12 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float y = floorf(y + transform.getTranslateY() + 0.5f); } - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); + int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha; + SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint); FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(); - if (CC_UNLIKELY(hasTextShadow(paint))) { + if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) { fontRenderer.setFont(paint, SkMatrix::I()); drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, oldX, oldY); @@ -2238,9 +2235,8 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, fontRenderer.setFont(paint, SkMatrix::I()); fontRenderer.setTextureFiltering(true); - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); + int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha; + SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint); TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint); const Rect* clip = &writableSnapshot()->getLocalClip(); @@ -2530,12 +2526,6 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot renderGlop(glop); } -void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, - SkXfermode::Mode* mode) const { - getAlphaAndModeDirect(paint, alpha, mode); - *alpha *= currentSnapshot()->alpha; -} - float OpenGLRenderer::getLayerAlpha(const Layer* layer) const { return (layer->getAlpha() / 255.0f) * currentSnapshot()->alpha; } diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 910af5705705..400c225b53a0 100755 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -260,57 +260,6 @@ public: void endMark() const; /** - * Gets the alpha and xfermode out of a paint object. If the paint is null - * alpha will be 255 and the xfermode will be SRC_OVER. This method does - * not multiply the paint's alpha by the current snapshot's alpha, and does - * not replace the alpha with the overrideLayerAlpha - * - * @param paint The paint to extract values from - * @param alpha Where to store the resulting alpha - * @param mode Where to store the resulting xfermode - */ - static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha, - SkXfermode::Mode* mode) { - *mode = getXfermodeDirect(paint); - *alpha = getAlphaDirect(paint); - } - - static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) { - if (!paint) return SkXfermode::kSrcOver_Mode; - return PaintUtils::getXfermode(paint->getXfermode()); - } - - static inline int getAlphaDirect(const SkPaint* paint) { - if (!paint) return 255; - return paint->getAlpha(); - } - - struct TextShadow { - SkScalar radius; - float dx; - float dy; - SkColor color; - }; - - static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) { - SkDrawLooper::BlurShadowRec blur; - if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) { - if (textShadow) { - textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma); - textShadow->dx = blur.fOffset.fX; - textShadow->dy = blur.fOffset.fY; - textShadow->color = blur.fColor; - } - return true; - } - return false; - } - - static inline bool hasTextShadow(const SkPaint* paint) { - return getTextShadow(paint, nullptr); - } - - /** * Build the best transform to use to rasterize text given a full * transform matrix, and whether filteration is needed. * @@ -493,16 +442,6 @@ protected: void drawTextureLayer(Layer* layer, const Rect& rect); /** - * Gets the alpha and xfermode out of a paint object. If the paint is null - * alpha will be 255 and the xfermode will be SRC_OVER. Accounts for snapshot alpha. - * - * @param paint The paint to extract values from - * @param alpha Where to store the resulting alpha - * @param mode Where to store the resulting xfermode - */ - inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const; - - /** * Gets the alpha from a layer, accounting for snapshot alpha * * @param layer The layer from which the alpha is extracted diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index ddc7ecd329b6..bf1b4d0b0d0e 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -725,7 +725,9 @@ template <class T> void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) { if (properties().getAlpha() <= 0.0f || properties().getOutline().getAlpha() <= 0.0f - || !properties().getOutline().getPath()) { + || !properties().getOutline().getPath() + || properties().getScaleX() == 0 + || properties().getScaleY() == 0) { // no shadow to draw return; } @@ -915,7 +917,10 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { const bool useViewProperties = (!mLayer || drawLayer); if (useViewProperties) { const Outline& outline = properties().getOutline(); - if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) { + if (properties().getAlpha() <= 0 + || (outline.getShouldClip() && outline.isEmpty()) + || properties().getScaleX() == 0 + || properties().getScaleY() == 0) { DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "", this, getName()); return; diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index ad74bff8dc25..ce1bd6ab8b03 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -52,11 +52,8 @@ bool LayerProperties::setColorFilter(SkColorFilter* filter) { bool LayerProperties::setFromPaint(const SkPaint* paint) { bool changed = false; - SkXfermode::Mode mode; - int alpha; - OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); - changed |= setAlpha(static_cast<uint8_t>(alpha)); - changed |= setXferMode(mode); + changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint))); + changed |= setXferMode(PaintUtils::getXfermodeDirect(paint)); changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr); return changed; } diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index 220936551a60..eb0fa74f5af0 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -73,8 +73,8 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, } #if DEBUG_SHADOW - ALOGD("light center %f %f %f", - adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z); + ALOGD("light center %f %f %f %d", + adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius); #endif // light position (because it's in local space) needs to compensate for receiver transform diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index 9b0a1aadf0bf..bdce73c79993 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -1051,7 +1051,7 @@ void SpotShadow::dumpPolygon(const Vector2* poly, int polyLength, const char* po */ void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* polyName) { for (int i = 0; i < polyLength; i++) { - ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y); + ALOGD("polygon %s i %d x %f y %f z %f", polyName, i, poly[i].x, poly[i].y, poly[i].z); } } diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index c5126def683c..dfa70ace2f44 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -208,7 +208,7 @@ void RenderState::postDecStrong(VirtualLightRefBase* object) { // Render /////////////////////////////////////////////////////////////////////////////// -void RenderState::render(const Glop& glop) { +void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { const Glop::Mesh& mesh = glop.mesh; const Glop::Mesh::Vertices& vertices = mesh.vertices; const Glop::Mesh::Indices& indices = mesh.indices; @@ -223,7 +223,7 @@ void RenderState::render(const Glop& glop) { fill.program->setColor(fill.color); } - fill.program->set(glop.transform.ortho, + fill.program->set(orthoMatrix, glop.transform.modelView, glop.transform.meshTransform(), glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor); diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 4fd792c1b503..9ae084506f1d 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -84,7 +84,7 @@ public: // more thinking... void postDecStrong(VirtualLightRefBase* object); - void render(const Glop& glop); + void render(const Glop& glop, const Matrix4& orthoMatrix); AssetAtlas& assetAtlas() { return mAssetAtlas; } Blend& blend() { return *mBlend; } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 9dc5b45a7738..ddfd62141f5d 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -62,7 +62,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) , mJankTracker(thread.timeLord().frameIntervalNanos()) , mProfiler(mFrames) - , mContentOverdrawProtectionBounds(0, 0, 0, 0) { + , mContentDrawBounds(0, 0, 0, 0) { mRenderNodes.emplace_back(rootRenderNode); mRenderThread.renderState().registerCanvasContext(this); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); @@ -309,7 +309,7 @@ void CanvasContext::draw() { Rect outBounds; // It there are multiple render nodes, they are as follows: // #0 - backdrop - // #1 - content (with - and clipped to - bounds mContentOverdrawProtectionBounds) + // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds) // #2 - frame // Usually the backdrop cannot be seen since it will be entirely covered by the content. While // resizing however it might become partially visible. The following render loop will crop the @@ -317,66 +317,72 @@ void CanvasContext::draw() { // against the backdrop (since that indicates a shrinking of the window) and then the frame // around everything. // The bounds of the backdrop against which the content should be clipped. - Rect backdropBounds = mContentOverdrawProtectionBounds; + Rect backdropBounds = mContentDrawBounds; + // Usually the contents bounds should be mContentDrawBounds - however - we will + // move it towards the fixed edge to give it a more stable appearance (for the moment). + Rect contentBounds; // If there is no content bounds we ignore the layering as stated above and start with 2. - int layer = mContentOverdrawProtectionBounds.isEmpty() ? 2 : 0; + int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() <= 2) ? 2 : 0; // Draw all render nodes. Note that for (const sp<RenderNode>& node : mRenderNodes) { if (layer == 0) { // Backdrop. - // Draw the backdrop clipped to the inverse content bounds. + // Draw the backdrop clipped to the inverse content bounds, but assume that the content + // was moved to the upper left corner. const RenderProperties& properties = node->properties(); Rect targetBounds(properties.getLeft(), properties.getTop(), properties.getRight(), properties.getBottom()); + // Move the content bounds towards the fixed corner of the backdrop. + const int x = targetBounds.left; + const int y = targetBounds.top; + contentBounds.set(x, y, x + mContentDrawBounds.getWidth(), + y + mContentDrawBounds.getHeight()); // Remember the intersection of the target bounds and the intersection bounds against // which we have to crop the content. + backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight()); backdropBounds.intersect(targetBounds); // Check if we have to draw something on the left side ... - if (targetBounds.left < mContentOverdrawProtectionBounds.left) { + if (targetBounds.left < contentBounds.left) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(targetBounds.left, targetBounds.top, - mContentOverdrawProtectionBounds.left, targetBounds.bottom, + contentBounds.left, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } // Reduce the target area by the area we have just painted. - targetBounds.left = std::min(mContentOverdrawProtectionBounds.left, - targetBounds.right); + targetBounds.left = std::min(contentBounds.left, targetBounds.right); mCanvas->restore(); } // ... or on the right side ... - if (targetBounds.right > mContentOverdrawProtectionBounds.right && + if (targetBounds.right > contentBounds.right && !targetBounds.isEmpty()) { mCanvas->save(SkCanvas::kClip_SaveFlag); - if (mCanvas->clipRect(mContentOverdrawProtectionBounds.right, targetBounds.top, + if (mCanvas->clipRect(contentBounds.right, targetBounds.top, targetBounds.right, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } // Reduce the target area by the area we have just painted. - targetBounds.right = std::max(targetBounds.left, - mContentOverdrawProtectionBounds.right); + targetBounds.right = std::max(targetBounds.left, contentBounds.right); mCanvas->restore(); } // ... or at the top ... - if (targetBounds.top < mContentOverdrawProtectionBounds.top && + if (targetBounds.top < contentBounds.top && !targetBounds.isEmpty()) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right, - mContentOverdrawProtectionBounds.top, + contentBounds.top, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } // Reduce the target area by the area we have just painted. - targetBounds.top = std::min(mContentOverdrawProtectionBounds.top, - targetBounds.bottom); + targetBounds.top = std::min(contentBounds.top, targetBounds.bottom); mCanvas->restore(); } // ... or at the bottom. - if (targetBounds.bottom > mContentOverdrawProtectionBounds.bottom && + if (targetBounds.bottom > contentBounds.bottom && !targetBounds.isEmpty()) { mCanvas->save(SkCanvas::kClip_SaveFlag); - if (mCanvas->clipRect(targetBounds.left, - mContentOverdrawProtectionBounds.bottom, targetBounds.right, + if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } @@ -384,10 +390,17 @@ void CanvasContext::draw() { } } else if (layer == 1) { // Content // It gets cropped against the bounds of the backdrop to stay inside. - mCanvas->save(SkCanvas::kClip_SaveFlag); - if (mCanvas->clipRect(backdropBounds.left, backdropBounds.top, - backdropBounds.right, backdropBounds.bottom, - SkRegion::kIntersect_Op)) { + mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); + + // We shift and clip the content to match its final location in the window. + const float left = mContentDrawBounds.left; + const float top = mContentDrawBounds.top; + const float dx = backdropBounds.left - left; + const float dy = backdropBounds.top - top; + const float width = backdropBounds.getWidth(); + const float height = backdropBounds.getHeight(); + mCanvas->translate(dx, dy); + if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } mCanvas->restore(); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 1c3845cac504..e0cbabdc933a 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -126,8 +126,8 @@ public: mRenderNodes.end()); } - void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) { - mContentOverdrawProtectionBounds.set(left, top, right, bottom); + void setContentDrawBounds(int left, int top, int right, int bottom) { + mContentDrawBounds.set(left, top, right, bottom); } private: @@ -167,7 +167,7 @@ private: std::set<RenderNode*> mPrefetechedLayers; // Stores the bounds of the main content. - Rect mContentOverdrawProtectionBounds; + Rect mContentDrawBounds; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index f43a769890a4..26aae90d5990 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -529,15 +529,14 @@ void RenderProxy::drawRenderNode(RenderNode* node) { staticPostAndWait(task); } -CREATE_BRIDGE5(setContentOverdrawProtectionBounds, CanvasContext* context, int left, int top, +CREATE_BRIDGE5(setContentDrawBounds, CanvasContext* context, int left, int top, int right, int bottom) { - args->context->setContentOverdrawProtectionBounds(args->left, args->top, args->right, - args->bottom); + args->context->setContentDrawBounds(args->left, args->top, args->right, args->bottom); return nullptr; } -void RenderProxy::setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) { - SETUP_TASK(setContentOverdrawProtectionBounds); +void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) { + SETUP_TASK(setContentDrawBounds); args->context = mContext; args->left = left; args->top = top; diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 046f24ac3f81..d1b62f1f64a6 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -109,7 +109,7 @@ public: ANDROID_API void addRenderNode(RenderNode* node, bool placeFront); ANDROID_API void removeRenderNode(RenderNode* node); ANDROID_API void drawRenderNode(RenderNode* node); - ANDROID_API void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom); + ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom); private: RenderThread& mRenderThread; diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index ba02f5f1a77d..019e5d3b0e7b 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -16,12 +16,18 @@ #ifndef PAINT_UTILS_H #define PAINT_UTILS_H +#include <utils/Blur.h> + #include <SkColorFilter.h> #include <SkXfermode.h> namespace android { namespace uirenderer { +/** + * Utility methods for accessing data within SkPaint, and providing defaults + * with optional SkPaint pointers. + */ class PaintUtils { public: @@ -73,6 +79,39 @@ public: return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0; } + struct TextShadow { + SkScalar radius; + float dx; + float dy; + SkColor color; + }; + + static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) { + SkDrawLooper::BlurShadowRec blur; + if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) { + if (textShadow) { + textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma); + textShadow->dx = blur.fOffset.fX; + textShadow->dy = blur.fOffset.fY; + textShadow->color = blur.fColor; + } + return true; + } + return false; + } + + static inline bool hasTextShadow(const SkPaint* paint) { + return getTextShadow(paint, nullptr); + } + + static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) { + return paint ? getXfermode(paint->getXfermode()) : SkXfermode::kSrcOver_Mode; + } + + static inline int getAlphaDirect(const SkPaint* paint) { + return paint ? paint->getAlpha() : 255; + } + }; // class PaintUtils } /* namespace uirenderer */ diff --git a/libs/hwui/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h deleted file mode 100644 index 4ff9a42f6d5d..000000000000 --- a/libs/hwui/utils/TinyHashMap.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_TINYHASHMAP_H -#define ANDROID_HWUI_TINYHASHMAP_H - -#include <utils/BasicHashtable.h> - -namespace android { -namespace uirenderer { - -/** - * A very simple hash map that doesn't allow duplicate keys, overwriting the older entry. - */ -template <typename TKey, typename TValue> -class TinyHashMap { -public: - typedef key_value_pair_t<TKey, TValue> TEntry; - - /** - * Puts an entry in the hash, removing any existing entry with the same key - */ - void put(TKey key, TValue value) { - hash_t hash = android::hash_type(key); - - ssize_t index = mTable.find(-1, hash, key); - if (index != -1) { - mTable.removeAt(index); - } - - TEntry initEntry(key, value); - mTable.add(hash, initEntry); - } - - /** - * Return true if key is in the map, in which case stores the value in the output ref - */ - bool get(TKey key, TValue& outValue) { - hash_t hash = android::hash_type(key); - ssize_t index = mTable.find(-1, hash, key); - if (index == -1) { - return false; - } - outValue = mTable.entryAt(index).value; - return true; - } - - void clear() { mTable.clear(); } - -private: - BasicHashtable<TKey, TEntry> mTable; -}; - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_TINYHASHMAP_H diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java index 54ad60e4a62d..4b8f81e01a1d 100644 --- a/media/java/android/media/Metadata.java +++ b/media/java/android/media/Metadata.java @@ -18,6 +18,7 @@ package android.media; import android.os.Parcel; import android.util.Log; +import android.util.MathUtils; import java.util.Calendar; import java.util.Collections; @@ -332,7 +333,14 @@ import java.util.TimeZone; } // Skip to the next one. - parcel.setDataPosition(start + size); + try { + parcel.setDataPosition(MathUtils.addOrThrow(start, size)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid size: " + e.getMessage()); + error = true; + break; + } + bytesLeft -= size; ++recCount; } diff --git a/media/java/android/media/SRTRenderer.java b/media/java/android/media/SRTRenderer.java index ee4edee3f9f0..a3e2abdaeca2 100644 --- a/media/java/android/media/SRTRenderer.java +++ b/media/java/android/media/SRTRenderer.java @@ -165,7 +165,6 @@ class SRTTrack extends WebVttTrack { return; } - final int _ = 0; for (Cue cue : activeCues) { TextTrackCue ttc = (TextTrackCue) cue; @@ -184,7 +183,8 @@ class SRTTrack extends WebVttTrack { parcel.writeInt(buf.length); parcel.writeByteArray(buf); - Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, _, _, parcel); + Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, 0 /* arg1 */, 0 /* arg2 */, + parcel); mEventHandler.sendMessage(msg); } activeCues.clear(); diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml index dec4e923e6ec..0dac0d550eb8 100644 --- a/packages/DocumentsUI/res/layout/drawer_layout.xml +++ b/packages/DocumentsUI/res/layout/drawer_layout.xml @@ -14,62 +14,71 @@ limitations under the License. --> -<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/drawer_layout" +<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and + floating action buttons) to operate correctly. --> +<android.support.design.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:id="@+id/coordinator_layout"> - <LinearLayout + <android.support.v4.widget.DrawerLayout + android:id="@+id/drawer_layout" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> + android:layout_height="match_parent"> - <com.android.documentsui.DocumentsToolBar - android:id="@+id/toolbar" + <LinearLayout android:layout_width="match_parent" - android:layout_height="?android:attr/actionBarSize" - android:background="?android:attr/colorPrimary" - android:elevation="8dp" - android:theme="?actionBarTheme" - android:popupTheme="?actionBarPopupTheme"> - - <Spinner - android:id="@+id/stack" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="4dp" - android:overlapAnchor="true" /> - - </com.android.documentsui.DocumentsToolBar> - - <include layout="@layout/directory_cluster"/> - - </LinearLayout> - - <LinearLayout - android:id="@+id/drawer_roots" - android:layout_width="256dp" - android:layout_height="match_parent" - android:layout_gravity="start" - android:orientation="vertical" - android:elevation="16dp" - android:background="@*android:color/white"> - - <Toolbar - android:id="@+id/roots_toolbar" - android:layout_width="match_parent" - android:layout_height="?android:attr/actionBarSize" - android:background="?android:attr/colorPrimary" - android:elevation="8dp" - android:theme="?actionBarTheme" - android:popupTheme="?actionBarPopupTheme" /> - - <FrameLayout - android:id="@+id/container_roots" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" /> + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.android.documentsui.DocumentsToolBar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?android:attr/actionBarSize" + android:background="?android:attr/colorPrimary" + android:elevation="8dp" + android:theme="?actionBarTheme" + android:popupTheme="?actionBarPopupTheme"> + + <Spinner + android:id="@+id/stack" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="4dp" + android:overlapAnchor="true" /> + + </com.android.documentsui.DocumentsToolBar> + + <include layout="@layout/directory_cluster"/> + + </LinearLayout> + + <LinearLayout + android:id="@+id/drawer_roots" + android:layout_width="256dp" + android:layout_height="match_parent" + android:layout_gravity="start" + android:orientation="vertical" + android:elevation="16dp" + android:background="@*android:color/white"> + + <Toolbar + android:id="@+id/roots_toolbar" + android:layout_width="match_parent" + android:layout_height="?android:attr/actionBarSize" + android:background="?android:attr/colorPrimary" + android:elevation="8dp" + android:theme="?actionBarTheme" + android:popupTheme="?actionBarPopupTheme" /> + + <FrameLayout + android:id="@+id/container_roots" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> - </LinearLayout> + </LinearLayout> -</android.support.v4.widget.DrawerLayout> + </android.support.v4.widget.DrawerLayout> +</android.support.design.widget.CoordinatorLayout> diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml index eba9af4297f7..403c6675ac30 100644 --- a/packages/DocumentsUI/res/layout/fixed_layout.xml +++ b/packages/DocumentsUI/res/layout/fixed_layout.xml @@ -14,49 +14,59 @@ limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and + floating action buttons) to operate correctly. --> +<android.support.design.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:id="@+id/coordinator_layout"> - <com.android.documentsui.DocumentsToolBar - android:id="@+id/toolbar" + <LinearLayout android:layout_width="match_parent" - android:layout_height="?android:attr/actionBarSize" - android:background="?android:attr/colorPrimary" - android:elevation="8dp" - android:theme="?actionBarTheme" - android:popupTheme="?actionBarPopupTheme"> - - <Spinner - android:id="@+id/stack" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="4dp" - android:overlapAnchor="true" /> - - </com.android.documentsui.DocumentsToolBar> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" - android:orientation="horizontal" - android:baselineAligned="false" - android:divider="?android:attr/dividerVertical" - android:showDividers="middle"> - - <FrameLayout - android:id="@+id/container_roots" - android:layout_width="256dp" - android:layout_height="match_parent" /> - - <include layout="@layout/directory_cluster" - android:layout_width="0dp" - android:layout_weight="1" + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.android.documentsui.DocumentsToolBar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?android:attr/actionBarSize" + android:background="?android:attr/colorPrimary" android:elevation="8dp" - android:background="@color/material_grey_50" /> + android:theme="?actionBarTheme" + android:popupTheme="?actionBarPopupTheme"> + + <Spinner + android:id="@+id/stack" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="4dp" + android:overlapAnchor="true" /> + + </com.android.documentsui.DocumentsToolBar> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:orientation="horizontal" + android:baselineAligned="false" + android:divider="?android:attr/dividerVertical" + android:showDividers="middle"> + + <FrameLayout + android:id="@+id/container_roots" + android:layout_width="256dp" + android:layout_height="match_parent" /> + + <include layout="@layout/directory_cluster" + android:layout_width="0dp" + android:layout_weight="1" + android:elevation="8dp" + android:background="@color/material_grey_50" /> + + </LinearLayout> </LinearLayout> -</LinearLayout> +</android.support.design.widget.CoordinatorLayout> diff --git a/packages/DocumentsUI/res/layout/single_pane_layout.xml b/packages/DocumentsUI/res/layout/single_pane_layout.xml index 20c3232c9686..c5a574568db0 100644 --- a/packages/DocumentsUI/res/layout/single_pane_layout.xml +++ b/packages/DocumentsUI/res/layout/single_pane_layout.xml @@ -14,29 +14,39 @@ limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and + floating action buttons) to operate correctly. --> +<android.support.design.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:id="@+id/coordinator_layout"> - <com.android.documentsui.DocumentsToolBar - android:id="@+id/toolbar" + <LinearLayout android:layout_width="match_parent" - android:layout_height="?android:attr/actionBarSize" - android:background="?android:attr/colorPrimary" - android:elevation="8dp" - android:theme="?actionBarTheme" - android:popupTheme="?actionBarPopupTheme"> + android:layout_height="match_parent" + android:orientation="vertical"> - <Spinner - android:id="@+id/stack" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="4dp" - android:overlapAnchor="true" /> + <com.android.documentsui.DocumentsToolBar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?android:attr/actionBarSize" + android:background="?android:attr/colorPrimary" + android:elevation="8dp" + android:theme="?actionBarTheme" + android:popupTheme="?actionBarPopupTheme"> - </com.android.documentsui.DocumentsToolBar> + <Spinner + android:id="@+id/stack" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="4dp" + android:overlapAnchor="true" /> - <include layout="@layout/directory_cluster"/> + </com.android.documentsui.DocumentsToolBar> -</LinearLayout> + <include layout="@layout/directory_cluster"/> + + </LinearLayout> + +</android.support.design.widget.CoordinatorLayout> diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml index 673a254f63f5..7e0649be722d 100644 --- a/packages/DocumentsUI/res/menu/activity.xml +++ b/packages/DocumentsUI/res/menu/activity.xml @@ -23,13 +23,6 @@ android:actionViewClass="android.widget.SearchView" android:imeOptions="actionSearch" /> <item - android:id="@+id/menu_create_dir" - android:title="@string/menu_create_dir" - android:icon="@drawable/ic_menu_new_folder" - android:alphabeticShortcut="e" - android:showAsAction="always" - android:visible="false" /> - <item android:id="@+id/menu_sort" android:title="@string/menu_sort" android:icon="@drawable/ic_menu_sortby" @@ -63,6 +56,13 @@ android:showAsAction="never" android:visible="false" /> <item + android:id="@+id/menu_create_dir" + android:title="@string/menu_create_dir" + android:icon="@drawable/ic_menu_new_folder" + android:alphabeticShortcut="e" + android:showAsAction="always" + android:visible="false" /> + <item android:id="@+id/menu_paste_from_clipboard" android:title="@string/menu_paste_from_clipboard" android:alphabeticShortcut="v" @@ -70,11 +70,11 @@ android:visible="false" /> <!-- Copy action is defined in mode_directory.xml --> <item - android:id="@+id/menu_advanced" + android:id="@+id/menu_file_size" android:showAsAction="never" android:visible="false" /> <item - android:id="@+id/menu_file_size" + android:id="@+id/menu_advanced" android:showAsAction="never" android:visible="false" /> <item diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml index a4acb604c1ef..d21b5eec7d4f 100644 --- a/packages/DocumentsUI/res/values/strings.xml +++ b/packages/DocumentsUI/res/values/strings.xml @@ -27,7 +27,7 @@ <string name="title_save">Save to</string> <!-- Menu item that creates a new directory/folder at the current location [CHAR LIMIT=24] --> - <string name="menu_create_dir">Create folder</string> + <string name="menu_create_dir">New folder</string> <!-- Menu item that switches view to show documents as a large-format grid of thumbnails [CHAR LIMIT=24] --> <string name="menu_grid">Grid view</string> <!-- Menu item that switches view to show documents as a list [CHAR LIMIT=24] --> diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index a6a45e59f469..caaa2b9be0a3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -20,6 +20,7 @@ import static com.android.documentsui.DirectoryFragment.ANIM_DOWN; import static com.android.documentsui.DirectoryFragment.ANIM_NONE; import static com.android.documentsui.DirectoryFragment.ANIM_SIDE; import static com.android.documentsui.DirectoryFragment.ANIM_UP; +import static com.android.documentsui.Shared.DEBUG; import static com.android.internal.util.Preconditions.checkArgument; import android.app.Activity; @@ -127,10 +128,10 @@ abstract class BaseActivity extends Activity { @Override public boolean onPrepareOptionsMenu(Menu menu) { - boolean shown = super.onPrepareOptionsMenu(menu); + super.onPrepareOptionsMenu(menu); final RootInfo root = getCurrentRoot(); - final DocumentInfo cwd = getCurrentDirectory(); + final boolean inRecents = getCurrentDirectory() == null; final MenuItem sort = menu.findItem(R.id.menu_sort); final MenuItem sortSize = menu.findItem(R.id.menu_sort_size); @@ -140,26 +141,28 @@ abstract class BaseActivity extends Activity { final MenuItem fileSize = menu.findItem(R.id.menu_file_size); final MenuItem settings = menu.findItem(R.id.menu_settings); - mSearchManager.update(root); + // I'm thinkin' this isn't necesary here. If it is...'cuz of a bug.... + // then uncomment the linke and let's get a proper bug reference here. + // mSearchManager.update(root); // Search uses backend ranking; no sorting - sort.setVisible(cwd != null && !mSearchManager.isSearching()); + sort.setVisible(!inRecents && !mSearchManager.isSearching()); + + // grid/list is effectively a toggle. + grid.setVisible(mState.derivedMode != State.MODE_GRID); + list.setVisible(mState.derivedMode != State.MODE_LIST); + + sortSize.setVisible(mState.showSize); // Only sort by size when visible + fileSize.setVisible(!mState.forceSize); + advanced.setVisible(!mState.forceAdvanced); + settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0); advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this) ? R.string.menu_advanced_hide : R.string.menu_advanced_show); fileSize.setTitle(LocalPreferences.getDisplayFileSize(this) ? R.string.menu_file_size_hide : R.string.menu_file_size_show); - State state = getDisplayState(); - - sortSize.setVisible(state.showSize); // Only sort by size when visible - fileSize.setVisible(!state.showSize); - grid.setVisible(state.derivedMode != State.MODE_GRID); - list.setVisible(state.derivedMode != State.MODE_LIST); - advanced.setVisible(!mState.showAdvanced); - settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0); - - return shown; + return true; } State buildDefaultState() { @@ -185,12 +188,10 @@ abstract class BaseActivity extends Activity { void onStackRestored(boolean restored, boolean external) {} void onRootPicked(RootInfo root) { - State state = getDisplayState(); - // Clear entire backstack and start in new root - state.stack.root = root; - state.stack.clear(); - state.stackTouched = true; + mState.stack.root = root; + mState.stack.clear(); + mState.stackTouched = true; mSearchManager.update(root); @@ -280,6 +281,7 @@ abstract class BaseActivity extends Activity { return cwd != null && cwd.isCreateSupported() && !mSearchManager.isSearching() + && !root.isRecents() && !root.isDownloads(); } @@ -289,8 +291,8 @@ abstract class BaseActivity extends Activity { } void openDirectory(DocumentInfo doc) { - getDisplayState().stack.push(doc); - getDisplayState().stackTouched = true; + mState.stack.push(doc); + mState.stackTouched = true; onCurrentDirectoryChanged(ANIM_DOWN); } @@ -367,16 +369,15 @@ abstract class BaseActivity extends Activity { } void setDisplayAdvancedDevices(boolean display) { - State state = getDisplayState(); LocalPreferences.setDisplayAdvancedDevices(this, display); - state.showAdvanced = state.forceAdvanced | display; + mState.showAdvanced = mState.forceAdvanced | display; RootsFragment.get(getFragmentManager()).onDisplayStateChanged(); invalidateOptionsMenu(); } void setDisplayFileSize(boolean display) { LocalPreferences.setDisplayFileSize(this, display); - getDisplayState().showSize = display; + mState.showSize = display; DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged(); invalidateOptionsMenu(); } @@ -389,7 +390,7 @@ abstract class BaseActivity extends Activity { * Set state sort order based on explicit user action. */ void setUserSortOrder(int sortOrder) { - getDisplayState().userSortOrder = sortOrder; + mState.userSortOrder = sortOrder; DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged(); } @@ -397,7 +398,7 @@ abstract class BaseActivity extends Activity { * Set state mode based on explicit user action. */ void setUserMode(int mode) { - getDisplayState().userMode = mode; + mState.userMode = mode; DirectoryFragment.get(getFragmentManager()).onUserModeChanged(); } @@ -411,7 +412,7 @@ abstract class BaseActivity extends Activity { @Override protected void onSaveInstanceState(Bundle state) { super.onSaveInstanceState(state); - state.putParcelable(EXTRA_STATE, getDisplayState()); + state.putParcelable(EXTRA_STATE, mState); } @Override @@ -420,16 +421,15 @@ abstract class BaseActivity extends Activity { } RootInfo getCurrentRoot() { - State state = getDisplayState(); - if (state.stack.root != null) { - return state.stack.root; + if (mState.stack.root != null) { + return mState.stack.root; } else { return mRoots.getRecentsRoot(); } } public DocumentInfo getCurrentDirectory() { - return getDisplayState().stack.peek(); + return mState.stack.peek(); } public Executor getExecutorForCurrentDirectory() { @@ -470,9 +470,8 @@ abstract class BaseActivity extends Activity { // Update the restored stack to ensure we have freshest data stack.updateDocuments(getContentResolver()); - State state = getDisplayState(); - state.stack = stack; - state.stackTouched = true; + mState.stack = stack; + mState.stackTouched = true; onCurrentDirectoryChanged(ANIM_SIDE); } catch (FileNotFoundException e) { @@ -502,9 +501,8 @@ abstract class BaseActivity extends Activity { @Override protected void onPostExecute(DocumentInfo result) { if (result != null) { - State state = getDisplayState(); - state.stack.push(result); - state.stackTouched = true; + mState.stack.push(result); + mState.stackTouched = true; onCurrentDirectoryChanged(ANIM_SIDE); } } @@ -516,7 +514,9 @@ abstract class BaseActivity extends Activity { @Override protected Void doInBackground(Void... params) { - State state = getDisplayState(); + if (DEBUG && !mState.stack.isEmpty()) { + Log.w(mTag, "Overwriting existing stack."); + } RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this); // Restore last stack for calling package @@ -528,7 +528,7 @@ abstract class BaseActivity extends Activity { mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0; final byte[] rawStack = cursor.getBlob( cursor.getColumnIndex(ResumeColumns.STACK)); - DurableUtils.readFromArray(rawStack, state.stack); + DurableUtils.readFromArray(rawStack, mState.stack); mRestoredStack = true; } } catch (IOException e) { @@ -539,13 +539,13 @@ abstract class BaseActivity extends Activity { if (mRestoredStack) { // Update the restored stack to ensure we have freshest data - final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(state); + final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState); try { - state.stack.updateRoot(matchingRoots); - state.stack.updateDocuments(getContentResolver()); + mState.stack.updateRoot(matchingRoots); + mState.stack.updateDocuments(getContentResolver()); } catch (FileNotFoundException e) { Log.w(mTag, "Failed to restore stack: " + e); - state.stack.reset(); + mState.stack.reset(); mRestoredStack = false; } } @@ -556,7 +556,7 @@ abstract class BaseActivity extends Activity { @Override protected void onPostExecute(Void result) { if (isDestroyed()) return; - getDisplayState().restored = true; + mState.restored = true; onCurrentDirectoryChanged(ANIM_NONE); onStackRestored(mRestoredStack, mExternal); } @@ -600,10 +600,9 @@ abstract class BaseActivity extends Activity { return; } - State state = getDisplayState(); - while (state.stack.size() > position + 1) { - state.stackTouched = true; - state.stack.pop(); + while (mState.stack.size() > position + 1) { + mState.stackTouched = true; + mState.stack.pop(); } onCurrentDirectoryChanged(ANIM_UP); } @@ -620,13 +619,12 @@ abstract class BaseActivity extends Activity { final class StackAdapter extends BaseAdapter { @Override public int getCount() { - return getDisplayState().stack.size(); + return mState.stack.size(); } @Override public DocumentInfo getItem(int position) { - State state = getDisplayState(); - return state.stack.get(state.stack.size() - position - 1); + return mState.stack.get(mState.stack.size() - position - 1); } @Override @@ -714,13 +712,12 @@ abstract class BaseActivity extends Activity { return; } - State state = getDisplayState(); - if (state.currentSearch != null) { + if (mState.currentSearch != null) { mMenu.expandActionView(); mView.setIconified(false); mView.clearFocus(); - mView.setQuery(state.currentSearch, false); + mView.setQuery(mState.currentSearch, false); } else { mView.clearFocus(); if (!mView.isIconified()) { @@ -746,7 +743,7 @@ abstract class BaseActivity extends Activity { mMenu.setVisible(visible); if (!visible) { - getDisplayState().currentSearch = null; + mState.currentSearch = null; } } @@ -764,7 +761,7 @@ abstract class BaseActivity extends Activity { } boolean isSearching() { - return getDisplayState().currentSearch != null; + return mState.currentSearch != null; } boolean isExpanded() { @@ -779,7 +776,7 @@ abstract class BaseActivity extends Activity { return false; } - getDisplayState().currentSearch = null; + mState.currentSearch = null; onCurrentDirectoryChanged(ANIM_NONE); return false; } @@ -798,7 +795,7 @@ abstract class BaseActivity extends Activity { mIgnoreNextCollapse = false; return true; } - getDisplayState().currentSearch = null; + mState.currentSearch = null; onCurrentDirectoryChanged(ANIM_NONE); return true; } @@ -806,7 +803,7 @@ abstract class BaseActivity extends Activity { @Override public boolean onQueryTextSubmit(String query) { mSearchExpanded = true; - getDisplayState().currentSearch = query; + mState.currentSearch = query; mView.clearFocus(); onCurrentDirectoryChanged(ANIM_NONE); return true; diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java index f1492dc7b185..66f8acd3fc50 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java @@ -20,6 +20,7 @@ import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.model.DocumentInfo.getCursorLong; import static com.android.documentsui.model.DocumentInfo.getCursorString; +import android.app.Activity; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; @@ -37,6 +38,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; +import android.support.design.widget.Snackbar; import android.text.format.DateUtils; import android.util.Log; import android.widget.Toast; @@ -60,7 +62,6 @@ public class CopyService extends IntentService { private static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL"; public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST"; - public static final String EXTRA_STACK = "com.android.documentsui.STACK"; public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE"; public static final String EXTRA_TRANSFER_MODE = "com.android.documentsui.TRANSFER_MODE"; @@ -107,21 +108,21 @@ public class CopyService extends IntentService { * @param srcDocs A list of src files to copy. * @param dstStack The copy destination stack. */ - public static void start(Context context, List<DocumentInfo> srcDocs, DocumentStack dstStack, + public static void start(Activity activity, List<DocumentInfo> srcDocs, DocumentStack dstStack, int mode) { - final Resources res = context.getResources(); - final Intent copyIntent = new Intent(context, CopyService.class); + final Resources res = activity.getResources(); + final Intent copyIntent = new Intent(activity, CopyService.class); copyIntent.putParcelableArrayListExtra( EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs)); - copyIntent.putExtra(EXTRA_STACK, (Parcelable) dstStack); + copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) dstStack); copyIntent.putExtra(EXTRA_TRANSFER_MODE, mode); int toastMessage = (mode == TRANSFER_MODE_COPY) ? R.plurals.copy_begin : R.plurals.move_begin; - Toast.makeText(context, + Snackbars.makeSnackbar(activity, res.getQuantityString(toastMessage, srcDocs.size(), srcDocs.size()), - Toast.LENGTH_SHORT).show(); - context.startService(copyIntent); + Snackbar.LENGTH_SHORT).show(); + activity.startService(copyIntent); } @Override @@ -140,7 +141,7 @@ public class CopyService extends IntentService { } final ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST); - final DocumentStack stack = intent.getParcelableExtra(EXTRA_STACK); + final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK); // Copy by default. final int transferMode = intent.getIntExtra(EXTRA_TRANSFER_MODE, TRANSFER_MODE_COPY); @@ -171,7 +172,7 @@ public class CopyService extends IntentService { Log.e(TAG, mFailedFiles.size() + " files failed to copy"); final Context context = getApplicationContext(); final Intent navigateIntent = new Intent(context, FilesActivity.class); - navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack); + navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack); navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY); navigateIntent.putExtra(EXTRA_TRANSFER_MODE, transferMode); navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, mFailedFiles); @@ -219,7 +220,7 @@ public class CopyService extends IntentService { final Context context = getApplicationContext(); final Intent navigateIntent = new Intent(context, FilesActivity.class); - navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack); + navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack); final String contentTitle = getString(copying ? R.string.copy_notification_title : R.string.move_notification_title); diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java index e408e6eb821d..c6425a6e41f2 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java @@ -32,6 +32,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; +import android.support.design.widget.Snackbar; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -39,7 +40,6 @@ import android.view.View; import android.widget.EditText; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; -import android.widget.Toast; import com.android.documentsui.model.DocumentInfo; @@ -147,7 +147,7 @@ public class CreateDirectoryFragment extends DialogFragment { // Navigate into newly created child mActivity.onDirectoryCreated(result); } else { - Toast.makeText(mActivity, R.string.create_error, Toast.LENGTH_SHORT).show(); + Snackbars.makeSnackbar(mActivity, R.string.create_error, Snackbar.LENGTH_SHORT).show(); } mActivity.setPending(false); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 5eacf2132a3c..0abbf4e4996f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -44,7 +44,6 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.Loader; -import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -54,7 +53,6 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.CancellationSignal; -import android.os.Handler; import android.os.Looper; import android.os.OperationCanceledException; import android.os.Parcelable; @@ -91,7 +89,6 @@ import android.view.ViewGroup; import android.view.ViewParent; import android.widget.ImageView; import android.widget.TextView; -import android.widget.Toast; import com.android.documentsui.BaseActivity.DocumentContext; import com.android.documentsui.MultiSelectManager.Selection; @@ -135,8 +132,6 @@ public class DirectoryFragment extends Fragment { private Model mModel; private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener(); - private final Handler mHandler = new Handler(Looper.getMainLooper()); - private View mEmptyView; private RecyclerView mRecView; @@ -218,8 +213,6 @@ public class DirectoryFragment extends Fragment { @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final Context context = inflater.getContext(); - final Resources res = context.getResources(); final View view = inflater.inflate(R.layout.fragment_directory, container, false); mMessageBar = MessageBar.create(getChildFragmentManager()); @@ -424,7 +417,7 @@ public class DirectoryFragment extends Fragment { } CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy, - (DocumentStack) data.getParcelableExtra(CopyService.EXTRA_STACK), + (DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK), data.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_NONE)); } @@ -679,6 +672,7 @@ public class DirectoryFragment extends Fragment { checkNotNull(mMenu); // Delegate update logic to our owning action, since specialized logic is desired. mFragmentTuner.updateActionMenu(mMenu, mType, mNoDeleteCount == 0); + Menus.disableHiddenItems(mMenu); } @Override @@ -800,13 +794,12 @@ public class DirectoryFragment extends Fragment { private void deleteDocuments(final Selection selected) { Context context = getActivity(); - ContentResolver resolver = context.getContentResolver(); String message = Shared.getQuantityString(context, R.plurals.deleting, selected.size()); mModel.markForDeletion(selected); - Activity activity = getActivity(); - Snackbar.make(this.getView(), message, Snackbar.LENGTH_LONG) + final Activity activity = getActivity(); + Snackbars.makeSnackbar(activity, message, Snackbar.LENGTH_LONG) .setAction( R.string.undo, new android.view.View.OnClickListener() { @@ -821,11 +814,11 @@ public class DirectoryFragment extends Fragment { mModel.undoDeletion(); } else { mModel.finalizeDeletion( - new Runnable() { + new Model.DeletionListener() { @Override - public void run() { - Snackbar.make( - DirectoryFragment.this.getView(), + public void onError() { + Snackbars.makeSnackbar( + activity, R.string.toast_failed_delete, Snackbar.LENGTH_LONG) .show(); @@ -1245,9 +1238,11 @@ public class DirectoryFragment extends Fragment { private void copyDocuments(final List<DocumentInfo> docs, final DocumentInfo destination) { if (!canCopy(docs, destination)) { - Toast.makeText( + Snackbars.makeSnackbar( getActivity(), - R.string.clipboard_files_cannot_paste, Toast.LENGTH_SHORT).show(); + R.string.clipboard_files_cannot_paste, + Snackbar.LENGTH_SHORT) + .show(); return; } @@ -1297,10 +1292,10 @@ public class DirectoryFragment extends Fragment { void onDocumentsReady(List<DocumentInfo> docs) { mClipper.clipDocuments(docs); Activity activity = getActivity(); - Toast.makeText(activity, + Snackbars.makeSnackbar(activity, activity.getResources().getQuantityString( R.plurals.clipboard_files_clipped, docs.size(), docs.size()), - Toast.LENGTH_SHORT).show(); + Snackbar.LENGTH_SHORT).show(); } }.execute(items); } @@ -1607,23 +1602,25 @@ public class DirectoryFragment extends Fragment { @Override public void updateActionMenu(Menu menu, int dirType, boolean canDelete) { + boolean copyEnabled = mManaging && dirType != TYPE_RECENT_OPEN; + // TODO: The selection needs to be deletable. + boolean moveEnabled = + SystemProperties.getBoolean("debug.documentsui.enable_move", false); + menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(copyEnabled); final MenuItem open = menu.findItem(R.id.menu_open); final MenuItem share = menu.findItem(R.id.menu_share); final MenuItem delete = menu.findItem(R.id.menu_delete); final MenuItem copyTo = menu.findItem(R.id.menu_copy_to); final MenuItem moveTo = menu.findItem(R.id.menu_move_to); - final MenuItem copyToClipboard = menu.findItem(R.id.menu_copy_to_clipboard); open.setVisible(!mManaging); share.setVisible(mManaging); delete.setVisible(mManaging && canDelete); - // Disable copying from the Recents view. - copyTo.setVisible(mManaging && dirType != TYPE_RECENT_OPEN); - moveTo.setVisible(SystemProperties.getBoolean("debug.documentsui.enable_move", false)); - - // Only shown in files mode. - copyToClipboard.setVisible(false); + copyTo.setVisible(copyEnabled); + copyTo.setEnabled(copyEnabled); + moveTo.setVisible(moveEnabled); + moveTo.setEnabled(moveEnabled); } @Override @@ -1637,13 +1634,14 @@ public class DirectoryFragment extends Fragment { @Override public void updateActionMenu(Menu menu, int dirType, boolean canDelete) { + menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(dirType != TYPE_RECENT_OPEN); + menu.findItem(R.id.menu_share).setVisible(true); menu.findItem(R.id.menu_delete).setVisible(canDelete); - menu.findItem(R.id.menu_copy_to_clipboard).setVisible(true); menu.findItem(R.id.menu_open).setVisible(false); - menu.findItem(R.id.menu_copy_to).setVisible(false); - menu.findItem(R.id.menu_move_to).setVisible(false); + menu.findItem(R.id.menu_copy_to).setVisible(true); + menu.findItem(R.id.menu_move_to).setVisible(true); } @Override @@ -1864,9 +1862,9 @@ public class DirectoryFragment extends Fragment { * @param view The view which will be used to interact with the user (e.g. surfacing * snackbars) for errors, info, etc. */ - void finalizeDeletion(Runnable errorCallback) { + void finalizeDeletion(DeletionListener listener) { final ContentResolver resolver = mContext.getContentResolver(); - DeleteFilesTask task = new DeleteFilesTask(resolver, errorCallback); + DeleteFilesTask task = new DeleteFilesTask(resolver, listener); task.execute(); } @@ -1876,16 +1874,16 @@ public class DirectoryFragment extends Fragment { */ private class DeleteFilesTask extends AsyncTask<Void, Void, List<DocumentInfo>> { private ContentResolver mResolver; - private Runnable mErrorCallback; + private DeletionListener mListener; /** * @param resolver A ContentResolver for performing the actual file deletions. * @param errorCallback A Runnable that is executed in the event that one or more errors * occured while copying files. Execution will occur on the UI thread. */ - public DeleteFilesTask(ContentResolver resolver, Runnable errorCallback) { + public DeleteFilesTask(ContentResolver resolver, DeletionListener listener) { mResolver = resolver; - mErrorCallback = errorCallback; + mListener = listener; } @Override @@ -1919,15 +1917,29 @@ public class DirectoryFragment extends Fragment { if (hadTrouble) { // TODO show which files failed? b/23720103 - mErrorCallback.run(); + mListener.onError(); if (DEBUG) Log.d(TAG, "Deletion task completed. Some deletions failed."); } else { if (DEBUG) Log.d(TAG, "Deletion task completed successfully."); } mMarkedForDeletion.clear(); + + mListener.onCompletion(); } } + static class DeletionListener { + /** + * Called when deletion has completed (regardless of whether an error occurred). + */ + void onCompletion() {} + + /** + * Called at the end of a deletion operation that produced one or more errors. + */ + void onError() {} + } + void addUpdateListener(UpdateListener listener) { checkState(mUpdateListener == null); mUpdateListener = listener; diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index 4658fe366bd8..6b428f58441c 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -41,6 +41,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.provider.DocumentsContract; +import android.support.design.widget.Snackbar; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -48,7 +49,6 @@ import android.view.View; import android.view.WindowManager; import android.widget.BaseAdapter; import android.widget.Spinner; -import android.widget.Toast; import android.widget.Toolbar; import com.android.documentsui.RecentsProvider.RecentColumns; @@ -89,8 +89,6 @@ public class DocumentsActivity extends BaseActivity { setTheme(R.style.DocumentsNonDialogTheme); } - final Context context = this; - if (mShowAsDialog) { mDrawer = DrawerController.createDummy(); @@ -314,42 +312,36 @@ public class DocumentsActivity extends BaseActivity { public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - final RootInfo root = getCurrentRoot(); final DocumentInfo cwd = getCurrentDirectory(); final MenuItem createDir = menu.findItem(R.id.menu_create_dir); final MenuItem grid = menu.findItem(R.id.menu_grid); final MenuItem list = menu.findItem(R.id.menu_list); - final MenuItem advanced = menu.findItem(R.id.menu_advanced); final MenuItem fileSize = menu.findItem(R.id.menu_file_size); final MenuItem settings = menu.findItem(R.id.menu_settings); - boolean fileSizeVisible = mState.showSize && !mState.forceSize; - if (mState.action == ACTION_CREATE + boolean recents = cwd == null; + boolean picking = mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE - || mState.action == ACTION_OPEN_COPY_DESTINATION) { - createDir.setVisible(cwd != null && cwd.isCreateSupported()); - mSearchManager.showMenu(false); - - // No display options in recent directories - if (cwd == null) { - grid.setVisible(false); - list.setVisible(false); - fileSizeVisible = false; - } + || mState.action == ACTION_OPEN_COPY_DESTINATION; - if (mState.action == ACTION_CREATE) { - final FragmentManager fm = getFragmentManager(); - SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported()); - } - } else { - createDir.setVisible(false); - } + createDir.setVisible(picking && !recents && cwd.isCreateSupported()); + mSearchManager.showMenu(!picking); - advanced.setVisible(!mState.forceAdvanced); - fileSize.setVisible(fileSizeVisible); + // No display options in recent directories + grid.setVisible(!(picking && recents)); + list.setVisible(!(picking && recents)); + + fileSize.setVisible(fileSize.isVisible() && !picking); settings.setVisible(false); + if (mState.action == ACTION_CREATE) { + final FragmentManager fm = getFragmentManager(); + SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported()); + } + + Menus.disableHiddenItems(menu); + return true; } @@ -510,7 +502,7 @@ public class DocumentsActivity extends BaseActivity { } else if (mState.action == ACTION_OPEN_COPY_DESTINATION) { // Picking a copy destination is only used internally by us, so we // don't need to extend permissions to the caller. - intent.putExtra(CopyService.EXTRA_STACK, (Parcelable) mState.stack); + intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack); intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mState.transferMode); } else { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION @@ -611,8 +603,8 @@ public class DocumentsActivity extends BaseActivity { if (result != null) { onTaskFinished(result); } else { - Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT) - .show(); + Snackbars.makeSnackbar( + DocumentsActivity.this, R.string.save_error, Snackbar.LENGTH_SHORT).show(); } setPending(false); diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java index ea0c18acf26f..120f6106a7a0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java @@ -66,7 +66,7 @@ public class FailureDialogFragment extends DialogFragment if (whichButton == DialogInterface.BUTTON_POSITIVE) { CopyService.start(getActivity(), mFailedSrcList, (DocumentStack) getActivity().getIntent().getParcelableExtra( - CopyService.EXTRA_STACK), + Shared.EXTRA_STACK), mTransferMode); } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java index e8d10888af47..70ddf5916f2c 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java @@ -30,7 +30,10 @@ import android.content.ContentValues; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.os.Parcelable; +import android.provider.DocumentsContract; import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; @@ -38,7 +41,6 @@ import android.view.MenuItem; import android.view.View; import android.widget.BaseAdapter; import android.widget.Spinner; -import android.widget.Toast; import android.widget.Toolbar; import com.android.documentsui.RecentsProvider.ResumeColumns; @@ -85,32 +87,49 @@ public class FilesActivity extends BaseActivity { mDrawer = DrawerController.create(this); RootsFragment.show(getFragmentManager(), null); - if (!mState.restored) { - Intent intent = getIntent(); - Uri rootUri = intent.getData(); - // If we've got a specific root to display, restore that root using a dedicated - // authority. That way a misbehaving provider won't result in an ANR. - if (rootUri != null && !LauncherActivity.isLaunchUri(rootUri)) { - new RestoreRootTask(rootUri).executeOnExecutor( - ProviderExecutor.forAuthority(rootUri.getAuthority())); + if (mState.restored) { + onCurrentDirectoryChanged(ANIM_NONE); + } else { + Intent intent = getIntent(); + Uri uri = intent.getData(); + + // If a non-empty stack is present in our state it was read (presumably) + // from EXTRA_STACK intent extra. In this case, we'll skip other means of + // loading or restoring the stack. + if (!mState.stack.isEmpty()) { + // When restoring from a stack, if a URI is present, it should only ever + // be a launch URI. Launch URIs support sensible activity management, but + // don't specify an real content target. + if (uri != null) { + checkState(LauncherActivity.isLaunchUri(uri)); + } + onCurrentDirectoryChanged(ANIM_NONE); + } else if (DocumentsContract.isRootUri(this, uri)) { + // If we've got a specific root to display, restore that root using a dedicated + // authority. That way a misbehaving provider won't result in an ANR. + new RestoreRootTask(uri).executeOnExecutor( + ProviderExecutor.forAuthority(uri.getAuthority())); } else { + // Finally, we try to restore a stack from recents. new RestoreStackTask().execute(); } + // TODO: Ensure we're handling CopyService errors correctly across all activities. // Show a failure dialog if there was a failed operation. - final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK); final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0); final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_NONE); if (failure != 0) { final ArrayList<DocumentInfo> failedSrcList = intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST); - FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList, dstStack, + FailureDialogFragment.show( + getFragmentManager(), + failure, + failedSrcList, + mState.stack, transferMode); } - } else { - onCurrentDirectoryChanged(ANIM_NONE); } } @@ -126,7 +145,7 @@ public class FilesActivity extends BaseActivity { // Options specific to the DocumentsActivity. checkArgument(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY)); - final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK); + final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK); if (stack != null) { state.stack = stack; } @@ -206,30 +225,23 @@ public class FilesActivity extends BaseActivity { @Override public boolean onPrepareOptionsMenu(Menu menu) { - boolean shown = super.onPrepareOptionsMenu(menu); - - menu.findItem(R.id.menu_file_size).setVisible(true); - menu.findItem(R.id.menu_advanced).setVisible(true); + super.onPrepareOptionsMenu(menu); final MenuItem createDir = menu.findItem(R.id.menu_create_dir); final MenuItem newWindow = menu.findItem(R.id.menu_new_window); final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard); - boolean canCreateDir = canCreateDirectory(); - createDir.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - createDir.setVisible(canCreateDir); - createDir.setEnabled(canCreateDir); + createDir.setVisible(true); + createDir.setEnabled(canCreateDirectory()); + + pasteFromCb.setEnabled(mClipper.hasItemsToPaste()); newWindow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); newWindow.setVisible(mProductivityDevice); - newWindow.setEnabled(mProductivityDevice); - pasteFromCb.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - pasteFromCb.setVisible(true); - pasteFromCb.setEnabled(mClipper.hasItemsToPaste()); - - return shown; + Menus.disableHiddenItems(menu, pasteFromCb); + return true; } @Override @@ -240,7 +252,7 @@ public class FilesActivity extends BaseActivity { showCreateDirectoryDialog(); return true; case R.id.menu_new_window: - startActivity(LauncherActivity.createLaunchIntent(this)); + createNewWindow(); return true; case R.id.menu_paste_from_clipboard: DirectoryFragment dir = DirectoryFragment.get(getFragmentManager()); @@ -252,6 +264,12 @@ public class FilesActivity extends BaseActivity { return super.onOptionsItemSelected(item); } + private void createNewWindow() { + Intent intent = LauncherActivity.createLaunchIntent(this); + intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack); + startActivity(intent); + } + @Override void onDirectoryChanged(int anim) { final FragmentManager fm = getFragmentManager(); @@ -322,7 +340,7 @@ public class FilesActivity extends BaseActivity { try { startActivity(intent); } catch (ActivityNotFoundException ex2) { - Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show(); + Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show(); } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java index 4754899b95cc..14a33f9c1cc1 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java @@ -31,12 +31,12 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.provider.DocumentsContract; +import android.support.design.widget.Snackbar; import android.util.Log; import android.view.Menu; import android.view.View; import android.widget.BaseAdapter; import android.widget.Spinner; -import android.widget.Toast; import android.widget.Toolbar; import com.android.documentsui.RecentsProvider.ResumeColumns; @@ -140,6 +140,7 @@ public class ManageRootActivity extends BaseActivity { @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); + Menus.disableHiddenItems(menu); return true; } @@ -184,7 +185,8 @@ public class ManageRootActivity extends BaseActivity { try { startActivity(view); } catch (ActivityNotFoundException ex2) { - Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show(); + Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT) + .show(); } } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/Menus.java b/packages/DocumentsUI/src/com/android/documentsui/Menus.java new file mode 100644 index 000000000000..3f43a3d30c8d --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/Menus.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import android.view.Menu; +import android.view.MenuItem; + +final class Menus { + + private Menus() {} + + /** + * Disables hidden menu items so that they are not invokable via command shortcuts + */ + static void disableHiddenItems(Menu menu, MenuItem... exclusions) { + for (int i = 0; i < menu.size(); i++) { + MenuItem item = menu.getItem(i); + if (item.isVisible()) { + continue; + } + if (contains(exclusions, item)) { + continue; + } + item.setEnabled(false); + } + } + + private static boolean contains(MenuItem[] exclusions, MenuItem item) { + for (int x = 0; x < exclusions.length; x++) { + if (exclusions[x] == item) { + return true; + } + } + return false; + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java index 9c884d4ef8c6..a4d6dc57cd34 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java +++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java @@ -18,12 +18,11 @@ package com.android.documentsui; import android.content.Context; -/** - * @hide - */ +/** @hide */ public final class Shared { public static final boolean DEBUG = true; public static final String TAG = "Documents"; + public static final String EXTRA_STACK = "com.android.documentsui.STACK"; /** * Generates a formatted quantity string. diff --git a/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java new file mode 100644 index 000000000000..f48b298a4f2f --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.app.Activity; +import android.support.design.widget.Snackbar; +import android.view.View; + +final class Snackbars { + private Snackbars() {} + + public static final Snackbar makeSnackbar(Activity activity, int messageId, int duration) { + return Snackbars.makeSnackbar(activity, activity.getResources().getText(messageId), duration); + } + + public static final Snackbar makeSnackbar(Activity activity, CharSequence message, int duration) + { + final View view = checkNotNull(activity.findViewById(R.id.coordinator_layout)); + return Snackbar.make(view, message, duration); + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java index 568e9e403960..fc42c3b12545 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java @@ -425,7 +425,7 @@ public class CopyTest extends ServiceTestCase<CopyService> { stack.push(DocumentInfo.fromUri(mResolver, dst)); final Intent copyIntent = new Intent(mContext, CopyService.class); copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs); - copyIntent.putExtra(CopyService.EXTRA_STACK, (Parcelable) stack); + copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack); // startService(copyIntent); return copyIntent; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java index 1895a6e66450..98ffb77c4cd6 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java @@ -34,6 +34,9 @@ import com.android.documentsui.MultiSelectManager.Selection; import com.android.documentsui.model.DocumentInfo; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + public class DirectoryFragmentModelTest extends AndroidTestCase { @@ -77,14 +80,6 @@ public class DirectoryFragmentModelTest extends AndroidTestCase { delete(2, 4); assertEquals(ITEM_COUNT - 2, model.getItemCount()); - - // Finalize the deletion. Provide a callback that just ignores errors. - model.finalizeDeletion( - new Runnable() { - @Override - public void run() {} - }); - assertEquals(ITEM_COUNT - 2, model.getItemCount()); } // Tests that the item count is correct after a deletion is undone. @@ -95,7 +90,6 @@ public class DirectoryFragmentModelTest extends AndroidTestCase { // Undo the deletion model.undoDeletion(); assertEquals(ITEM_COUNT, model.getItemCount()); - } // Tests that the right things are marked for deletion. @@ -125,6 +119,15 @@ public class DirectoryFragmentModelTest extends AndroidTestCase { assertEquals("0", docs.get(0).documentId); assertEquals("1", docs.get(1).documentId); assertEquals("4", docs.get(2).documentId); + + TestDeletionListener testListener = new TestDeletionListener(); + model.finalizeDeletion(testListener); + testListener.waitForDone(); + + docs = getDocumentInfo(0, 1, 2); + assertEquals("0", docs.get(0).documentId); + assertEquals("1", docs.get(1).documentId); + assertEquals("2", docs.get(2).documentId); } // Tests that Model.getItem returns the right items after a deletion is undone. @@ -176,4 +179,20 @@ public class DirectoryFragmentModelTest extends AndroidTestCase { return null; } } + + private static class TestDeletionListener extends Model.DeletionListener { + final CountDownLatch mSignal = new CountDownLatch(1); + + @Override + public void onCompletion() { + mSignal.countDown(); + } + + public void waitForDone() { + try { + boolean timeout = mSignal.await(10, TimeUnit.SECONDS); + assertTrue("Timed out waiting for deletion completion", timeout); + } catch (InterruptedException e) {} + } + } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java index 25d4ed4183f0..2447469f9c64 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.support.v7.widget.RecyclerView; +import android.test.AndroidTestCase; import android.util.SparseBooleanArray; import android.view.MotionEvent; import android.view.View; @@ -27,8 +28,6 @@ import android.view.ViewGroup; import com.android.documentsui.MultiSelectManager.Selection; -import org.junit.Before; -import org.junit.Test; import org.mockito.Mockito; import java.util.ArrayList; @@ -36,7 +35,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -public class MultiSelectManagerTest { +public class MultiSelectManagerTest extends AndroidTestCase { private static final List<String> items; static { @@ -54,7 +53,6 @@ public class MultiSelectManagerTest { private TestCallback mCallback; private EventHelper mEventHelper; - @Before public void setUp() throws Exception { mAdapter = new TestAdapter(items); mCallback = new TestCallback(); @@ -63,65 +61,61 @@ public class MultiSelectManagerTest { mManager.addCallback(mCallback); } - @Test - public void mouseClick_StartsSelectionMode() { + public void testMouseClick_StartsSelectionMode() { click(7); assertSelection(7); } - @Test - public void mouseClick_ShiftClickExtendsSelection() { + public void testMouseClick_NotifiesSelectionChanged() { + click(7); + mCallback.assertSelectionChanged(); + } + + public void testMouseClick_ShiftClickExtendsSelection() { longPress(7); shiftClick(11); assertRangeSelection(7, 11); } - @Test - public void mouseClick_NoPosition_ClearsSelection() { + public void testMouseClick_NoPosition_ClearsSelection() { longPress(7); click(11); click(RecyclerView.NO_POSITION); assertSelection(); } - @Test - public void setSelectionFocusBegin() { + public void testSetSelectionFocusBegin() { mManager.setItemSelected(7, true); mManager.setSelectionFocusBegin(7); shiftClick(11); assertRangeSelection(7, 11); } - @Test - public void longPress_StartsSelectionMode() { + public void testLongPress_StartsSelectionMode() { longPress(7); assertSelection(7); } - @Test - public void longPress_SecondPressExtendsSelection() { + public void testLongPress_SecondPressExtendsSelection() { longPress(7); longPress(99); assertSelection(7, 99); } - @Test - public void singleTapUp_UnselectsSelectedItem() { + public void testSingleTapUp_UnselectsSelectedItem() { longPress(7); tap(7); assertSelection(); } - @Test - public void singleTapUp_NoPosition_ClearsSelection() { + public void testSingleTapUp_NoPosition_ClearsSelection() { longPress(7); tap(11); tap(RecyclerView.NO_POSITION); assertSelection(); } - @Test - public void singleTapUp_ExtendsSelection() { + public void testSingleTapUp_ExtendsSelection() { longPress(99); tap(7); tap(13); @@ -129,30 +123,26 @@ public class MultiSelectManagerTest { assertSelection(7, 99, 13, 129899); } - @Test - public void singleTapUp_ShiftCreatesRangeSelection() { + public void testSingleTapUp_ShiftCreatesRangeSelection() { longPress(7); shiftTap(17); assertRangeSelection(7, 17); } - @Test - public void singleTapUp_ShiftCreatesRangeSeletion_Backwards() { + public void testSingleTapUp_ShiftCreatesRangeSeletion_Backwards() { longPress(17); shiftTap(7); assertRangeSelection(7, 17); } - @Test - public void singleTapUp_SecondShiftClickExtendsSelection() { + public void testSingleTapUp_SecondShiftClickExtendsSelection() { longPress(7); shiftTap(11); shiftTap(17); assertRangeSelection(7, 17); } - @Test - public void singleTapUp_MultipleContiguousRangesSelected() { + public void testSingleTapUp_MultipleContiguousRangesSelected() { longPress(7); shiftTap(11); tap(20); @@ -162,16 +152,14 @@ public class MultiSelectManagerTest { assertSelectionSize(11); } - @Test - public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() { + public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() { longPress(7); shiftTap(17); shiftTap(10); assertRangeSelection(7, 10); } - @Test - public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() { + public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() { mManager.onLongPress(TestInputEvent.tap(17)); shiftTap(7); shiftTap(14); @@ -179,16 +167,14 @@ public class MultiSelectManagerTest { } - @Test - public void singleTapUp_ShiftReversesSelectionDirection() { + public void testSingleTapUp_ShiftReversesSelectionDirection() { longPress(7); shiftTap(17); shiftTap(0); assertRangeSelection(0, 7); } - @Test - public void singleSelectMode() { + public void testSingleSelectMode() { mManager = new MultiSelectManager(mAdapter, mEventHelper, MultiSelectManager.MODE_SINGLE); mManager.addCallback(mCallback); longPress(20); @@ -196,8 +182,7 @@ public class MultiSelectManagerTest { assertSelection(13); } - @Test - public void singleSelectMode_ShiftTap() { + public void testSingleSelectMode_ShiftTap() { mManager = new MultiSelectManager(mAdapter, mEventHelper, MultiSelectManager.MODE_SINGLE); mManager.addCallback(mCallback); longPress(13); @@ -205,8 +190,7 @@ public class MultiSelectManagerTest { assertSelection(20); } - @Test - public void provisionaSelection() { + public void testProvisionalSelection() { Selection s = mManager.getSelection(); assertSelection(); @@ -298,6 +282,7 @@ public class MultiSelectManagerTest { Set<Integer> ignored = new HashSet<>(); private int mLastChangedPosition; private boolean mLastChangedSelected; + private boolean mSelectionChanged = false; @Override public void onItemStateChanged(int position, boolean selected) { @@ -311,7 +296,13 @@ public class MultiSelectManagerTest { } @Override - public void onSelectionChanged() {} + public void onSelectionChanged() { + mSelectionChanged = true; + } + + void assertSelectionChanged() { + assertTrue(mSelectionChanged); + } } private static final class TestHolder extends RecyclerView.ViewHolder { diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java index 87d7e15a4072..aa50b48e6c16 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java @@ -22,14 +22,12 @@ import static org.junit.Assert.assertTrue; import android.graphics.Point; import android.graphics.Rect; import android.support.v7.widget.RecyclerView.OnScrollListener; +import android.test.AndroidTestCase; import android.util.SparseBooleanArray; import com.android.documentsui.MultiSelectManager.GridModel; -import org.junit.After; -import org.junit.Test; - -public class MultiSelectManager_GridModelTest { +public class MultiSelectManager_GridModelTest extends AndroidTestCase { private static final int VIEW_PADDING_PX = 5; private static final int CHILD_VIEW_EDGE_PX = 100; @@ -53,14 +51,13 @@ public class MultiSelectManager_GridModelTest { }); } - @After + @Override public void tearDown() { model = null; helper = null; lastSelection = null; } - @Test public void testSelectionLeftOfItems() { setUp(20, 5); model.startSelection(new Point(0, 10)); @@ -69,7 +66,6 @@ public class MultiSelectManager_GridModelTest { assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin()); } - @Test public void testSelectionRightOfItems() { setUp(20, 4); model.startSelection(new Point(viewWidth - 1, 10)); @@ -78,7 +74,6 @@ public class MultiSelectManager_GridModelTest { assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin()); } - @Test public void testSelectionAboveItems() { setUp(20, 4); model.startSelection(new Point(10, 0)); @@ -87,7 +82,6 @@ public class MultiSelectManager_GridModelTest { assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin()); } - @Test public void testSelectionBelowItems() { setUp(5, 4); model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1)); @@ -96,7 +90,6 @@ public class MultiSelectManager_GridModelTest { assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin()); } - @Test public void testVerticalSelectionBetweenItems() { setUp(20, 4); model.startSelection(new Point(106, 0)); @@ -105,7 +98,6 @@ public class MultiSelectManager_GridModelTest { assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin()); } - @Test public void testHorizontalSelectionBetweenItems() { setUp(20, 4); model.startSelection(new Point(0, 105)); @@ -114,7 +106,6 @@ public class MultiSelectManager_GridModelTest { assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin()); } - @Test public void testGrowingAndShrinkingSelection() { setUp(20, 4); model.startSelection(new Point(0, 0)); @@ -145,7 +136,6 @@ public class MultiSelectManager_GridModelTest { assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin()); } - @Test public void testSelectionMovingAroundOrigin() { setUp(16, 4); model.startSelection(new Point(210, 210)); @@ -160,7 +150,6 @@ public class MultiSelectManager_GridModelTest { assertEquals(10, model.getPositionNearestOrigin()); } - @Test public void testScrollingBandSelect() { setUp(40, 4); model.startSelection(new Point(0, 0)); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java index 51b542b2ae8c..eddf4ef6fede 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java @@ -18,16 +18,16 @@ package com.android.documentsui; import static org.junit.Assert.*; +import android.test.AndroidTestCase; + import com.android.documentsui.MultiSelectManager.Selection; -import org.junit.Before; -import org.junit.Test; -public class MultiSelectManager_SelectionTest { +public class MultiSelectManager_SelectionTest extends AndroidTestCase{ private Selection selection; - @Before + @Override public void setUp() throws Exception { selection = new Selection(); selection.add(3); @@ -35,8 +35,7 @@ public class MultiSelectManager_SelectionTest { selection.add(9); } - @Test - public void add() { + public void testAdd() { // We added in setUp. assertEquals(3, selection.size()); assertContains(3); @@ -44,29 +43,25 @@ public class MultiSelectManager_SelectionTest { assertContains(9); } - @Test - public void remove() { + public void testRemove() { selection.remove(3); selection.remove(5); assertEquals(1, selection.size()); assertContains(9); } - @Test - public void clear() { + public void testClear() { selection.clear(); assertEquals(0, selection.size()); } - @Test - public void isEmpty() { + public void testIsEmpty() { assertTrue(new Selection().isEmpty()); selection.clear(); assertTrue(selection.isEmpty()); } - @Test - public void sizeAndGet() { + public void testSizeAndGet() { Selection other = new Selection(); for (int i = 0; i < selection.size(); i++) { other.add(selection.get(i)); @@ -74,13 +69,11 @@ public class MultiSelectManager_SelectionTest { assertEquals(selection.size(), other.size()); } - @Test - public void equalsSelf() { + public void testEqualsSelf() { assertEquals(selection, selection); } - @Test - public void equalsOther() { + public void testEqualsOther() { Selection other = new Selection(); other.add(3); other.add(5); @@ -89,23 +82,20 @@ public class MultiSelectManager_SelectionTest { assertEquals(selection.hashCode(), other.hashCode()); } - @Test - public void equalsCopy() { + public void testEqualsCopy() { Selection other = new Selection(); other.copyFrom(selection); assertEquals(selection, other); assertEquals(selection.hashCode(), other.hashCode()); } - @Test - public void notEquals() { + public void testNotEquals() { Selection other = new Selection(); other.add(789); assertFalse(selection.equals(other)); } - @Test - public void expandBefore() { + public void testExpandBefore() { selection.expand(2, 10); assertEquals(3, selection.size()); assertContains(13); @@ -113,8 +103,7 @@ public class MultiSelectManager_SelectionTest { assertContains(19); } - @Test - public void expandAfter() { + public void testExpandAfter() { selection.expand(10, 10); assertEquals(3, selection.size()); assertContains(3); @@ -122,8 +111,7 @@ public class MultiSelectManager_SelectionTest { assertContains(9); } - @Test - public void expandSplit() { + public void testExpandSplit() { selection.expand(5, 10); assertEquals(3, selection.size()); assertContains(3); @@ -131,8 +119,7 @@ public class MultiSelectManager_SelectionTest { assertContains(19); } - @Test - public void expandEncompased() { + public void testExpandEncompased() { selection.expand(2, 10); assertEquals(3, selection.size()); assertContains(13); @@ -140,8 +127,7 @@ public class MultiSelectManager_SelectionTest { assertContains(19); } - @Test - public void collapseBefore() { + public void testCollapseBefore() { selection.collapse(0, 2); assertEquals(3, selection.size()); assertContains(1); @@ -149,8 +135,7 @@ public class MultiSelectManager_SelectionTest { assertContains(7); } - @Test - public void collapseAfter() { + public void testCollapseAfter() { selection.collapse(10, 10); assertEquals(3, selection.size()); assertContains(3); @@ -158,8 +143,7 @@ public class MultiSelectManager_SelectionTest { assertContains(9); } - @Test - public void collapseAcross() { + public void testCollapseAcross() { selection.collapse(0, 10); assertEquals(0, selection.size()); } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java deleted file mode 100644 index be3f2515057e..000000000000 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.documentsui; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ - MultiSelectManager_GridModelTest.class, - MultiSelectManager_SelectionTest.class, - MultiSelectManagerTest.class -}) - -/** - * This test suite can be run using the "art" runtime (which can be built - * via the `build-art-host` target.) You'll also need to "mma -j32" the - * DocumentsUI package to ensure all deps are built. - * - * <p>Once the dependencies have been built, the tests can be executed as follows: - * - * <pre> - * CP=$OUT/system/framework/framework.jar:\ - * $OUT/system/framework/core-junit.jar:\ - * $OUT/system/app/DocumentsUI/DocumentsUI.apk:\ - * $OUT/data/app/DocumentsUITests/DocumentsUITests.apk - * - * art -cp $CP org.junit.runner.JUnitCore com.android.documentsui.UnitTests - * </pre> - */ -public class UnitTests {} diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 393771a9b447..18335b6586a8 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -38,6 +38,7 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.provider.DocumentsProvider; +import android.provider.MediaStore; import android.text.TextUtils; import android.util.ArrayMap; import android.util.DebugUtils; @@ -380,12 +381,31 @@ public class ExternalStorageProvider extends DocumentsProvider { @Override public void deleteDocument(String docId) throws FileNotFoundException { final File file = getFileForDocId(docId); - if (file.isDirectory()) { + final boolean isDirectory = file.isDirectory(); + if (isDirectory) { FileUtils.deleteContents(file); } if (!file.delete()) { throw new IllegalStateException("Failed to delete " + file); } + + final ContentResolver resolver = getContext().getContentResolver(); + final Uri externalUri = MediaStore.Files.getContentUri("external"); + + // Remove media store entries for any files inside this directory, using + // path prefix match. Logic borrowed from MtpDatabase. + if (isDirectory) { + final String path = file.getAbsolutePath() + "/"; + resolver.delete(externalUri, + "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)", + new String[] { path + "%", Integer.toString(path.length()), path }); + } + + // Remove media store entry for this exact file. + final String path = file.getAbsolutePath(); + resolver.delete(externalUri, + "_data LIKE ?1 AND lower(_data)=lower(?2)", + new String[] { path, path }); } @Override diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 1cd2908e77e9..c324abd0dd22 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -204,9 +204,6 @@ <!-- Default for Settings.Secure.WAKE_GESTURE_ENABLED --> <bool name="def_wake_gesture_enabled">true</bool> - <!-- Default for Settings.Global.GUEST_USER_ENABLED --> - <bool name="def_guest_user_enabled">true</bool> - <!-- Default state of tap to wake --> <bool name="def_double_tap_to_wake">true</bool> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index ee296d90608f..d4e428e280b6 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -1693,20 +1693,7 @@ class DatabaseHelper extends SQLiteOpenHelper { } if (upgradeVersion < 105) { - if (mUserHandle == UserHandle.USER_SYSTEM) { - db.beginTransaction(); - SQLiteStatement stmt = null; - try { - stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)" - + " VALUES(?,?);"); - loadBooleanSetting(stmt, Settings.Global.GUEST_USER_ENABLED, - R.bool.def_guest_user_enabled); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - if (stmt != null) stmt.close(); - } - } + // No-op: GUEST_USER_ENABLED setting was removed upgradeVersion = 105; } @@ -2705,8 +2692,6 @@ class DatabaseHelper extends SQLiteOpenHelper { loadSetting(stmt, Settings.Global.DEVICE_NAME, getDefaultDeviceName()); - loadBooleanSetting(stmt, Settings.Global.GUEST_USER_ENABLED, - R.bool.def_guest_user_enabled); loadSetting(stmt, Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON); diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index 47e24e8e82f3..368f9f79a20b 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -13,3 +13,11 @@ -keep class com.android.systemui.statusbar.phone.PhoneStatusBar -keep class com.android.systemui.statusbar.tv.TvStatusBar -keep class com.android.systemui.recents.* + +-keepclassmembers class ** { + public void onBusEvent(**); + public void onInterprocessBusEvent(**); +} +-keepclassmembers class ** extends **.EventBus$InterprocessEvent { + public <init>(android.os.Bundle); +}
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_qs_lock.xml b/packages/SystemUI/res/drawable/ic_qs_lock.xml new file mode 100644 index 000000000000..204af7e81f4c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_lock.xml @@ -0,0 +1,25 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + + <path + android:fillColor="@color/keyguard_affordance" + android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_lock_open.xml b/packages/SystemUI/res/drawable/ic_qs_lock_open.xml new file mode 100644 index 000000000000..c877f063b7a8 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_lock_open.xml @@ -0,0 +1,25 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + + <path + android:fillColor="@color/keyguard_affordance" + android:pathData="M12.0,17.0c1.1,0.0 2.0,-0.9 2.0,-2.0s-0.9,-2.0 -2.0,-2.0c-1.1,0.0 -2.0,0.9 -2.0,2.0S10.9,17.0 12.0,17.0zM18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l1.9,0.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM18.0,20.0L6.0,20.0L6.0,10.0l12.0,0.0L18.0,20.0z"/> +</vector> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 1473f24bae42..c7c7f1a7e53b 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Net\nprioriteit"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Net\nwekkers"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laai tans (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tot vol)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Laai tans vinnig (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tot vol)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Laai tans stadig (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tot vol)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Wissel gebruiker"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Wissel gebruiker, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 7ab6af9cb72d..f49f5bc60c11 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ቅድሚያ ተሰጪ\nብቻ"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ማንቂያዎች\nብቻ"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ሃይል በመሙላት ላይ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> እስከሚሞላ ድረስ)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ኃይል በፍጥነት በመሙላት ላይ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> እስከሚሞላ ድረስ)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ኃይል በዝግታ በመሙላት ላይ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> እስከሚሞላ ድረስ)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ተጠቃሚ ቀይር"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ተጠቃሚ ይለውጡ፣ የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index bd026501dc45..aaf20cce429a 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -329,6 +329,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"الأولوية \nفقط"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"التنبيهات\nفقط"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"جارٍ الشحن (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> حتى الامتلاء)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"جارٍ الشحن سريعًا (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> حتى الاكتمال)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"جارٍ الشحن ببطء (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> حتى الاكتمال)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"تبديل المستخدم"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"تبديل المستخدم، المستخدم الحالي <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"المستخدم الحالي <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml index a0d7d2b21c7c..0efc82b03b2b 100644 --- a/packages/SystemUI/res/values-az-rAZ/strings.xml +++ b/packages/SystemUI/res/values-az-rAZ/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Yalnız\nprioritet"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Yalnız\nalarmlar"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Qidalanır (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> dolana kimi)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Sürətli qidalanır (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> dolana kimi)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Ləng qidalanır (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> dolana kimi)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Switch user"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"İstifadəçiləri dəyişin, indiki istifadəçi: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Cari istifadəçi <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index a14d0fc70c07..680577947a35 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Само\nс приоритет"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Само\nбудилници"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарежда се (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до пълно зареждане)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Зарежда се бързо (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до пълно зареждане)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Зарежда се бавно (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до пълно зареждане)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Превключване между потребителите"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Превключване на потребителя – текущият е <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Текущ потребител – <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml index e819d54e0901..c0ffe2c720e6 100644 --- a/packages/SystemUI/res/values-bn-rBD/strings.xml +++ b/packages/SystemUI/res/values-bn-rBD/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"শুধুমাত্র\nঅগ্রাধিকার"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"শুধুমাত্র\nঅ্যালার্মগুলি"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"চার্জ হচ্ছে (পূর্ণ হতে <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> সময় বাকি)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"দ্রুত চার্জ হচ্ছে (পূর্ণ হতে <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> সময় বাকি)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ধীরে ধীরে চার্জ হচ্ছে (পূর্ণ হতে <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> সময় বাকি)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ব্যবহারকারী পাল্টে দিন"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ব্যবহারকারী পাল্টান, বর্তমান ব্যবহারকারী <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"<xliff:g id="CURRENT_USER_NAME">%s</xliff:g> হল বর্তমান ব্যবহারকারী"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 8b2951a80a30..c955f34ad8c3 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Només\ninterr. prior."</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Només\nalarmes"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregant (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar la càrrega)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Càrrega ràpida (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Càrrega lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Canvia d\'usuari"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Canvia l\'usuari. Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index a73947fdfb05..18fd994f232e 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -329,6 +329,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Pouze\nprioritní"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Pouze\nbudíky"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nabíjení (plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Rychlé nabíjení (plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Pomalé nabíjení (plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Přepnout uživatele"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Přepnout uživatele, aktuální uživatel: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Aktuální uživatel <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 91d975bf08f7..5a2dd3300b23 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Kun\nprioritet"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Kun\nalarmer"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Oplader (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Hurtig opladning (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Langsom opladning (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Skift bruger"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Skift bruger. Nuværende bruger er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Nuværende bruger: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index a659077bd821..a8143a461090 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Nur\nwichtige"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Nur\nWecker"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Wird aufgeladen (voll in <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Wird schnell aufgeladen (voll in <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Wird langsam aufgeladen (voll in <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Nutzer wechseln"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Nutzer wechseln. Aktueller Nutzer: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Aktueller Nutzer <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index bcfc4589a6b8..6031d3bb4fff 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Μόνο\nπροτεραιότητας"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Μόνο\nειδοποιήσεις"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Φόρτιση (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> για πλήρη φόρτιση)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Γρήγορη φόρτιση (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> για πλήρη φόρτιση)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Αργή φόρτιση (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> για πλήρη φόρτιση)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Εναλλαγή χρήστη"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Εναλλαγή χρήστη, τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index fb781d4d91ea..bef7661a52ef 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priority\nonly"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarms\nonly"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charging rapidly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charging slowly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Switch user"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Switch user, current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index fb781d4d91ea..bef7661a52ef 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priority\nonly"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarms\nonly"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charging rapidly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charging slowly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Switch user"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Switch user, current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index fb781d4d91ea..bef7661a52ef 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priority\nonly"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarms\nonly"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charging rapidly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charging slowly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Switch user"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Switch user, current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 10834a23baa7..15902653a7f2 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Solo\nprioridad"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Solo\nalarmas"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (faltan <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Carga rápida (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar la carga)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Carga lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar la carga)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Cambiar usuario"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Cambiar de usuario (usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"El usuario actual es <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 37c81f4936c0..082e7624cbd0 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Solo\ncon prioridad"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Solo\nalarmas"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Cargando rápidamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hasta completar)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Cargando lentamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hasta completar)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Cambiar de usuario"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Cambiar de usuario (usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml index f4218a3a6eb3..4add1471be93 100644 --- a/packages/SystemUI/res/values-et-rEE/strings.xml +++ b/packages/SystemUI/res/values-et-rEE/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Ainult\nprioriteetsed"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Ainult\nalarmid"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laadimine (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>, kuni seade on täis)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Kiirlaadimine (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>, kuni seade on täis)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Aeglane laadimine (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>, kuni seade on täis)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Kasutaja vahetamine"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Kasutaja vahetamine, praegune kasutaja: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Praegune kasutaja <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml index 0e5532cd9db7..4f5c9f26799e 100644 --- a/packages/SystemUI/res/values-eu-rES/strings.xml +++ b/packages/SystemUI/res/values-eu-rES/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Lehentasunezkoak\nsoilik"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarmak\nsoilik"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Kargatzen (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> guztiz kargatu arte)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Bizkor kargatzen (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> guztiz kargatu arte)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Mantso kargatzen (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> guztiz kargatu arte)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Aldatu erabiltzailea"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Aldatu erabiltzailez. <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> da saioa hasita duena."</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Uneko erabiltzailea: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 147cbe4a5c80..6fa7e1c19efd 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"فقط\nاولویتدار"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"فقط\nهشدارها"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"در حال شارژ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> تا شارژ کامل)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"در حال شارژ سریع (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> تا شارژ کامل)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"در حال شارژ آهسته (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> تا شارژ کامل)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"تغییر کاربر"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"تعویض کاربر، کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 6c4a0299069b..655cf312b5dd 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Vain\ntärkeät"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Vain\nherätykset"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ladataan (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kunnes täynnä)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Nopea lataus (latausaikaa jäljellä <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Hidas lataus (latausaikaa jäljellä <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Vaihda käyttäjää"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Vaihda käyttäjä (nyt <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Nykyinen käyttäjä: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 07a2fc349f5e..2f2c3319ac17 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priorités\nuniquement"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarmes\nuniquement"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours... (chargée à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charge rapide en cours... (chargé dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charge lente en cours... (chargé dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Changer d\'utilisateur"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Changer d\'utilisateur (utilisateur actuel <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 0aa45ae8a393..9f3443197a52 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priorité\nuniquement"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarmes\nuniquement"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charge rapide… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charge lente… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Changer d\'utilisateur"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Changer d\'utilisateur (utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml index ec64f229ea4e..df90ef4dd550 100644 --- a/packages/SystemUI/res/values-gl-rES/strings.xml +++ b/packages/SystemUI/res/values-gl-rES/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Só\nprioridade"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Só\nalarmas"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para finalizar a carga)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Cargando rápido (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para rematar a carga)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Cargando lento (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para rematar a carga)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Cambiar usuario"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Cambiar usuario, usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml index e873c95f14f7..42ee460ad880 100644 --- a/packages/SystemUI/res/values-gu-rIN/strings.xml +++ b/packages/SystemUI/res/values-gu-rIN/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ફક્ત\nપ્રાધાન્યતા"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ફક્ત\nએલાર્મ્સ"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ચાર્જ થઈ રહ્યું છે (પૂર્ણ થવામાં <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> બાકી)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ઝડપથી ચાર્જિંગ થઇ રહી છે (પૂર્ણ થવામાં <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> બાકી)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ધીમેથી ચાર્જિંગ થઇ રહી છે (પૂર્ણ થવામાં <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> બાકી)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"વપરાશકર્તા સ્વિચ કરો"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"વપરાશકર્તાને સ્વિચ કરો, વર્તમાન વપરાશકર્તા <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"વર્તમાન વપરાશકર્તા <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 83ac46eaf42d..86f536dce040 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"केवल\nप्राथमिकता"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"केवल\nअलार्म"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"चार्ज हो रहा है (पूरा होने में <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> बाकी)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"तेज़ी से चार्ज हो रहा है (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> में हो जाएगा)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"धीरे चार्ज हो रहा है (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> में पूरा हो जाएगा)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"उपयोगकर्ता स्विच करें"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"उपयोगकर्ता स्विच करें, वर्तमान उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"वर्तमान उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index b5d993117334..043e7ada0ddc 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -326,6 +326,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Samo\nprioritetno"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Samo\nalarmi"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Brzo punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Sporo punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Promjena korisnika"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Promjena korisnika, trenutačni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Trenutačan korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 79390a69990e..e2b08129eabc 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Csak\nprioritás"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Csak\nriasztások"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Töltés (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> a teljes töltöttségig)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Gyors töltés (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> a teljes töltöttségig)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Lassú töltés (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> a teljes töltöttségig)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Felhasználóváltás"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Felhasználóváltás (a jelenlegi felhasználó: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Jelenlegi felhasználó (<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string> diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml index 9f48b2618e3e..f8fc232106bb 100644 --- a/packages/SystemUI/res/values-hy-rAM/strings.xml +++ b/packages/SystemUI/res/values-hy-rAM/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Միայն\nկարևորները"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Միայն\nզարթուցիչ"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> մինչև լրիվ լիցքավորումը)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Արագ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև լցվելը)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Դանդաղ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև լցվելը)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Անջատել օգտվողին"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Փոխել օգտվողին. ներկայիս օգտվողն է՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Ընթացիկ օգտվողը՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 600241151aab..7dbcda92ede1 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Hanya\nprioritas"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Hanya\nalarm"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengisi daya (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga penuh)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Mengisi daya dengan cepat (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga penuh)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Mengisi daya dengan lambat (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga penuh)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Beralih pengguna"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Ganti pengguna, pengguna saat ini <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Pengguna saat ini <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml index e8e053090fda..b28b6fd80e31 100644 --- a/packages/SystemUI/res/values-is-rIS/strings.xml +++ b/packages/SystemUI/res/values-is-rIS/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Aðeins\nforgangur"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Aðeins\nvekjarar"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Í hleðslu (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> fram að fullri hleðslu)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Í hraðri hleðslu (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> fram að fullri hleðslu)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Í hægri hleðslu (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> fram að fullri hleðslu)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Skipta um notanda"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Skipta um notanda; núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 0d0663675b02..54e6f560ca67 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Solo con\npriorità"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Solo\nsveglie"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"In carica (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> al termine)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Ricarica veloce (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> al termine)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Ricarica lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> al termine)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Cambio utente"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Cambia utente, utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 44058a34db49..c959febd9ab9 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"התראות בעדיפות\nבלבד"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"התראות\nבלבד"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"טוען (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד לסיום)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"בטעינה מהירה (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד למילוי)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"בטעינה איטית (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד למילוי)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"החלפת משתמש"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"החלף משתמש. המשתמש הנוכחי הוא <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"משתמש נוכחי <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index a034f4f32c3a..29476d80e094 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"重要な\n通知のみ"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"アラーム\nのみ"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中(フル充電まで<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"急速充電中(完了まで<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"低速充電中(完了まで<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ユーザーを切り替える"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ユーザーを切り替える、現在のユーザーは<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"現在のユーザー: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml index f1035a8d8fc5..d83c9e9e8ede 100644 --- a/packages/SystemUI/res/values-ka-rGE/strings.xml +++ b/packages/SystemUI/res/values-ka-rGE/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"მხოლოდ\nპრიორიტეტულები"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"მხოლოდ\nგაფრთხილებები"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>-ის შეცვლა დასრულებამდე)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"იტენება სწრაფად (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> სრულ დატენვამდე)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"იტენება ნელა (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> სრულ დატენვამდე)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"მომხმარებლის გადართვა"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"მომხმარებლის გდართვა. ამჟამინდელი მომხმარებელი <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ამჟამინდელი მომხმარებელი <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml index 8b82e5fce6c9..5c066b3c9d14 100644 --- a/packages/SystemUI/res/values-kk-rKZ/strings.xml +++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Тек\nбасымдық"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Тек\nдабылдар"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарядталуда (толғанша <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Жылдам зарядталуда (толғанша <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Баяу зарядталуда (толғанша <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Пайдаланушыны ауыстыру"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Пайдаланушыны ауыстыру, ағымдағы пайдаланушы <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Ағымдағы пайдаланушы: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml index b8db00f9067c..4c469b05e7e4 100644 --- a/packages/SystemUI/res/values-km-rKH/strings.xml +++ b/packages/SystemUI/res/values-km-rKH/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"អាទិភាព\nប៉ុណ្ណោះ"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"សំឡេងរោទ៍\nប៉ុណ្ណោះ"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"កំពុងបញ្ចូលថ្ម (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ទើបពេញ)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ថ្មកំពុងសាកលឿន (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ទើបពេញ)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ថ្មកំពុងសាកយឺតៗ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ទើបពេញ)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ប្ដូរអ្នកប្រើ"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ប្ដូរអ្នកប្រើ អ្នកប្រើបច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"អ្នកប្រើបច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml index 316c04fe12a2..4215a9193cd7 100644 --- a/packages/SystemUI/res/values-kn-rIN/strings.xml +++ b/packages/SystemUI/res/values-kn-rIN/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ಆದ್ಯತೆ\nಮಾತ್ರ"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ಅಲಾರಮ್ಗಳು\nಮಾತ್ರ"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ ( ಪೂರ್ತಿ ಆಗುವವರೆಗೆ <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ವೇಗವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ (ಪೂರ್ಣಗೊಳ್ಳಲು <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ನಿಧಾನ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ (ಪೂರ್ಣಗೊಳ್ಳಲು <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ, ಪ್ರಸ್ತುತ ಬಳಕೆದಾರ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"<xliff:g id="CURRENT_USER_NAME">%s</xliff:g> ಪ್ರಸ್ತುತ ಬಳಕೆದಾರ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index d7f1543a87d0..8ee594652bd3 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"중요 알림만\n허용"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"알람만\n"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"충전 중(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> 후 충전 완료)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"고속 충전 중(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> 후 충전 완료)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"저속 충전 중(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> 후 충전 완료)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"사용자 전환"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"사용자 전환, 현재 사용자 <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"현재 사용자: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml index 65f91a2d62a6..ce0afd42d6de 100644 --- a/packages/SystemUI/res/values-ky-rKG/strings.xml +++ b/packages/SystemUI/res/values-ky-rKG/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Артыкчылыктуу\nгана"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Ойготкучтар\nгана"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Кубатталууда (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> толгонго чейин)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Тез кубатталууда (толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> калды)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Жай кубатталууда (толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> калды)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Колдонуучуну которуу"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Колдонуучуну күйгүзүү, учурдагы колдонуучу <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Учурдагы колдонуучу <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml index 50b4522caabf..b45395efd6ef 100644 --- a/packages/SystemUI/res/values-lo-rLA/strings.xml +++ b/packages/SystemUI/res/values-lo-rLA/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ບຸລິມະສິດ\nເທົ່ານັ້ນ"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ໂມງປຸກ\nເທົ່ານັ້ນ"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ກຳລັງສາກໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າຈະເຕັມ)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ກຳລັງສາກໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າຈະເຕັມ)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ກຳລັງສາກໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າຈະເຕັມ)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ສະລັບຜູ່ໃຊ້"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ປ່ຽນຜູ່ໃຊ້, ຜູ່ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ຜູ້ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 90827785d109..194668b8b922 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Tik\nprioritetiniai"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Tik\nsignalai"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Kraunama (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> iki visiško įkrovimo)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Greitai kraunama (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> iki visiško įkrovimo)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Lėtai kraunama (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> iki visiško įkrovimo)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Perjungti naudotoją"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Perjungti naudotoją, dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 5fb4bb0c2f86..2b3be5838d23 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -326,6 +326,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Tikai\nprioritārie"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Tikai\nsignāli"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Notiek uzlāde (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> līdz pilnīgai uzlādei)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Ātra uzlāde (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> līdz pilnīgai uzlādei)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Lēna uzlāde (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> līdz pilnīgai uzlādei)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Mainīt lietotāju"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Pārslēgt lietotāju; pašreizējais lietotājs: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Pašreizējais lietotājs: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml index 8571b3f441f1..912c30f1c225 100644 --- a/packages/SystemUI/res/values-mk-rMK/strings.xml +++ b/packages/SystemUI/res/values-mk-rMK/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Само\nприоритетни"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Само\nаларми"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Се полни (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> додека не се наполни)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Брзо полнење (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> додека не се наполни)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Бавно полнење (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> додека не се наполни)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Промени го корисникот"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Промени го корисникот, тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml index 4b11962a91c4..c85ecc7b8441 100644 --- a/packages/SystemUI/res/values-ml-rIN/strings.xml +++ b/packages/SystemUI/res/values-ml-rIN/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"മുൻഗണന\nമാത്രം"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"അലാറങ്ങൾ\nമാത്രം"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ചാർജ്ജുചെയ്യുന്നു (പൂർണ്ണമാകുന്നതിന്, <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"വേഗത്തിൽ ചാർജുചെയ്യുന്നു (പൂർണ്ണമാകാൻ <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"പതുക്കെ ചാർജുചെയ്യുന്നു (പൂർണ്ണമാകാൻ <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ഉപയോക്താവ് മാറുക"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ഉപയോക്താവിനെ മാറ്റുക, <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> എന്നയാളാണ് നിലവിലുള്ള ഉപയോക്താവ്"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"നിലവിലെ ഉപയോക്താവ് <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml index d1dbd6dbf2d4..6b29affcfb6c 100644 --- a/packages/SystemUI/res/values-mn-rMN/strings.xml +++ b/packages/SystemUI/res/values-mn-rMN/strings.xml @@ -323,6 +323,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Зөвхөн\nхамгийн чухлыг"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Зөвхөн\nсэрүүлэг"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Цэнэглэж байна (дүүргэхэд <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Цэнэглэж байна (дүүргэхэд <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> шаардлагатай)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Цэнэглэж байна (дүүргэхэд <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> шаардлагатай)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Хэрэглэгчийг сэлгэх"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Хэрэглэгчийг сэлгэх, одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml index 2b9d953486b9..9d518a0c7008 100644 --- a/packages/SystemUI/res/values-mr-rIN/strings.xml +++ b/packages/SystemUI/res/values-mr-rIN/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"केवळ\nप्राधान्य"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"केवळ\nअलार्म"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण होईपर्यंत) चार्ज होत आहे"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण होईपर्यंत) वेगाने चार्ज होत आहे"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण होईपर्यंत) हळूहळू चार्ज होत आहे"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"वापरकर्ता स्विच करा"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"वापरकर्ता स्विच करा, वर्तमान वापरकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"वर्तमान वापरकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index 01f7edfd6066..10dca3e4545b 100644 --- a/packages/SystemUI/res/values-ms-rMY/strings.xml +++ b/packages/SystemUI/res/values-ms-rMY/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Keutamaan\nsahaja"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Penggera\nsahaja"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengecas (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Mengecas cepat (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Mengecas perlahan (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Tukar pengguna"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Tukar pengguna, pengguna semasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Pengguna semasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml index d0eaccfaf550..adfd9f821f02 100644 --- a/packages/SystemUI/res/values-my-rMM/strings.xml +++ b/packages/SystemUI/res/values-my-rMM/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ဦးစားပေးမှု\nသာ"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"နှိုးစက်များ\nသာ"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> အပြည့် အထိ) အားသွင်းနေ"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"လျှင်မြန်စွာအားသွင်းခြင်း (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ပြည့်သည်အထိ)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"နှေးကွေးစွာ အားသွင်းခြင်း (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ပြည့်သည်အထိ)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"အသုံးပြုသူကို ပြောင်းရန်၊ လက်ရှိ အသုံးပြုသူ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"လတ်တလော သုံးစွဲသူ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index e582039142db..ecfb7f24741f 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Bare\nPrioritet"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Bare\nalarmer"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Lader (fulladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Lader raskt (fulladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Lader sakte (fulladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Bytt bruker"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Bytt bruker, gjeldende bruker er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Gjeldende bruker: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml index c3dc703e72ad..d997affa6b8a 100644 --- a/packages/SystemUI/res/values-ne-rNP/strings.xml +++ b/packages/SystemUI/res/values-ne-rNP/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"प्राथमिकता \nमात्र"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"अलार्महरू \nमात्र"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"चार्ज हुँदै (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण भएसम्म)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"छिटो चार्ज हुँदै (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण नभएसम्म)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"बिस्तारै चार्ज हुँदै (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण नभएसम्म)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"प्रयोगकर्ता फेर्नुहोस्"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"प्रयोगकर्ता, हालको प्रयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> मा स्विच गर्नुहोस्"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"हालको प्रयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 79f3333ead53..c6bab06f0acd 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Alleen\nprioriteit"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alleen\nalarmen"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Opladen (vol over <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Snel opladen (vol over <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Langzaam opladen (vol over <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Gebruiker wijzigen"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Schakelen tussen gebruikers, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml index ef3d2f3585bd..74e5fcb2096a 100644 --- a/packages/SystemUI/res/values-pa-rIN/strings.xml +++ b/packages/SystemUI/res/values-pa-rIN/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ਕੇਵਲ\nਤਰਜੀਹੀ"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ਕੇਵਲ\nਅਲਾਰਮ"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ਚਾਰਜਿੰਗ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ਪੂਰਾ ਹੋਣ ਤੱਕ)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ਪੂਰੀ ਹੋਣ ਤੱਕ)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ਪੂਰੀ ਹੋਣ ਤੱਕ)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ਉਪਭੋਗਤਾ ਸਵਿਚ ਕਰੋ"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ਉਪਭੋਗਤਾ, ਵਰਤਮਾਨ ਉਪਭੋਗਤਾ ਸਵਿਚ ਕਰੋ<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ਮੌਜੂਦਾ ਉਪਭੋਗਤਾ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 8c2c5c430213..e04fd71a1b55 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Tylko\npriorytetowe"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Tylko\nalarmy"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ładuje się (pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Szybkie ładowanie (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do końca)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Wolne ładowanie (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do końca)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Przełącz użytkownika"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Przełącz użytkownika. Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 3803b812013d..2a8e3ab51483 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Somente\nprioridade"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Somente\nalarmes"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até concluir)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Carregando rapidamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para conclusão)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Carregando lentamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para conclusão)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Trocar usuário"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 12609c747e59..8453e5bc3106 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Apenas\nprioridade"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Apenas\nalarmes"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"A carregar (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até à carga máxima)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"A carregar rapid. (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até à carga máxima)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"A carregar lentam. (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até à carga máxima)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Mudar utilizador"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Mudar de utilizador; o utilizador atual é <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilizador atual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 3803b812013d..2a8e3ab51483 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Somente\nprioridade"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Somente\nalarmes"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até concluir)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Carregando rapidamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para conclusão)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Carregando lentamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para conclusão)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Trocar usuário"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index bb8f2c3fe6b5..d85e791d0454 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -326,6 +326,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Numai\ncu prioritate"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Numai\nalarme"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Se încarcă (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Se încarcă rapid (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Se încarcă lent (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Comutați între utilizatori"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Schimbați utilizatorul (utilizator actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilizator actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 9609f40bdff8..39d861747968 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -329,6 +329,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Только\nважные"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Только\nбудильник"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарядка батареи (осталось <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Быстрая зарядка (осталось <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Медленная зарядка (осталось <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Сменить пользователя."</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Сменить аккаунт. Вход выполнен под именем <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>."</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Выбран аккаунт пользователя <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml index 9e7883422230..51bb047e5360 100644 --- a/packages/SystemUI/res/values-si-rLK/strings.xml +++ b/packages/SystemUI/res/values-si-rLK/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ප්රමුඛතා\nපමණි"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ඇඟවීම්\nපමණි"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ආරෝපණය වෙමින් (සම්පුර්ණ වන තෙක් <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ඉක්මනින් ආරෝපණය වෙමින් (සම්පුර්ණ වන තෙක් <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"සෙමින් ආරෝපණය වෙමින් (සම්පුර්ණ වන තෙක් <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"පරිශීලක මාරුව"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"පරිශීලකයා මාරු කරන්න,දැන් සිටින පරිශීලකයා <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"වත්මන් පරිශීලක <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 2e41b2969dd2..2886e36c9667 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -329,6 +329,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Iba\nprioritné"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Iba\nbudíky"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nabíja sa (úplné nabitie o <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Nabíja sa rýchlo (úplné nabitie o <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Nabíja sa pomaly (úplné nabitie o <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Prepnutie používateľa"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Prepnúť používateľa (súčasný používateľ: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Aktuálny používateľ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 4dffcfa2bcd1..e325ef1b8771 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Samo\nprednostno"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Samo\nalarmi"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Hitro polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Počasno polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Preklop med uporabniki"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Preklop med uporabniki, trenutni uporabnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Trenutni uporabnik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml index 20c342638aaa..5bcf941216eb 100644 --- a/packages/SystemUI/res/values-sq-rAL/strings.xml +++ b/packages/SystemUI/res/values-sq-rAL/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Vetëm\nme prioritet"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Vetëm\nalarmet"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Po ngarkohet (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> deri sa të mbushet)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Po ngarkon me shpejtësi (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> derisa të mbushet)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Po ngarkon me ngadalë (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> derisa të mbushet)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Ndërro përdorues"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Ndërro përdoruesin. Përdoruesi aktual është <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Përdoruesi aktual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 1dcde3a1b0bb..522b2af32221 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -326,6 +326,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Само\nприорит. прекиди"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Само\nаларми"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Пуњење (пун је за <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Брзо се пуни (напуниће се за <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Споро се пуни (напуниће се за <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Замени корисника"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Промените корисника, актуелни корисник је <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Актуелни корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 0abeb1eb4d85..d91335c28279 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Endast\nprioriterade"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Endast\nalarm"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laddar (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tills batteriet är fulladdat)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Laddas snabbt (batteriet fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Laddas sakta (batteriet fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Byt användare"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Byt användare. Aktuell användare: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Aktuell användare <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index d000004c530a..1cf429380b34 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Kipaumbele\npekee"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Kengele\npekee"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Inachaji (Imebakisha <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ijae)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Inachaji kwa kasi (itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Inachaji pole pole (itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Badili mtumiaji"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Badili mtumiaji, mtumiaji wa sasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Mtumiaji wa sasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml index 7b1ded375bac..670bccc71320 100644 --- a/packages/SystemUI/res/values-ta-rIN/strings.xml +++ b/packages/SystemUI/res/values-ta-rIN/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"முன்னுரிமைகள்\nமட்டும்"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"அலாரங்கள்\nமட்டும்"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"சார்ஜாகிறது (முழு சார்ஜிற்கு <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ஆகும்)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"வேகமாக சார்ஜாகிறது (முழு சார்ஜிற்கு: <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"மெதுவாக சார்ஜாகிறது (முழு சார்ஜிற்கு: <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"பயனரை மாற்று"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"பயனரை மாற்று, தற்போதைய பயனர் <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"தற்போதைய பயனர்: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml index 06762bd7ef4e..bbd9979d0585 100644 --- a/packages/SystemUI/res/values-te-rIN/strings.xml +++ b/packages/SystemUI/res/values-te-rIN/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ప్రాధాన్యమైనవి\nమాత్రమే"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"అలారాలు\nమాత్రమే"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ఛార్జ్ అవుతోంది (పూర్తిగా నిండటానికి <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"వేగంగా ఛార్జ్ అవుతోంది (నిండటానికి <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"నెమ్మదిగా ఛార్జ్ అవుతోంది (నిండటానికి <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"వినియోగదారుని మార్చు"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"వినియోగదారుని మార్చు, ప్రస్తుత వినియోగదారు <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ప్రస్తుత వినియోగదారు <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 52c7af7f2f27..2486519f019f 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"เฉพาะเรื่อง\nสำคัญ"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"เฉพาะปลุก\nเท่านั้น"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"กำลังชาร์จ (อีก <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> เต็ม)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"กำลังชาร์จอย่างรวดเร็ว (อีก <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> จะเต็ม)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"กำลังชาร์จอย่างช้าๆ (อีก <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> จะเต็ม)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"สลับผู้ใช้"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"เปลี่ยนผู้ใช้จากผู้ใช้ปัจจุบัน <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ผู้ใช้ปัจจุบัน <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 66056d10dc64..3d1243e2f585 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priyoridad\nlang"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Mga alarm\nlang"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nagtsa-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang mapuno)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Mabilis mag-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang sa mapuno)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Mabagal mag-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang sa mapuno)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Magpalit ng user"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Magpalit ng user, kasalukuyang user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Kasalukuyang user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 49edb5746548..4dadfbc9e74f 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Yalnızca\nöncelik"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Yalnızca\nalarmlar"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Şarj oluyor (tamamen dolmasına <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kaldı)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Hızlı şarj oluyor (tam dolmasına <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kaldı)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Yavaş şarj oluyor (tam dolmasına <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kaldı)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Kullanıcı değiştirme"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Kullanıcı değiştir. Geçerli kullanıcı: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Geçerli kullanıcı: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 9dde80178d2d..d158dbef756b 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Лише\nприорітетні"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Лише\nсигнали"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Заряджання (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до повного зарядження)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Швидке заряджання (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до повного заряду)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Повільне заряджання (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до повного заряду)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Змінити користувача"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Змінити користувача, поточний користувач – <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Поточний користувач: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml index a4ef16acae62..f8d8c71e8f85 100644 --- a/packages/SystemUI/res/values-ur-rPK/strings.xml +++ b/packages/SystemUI/res/values-ur-rPK/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"صرف\nترجیحی"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"صرف\nالارمز"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"چارج ہو رہا ہے (مکمل ہونے تک <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> باقی ہیں)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"تیزی سے چارج ہو رہا ہے (مکمل ہونے میں <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"آہستہ چارج ہو رہا ہے (مکمل ہونے میں <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"صارف سوئچ کریں"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"صارف سوئچ کریں، موجودہ صارف <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"موجودہ صارف <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml index 9f9fe5f6e427..018680956bdf 100644 --- a/packages/SystemUI/res/values-uz-rUZ/strings.xml +++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Faqat\nmuhimlar"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Faqat\nsignallar"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Quvvat olmoqda (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>da to‘ladi)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Tez quvvat olmoqda (to‘lishiga <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> qoldi)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Sekin quvvat olmoqda (to‘lishiga <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> qoldi)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Foydalanuvchini almashtirish"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Foydalanuvchini o‘zgartirish. Joriy foydalanuvchi – <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Joriy foydalanuvchi <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 460d57ae4410..49b5d1d29617 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Chỉ\nưu tiên"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Chỉ\nbáo thức"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Đang sạc (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> cho đến khi đầy)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Sạc nhanh (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> cho tới khi đầy)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Sạc chậm (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> cho tới khi đầy)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Chuyển đổi người dùng"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Chuyển người dùng, người dùng hiện tại <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Người dùng hiện tại <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 4d70f24b9ea8..93ed5ca876a5 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"仅限\n优先打扰"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"仅限\n闹钟"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"正在充电(还需<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>充满)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"正在快速充电(还需 <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>才能充满)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"正在慢速充电(还需 <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>才能充满)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"切换用户"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"切换用户,当前用户为<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"当前用户为<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index f7cac90e1522..219611133f50 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"僅限\n優先"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"僅限\n鬧鐘"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後完成充電)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"正在快速充電 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後完成充電)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"正在緩慢充電 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後完成充電)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"切換使用者"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"切換使用者,目前使用者是<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"目前的使用者是 <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 369172f36836..fe4fe0600254 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -327,6 +327,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"僅允許\n優先通知"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"僅允許\n鬧鐘"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後充飽)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"快速充電中 (充飽需要 <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"慢速充電中 (充飽需要 <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"切換使用者"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"切換使用者,目前使用者是<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"目前使用者是「<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>」"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 2a14a9f2b6d4..2f068af66dfc 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -325,6 +325,8 @@ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Okubalulekile\nkuphela"</string> <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Ama-alamu\nkuphela"</string> <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Iyashaja (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ize igcwale)"</string> + <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Iyashaja ngokushesha (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ize igcwale)"</string> + <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Iyashaja kancane (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ize igcwale)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Shintsha umsebenzisi"</string> <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Shintsha umsebenzisi, umsebenzisi wamanje ngu-<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Umsebenzisi wamanje <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java new file mode 100755 index 000000000000..3eb12711d1a2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui; + +import android.animation.ArgbEvaluator; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.ContentObserver; +import android.graphics.*; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.provider.Settings; +import com.android.systemui.statusbar.policy.BatteryController; + +public class BatteryMeterDrawable extends Drawable implements DemoMode, + BatteryController.BatteryStateChangeCallback { + + private static final float ASPECT_RATIO = 9.5f / 14.5f; + public static final String TAG = BatteryMeterDrawable.class.getSimpleName(); + public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent"; + + private static final boolean SINGLE_DIGIT_PERCENT = false; + + private static final int FULL = 96; + + private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction + + private final int[] mColors; + + private boolean mShowPercent; + private float mButtonHeightFraction; + private float mSubpixelSmoothingLeft; + private float mSubpixelSmoothingRight; + private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint; + private float mTextHeight, mWarningTextHeight; + private int mIconTint = Color.WHITE; + + private int mHeight; + private int mWidth; + private String mWarningString; + private final int mCriticalLevel; + private int mChargeColor; + private final float[] mBoltPoints; + private final Path mBoltPath = new Path(); + + private final RectF mFrame = new RectF(); + private final RectF mButtonFrame = new RectF(); + private final RectF mBoltFrame = new RectF(); + + private final Path mShapePath = new Path(); + private final Path mClipPath = new Path(); + private final Path mTextPath = new Path(); + + private BatteryController mBatteryController; + private boolean mPowerSaveEnabled; + + private int mDarkModeBackgroundColor; + private int mDarkModeFillColor; + + private int mLightModeBackgroundColor; + private int mLightModeFillColor; + + private final SettingObserver mSettingObserver = new SettingObserver(); + + private final Context mContext; + private final Handler mHandler; + + private int mLevel = -1; + private boolean mPluggedIn; + private boolean mListening; + + public BatteryMeterDrawable(Context context, Handler handler, int frameColor) { + mContext = context; + mHandler = handler; + final Resources res = context.getResources(); + TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels); + TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values); + + final int N = levels.length(); + mColors = new int[2*N]; + for (int i=0; i<N; i++) { + mColors[2*i] = levels.getInt(i, 0); + mColors[2*i+1] = colors.getColor(i, 0); + } + levels.recycle(); + colors.recycle(); + updateShowPercent(); + mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol); + mCriticalLevel = mContext.getResources().getInteger( + com.android.internal.R.integer.config_criticalBatteryWarningLevel); + mButtonHeightFraction = context.getResources().getFraction( + R.fraction.battery_button_height_fraction, 1, 1); + mSubpixelSmoothingLeft = context.getResources().getFraction( + R.fraction.battery_subpixel_smoothing_left, 1, 1); + mSubpixelSmoothingRight = context.getResources().getFraction( + R.fraction.battery_subpixel_smoothing_right, 1, 1); + + mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mFramePaint.setColor(frameColor); + mFramePaint.setDither(true); + mFramePaint.setStrokeWidth(0); + mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE); + + mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mBatteryPaint.setDither(true); + mBatteryPaint.setStrokeWidth(0); + mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE); + + mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD); + mTextPaint.setTypeface(font); + mTextPaint.setTextAlign(Paint.Align.CENTER); + + mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mWarningTextPaint.setColor(mColors[1]); + font = Typeface.create("sans-serif", Typeface.BOLD); + mWarningTextPaint.setTypeface(font); + mWarningTextPaint.setTextAlign(Paint.Align.CENTER); + + mChargeColor = context.getColor(R.color.batterymeter_charge_color); + + mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color)); + mBoltPoints = loadBoltPoints(res); + + mDarkModeBackgroundColor = + context.getColor(R.color.dark_mode_icon_color_dual_tone_background); + mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill); + mLightModeBackgroundColor = + context.getColor(R.color.light_mode_icon_color_dual_tone_background); + mLightModeFillColor = context.getColor(R.color.light_mode_icon_color_dual_tone_fill); + } + + public void startListening() { + mListening = true; + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver); + if (mDemoMode) return; + mBatteryController.addStateChangedCallback(this); + } + + public void stopListening() { + mListening = false; + mContext.getContentResolver().unregisterContentObserver(mSettingObserver); + if (mDemoMode) return; + mBatteryController.removeStateChangedCallback(this); + } + + private void postInvalidate() { + mHandler.post(new Runnable() { + @Override + public void run() { + invalidateSelf(); + } + }); + } + + public void setBatteryController(BatteryController batteryController) { + mBatteryController = batteryController; + mPowerSaveEnabled = mBatteryController.isPowerSave(); + } + + @Override + public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + mLevel = level; + mPluggedIn = pluggedIn; + + postInvalidate(); + } + + @Override + public void onPowerSaveChanged() { + mPowerSaveEnabled = mBatteryController.isPowerSave(); + invalidateSelf(); + } + + private static float[] loadBoltPoints(Resources res) { + final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points); + int maxX = 0, maxY = 0; + for (int i = 0; i < pts.length; i += 2) { + maxX = Math.max(maxX, pts[i]); + maxY = Math.max(maxY, pts[i + 1]); + } + final float[] ptsF = new float[pts.length]; + for (int i = 0; i < pts.length; i += 2) { + ptsF[i] = (float)pts[i] / maxX; + ptsF[i + 1] = (float)pts[i + 1] / maxY; + } + return ptsF; + } + + @Override + public void setBounds(int left, int top, int right, int bottom) { + super.setBounds(left, top, right, bottom); + mHeight = bottom - top; + mWidth = right - left; + mWarningTextPaint.setTextSize(mHeight * 0.75f); + mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent; + } + + private void updateShowPercent() { + mShowPercent = 0 != Settings.System.getInt(mContext.getContentResolver(), + SHOW_PERCENT_SETTING, 0); + } + + private int getColorForLevel(int percent) { + + // If we are in power save mode, always use the normal color. + if (mPowerSaveEnabled) { + return mColors[mColors.length-1]; + } + int thresh, color = 0; + for (int i=0; i<mColors.length; i+=2) { + thresh = mColors[i]; + color = mColors[i+1]; + if (percent <= thresh) { + + // Respect tinting for "normal" level + if (i == mColors.length-2) { + return mIconTint; + } else { + return color; + } + } + } + return color; + } + + public void setDarkIntensity(float darkIntensity) { + int backgroundColor = getBackgroundColor(darkIntensity); + int fillColor = getFillColor(darkIntensity); + mIconTint = fillColor; + mFramePaint.setColor(backgroundColor); + mBoltPaint.setColor(fillColor); + mChargeColor = fillColor; + invalidateSelf(); + } + + private int getBackgroundColor(float darkIntensity) { + return getColorForDarkIntensity( + darkIntensity, mLightModeBackgroundColor, mDarkModeBackgroundColor); + } + + private int getFillColor(float darkIntensity) { + return getColorForDarkIntensity( + darkIntensity, mLightModeFillColor, mDarkModeFillColor); + } + + private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) { + return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor); + } + + @Override + public void draw(Canvas c) { + final int level = mLevel; + + if (level == -1) return; + + float drawFrac = (float) level / 100f; + final int height = mHeight; + final int width = (int) (ASPECT_RATIO * mHeight); + int px = (mWidth - width) / 2; + + final int buttonHeight = (int) (height * mButtonHeightFraction); + + mFrame.set(0, 0, width, height); + mFrame.offset(px, 0); + + // button-frame: area above the battery body + mButtonFrame.set( + mFrame.left + Math.round(width * 0.25f), + mFrame.top, + mFrame.right - Math.round(width * 0.25f), + mFrame.top + buttonHeight); + + mButtonFrame.top += mSubpixelSmoothingLeft; + mButtonFrame.left += mSubpixelSmoothingLeft; + mButtonFrame.right -= mSubpixelSmoothingRight; + + // frame: battery body area + mFrame.top += buttonHeight; + mFrame.left += mSubpixelSmoothingLeft; + mFrame.top += mSubpixelSmoothingLeft; + mFrame.right -= mSubpixelSmoothingRight; + mFrame.bottom -= mSubpixelSmoothingRight; + + // set the battery charging color + mBatteryPaint.setColor(mPluggedIn ? mChargeColor : getColorForLevel(level)); + + if (level >= FULL) { + drawFrac = 1f; + } else if (level <= mCriticalLevel) { + drawFrac = 0f; + } + + final float levelTop = drawFrac == 1f ? mButtonFrame.top + : (mFrame.top + (mFrame.height() * (1f - drawFrac))); + + // define the battery shape + mShapePath.reset(); + mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top); + mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top); + mShapePath.lineTo(mButtonFrame.right, mFrame.top); + mShapePath.lineTo(mFrame.right, mFrame.top); + mShapePath.lineTo(mFrame.right, mFrame.bottom); + mShapePath.lineTo(mFrame.left, mFrame.bottom); + mShapePath.lineTo(mFrame.left, mFrame.top); + mShapePath.lineTo(mButtonFrame.left, mFrame.top); + mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top); + + if (mPluggedIn) { + // define the bolt shape + final float bl = mFrame.left + mFrame.width() / 4.5f; + final float bt = mFrame.top + mFrame.height() / 6f; + final float br = mFrame.right - mFrame.width() / 7f; + final float bb = mFrame.bottom - mFrame.height() / 10f; + if (mBoltFrame.left != bl || mBoltFrame.top != bt + || mBoltFrame.right != br || mBoltFrame.bottom != bb) { + mBoltFrame.set(bl, bt, br, bb); + mBoltPath.reset(); + mBoltPath.moveTo( + mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), + mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); + for (int i = 2; i < mBoltPoints.length; i += 2) { + mBoltPath.lineTo( + mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(), + mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height()); + } + mBoltPath.lineTo( + mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), + mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); + } + + float boltPct = (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top); + boltPct = Math.min(Math.max(boltPct, 0), 1); + if (boltPct <= BOLT_LEVEL_THRESHOLD) { + // draw the bolt if opaque + c.drawPath(mBoltPath, mBoltPaint); + } else { + // otherwise cut the bolt out of the overall shape + mShapePath.op(mBoltPath, Path.Op.DIFFERENCE); + } + } + + // compute percentage text + boolean pctOpaque = false; + float pctX = 0, pctY = 0; + String pctText = null; + if (!mPluggedIn && level > mCriticalLevel && mShowPercent) { + mTextPaint.setColor(getColorForLevel(level)); + mTextPaint.setTextSize(height * + (SINGLE_DIGIT_PERCENT ? 0.75f + : (mLevel == 100 ? 0.38f : 0.5f))); + mTextHeight = -mTextPaint.getFontMetrics().ascent; + pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level); + pctX = mWidth * 0.5f; + pctY = (mHeight + mTextHeight) * 0.47f; + pctOpaque = levelTop > pctY; + if (!pctOpaque) { + mTextPath.reset(); + mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath); + // cut the percentage text out of the overall shape + mShapePath.op(mTextPath, Path.Op.DIFFERENCE); + } + } + + // draw the battery shape background + c.drawPath(mShapePath, mFramePaint); + + // draw the battery shape, clipped to charging level + mFrame.top = levelTop; + mClipPath.reset(); + mClipPath.addRect(mFrame, Path.Direction.CCW); + mShapePath.op(mClipPath, Path.Op.INTERSECT); + c.drawPath(mShapePath, mBatteryPaint); + + if (!mPluggedIn) { + if (level <= mCriticalLevel) { + // draw the warning text + final float x = mWidth * 0.5f; + final float y = (mHeight + mWarningTextHeight) * 0.48f; + c.drawText(mWarningString, x, y, mWarningTextPaint); + } else if (pctOpaque) { + // draw the percentage text + c.drawText(pctText, pctX, pctY, mTextPaint); + } + } + } + + // Some stuff required by Drawable. + @Override + public void setAlpha(int alpha) { + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + } + + @Override + public int getOpacity() { + return 0; + } + + private boolean mDemoMode; + + @Override + public void dispatchDemoCommand(String command, Bundle args) { + if (!mDemoMode && command.equals(COMMAND_ENTER)) { + mBatteryController.removeStateChangedCallback(this); + mDemoMode = true; + if (mListening) { + mBatteryController.removeStateChangedCallback(this); + } + } else if (mDemoMode && command.equals(COMMAND_EXIT)) { + mDemoMode = false; + postInvalidate(); + if (mListening) { + mBatteryController.addStateChangedCallback(this); + } + } else if (mDemoMode && command.equals(COMMAND_BATTERY)) { + String level = args.getString("level"); + String plugged = args.getString("plugged"); + if (level != null) { + mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100); + } + if (plugged != null) { + mPluggedIn = Boolean.parseBoolean(plugged); + } + postInvalidate(); + } + } + + private final class SettingObserver extends ContentObserver { + public SettingObserver() { + super(new Handler()); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + updateShowPercent(); + postInvalidate(); + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 95b58e5e527f..6cb8da4a1016 100755..100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -13,82 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.systemui; -import android.animation.ArgbEvaluator; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; import android.content.res.TypedArray; -import android.database.ContentObserver; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; -import android.graphics.Typeface; -import android.net.Uri; -import android.os.BatteryManager; -import android.os.Bundle; import android.os.Handler; -import android.provider.Settings; import android.util.AttributeSet; -import android.view.View; - +import android.widget.ImageView; import com.android.systemui.statusbar.policy.BatteryController; -public class BatteryMeterView extends View implements DemoMode, - BatteryController.BatteryStateChangeCallback { - public static final String TAG = BatteryMeterView.class.getSimpleName(); - public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST"; - public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent"; - - private static final boolean SINGLE_DIGIT_PERCENT = false; - - private static final int FULL = 96; - - private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction - - private final int[] mColors; - - private boolean mShowPercent; - private float mButtonHeightFraction; - private float mSubpixelSmoothingLeft; - private float mSubpixelSmoothingRight; - private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint; - private float mTextHeight, mWarningTextHeight; - private int mIconTint = Color.WHITE; - - private int mHeight; - private int mWidth; - private String mWarningString; - private final int mCriticalLevel; - private int mChargeColor; - private final float[] mBoltPoints; - private final Path mBoltPath = new Path(); - - private final RectF mFrame = new RectF(); - private final RectF mButtonFrame = new RectF(); - private final RectF mBoltFrame = new RectF(); - - private final Path mShapePath = new Path(); - private final Path mClipPath = new Path(); - private final Path mTextPath = new Path(); +public class BatteryMeterView extends ImageView implements BatteryController.BatteryStateChangeCallback { + private final BatteryMeterDrawable mDrawable; private BatteryController mBatteryController; - private boolean mPowerSaveEnabled; - - private int mDarkModeBackgroundColor; - private int mDarkModeFillColor; - - private int mLightModeBackgroundColor; - private int mLightModeFillColor; - - private BatteryTracker mTracker = new BatteryTracker(); - private final SettingObserver mSettingObserver = new SettingObserver(); public BatteryMeterView(Context context) { this(context, null, 0); @@ -101,443 +38,52 @@ public class BatteryMeterView extends View implements DemoMode, public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - final Resources res = context.getResources(); TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView, defStyle, 0); final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor, context.getColor(R.color.batterymeter_frame_color)); - TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels); - TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values); - - final int N = levels.length(); - mColors = new int[2*N]; - for (int i=0; i<N; i++) { - mColors[2*i] = levels.getInt(i, 0); - mColors[2*i+1] = colors.getColor(i, 0); - } - levels.recycle(); - colors.recycle(); + mDrawable = new BatteryMeterDrawable(context, new Handler(), frameColor); atts.recycle(); - updateShowPercent(); - mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol); - mCriticalLevel = mContext.getResources().getInteger( - com.android.internal.R.integer.config_criticalBatteryWarningLevel); - mButtonHeightFraction = context.getResources().getFraction( - R.fraction.battery_button_height_fraction, 1, 1); - mSubpixelSmoothingLeft = context.getResources().getFraction( - R.fraction.battery_subpixel_smoothing_left, 1, 1); - mSubpixelSmoothingRight = context.getResources().getFraction( - R.fraction.battery_subpixel_smoothing_right, 1, 1); - - mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mFramePaint.setColor(frameColor); - mFramePaint.setDither(true); - mFramePaint.setStrokeWidth(0); - mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE); - - mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBatteryPaint.setDither(true); - mBatteryPaint.setStrokeWidth(0); - mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE); - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD); - mTextPaint.setTypeface(font); - mTextPaint.setTextAlign(Paint.Align.CENTER); - - mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mWarningTextPaint.setColor(mColors[1]); - font = Typeface.create("sans-serif", Typeface.BOLD); - mWarningTextPaint.setTypeface(font); - mWarningTextPaint.setTextAlign(Paint.Align.CENTER); - - mChargeColor = context.getColor(R.color.batterymeter_charge_color); - - mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color)); - mBoltPoints = loadBoltPoints(res); + setImageDrawable(mDrawable); + } - mDarkModeBackgroundColor = - context.getColor(R.color.dark_mode_icon_color_dual_tone_background); - mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill); - mLightModeBackgroundColor = - context.getColor(R.color.light_mode_icon_color_dual_tone_background); - mLightModeFillColor = context.getColor(R.color.light_mode_icon_color_dual_tone_fill); + @Override + public boolean hasOverlappingRendering() { + return false; } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); - - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); - filter.addAction(ACTION_LEVEL_TEST); - final Intent sticky = getContext().registerReceiver(mTracker, filter); - if (sticky != null) { - // preload the battery level - mTracker.onReceive(getContext(), sticky); - } mBatteryController.addStateChangedCallback(this); - getContext().getContentResolver().registerContentObserver( - Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver); + mDrawable.startListening(); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); - - getContext().unregisterReceiver(mTracker); mBatteryController.removeStateChangedCallback(this); - getContext().getContentResolver().unregisterContentObserver(mSettingObserver); - } - - public void setBatteryController(BatteryController batteryController) { - mBatteryController = batteryController; - mPowerSaveEnabled = mBatteryController.isPowerSave(); + mDrawable.stopListening(); } @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { - // TODO: Use this callback instead of own broadcast receiver. + setContentDescription( + getContext().getString(R.string.accessibility_battery_level, level)); } @Override public void onPowerSaveChanged() { - mPowerSaveEnabled = mBatteryController.isPowerSave(); - invalidate(); - } - private static float[] loadBoltPoints(Resources res) { - final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points); - int maxX = 0, maxY = 0; - for (int i = 0; i < pts.length; i += 2) { - maxX = Math.max(maxX, pts[i]); - maxY = Math.max(maxY, pts[i + 1]); - } - final float[] ptsF = new float[pts.length]; - for (int i = 0; i < pts.length; i += 2) { - ptsF[i] = (float)pts[i] / maxX; - ptsF[i + 1] = (float)pts[i + 1] / maxY; - } - return ptsF; } - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - mHeight = h; - mWidth = w; - mWarningTextPaint.setTextSize(h * 0.75f); - mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent; + public void setBatteryController(BatteryController mBatteryController) { + this.mBatteryController = mBatteryController; + mDrawable.setBatteryController(mBatteryController); } - private void updateShowPercent() { - mShowPercent = 0 != Settings.System.getInt(getContext().getContentResolver(), - SHOW_PERCENT_SETTING, 0); + public void setDarkIntensity(float f) { + mDrawable.setDarkIntensity(f); } - - private int getColorForLevel(int percent) { - - // If we are in power save mode, always use the normal color. - if (mPowerSaveEnabled) { - return mColors[mColors.length-1]; - } - int thresh, color = 0; - for (int i=0; i<mColors.length; i+=2) { - thresh = mColors[i]; - color = mColors[i+1]; - if (percent <= thresh) { - - // Respect tinting for "normal" level - if (i == mColors.length-2) { - return mIconTint; - } else { - return color; - } - } - } - return color; - } - - public void setDarkIntensity(float darkIntensity) { - int backgroundColor = getBackgroundColor(darkIntensity); - int fillColor = getFillColor(darkIntensity); - mIconTint = fillColor; - mFramePaint.setColor(backgroundColor); - mBoltPaint.setColor(fillColor); - mChargeColor = fillColor; - invalidate(); - } - - private int getBackgroundColor(float darkIntensity) { - return getColorForDarkIntensity( - darkIntensity, mLightModeBackgroundColor, mDarkModeBackgroundColor); - } - - private int getFillColor(float darkIntensity) { - return getColorForDarkIntensity( - darkIntensity, mLightModeFillColor, mDarkModeFillColor); - } - - private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) { - return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor); - } - - @Override - public void draw(Canvas c) { - BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker; - final int level = tracker.level; - - if (level == BatteryTracker.UNKNOWN_LEVEL) return; - - float drawFrac = (float) level / 100f; - final int pt = getPaddingTop(); - final int pl = getPaddingLeft(); - final int pr = getPaddingRight(); - final int pb = getPaddingBottom(); - final int height = mHeight - pt - pb; - final int width = mWidth - pl - pr; - - final int buttonHeight = (int) (height * mButtonHeightFraction); - - mFrame.set(0, 0, width, height); - mFrame.offset(pl, pt); - - // button-frame: area above the battery body - mButtonFrame.set( - mFrame.left + Math.round(width * 0.25f), - mFrame.top, - mFrame.right - Math.round(width * 0.25f), - mFrame.top + buttonHeight); - - mButtonFrame.top += mSubpixelSmoothingLeft; - mButtonFrame.left += mSubpixelSmoothingLeft; - mButtonFrame.right -= mSubpixelSmoothingRight; - - // frame: battery body area - mFrame.top += buttonHeight; - mFrame.left += mSubpixelSmoothingLeft; - mFrame.top += mSubpixelSmoothingLeft; - mFrame.right -= mSubpixelSmoothingRight; - mFrame.bottom -= mSubpixelSmoothingRight; - - // set the battery charging color - mBatteryPaint.setColor(tracker.plugged ? mChargeColor : getColorForLevel(level)); - - if (level >= FULL) { - drawFrac = 1f; - } else if (level <= mCriticalLevel) { - drawFrac = 0f; - } - - final float levelTop = drawFrac == 1f ? mButtonFrame.top - : (mFrame.top + (mFrame.height() * (1f - drawFrac))); - - // define the battery shape - mShapePath.reset(); - mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top); - mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top); - mShapePath.lineTo(mButtonFrame.right, mFrame.top); - mShapePath.lineTo(mFrame.right, mFrame.top); - mShapePath.lineTo(mFrame.right, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top); - - if (tracker.plugged) { - // define the bolt shape - final float bl = mFrame.left + mFrame.width() / 4.5f; - final float bt = mFrame.top + mFrame.height() / 6f; - final float br = mFrame.right - mFrame.width() / 7f; - final float bb = mFrame.bottom - mFrame.height() / 10f; - if (mBoltFrame.left != bl || mBoltFrame.top != bt - || mBoltFrame.right != br || mBoltFrame.bottom != bb) { - mBoltFrame.set(bl, bt, br, bb); - mBoltPath.reset(); - mBoltPath.moveTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); - for (int i = 2; i < mBoltPoints.length; i += 2) { - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height()); - } - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); - } - - float boltPct = (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top); - boltPct = Math.min(Math.max(boltPct, 0), 1); - if (boltPct <= BOLT_LEVEL_THRESHOLD) { - // draw the bolt if opaque - c.drawPath(mBoltPath, mBoltPaint); - } else { - // otherwise cut the bolt out of the overall shape - mShapePath.op(mBoltPath, Path.Op.DIFFERENCE); - } - } - - // compute percentage text - boolean pctOpaque = false; - float pctX = 0, pctY = 0; - String pctText = null; - if (!tracker.plugged && level > mCriticalLevel && mShowPercent) { - mTextPaint.setColor(getColorForLevel(level)); - mTextPaint.setTextSize(height * - (SINGLE_DIGIT_PERCENT ? 0.75f - : (tracker.level == 100 ? 0.38f : 0.5f))); - mTextHeight = -mTextPaint.getFontMetrics().ascent; - pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level); - pctX = mWidth * 0.5f; - pctY = (mHeight + mTextHeight) * 0.47f; - pctOpaque = levelTop > pctY; - if (!pctOpaque) { - mTextPath.reset(); - mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath); - // cut the percentage text out of the overall shape - mShapePath.op(mTextPath, Path.Op.DIFFERENCE); - } - } - - // draw the battery shape background - c.drawPath(mShapePath, mFramePaint); - - // draw the battery shape, clipped to charging level - mFrame.top = levelTop; - mClipPath.reset(); - mClipPath.addRect(mFrame, Path.Direction.CCW); - mShapePath.op(mClipPath, Path.Op.INTERSECT); - c.drawPath(mShapePath, mBatteryPaint); - - if (!tracker.plugged) { - if (level <= mCriticalLevel) { - // draw the warning text - final float x = mWidth * 0.5f; - final float y = (mHeight + mWarningTextHeight) * 0.48f; - c.drawText(mWarningString, x, y, mWarningTextPaint); - } else if (pctOpaque) { - // draw the percentage text - c.drawText(pctText, pctX, pctY, mTextPaint); - } - } - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } - - private boolean mDemoMode; - private BatteryTracker mDemoTracker = new BatteryTracker(); - - @Override - public void dispatchDemoCommand(String command, Bundle args) { - if (!mDemoMode && command.equals(COMMAND_ENTER)) { - mDemoMode = true; - mDemoTracker.level = mTracker.level; - mDemoTracker.plugged = mTracker.plugged; - } else if (mDemoMode && command.equals(COMMAND_EXIT)) { - mDemoMode = false; - postInvalidate(); - } else if (mDemoMode && command.equals(COMMAND_BATTERY)) { - String level = args.getString("level"); - String plugged = args.getString("plugged"); - if (level != null) { - mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100); - } - if (plugged != null) { - mDemoTracker.plugged = Boolean.parseBoolean(plugged); - } - postInvalidate(); - } - } - - private final class BatteryTracker extends BroadcastReceiver { - public static final int UNKNOWN_LEVEL = -1; - - // current battery status - int level = UNKNOWN_LEVEL; - String percentStr; - int plugType; - boolean plugged; - int health; - int status; - String technology; - int voltage; - int temperature; - boolean testmode = false; - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { - if (testmode && ! intent.getBooleanExtra("testmode", false)) return; - - level = (int)(100f - * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0) - / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100)); - - plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0); - plugged = plugType != 0; - health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH, - BatteryManager.BATTERY_HEALTH_UNKNOWN); - status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, - BatteryManager.BATTERY_STATUS_UNKNOWN); - technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY); - voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0); - temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0); - - setContentDescription( - context.getString(R.string.accessibility_battery_level, level)); - postInvalidate(); - } else if (action.equals(ACTION_LEVEL_TEST)) { - testmode = true; - post(new Runnable() { - int curLevel = 0; - int incr = 1; - int saveLevel = level; - int savePlugged = plugType; - Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED); - @Override - public void run() { - if (curLevel < 0) { - testmode = false; - dummy.putExtra("level", saveLevel); - dummy.putExtra("plugged", savePlugged); - dummy.putExtra("testmode", false); - } else { - dummy.putExtra("level", curLevel); - dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC - : 0); - dummy.putExtra("testmode", true); - } - getContext().sendBroadcast(dummy); - - if (!testmode) return; - - curLevel += incr; - if (curLevel == 100) { - incr *= -1; - } - postDelayed(this, 200); - } - }); - } - } - } - - private final class SettingObserver extends ContentObserver { - public SettingObserver() { - super(new Handler()); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - super.onChange(selfChange, uri); - updateShowPercent(); - postInvalidate(); - } - } - } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index 3b3593b4c477..e562682551e4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -30,16 +30,7 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.qs.QSTile.State; -import com.android.systemui.statusbar.policy.BluetoothController; -import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.FlashlightController; -import com.android.systemui.statusbar.policy.HotspotController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; -import com.android.systemui.statusbar.policy.Listenable; -import com.android.systemui.statusbar.policy.LocationController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.RotationLockController; -import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.statusbar.policy.*; import java.util.Collection; import java.util.Objects; @@ -349,6 +340,9 @@ public abstract class QSTile<TState extends State> implements Listenable { CastController getCastController(); FlashlightController getFlashlightController(); KeyguardMonitor getKeyguardMonitor(); + UserSwitcherController getUserSwitcherController(); + UserInfoController getUserInfoController(); + BatteryController getBatteryController(); public interface Callback { void onTilesChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java index 08cdc1eee65c..e575923fdd09 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java @@ -267,7 +267,7 @@ public class QSTileView extends ViewGroup { final int w = MeasureSpec.getSize(widthMeasureSpec); final int h = MeasureSpec.getSize(heightMeasureSpec); final int iconSpec = exactly(mIconSizePx); - mIcon.measure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.AT_MOST), iconSpec); + mIcon.measure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), iconSpec); switch (mType) { case QS_TYPE_QUICK: mCircle.measure( diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java index 84b05d0055ea..f676ea3499cb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java @@ -44,7 +44,8 @@ public class CustomQSTileHost extends QSTileHost { host.getRotationLockController(), host.getNetworkController(), host.getZenModeController(), host.getHotspotController(), host.getCastController(), host.getFlashlightController(), host.getUserSwitcherController(), - host.getKeyguardMonitor(), new BlankSecurityController()); + host.getUserInfoController(), host.getKeyguardMonitor(), + new BlankSecurityController(), host.getBatteryController()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java new file mode 100644 index 000000000000..8f9655d65d15 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles; + +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.BatteryMeterDrawable; +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.BatteryController; + +import java.text.NumberFormat; + +public class BatteryTile extends QSTile<QSTile.State> implements BatteryController.BatteryStateChangeCallback { + + private final BatteryMeterDrawable mDrawable; + private final BatteryController mBatteryController; + + private int mLevel; + + public BatteryTile(Host host) { + super(host); + mBatteryController = host.getBatteryController(); + mDrawable = new BatteryMeterDrawable(host.getContext(), new Handler(), + host.getContext().getColor(R.color.batterymeter_frame_color)); + mDrawable.setBatteryController(mBatteryController); + } + + @Override + protected State newTileState() { + return new QSTile.State(); + } + + @Override + public int getMetricsCategory() { + return MetricsLogger.QS_BATTERY_TILE; + } + + @Override + public void setListening(boolean listening) { + if (listening) { + mDrawable.startListening(); + mBatteryController.addStateChangedCallback(this); + } else { + mDrawable.stopListening(); + mBatteryController.removeStateChangedCallback(this); + } + } + + @Override + protected void handleClick() { + mHost.startActivityDismissingKeyguard(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY)); + } + + @Override + protected void handleUpdateState(State state, Object arg) { + int level = (arg != null) ? (Integer) arg : mLevel; + String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0); + + state.visible = true; + state.icon = new Icon() { + @Override + public Drawable getDrawable(Context context) { + return mDrawable; + } + }; + state.label = percentage; + } + + @Override + public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + mLevel = level; + refreshState((Integer) level); + } + + @Override + public void onPowerSaveChanged() { + + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java new file mode 100644 index 000000000000..3675f02b9dee --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.qs.QSTile; +import com.android.systemui.qs.QSTileView; +import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.R; + +public class QLockTile extends QSTile<QSTile.State> implements KeyguardMonitor.Callback { + + private final KeyguardMonitor mKeyguard; + + public QLockTile(Host host) { + super(host); + mKeyguard = host.getKeyguardMonitor(); + } + + @Override + public int getTileType() { + return QSTileView.QS_TYPE_QUICK; + } + + @Override + protected State newTileState() { + return new State(); + } + + @Override + public void setListening(boolean listening) { + if (listening) { + mKeyguard.addCallback(this); + } else { + mKeyguard.removeCallback(this); + } + } + + @Override + public int getMetricsCategory() { + return MetricsLogger.QS_LOCK_TILE; + } + + @Override + public void onKeyguardChanged() { + refreshState(); + } + + @Override + protected void handleClick() { + if (mKeyguard.isShowing()) { + mKeyguard.unlock(); + } else { + mKeyguard.lock(); + } + } + + @Override + protected void handleUpdateState(State state, Object arg) { + // TOD: Content description. + state.visible = true; + if (mKeyguard.isShowing()) { + state.icon = ResourceIcon.get(R.drawable.ic_qs_lock); + } else { + state.icon = ResourceIcon.get(R.drawable.ic_qs_lock_open); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java new file mode 100644 index 000000000000..3c5ab8d491b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.Pair; +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.UserInfoController; +import com.android.systemui.statusbar.policy.UserSwitcherController; + +public class UserTile extends QSTile<QSTile.State> implements UserInfoController.OnUserInfoChangedListener { + + private final UserSwitcherController mUserSwitcherController; + private final UserInfoController mUserInfoController; + private Pair<String, Drawable> mLastUpdate; + + public UserTile(Host host) { + super(host); + mUserSwitcherController = host.getUserSwitcherController(); + mUserInfoController = host.getUserInfoController(); + } + + @Override + protected State newTileState() { + return new QSTile.State(); + } + + @Override + protected void handleClick() { + showDetail(true); + } + + @Override + public DetailAdapter getDetailAdapter() { + return mUserSwitcherController.userDetailAdapter; + } + + @Override + public int getMetricsCategory() { + return MetricsLogger.QS_USER_TILE; + } + + @Override + public void setListening(boolean listening) { + if (listening) { + mUserInfoController.addListener(this); + } else { + mUserInfoController.remListener(this); + } + } + + @Override + protected void handleUpdateState(State state, Object arg) { + final Pair<String, Drawable> p = arg != null ? (Pair<String, Drawable>) arg : mLastUpdate; + state.visible = p != null; + if (!state.visible) return; + state.label = p.first; + // TODO: Better content description. + state.contentDescription = p.first; + state.icon = new Icon() { + @Override + public Drawable getDrawable(Context context) { + return p.second; + } + }; + } + + @Override + public void onUserInfoChanged(String name, Drawable picture) { + mLastUpdate = new Pair<>(name, picture); + refreshState(mLastUpdate); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 23cc8f067d83..a78351a1ffbc 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -74,6 +74,8 @@ import java.util.ArrayList; public class Recents extends SystemUI implements ActivityOptions.OnAnimationStartedListener, RecentsComponent { + public final static int EVENT_BUS_PRIORITY = 1; + final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab"; final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey"; final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility"; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 9ce6b2c764b4..1a0eb24e1fb5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -33,9 +33,12 @@ import android.view.View; import android.view.ViewStub; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent; import com.android.systemui.recents.misc.Console; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.recents.model.RecentsPackageMonitor; import com.android.systemui.recents.model.RecentsTaskLoadPlan; import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; @@ -44,16 +47,17 @@ import com.android.systemui.recents.views.RecentsView; import com.android.systemui.recents.views.SystemBarScrimViews; import com.android.systemui.recents.views.ViewAnimation; -import java.lang.ref.WeakReference; import java.util.ArrayList; /** * The main Recents activity that is started from AlternateRecentsComponent. */ -public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks, - RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks { +public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks { + + public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1; RecentsConfiguration mConfig; + RecentsPackageMonitor mPackageMonitor; long mLastTabKeyEventTime; // Top level views @@ -318,12 +322,17 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + // Register this activity with the event bus + EventBus.getDefault().register(this, EVENT_BUS_PRIORITY); + // For the non-primary user, ensure that the SystemServicesProxy and configuration is // initialized RecentsTaskLoader.initialize(this); SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); mConfig = RecentsConfiguration.initialize(this, ssp); mConfig.update(this, ssp, ssp.getWindowRect()); + mPackageMonitor = new RecentsPackageMonitor(); // Initialize the widget host (the host id is static and does not change) mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId); @@ -371,7 +380,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView registerReceiver(mServiceBroadcastReceiver, filter); // Register any broadcast receivers for the task loader - loader.registerReceivers(this, mRecentsView); + mPackageMonitor.register(this); // Update the recent tasks updateRecentsTasks(); @@ -415,7 +424,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView unregisterReceiver(mServiceBroadcastReceiver); // Unregister any broadcast receivers for the task loader - loader.unregisterReceivers(); + mPackageMonitor.unregister(); // Workaround for b/22542869, if the RecentsActivity is started again, but without going // through SystemUI, we need to reset the config launch flags to ensure that we do not @@ -437,6 +446,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Stop listening for widget package changes if there was one bound mAppWidgetHost.stopListening(); + EventBus.getDefault().unregister(this); } public void onEnterAnimationTriggered() { @@ -446,16 +456,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mRecentsView.startEnterRecentsAnimation(ctx); if (mSearchWidgetInfo != null) { - final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> cbRef = - new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>( - RecentsActivity.this); ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { @Override public void run() { // Start listening for widget package changes if there is one bound - RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = cbRef.get(); - if (cb != null) { - mAppWidgetHost.startListening(cb); + if (mAppWidgetHost != null) { + mAppWidgetHost.startListening(); } } }); @@ -572,10 +578,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mAfterPauseRunnable = r; } - /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/ + /**** EventBus events ****/ - @Override - public void refreshSearchWidgetView() { + public final void onBusEvent(AppWidgetProviderChangedEvent event) { + refreshSearchWidgetView(); + } + + private void refreshSearchWidgetView() { if (mSearchWidgetInfo != null) { SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); int searchWidgetId = ssp.getSearchAppWidgetId(this); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java index 0102332b17cb..fc96c11e4124 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java @@ -20,24 +20,19 @@ import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent; /** Our special app widget host for the Search widget */ public class RecentsAppWidgetHost extends AppWidgetHost { - /* Callbacks to notify when an app package changes */ - interface RecentsAppWidgetHostCallbacks { - void refreshSearchWidgetView(); - } - - RecentsAppWidgetHostCallbacks mCb; boolean mIsListening; public RecentsAppWidgetHost(Context context, int hostId) { super(context, hostId); } - public void startListening(RecentsAppWidgetHostCallbacks cb) { - mCb = cb; + public void startListening() { if (!mIsListening) { mIsListening = true; super.startListening(); @@ -47,11 +42,9 @@ public class RecentsAppWidgetHost extends AppWidgetHost { @Override public void stopListening() { if (mIsListening) { + mIsListening = false; super.stopListening(); } - // Ensure that we release any references to the callbacks - mCb = null; - mIsListening = false; } @Override @@ -66,8 +59,8 @@ public class RecentsAppWidgetHost extends AppWidgetHost { @Override protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) { super.onProviderChanged(appWidgetId, appWidgetInfo); - if (mIsListening && mCb != null) { - mCb.refreshSearchWidgetView(); + if (mIsListening) { + EventBus.getDefault().send(new AppWidgetProviderChangedEvent()); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java new file mode 100644 index 000000000000..4addfa57e900 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java @@ -0,0 +1,844 @@ +/* + * 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.systemui.recents.events; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.Log; +import android.util.MutableBoolean; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +/** + * Represents a subscriber, which implements various event bus handler methods. + */ +class Subscriber { + private WeakReference<Object> mSubscriber; + + long registrationTime; + + Subscriber(Object subscriber, long registrationTime) { + mSubscriber = new WeakReference<>(subscriber); + this.registrationTime = registrationTime; + } + + public String toString(int priority) { + Object sub = mSubscriber.get(); + String id = Integer.toHexString(System.identityHashCode(sub)); + return sub.getClass().getSimpleName() + " [0x" + id + ", P" + priority + "]"; + } + + public Object getReference() { + return mSubscriber.get(); + } +} + +/** + * Represents an event handler with a priority. + */ +class EventHandler { + int priority; + Subscriber subscriber; + EventHandlerMethod method; + + EventHandler(Subscriber subscriber, EventHandlerMethod method, int priority) { + this.subscriber = subscriber; + this.method = method; + this.priority = priority; + } + + @Override + public String toString() { + return subscriber.toString(priority) + " " + method.toString(); + } +} + +/** + * Represents the low level method handling a particular event. + */ +class EventHandlerMethod { + private Method mMethod; + Class<? extends EventBus.Event> eventType; + + EventHandlerMethod(Method method, Class<? extends EventBus.Event> eventType) { + mMethod = method; + mMethod.setAccessible(true); + this.eventType = eventType; + } + + public void invoke(Object target, EventBus.Event event) + throws InvocationTargetException, IllegalAccessException { + mMethod.invoke(target, event); + } + + @Override + public String toString() { + return mMethod.getName() + "(" + eventType.getSimpleName() + ")"; + } +} + +/** + * A simple in-process event bus. It is simple because we can make assumptions about the state of + * SystemUI and Recent's lifecycle. + * + * <p> + * Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber + * on the main application thread. Publishers can send() events to synchronously call subscribers + * of that event, or post() events to be processed in the next run of the {@link Looper}. In + * addition, the EventBus supports sending and handling {@link EventBus.InterprocessEvent}s + * (within the same package) implemented using standard {@link BroadcastReceiver} mechanism. + * Interprocess events must be posted using postInterprocess() to ensure that it is dispatched + * correctly across processes. + * + * <p> + * Subscribers must be registered with a particular EventBus before they will receive events, and + * handler methods must match a specific signature. + * + * <p> + * Event method signature:<ul> + * <li>Methods must be public final + * <li>Methods must return void + * <li>Methods must be called "onBusEvent" + * <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event} + * </ul> + * + * <p> + * Interprocess-Event method signature:<ul> + * <li>Methods must be public final + * <li>Methods must return void + * <li>Methods must be called "onInterprocessBusEvent" + * <li>Methods must take one parameter, of class type deriving from {@link EventBus.InterprocessEvent} + * </ul> + * </p> + * + * </p> + * Each subscriber can be registered with a given priority (default 1), and events will be dispatch + * in decreasing order of priority. For subscribers with the same priority, events will be + * dispatched by latest registration time to earliest. + * + * <p> + * Interprocess events must extend {@link EventBus.InterprocessEvent}, have a constructor which + * takes a {@link Bundle} and implement toBundle(). This allows us to serialize events to be sent + * across processes. + * + * <p> + * Caveats:<ul> + * <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so + * there must be another strong reference to the publisher for it to not get garbage-collected and + * continue receiving events. + * <li>Because the event handlers are called back using reflection, the EventBus is not intended + * for use in tight, performance criticial loops. For most user input/system callback events, this + * is generally of low enough frequency to use the EventBus. + * <li>Because the event handlers are called back using reflection, there will often be no + * references to them from actual code. The proguard configuration will be need to be updated to + * keep these extra methods: + * + * -keepclassmembers class ** { + * public void onBusEvent(**); + * public void onInterprocessBusEvent(**); + * } + * -keepclassmembers class ** extends **.EventBus$InterprocessEvent { + * public <init>(android.os.Bundle); + * } + * + * <li>Subscriber registration can be expensive depending on the subscriber's {@link Class}. This + * is only done once per class type, but if possible, it is best to pre-register an instance of + * that class beforehand or when idle. + * <li>Each event should be sent once. Events may hold internal information about the current + * dispatch, or may be queued to be dispatched on another thread (if posted from a non-main thread), + * so it may be unsafe to edit, change, or re-send the event again. + * <li>Events should follow a pattern of public-final POD (plain old data) objects, where they are + * initialized by the constructor and read by each subscriber of that event. Subscribers should + * never alter events as they are processed, and this enforces that pattern. + * </ul> + * + * <p> + * Future optimizations: + * <li>throw exception/log when a subscriber loses the reference + * <li>trace cost per registration & invocation + * <li>trace cross-process invocation + * <li>register(subscriber, Class<?>...) -- pass in exact class types you want registered + * <li>setSubscriberEventHandlerPriority(subscriber, Class<Event>, priority) + * <li>allow subscribers to implement interface, ie. EventBus.Subscriber, which lets then test a + * message before invocation (ie. check if task id == this task id) + * <li>add postOnce() which automatically debounces + * <li>add postDelayed() which delays / postDelayedOnce() which delays and bounces + * <li>consolidate register() and registerInterprocess() + * <li>sendForResult<ReturnType>(Event) to send and get a result, but who will send the + * result? + * </p> + */ +public class EventBus extends BroadcastReceiver { + + public static final String TAG = "EventBus"; + + /** + * An event super class that allows us to track internal event state across subscriber + * invocations. + * + * Events should not be edited by subscribers. + */ + public static class Event { + // Indicates that this event's dispatch should be traced and logged to logcat + boolean trace; + // Indicates that this event must be posted on the EventBus's looper thread before invocation + boolean requiresPost; + // Not currently exposed, allows a subscriber to cancel further dispatch of this event + boolean cancelled; + + // Only accessible from derived events + protected Event() {} + } + + /** + * An inter-process event super class that allows us to track user state across subscriber + * invocations. + */ + public static class InterprocessEvent extends Event { + private static final String EXTRA_USER = "_user"; + + // The user which this event originated from + public final int user; + + // Only accessible from derived events + protected InterprocessEvent(int user) { + this.user = user; + } + + /** + * Called from the event bus + */ + protected InterprocessEvent(Bundle b) { + user = b.getInt(EXTRA_USER); + } + + protected Bundle toBundle() { + Bundle b = new Bundle(); + b.putInt(EXTRA_USER, user); + return b; + } + } + + /** + * Proguard must also know, and keep, all methods matching this signature. + * + * -keepclassmembers class ** { + * public void onBusEvent(**); + * public void onInterprocessBusEvent(**); + * } + */ + private static final String METHOD_PREFIX = "onBusEvent"; + private static final String INTERPROCESS_METHOD_PREFIX = "onInterprocessBusEvent"; + + // Ensures that interprocess events can only be sent from a process holding this permission. */ + private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; + + // Used for passing event data across process boundaries + private static final String EXTRA_INTERPROCESS_EVENT_BUNDLE = "interprocess_event_bundle"; + + // The default priority of all subscribers + private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1; + + // Used for debugging everything + private static final boolean DEBUG_TRACE_ALL = false; + + // Orders the handlers by priority and registration time + private static final Comparator<EventHandler> EVENT_HANDLER_COMPARATOR = new Comparator<EventHandler>() { + @Override + public int compare(EventHandler h1, EventHandler h2) { + // Rank the handlers by priority descending, followed by registration time descending. + // aka. the later registered + if (h1.priority != h2.priority) { + return h2.priority - h1.priority; + } else { + return Long.compare(h2.subscriber.registrationTime, h1.subscriber.registrationTime); + } + } + }; + + // Used for initializing the default bus + private static final Object sLock = new Object(); + private static EventBus sDefaultBus; + + // The handler to post all events + private Handler mHandler; + + // Keep track of whether we have registered a broadcast receiver already, so that we can + // unregister ourselves before re-registering again with a new IntentFilter. + private boolean mHasRegisteredReceiver; + + /** + * Map from event class -> event handler list. Keeps track of the actual mapping from event + * to subscriber method. + */ + private HashMap<Class<? extends Event>, ArrayList<EventHandler>> mEventTypeMap = new HashMap<>(); + + /** + * Map from subscriber class -> event handler method lists. Used to determine upon registration + * of a new subscriber whether we need to read all the subscriber's methods again using + * reflection or whether we can just add the subscriber to the event type map. + */ + private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>(); + + /** + * Map from interprocess event name -> interprocess event class. Used for mapping the event + * name after receiving the broadcast, to the event type. After which a new instance is created + * and posted in the local process. + */ + private HashMap<String, Class<? extends InterprocessEvent>> mInterprocessEventNameMap = new HashMap<>(); + + /** + * Set of all currently registered subscribers + */ + private ArrayList<Subscriber> mSubscribers = new ArrayList<>(); + + // For tracing + private int mCallCount; + private long mCallDurationMicros; + + /** + * Private constructor to create an event bus for a given looper. + */ + private EventBus(Looper looper) { + mHandler = new Handler(looper); + } + + /** + * @return the default event bus for the application's main thread. + */ + public static EventBus getDefault() { + if (sDefaultBus == null) + synchronized (sLock) { + if (sDefaultBus == null) { + if (DEBUG_TRACE_ALL) { + logWithPid("New EventBus"); + } + sDefaultBus = new EventBus(Looper.getMainLooper()); + } + } + return sDefaultBus; + } + + /** + * Registers a subscriber to receive events with the default priority. + * + * @param subscriber the subscriber to handle events. If this is the first instance of the + * subscriber's class type that has been registered, the class's methods will + * be scanned for appropriate event handler methods. + */ + public void register(Object subscriber) { + registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY, null); + } + + /** + * Registers a subscriber to receive events with the given priority. + * + * @param subscriber the subscriber to handle events. If this is the first instance of the + * subscriber's class type that has been registered, the class's methods will + * be scanned for appropriate event handler methods. + * @param priority the priority that this subscriber will receive events relative to other + * subscribers + */ + public void register(Object subscriber, int priority) { + registerSubscriber(subscriber, priority, null); + } + + /** + * Explicitly registers a subscriber to receive interprocess events with the default priority. + * + * @param subscriber the subscriber to handle events. If this is the first instance of the + * subscriber's class type that has been registered, the class's methods will + * be scanned for appropriate event handler methods. + */ + public void registerInterprocessAsCurrentUser(Context context, Object subscriber) { + registerInterprocessAsCurrentUser(context, subscriber, DEFAULT_SUBSCRIBER_PRIORITY); + } + + /** + * Registers a subscriber to receive interprocess events with the given priority. + * + * @param subscriber the subscriber to handle events. If this is the first instance of the + * subscriber's class type that has been registered, the class's methods will + * be scanned for appropriate event handler methods. + * @param priority the priority that this subscriber will receive events relative to other + * subscribers + */ + public void registerInterprocessAsCurrentUser(Context context, Object subscriber, int priority) { + if (DEBUG_TRACE_ALL) { + logWithPid("registerInterprocessAsCurrentUser(" + subscriber.getClass().getSimpleName() + ")"); + } + + // Register the subscriber normally, and update the broadcast receiver filter if this is + // a new subscriber type with interprocess events + MutableBoolean hasInterprocessEventsChanged = new MutableBoolean(false); + registerSubscriber(subscriber, priority, hasInterprocessEventsChanged); + if (DEBUG_TRACE_ALL) { + logWithPid("hasInterprocessEventsChanged: " + hasInterprocessEventsChanged.value); + } + if (hasInterprocessEventsChanged.value) { + registerReceiverForInterprocessEvents(context); + } + } + + /** + * Remove all EventHandlers pointing to the specified subscriber. This does not remove the + * mapping of subscriber type to event handler method, in case new instances of this subscriber + * are registered. + */ + public void unregister(Object subscriber) { + if (DEBUG_TRACE_ALL) { + logWithPid("unregister()"); + } + + // Fail immediately if we are being called from the non-main thread + long callingThreadId = Thread.currentThread().getId(); + if (callingThreadId != mHandler.getLooper().getThread().getId()) { + throw new RuntimeException("Can not unregister() a subscriber from a non-main thread."); + } + + // Return early if this is not a registered subscriber + if (!findRegisteredSubscriber(subscriber, true /* removeFoundSubscriber */)) { + return; + } + + Class<?> subscriberType = subscriber.getClass(); + ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType); + if (subscriberMethods != null) { + // For each of the event handlers the subscriber handles, remove all references of that + // handler + for (EventHandlerMethod method : subscriberMethods) { + ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(method.eventType); + for (int i = eventHandlers.size() - 1; i >= 0; i--) { + if (eventHandlers.get(i).subscriber.getReference() == subscriber) { + eventHandlers.remove(i); + } + } + } + } + } + + /** + * Explicit unregistration for interprocess event subscribers. This actually behaves exactly + * the same as unregister() since we also do not want to stop listening for specific + * inter-process messages in case new instances of that subscriber is registered. + */ + public void unregisterInterprocess(Context context, Object subscriber) { + if (DEBUG_TRACE_ALL) { + logWithPid("unregisterInterprocess()"); + } + unregister(subscriber); + } + + /** + * Sends an event to the subscribers of the given event type immediately. This can only be + * called from the same thread as the EventBus's looper thread (for the default EventBus, this + * is the main application thread). + */ + public void send(Event event) { + // Fail immediately if we are being called from the non-main thread + long callingThreadId = Thread.currentThread().getId(); + if (callingThreadId != mHandler.getLooper().getThread().getId()) { + throw new RuntimeException("Can not send() a message from a non-main thread."); + } + + if (DEBUG_TRACE_ALL) { + logWithPid("send(" + event.getClass().getSimpleName() + ")"); + } + + // Reset the event's cancelled state + event.requiresPost = false; + event.cancelled = false; + queueEvent(event); + } + + /** + * Post a message to the subscribers of the given event type. The messages will be posted on + * the EventBus's looper thread (for the default EventBus, this is the main application thread). + */ + public void post(Event event) { + if (DEBUG_TRACE_ALL) { + logWithPid("post(" + event.getClass().getSimpleName() + ")"); + } + + // Reset the event's cancelled state + event.requiresPost = true; + event.cancelled = false; + queueEvent(event); + } + + /** Prevent post()ing an InterprocessEvent */ + @Deprecated + public void post(InterprocessEvent event) { + throw new RuntimeException("Not supported, use postInterprocess"); + } + + /** Prevent send()ing an InterprocessEvent */ + @Deprecated + public void send(InterprocessEvent event) { + throw new RuntimeException("Not supported, use postInterprocess"); + } + + /** + * Posts an interprocess event. + */ + public void postInterprocess(Context context, final InterprocessEvent event) { + if (DEBUG_TRACE_ALL) { + logWithPid("postInterprocess(" + event.getClass().getSimpleName() + ")"); + } + String eventType = event.getClass().getName(); + Bundle eventBundle = event.toBundle(); + Intent intent = new Intent(eventType); + intent.setPackage(context.getPackageName()); + intent.putExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE, eventBundle); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | + Intent.FLAG_RECEIVER_FOREGROUND); + context.sendBroadcastAsUser(intent, UserHandle.ALL); + } + + /** + * Receiver for interprocess events. + */ + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG_TRACE_ALL) { + logWithPid("onReceive(" + intent.getAction() + ", user " + UserHandle.myUserId() + ")"); + } + + Bundle eventBundle = intent.getBundleExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE); + Class<? extends InterprocessEvent> eventType = mInterprocessEventNameMap.get(intent.getAction()); + try { + Constructor<? extends InterprocessEvent> ctor = eventType.getConstructor(Bundle.class); + send((Event) ctor.newInstance(eventBundle)); + } catch (NoSuchMethodException| + InvocationTargetException| + InstantiationException| + IllegalAccessException e) { + Log.e(TAG, "Failed to create InterprocessEvent", e); + } + } + + /** + * @return a dump of the current state of the EventBus + */ + public String dump() { + StringBuilder output = new StringBuilder(); + output.append("Registered class types:"); + output.append("\n"); + for (Class<?> clz : mSubscriberTypeMap.keySet()) { + output.append("\t"); + output.append(clz.getSimpleName()); + output.append("\n"); + } + output.append("Event map:"); + output.append("\n"); + for (Class<?> clz : mEventTypeMap.keySet()) { + output.append("\t"); + output.append(clz.getSimpleName()); + output.append(" -> "); + output.append("\n"); + ArrayList<EventHandler> handlers = mEventTypeMap.get(clz); + for (EventHandler handler : handlers) { + Object subscriber = handler.subscriber.getReference(); + if (subscriber != null) { + String id = Integer.toHexString(System.identityHashCode(subscriber)); + output.append("\t\t"); + output.append(subscriber.getClass().getSimpleName()); + output.append(" [0x" + id + ", #" + handler.priority + "]"); + output.append("\n"); + } + } + } + return output.toString(); + } + + /** + * Registers a new subscriber. + * + * @return return whether or not this + */ + private void registerSubscriber(Object subscriber, int priority, + MutableBoolean hasInterprocessEventsChangedOut) { + // Fail immediately if we are being called from the non-main thread + long callingThreadId = Thread.currentThread().getId(); + if (callingThreadId != mHandler.getLooper().getThread().getId()) { + throw new RuntimeException("Can not register() a subscriber from a non-main thread."); + } + + // Return immediately if this exact subscriber is already registered + if (findRegisteredSubscriber(subscriber, false /* removeFoundSubscriber */)) { + return; + } + + long t1 = 0; + if (DEBUG_TRACE_ALL) { + t1 = SystemClock.currentTimeMicro(); + logWithPid("registerSubscriber(" + subscriber.getClass().getSimpleName() + ")"); + } + Subscriber sub = new Subscriber(subscriber, SystemClock.uptimeMillis()); + Class<?> subscriberType = subscriber.getClass(); + ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType); + if (subscriberMethods != null) { + if (DEBUG_TRACE_ALL) { + logWithPid("Subscriber class type already registered"); + } + + // If we've parsed this subscriber type before, just add to the set for all the known + // events + for (EventHandlerMethod method : subscriberMethods) { + ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(method.eventType); + eventTypeHandlers.add(new EventHandler(sub, method, priority)); + sortEventHandlersByPriority(eventTypeHandlers); + } + mSubscribers.add(sub); + return; + } else { + if (DEBUG_TRACE_ALL) { + logWithPid("Subscriber class type requires registration"); + } + + // If we are parsing this type from scratch, ensure we add it to the subscriber type + // map, and pull out he handler methods below + subscriberMethods = new ArrayList<>(); + mSubscriberTypeMap.put(subscriberType, subscriberMethods); + mSubscribers.add(sub); + } + + // Find all the valid event bus handler methods of the subscriber + MutableBoolean isInterprocessEvent = new MutableBoolean(false); + Method[] methods = subscriberType.getMethods(); + for (Method m : methods) { + Class<?>[] parameterTypes = m.getParameterTypes(); + isInterprocessEvent.value = false; + if (isValidEventBusHandlerMethod(m, parameterTypes, isInterprocessEvent)) { + Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0]; + ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType); + if (eventTypeHandlers == null) { + eventTypeHandlers = new ArrayList<>(); + mEventTypeMap.put(eventType, eventTypeHandlers); + } + if (isInterprocessEvent.value) { + try { + // Enforce that the event must have a Bundle constructor + eventType.getConstructor(Bundle.class); + + mInterprocessEventNameMap.put(eventType.getName(), + (Class<? extends InterprocessEvent>) eventType); + if (hasInterprocessEventsChangedOut != null) { + hasInterprocessEventsChangedOut.value = true; + } + } catch (NoSuchMethodException e) { + throw new RuntimeException("Expected InterprocessEvent to have a Bundle constructor"); + } + } + EventHandlerMethod method = new EventHandlerMethod(m, eventType); + EventHandler handler = new EventHandler(sub, method, priority); + eventTypeHandlers.add(handler); + subscriberMethods.add(method); + sortEventHandlersByPriority(eventTypeHandlers); + + if (DEBUG_TRACE_ALL) { + logWithPid(" * Method: " + m.getName() + + " event: " + parameterTypes[0].getSimpleName() + + " interprocess? " + isInterprocessEvent.value); + } + } + } + if (DEBUG_TRACE_ALL) { + logWithPid("Registered " + subscriber.getClass().getSimpleName() + " in " + + (SystemClock.currentTimeMicro() - t1) + " microseconds"); + } + } + + /** + * Adds a new message. + */ + private void queueEvent(final Event event) { + ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(event.getClass()); + if (eventHandlers == null) { + return; + } + // We need to clone the list in case a subscriber unregisters itself during traversal + eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone(); + for (final EventHandler eventHandler : eventHandlers) { + if (eventHandler.subscriber.getReference() != null) { + if (event.requiresPost) { + mHandler.post(new Runnable() { + @Override + public void run() { + processEvent(eventHandler, event); + } + }); + } else { + processEvent(eventHandler, event); + } + } + } + } + + /** + * Processes and dispatches the given event to the given event handler, on the thread of whoever + * calls this method. + */ + private void processEvent(final EventHandler eventHandler, final Event event) { + // Skip if the event was already cancelled + if (event.cancelled) { + if (event.trace || DEBUG_TRACE_ALL) { + logWithPid("Event dispatch cancelled"); + } + return; + } + + try { + if (event.trace || DEBUG_TRACE_ALL) { + logWithPid(" -> " + eventHandler.toString()); + } + Object sub = eventHandler.subscriber.getReference(); + if (sub != null) { + long t1 = 0; + if (DEBUG_TRACE_ALL) { + t1 = SystemClock.currentTimeMicro(); + } + eventHandler.method.invoke(sub, event); + if (DEBUG_TRACE_ALL) { + long duration = (SystemClock.currentTimeMicro() - t1); + mCallDurationMicros += duration; + mCallCount++; + logWithPid(eventHandler.method.toString() + " duration: " + duration + + " microseconds, avg: " + (mCallDurationMicros / mCallCount)); + } + } else { + Log.e(TAG, "Failed to deliver event to null subscriber"); + } + } catch (IllegalAccessException e) { + Log.e(TAG, "Failed to invoke method", e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getCause()); + } + } + + /** + * Re-registers the broadcast receiver for any new messages that we want to listen for. + */ + private void registerReceiverForInterprocessEvents(Context context) { + if (DEBUG_TRACE_ALL) { + logWithPid("registerReceiverForInterprocessEvents()"); + } + // Rebuild the receiver filter with the new interprocess events + IntentFilter filter = new IntentFilter(); + for (String eventName : mInterprocessEventNameMap.keySet()) { + filter.addAction(eventName); + if (DEBUG_TRACE_ALL) { + logWithPid(" filter: " + eventName); + } + } + // Re-register the receiver with the new filter + if (mHasRegisteredReceiver) { + context.unregisterReceiver(this); + } + context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler); + mHasRegisteredReceiver = true; + } + + /** + * Returns whether this subscriber is currently registered. If {@param removeFoundSubscriber} + * is true, then remove the subscriber before returning. + */ + private boolean findRegisteredSubscriber(Object subscriber, boolean removeFoundSubscriber) { + for (int i = mSubscribers.size() - 1; i >= 0; i--) { + Subscriber sub = mSubscribers.get(i); + if (sub.getReference() == subscriber) { + if (removeFoundSubscriber) { + mSubscribers.remove(i); + } + return true; + } + } + return false; + } + + /** + * @return whether {@param method} is a valid (normal or interprocess) event bus handler method + */ + private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes, + MutableBoolean isInterprocessEventOut) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) && + Modifier.isFinal(modifiers) && + method.getReturnType().equals(Void.TYPE) && + parameterTypes.length == 1) { + if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) && + method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) { + isInterprocessEventOut.value = true; + return true; + } else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) && + method.getName().startsWith(METHOD_PREFIX)) { + isInterprocessEventOut.value = false; + return true; + } else { + if (DEBUG_TRACE_ALL) { + if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) { + logWithPid(" Expected method take an Event-based parameter: " + method.getName()); + } else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) && + !method.getName().startsWith(METHOD_PREFIX)) { + logWithPid(" Expected method start with method prefix: " + method.getName()); + } + } + } + } else { + if (DEBUG_TRACE_ALL) { + if (!Modifier.isPublic(modifiers)) { + logWithPid(" Expected method to be public: " + method.getName()); + } else if (!Modifier.isFinal(modifiers)) { + logWithPid(" Expected method to be final: " + method.getName()); + } else if (!method.getReturnType().equals(Void.TYPE)) { + logWithPid(" Expected method to return null: " + method.getName()); + } + } + } + return false; + } + + /** + * Sorts the event handlers by priority and registration time. + */ + private void sortEventHandlersByPriority(List<EventHandler> eventHandlers) { + Collections.sort(eventHandlers, EVENT_HANDLER_COMPARATOR); + } + + /** + * Helper method to log the given {@param text} with the current process and user id. + */ + private static void logWithPid(String text) { + Log.d(TAG, "[" + android.os.Process.myPid() + ", u" + UserHandle.myUserId() + "] " + text); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java new file mode 100644 index 000000000000..52cfe18a9e26 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.RecentsAppWidgetHost; +import com.android.systemui.recents.events.EventBus; + +/** + * This is sent by the {@link RecentsAppWidgetHost} whenever the search provider widget changes, and + * subscribers can update accordingly. + */ +public class AppWidgetProviderChangedEvent extends EventBus.Event { + // Simple event +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java new file mode 100644 index 000000000000..3b68574c2830 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.model.RecentsPackageMonitor; +import com.android.systemui.recents.views.TaskStackView; + +/** + * This event is sent by {@link RecentsPackageMonitor} when a package on the the system changes. + * {@link TaskStackView}s listen for this event, and remove the tasks associated with the removed + * packages. + */ +public class PackagesChangedEvent extends EventBus.Event { + + public final RecentsPackageMonitor monitor; + public final String packageName; + public final int userId; + + public PackagesChangedEvent(RecentsPackageMonitor monitor, String packageName, int userId) { + this.monitor = monitor; + this.packageName = packageName; + this.userId = userId; + } +} 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 472303e64043..b6d25f507cb9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -697,14 +697,6 @@ public class SystemServicesProxy { ActivityManager.StackInfo stackInfo = mIam.getStackInfo(ActivityManager.HOME_STACK_ID); if (stackInfo != null) { windowRect.set(stackInfo.bounds); - // Temporary workaround, if we query this too early, occasionally, the home stack - // bounds are offset incorrectly - if (windowRect.left < 0) { - windowRect.offset(-windowRect.left, 0); - } - if (windowRect.top < 0) { - windowRect.offset(0, -windowRect.top); - } } } catch (RemoteException e) { e.printStackTrace(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java index e48e5f053f5f..8f9a2935c3a6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java @@ -21,6 +21,8 @@ import android.content.Context; import android.os.Looper; import android.os.UserHandle; import com.android.internal.content.PackageMonitor; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.PackagesChangedEvent; import com.android.systemui.recents.misc.SystemServicesProxy; import java.util.HashSet; @@ -31,18 +33,9 @@ import java.util.List; * Recents list. */ public class RecentsPackageMonitor extends PackageMonitor { - public interface PackageCallbacks { - public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, - int userId); - } - - PackageCallbacks mCb; - SystemServicesProxy mSystemServicesProxy; /** Registers the broadcast receivers with the specified callbacks. */ - public void register(Context context, PackageCallbacks cb) { - mSystemServicesProxy = new SystemServicesProxy(context); - mCb = cb; + public void register(Context context) { try { // We register for events from all users, but will cross-reference them with // packages for the current user and any profiles they have @@ -60,17 +53,13 @@ public class RecentsPackageMonitor extends PackageMonitor { } catch (IllegalStateException e) { e.printStackTrace(); } - mSystemServicesProxy = null; - mCb = null; } @Override public void onPackageRemoved(String packageName, int uid) { - if (mCb == null) return; - // Notify callbacks that a package has changed final int eventUserId = getChangingUserId(); - mCb.onPackagesChanged(this, packageName, eventUserId); + EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId)); } @Override @@ -81,11 +70,9 @@ public class RecentsPackageMonitor extends PackageMonitor { @Override public void onPackageModified(String packageName) { - if (mCb == null) return; - // Notify callbacks that a package has changed final int eventUserId = getChangingUserId(); - mCb.onPackagesChanged(this, packageName, eventUserId); + EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId)); } /** @@ -108,7 +95,8 @@ public class RecentsPackageMonitor extends PackageMonitor { // If we know that the component still exists in the package, then skip continue; } - if (mSystemServicesProxy.getActivityInfo(cn, userId) != null) { + SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); + if (ssp.getActivityInfo(cn, userId) != null) { existingComponents.add(cn); } else { removedComponents.add(cn); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index 760382ed469e..39bef81386b4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -262,8 +262,6 @@ public class RecentsTaskLoader { TaskResourceLoadQueue mLoadQueue; TaskResourceLoader mLoader; - RecentsPackageMonitor mPackageMonitor; - int mMaxThumbnailCacheSize; int mMaxIconCacheSize; int mNumVisibleTasksLoaded; @@ -293,7 +291,6 @@ public class RecentsTaskLoader { // Initialize the proxy, cache and loaders mSystemServicesProxy = new SystemServicesProxy(context); - mPackageMonitor = new RecentsPackageMonitor(); mLoadQueue = new TaskResourceLoadQueue(); mApplicationIconCache = new DrawableLruCache(iconCacheSize); mThumbnailCache = new BitmapLruCache(thumbnailCacheSize); @@ -519,17 +516,6 @@ public class RecentsTaskLoader { mLoadQueue.clearTasks(); } - /** Registers any broadcast receivers. */ - public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) { - // Register the broadcast receiver to handle messages related to packages being added/removed - mPackageMonitor.register(context, cb); - } - - /** Unregisters any broadcast receivers. */ - public void unregisterReceivers() { - mPackageMonitor.unregister(); - } - /** * Handles signals from the system, trimming memory when requested to prevent us from running * out of memory. 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 73c9be94eeae..68faccc93c14 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -59,8 +59,7 @@ import java.util.List; * This view is the the top level layout that contains TaskStacks (which are laid out according * to their SpaceNode bounds. */ -public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks, - RecentsPackageMonitor.PackageCallbacks { +public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks { private static final String TAG = "RecentsView"; @@ -732,14 +731,4 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV mCb.onTaskResize(t); } } - - /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/ - - @Override - public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) { - // Propagate this event down to each task stack view - if (mTaskStackView != null) { - mTaskStackView.onPackagesChanged(monitor, packageName, userId); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index b5f29a01c2b0..76b091cfc28b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -33,11 +33,13 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsActivityLaunchState; +import com.android.systemui.recents.RecentsActivity; import com.android.systemui.recents.RecentsConfiguration; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.PackagesChangedEvent; import com.android.systemui.recents.misc.DozeTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.RecentsPackageMonitor; import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; @@ -54,7 +56,7 @@ import java.util.List; /* The visual representation of a task stack view */ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks, TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks, - ViewPool.ViewPoolConsumer<TaskView, Task>, RecentsPackageMonitor.PackageCallbacks { + ViewPool.ViewPoolConsumer<TaskView, Task> { /** The TaskView callbacks */ interface TaskStackViewCallbacks { @@ -77,7 +79,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskStackViewTouchHandler mTouchHandler; TaskStackViewCallbacks mCb; ViewPool<TaskView, Task> mViewPool; - ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>(); + ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>(); DozeTrigger mUIDozeTrigger; DismissView mDismissAllButton; boolean mDismissAllButtonAnimating; @@ -97,9 +99,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Matrix mTmpMatrix = new Matrix(); Rect mTmpRect = new Rect(); TaskViewTransform mTmpTransform = new TaskViewTransform(); - HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<Task, TaskView>(); - ArrayList<TaskView> mTaskViews = new ArrayList<TaskView>(); - List<TaskView> mImmutableTaskViews = new ArrayList<TaskView>(); + HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<>(); + ArrayList<TaskView> mTaskViews = new ArrayList<>(); + List<TaskView> mImmutableTaskViews = new ArrayList<>(); LayoutInflater mInflater; boolean mLayersDisabled; @@ -147,6 +149,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mCb = cb; } + @Override + protected void onAttachedToWindow() { + EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); + super.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + EventBus.getDefault().unregister(this); + } + /** Sets the task stack */ void setStack(TaskStack stack) { // Set the new stack @@ -1430,13 +1444,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal postInvalidateOnAnimation(); } - /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/ + /**** EventBus Events ****/ - @Override - public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) { + public final void onBusEvent(PackagesChangedEvent event) { // Compute which components need to be removed - HashSet<ComponentName> removedComponents = monitor.computeComponentsRemoved( - mStack.getTaskKeys(), packageName, userId); + HashSet<ComponentName> removedComponents = event.monitor.computeComponentsRemoved( + mStack.getTaskKeys(), event.packageName, event.userId); // For other tasks, just remove them directly if they no longer exist ArrayList<Task> tasks = mStack.getTasks(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java index e70d146119f4..71267cd7c582 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java @@ -81,7 +81,7 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener } private void registerListener() { - if (UserSwitcherController.isUserSwitcherAvailable(mUserManager) && mUserListener == null) { + if (mUserManager.isUserSwitcherEnabled() && mUserListener == null) { final UserSwitcherController controller = mUserSwitcherController; if (controller != null) { @@ -103,7 +103,7 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener @Override public void onClick(View v) { - if (UserSwitcherController.isUserSwitcherAvailable(mUserManager)) { + if (mUserManager.isUserSwitcherEnabled()) { if (mKeyguardMode) { if (mKeyguardUserSwitcher != null) { mKeyguardUserSwitcher.show(true /* animate */); @@ -135,14 +135,14 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener private void refreshContentDescription() { String currentUser = null; - if (UserSwitcherController.isUserSwitcherAvailable(mUserManager) + if (mUserManager.isUserSwitcherEnabled() && mUserSwitcherController != null) { currentUser = mUserSwitcherController.getCurrentUserName(mContext); } String text = null; if (isClickable()) { - if (UserSwitcherController.isUserSwitcherAvailable(mUserManager)) { + if (mUserManager.isUserSwitcherEnabled()) { if (TextUtils.isEmpty(currentUser)) { text = mContext.getString(R.string.accessibility_multi_user_switch_switcher); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 98f5444b16d8..37edc28b765d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -882,7 +882,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardBottomArea.setAccessibilityController(mAccessibilityController); mNextAlarmController = new NextAlarmController(mContext); mKeyguardMonitor = new KeyguardMonitor(mContext); - if (UserSwitcherController.isUserSwitcherAvailable(UserManager.get(mContext))) { + if (UserManager.get(mContext).isUserSwitcherEnabled()) { mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor, mHandler); if (mUserSwitcherController.useFullscreenUserSwitcher()) { @@ -904,8 +904,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mBluetoothController, mLocationController, mRotationLockController, mNetworkController, mZenModeController, mHotspotController, mCastController, mFlashlightController, - mUserSwitcherController, mKeyguardMonitor, - mSecurityController); + mUserSwitcherController, mUserInfoController, mKeyguardMonitor, + mSecurityController, mBatteryController); mQSPanel.setHost(qsh); mQSPanel.setTiles(qsh.getTiles()); mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow); @@ -3001,6 +3001,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone); } + public void dismissKeyguard() { + mStatusBarKeyguardViewManager.dismiss(); + } + private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) { if (mStatusBarKeyguardViewManager.isShowing()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 385c5d5bb736..6ec94946d2e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -26,34 +26,8 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.qs.QSTile; -import com.android.systemui.qs.tiles.AirplaneModeTile; -import com.android.systemui.qs.tiles.BluetoothTile; -import com.android.systemui.qs.tiles.CastTile; -import com.android.systemui.qs.tiles.CellularTile; -import com.android.systemui.qs.tiles.ColorInversionTile; -import com.android.systemui.qs.tiles.DndTile; -import com.android.systemui.qs.tiles.FlashlightTile; -import com.android.systemui.qs.tiles.HotspotTile; -import com.android.systemui.qs.tiles.IntentTile; -import com.android.systemui.qs.tiles.LocationTile; -import com.android.systemui.qs.tiles.QAirplaneTile; -import com.android.systemui.qs.tiles.QBluetoothTile; -import com.android.systemui.qs.tiles.QFlashlightTile; -import com.android.systemui.qs.tiles.QRotationLockTile; -import com.android.systemui.qs.tiles.QWifiTile; -import com.android.systemui.qs.tiles.RotationLockTile; -import com.android.systemui.qs.tiles.WifiTile; -import com.android.systemui.statusbar.policy.BluetoothController; -import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.FlashlightController; -import com.android.systemui.statusbar.policy.HotspotController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; -import com.android.systemui.statusbar.policy.LocationController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.RotationLockController; -import com.android.systemui.statusbar.policy.SecurityController; -import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.qs.tiles.*; +import com.android.systemui.statusbar.policy.*; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -85,8 +59,10 @@ public class QSTileHost implements QSTile.Host, Tunable { private final Looper mLooper; private final FlashlightController mFlashlight; private final UserSwitcherController mUserSwitcherController; + private final UserInfoController mUserInfoController; private final KeyguardMonitor mKeyguard; private final SecurityController mSecurity; + private final BatteryController mBattery; private Callback mCallback; @@ -95,8 +71,8 @@ public class QSTileHost implements QSTile.Host, Tunable { RotationLockController rotation, NetworkController network, ZenModeController zen, HotspotController hotspot, CastController cast, FlashlightController flashlight, - UserSwitcherController userSwitcher, KeyguardMonitor keyguard, - SecurityController security) { + UserSwitcherController userSwitcher, UserInfoController userInfo, KeyguardMonitor keyguard, + SecurityController security, BatteryController battery) { mContext = context; mStatusBar = statusBar; mBluetooth = bluetooth; @@ -108,8 +84,10 @@ public class QSTileHost implements QSTile.Host, Tunable { mCast = cast; mFlashlight = flashlight; mUserSwitcherController = userSwitcher; + mUserInfoController = userInfo; mKeyguard = keyguard; mSecurity = security; + mBattery = battery; final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(), Process.THREAD_PRIORITY_BACKGROUND); @@ -203,10 +181,21 @@ public class QSTileHost implements QSTile.Host, Tunable { return mKeyguard; } + @Override public UserSwitcherController getUserSwitcherController() { return mUserSwitcherController; } + @Override + public UserInfoController getUserInfoController() { + return mUserInfoController; + } + + @Override + public BatteryController getBatteryController() { + return mBattery; + } + public SecurityController getSecurityController() { return mSecurity; } @@ -259,6 +248,8 @@ public class QSTileHost implements QSTile.Host, Tunable { else if (tileSpec.equals("location")) return new LocationTile(this); else if (tileSpec.equals("cast")) return new CastTile(this); else if (tileSpec.equals("hotspot")) return new HotspotTile(this); + else if (tileSpec.equals("user")) return new UserTile(this); + else if (tileSpec.equals("battery")) return new BatteryTile(this); // Detail only versions of wifi and bluetooth. else if (tileSpec.equals("dwifi")) return new WifiTile(this, true); else if (tileSpec.equals("dbt")) return new BluetoothTile(this, true); @@ -268,6 +259,7 @@ public class QSTileHost implements QSTile.Host, Tunable { else if (tileSpec.equals("qairplane")) return new QAirplaneTile(this); else if (tileSpec.equals("qrotation")) return new QRotationLockTile(this); else if (tileSpec.equals("qflashlight")) return new QFlashlightTile(this); + else if (tileSpec.equals("qlock")) return new QLockTile(this); // Intent tiles. else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec); else throw new IllegalArgumentException("Bad tile spec: " + tileSpec); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index d1b69aba6528..5071df0dee48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; +import android.os.Handler; import android.os.PowerManager; import android.util.Log; @@ -30,24 +31,31 @@ import java.util.ArrayList; public class BatteryController extends BroadcastReceiver { private static final String TAG = "BatteryController"; + + public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>(); private final PowerManager mPowerManager; + private final Handler mHandler; private int mLevel; private boolean mPluggedIn; private boolean mCharging; private boolean mCharged; private boolean mPowerSave; + private boolean mTestmode = false; public BatteryController(Context context) { + mHandler = new Handler(); mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING); + filter.addAction(ACTION_LEVEL_TEST); context.registerReceiver(this, filter); updatePowerSave(); @@ -71,9 +79,10 @@ public class BatteryController extends BroadcastReceiver { mChangeCallbacks.remove(cb); } - public void onReceive(Context context, Intent intent) { + public void onReceive(final Context context, Intent intent) { final String action = intent.getAction(); if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { + if (mTestmode && !intent.getBooleanExtra("testmode", false)) return; mLevel = (int)(100f * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0) / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100)); @@ -89,6 +98,38 @@ public class BatteryController extends BroadcastReceiver { updatePowerSave(); } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) { setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false)); + } else if (action.equals(ACTION_LEVEL_TEST)) { + mTestmode = true; + mHandler.post(new Runnable() { + int curLevel = 0; + int incr = 1; + int saveLevel = mLevel; + boolean savePlugged = mPluggedIn; + Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED); + @Override + public void run() { + if (curLevel < 0) { + mTestmode = false; + dummy.putExtra("level", saveLevel); + dummy.putExtra("plugged", savePlugged); + dummy.putExtra("testmode", false); + } else { + dummy.putExtra("level", curLevel); + dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC + : 0); + dummy.putExtra("testmode", true); + } + context.sendBroadcast(dummy); + + if (!mTestmode) return; + + curLevel += incr; + if (curLevel == 100) { + incr *= -1; + } + mHandler.postDelayed(this, 200); + } + }); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java index d907b0072eb1..cec0c0a25b35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java @@ -18,7 +18,8 @@ package com.android.systemui.statusbar.policy; import android.app.ActivityManager; import android.content.Context; - +import android.os.RemoteException; +import android.view.WindowManagerGlobal; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.settings.CurrentUserTracker; @@ -83,6 +84,20 @@ public final class KeyguardMonitor extends KeyguardUpdateMonitorCallback { return mCanSkipBouncer; } + public void unlock() { + try { + WindowManagerGlobal.getWindowManagerService().dismissKeyguard(); + } catch (RemoteException e) { + } + } + + public void lock() { + try { + WindowManagerGlobal.getWindowManagerService().lockNow(null /* options */); + } catch (RemoteException e) { + } + } + public void notifyKeyguardState(boolean showing, boolean secure) { if (mShowing == showing && mSecure == secure) return; mShowing = showing; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java index a8d4f131b54e..6931d1ea6a6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java @@ -71,6 +71,11 @@ public final class UserInfoController { public void addListener(OnUserInfoChangedListener callback) { mCallbacks.add(callback); + callback.onUserInfoChanged(mUserName, mUserDrawable); + } + + public void remListener(OnUserInfoChangedListener callback) { + mCallbacks.remove(callback); } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 81658941b339..e00b8907a01a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -752,9 +752,4 @@ public class UserSwitcherController { } } } - - public static boolean isUserSwitcherAvailable(UserManager um) { - return UserManager.supportsMultipleUsers() && um.isUserSwitcherEnabled(); - } - } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java index 4387b33d0797..04a51f07b1d0 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java @@ -9,8 +9,8 @@ import com.android.systemui.statusbar.phone.QSTileHost; public class QSPagingSwitch extends TunerSwitch { public static final String QS_PAGE_TILES = - "dwifi,dbt,inversion,dnd,cell,airplane,rotation,flashlight,location," - + "hotspot,qwifi,qbt,qrotation,qflashlight,qairplane,cast"; + "dwifi,dbt,dnd,cell,battery,user,rotation,flashlight,location," + + "hotspot,qwifi,qbt,qlock,qflashlight,qairplane,inversion,cast"; public QSPagingSwitch(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java index 772f86643c2b..703ee6610314 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java @@ -206,8 +206,8 @@ public class QsTuner extends Fragment implements Callback { private static class CustomHost extends QSTileHost { public CustomHost(Context context) { - super(context, null, null, null, null, null, null, null, null, null, - null, null, new BlankSecurityController()); + super(context, null, null, null, null, null, null, null, null, null, null, + null, null, new BlankSecurityController(), null); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java index 920f875576a9..9a78b6cbc023 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -39,7 +39,7 @@ import com.android.systemui.R; import com.android.systemui.qs.QSPanel; import com.android.systemui.tuner.TunerService.Tunable; -import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING; +import static com.android.systemui.BatteryMeterDrawable.SHOW_PERCENT_SETTING; public class TunerFragment extends PreferenceFragment { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index 50234b2e1b1b..715593891ccb 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -33,7 +33,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; -import com.android.systemui.BatteryMeterView; +import com.android.systemui.BatteryMeterDrawable; import com.android.systemui.DemoMode; import com.android.systemui.R; import com.android.systemui.SystemUI; @@ -135,7 +135,7 @@ public class TunerService extends SystemUI { public void clearAll() { // A couple special cases. Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null); - Settings.System.putString(mContentResolver, BatteryMeterView.SHOW_PERCENT_SETTING, null); + Settings.System.putString(mContentResolver, BatteryMeterDrawable.SHOW_PERCENT_SETTING, null); Intent intent = new Intent(DemoMode.ACTION_DEMO); intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT); mContext.sendBroadcast(intent); diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 8c830e8d9257..3759c918a519 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -611,7 +611,7 @@ nScriptIntrinsicBLAS_Double(JNIEnv *_env, jobject _this, jlong con, jlong id, ji in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } @@ -646,7 +646,7 @@ nScriptIntrinsicBLAS_Complex(JNIEnv *_env, jobject _this, jlong con, jlong id, j in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } @@ -681,7 +681,7 @@ nScriptIntrinsicBLAS_Z(JNIEnv *_env, jobject _this, jlong con, jlong id, jint fu in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } @@ -707,7 +707,7 @@ nScriptIntrinsicBLAS_BNNM(JNIEnv *_env, jobject _this, jlong con, jlong id, jint in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 87a0b808f573..b52687a78298 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -45,28 +45,28 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo /** * Flag for enabling the screen magnification feature. * - * @see #setEnabledFeatures(int) + * @see #setUserAndEnabledFeatures(int, int) */ static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001; /** * Flag for enabling the touch exploration feature. * - * @see #setEnabledFeatures(int) + * @see #setUserAndEnabledFeatures(int, int) */ static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002; /** * Flag for enabling the filtering key events feature. * - * @see #setEnabledFeatures(int) + * @see #setUserAndEnabledFeatures(int, int) */ static final int FLAG_FEATURE_FILTER_KEY_EVENTS = 0x00000004; /** * Flag for enabling "Automatically click on mouse stop" feature. * - * @see #setEnabledFeatures(int) + * @see #setUserAndEnabledFeatures(int, int) */ static final int FLAG_FEATURE_AUTOCLICK = 0x00000008; @@ -97,6 +97,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private boolean mInstalled; + private int mUserId; + private int mEnabledFeatures; private TouchExplorer mTouchExplorer; @@ -327,13 +329,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo /* do nothing */ } - void setEnabledFeatures(int enabledFeatures) { - if (mEnabledFeatures == enabledFeatures) { + void setUserAndEnabledFeatures(int userId, int enabledFeatures) { + if (mEnabledFeatures == enabledFeatures && mUserId == userId) { return; } if (mInstalled) { disableFeatures(); } + mUserId = userId; mEnabledFeatures = enabledFeatures; if (mInstalled) { enableFeatures(); @@ -350,7 +353,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo resetStreamState(); if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) { - mAutoclickController = new AutoclickController(mContext); + mAutoclickController = new AutoclickController(mContext, mUserId); addFirstEventHandler(mAutoclickController); } @@ -360,7 +363,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } if ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) { - mScreenMagnifier = new ScreenMagnifier(mContext, + mScreenMagnifier = new ScreenMagnifier(mContext, mUserId, Display.DEFAULT_DISPLAY, mAms); addFirstEventHandler(mScreenMagnifier); } @@ -386,7 +389,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo mEventHandler = handler; } - void disableFeatures() { + private void disableFeatures() { if (mAutoclickController != null) { mAutoclickController.onDestroy(); mAutoclickController = null; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index ff2a2ee3ca26..749a0807151a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1296,11 +1296,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { inputFilter = mInputFilter; setInputFilter = true; } - mInputFilter.setEnabledFeatures(flags); + mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags); } else { if (mHasInputFilter) { mHasInputFilter = false; - mInputFilter.disableFeatures(); + mInputFilter.setUserAndEnabledFeatures(userState.mUserId, 0); inputFilter = null; setInputFilter = true; } diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java index 8989625f979a..892e9da46a3d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java +++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java @@ -32,6 +32,7 @@ import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; /** * Implements "Automatically click on mouse stop" feature. @@ -51,22 +52,24 @@ import android.view.accessibility.AccessibilityEvent; * * It is expected that each instance will receive mouse events from a single mouse device. User of * the class should handle cases where multiple mouse devices are present. + * + * Each instance is associated to a single user (and it does not handle user switch itself). */ public class AutoclickController implements EventStreamTransformation { - public static final int DEFAULT_CLICK_DELAY_MS = 600; - private static final String LOG_TAG = AutoclickController.class.getSimpleName(); private EventStreamTransformation mNext; private final Context mContext; + private final int mUserId; // Lazily created on the first mouse motion event. private ClickScheduler mClickScheduler; private ClickDelayObserver mClickDelayObserver; - public AutoclickController(Context context) { + public AutoclickController(Context context, int userId) { mContext = context; + mUserId = userId; } @Override @@ -74,8 +77,9 @@ public class AutoclickController implements EventStreamTransformation { if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { if (mClickScheduler == null) { Handler handler = new Handler(mContext.getMainLooper()); - mClickScheduler = new ClickScheduler(handler, DEFAULT_CLICK_DELAY_MS); - mClickDelayObserver = new ClickDelayObserver(handler); + mClickScheduler = + new ClickScheduler(handler, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT); + mClickDelayObserver = new ClickDelayObserver(mUserId, handler); mClickDelayObserver.start(mContext.getContentResolver(), mClickScheduler); } @@ -168,9 +172,11 @@ public class AutoclickController implements EventStreamTransformation { private ContentResolver mContentResolver; private ClickScheduler mClickScheduler; + private final int mUserId; - public ClickDelayObserver(Handler handler) { + public ClickDelayObserver(int userId, Handler handler) { super(handler); + mUserId = userId; } /** @@ -199,7 +205,7 @@ public class AutoclickController implements EventStreamTransformation { mContentResolver = contentResolver; mClickScheduler = clickScheduler; mContentResolver.registerContentObserver(mAutoclickDelaySettingUri, false, this, - UserHandle.USER_ALL); + mUserId); // Initialize mClickScheduler's initial delay value. onChange(true, mAutoclickDelaySettingUri); @@ -222,10 +228,9 @@ public class AutoclickController implements EventStreamTransformation { @Override public void onChange(boolean selfChange, Uri uri) { if (mAutoclickDelaySettingUri.equals(uri)) { - // TODO: Plumb current user id down to here and use getIntForUser. - int delay = Settings.Secure.getInt( + int delay = Settings.Secure.getIntForUser( mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, - DEFAULT_CLICK_DELAY_MS); + AccessibilityManager.AUTOCLICK_DELAY_DEFAULT, mUserId); mClickScheduler.updateDelay(delay); } } diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java index 37276bdb8145..8845bc0246ba 100644 --- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java +++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java @@ -29,7 +29,6 @@ import android.os.AsyncTask; import android.os.Binder; import android.os.Handler; import android.os.Message; -import android.os.SystemClock; import android.provider.Settings; import android.text.TextUtils; import android.util.Property; @@ -137,6 +136,8 @@ public final class ScreenMagnifier implements WindowManagerInternal.Magnificatio private final AccessibilityManagerService mAms; + private final int mUserId; + private final int mTapTimeSlop = ViewConfiguration.getJumpTapTimeout(); private final int mMultiTapTimeSlop; private final int mTapDistanceSlop; @@ -188,8 +189,10 @@ public final class ScreenMagnifier implements WindowManagerInternal.Magnificatio } }; - public ScreenMagnifier(Context context, int displayId, AccessibilityManagerService service) { + public ScreenMagnifier(Context context, int userId, int displayId, + AccessibilityManagerService service) { mContext = context; + mUserId = userId; mWindowManager = LocalServices.getService(WindowManagerInternal.class); mAms = service; @@ -813,33 +816,12 @@ public final class ScreenMagnifier implements WindowManagerInternal.Magnificatio while (mDelayedEventQueue != null) { MotionEventInfo info = mDelayedEventQueue; mDelayedEventQueue = info.mNext; - final long offset = SystemClock.uptimeMillis() - info.mCachedTimeMillis; - MotionEvent event = obtainEventWithOffsetTimeAndDownTime(info.mEvent, offset); - MotionEvent rawEvent = obtainEventWithOffsetTimeAndDownTime(info.mRawEvent, offset); - ScreenMagnifier.this.onMotionEvent(event, rawEvent, info.mPolicyFlags); - event.recycle(); - rawEvent.recycle(); + ScreenMagnifier.this.onMotionEvent(info.mEvent, info.mRawEvent, + info.mPolicyFlags); info.recycle(); } } - private MotionEvent obtainEventWithOffsetTimeAndDownTime(MotionEvent event, long offset) { - final int pointerCount = event.getPointerCount(); - PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount); - PointerProperties[] properties = getTempPointerPropertiesWithMinSize(pointerCount); - for (int i = 0; i < pointerCount; i++) { - event.getPointerCoords(i, coords[i]); - event.getPointerProperties(i, properties[i]); - } - final long downTime = event.getDownTime() + offset; - final long eventTime = event.getEventTime() + offset; - return MotionEvent.obtain(downTime, eventTime, - event.getAction(), pointerCount, properties, coords, - event.getMetaState(), event.getButtonState(), - 1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(), - event.getSource(), event.getFlags()); - } - private void clearDelayedMotionEvents() { while (mDelayedEventQueue != null) { MotionEventInfo info = mDelayedEventQueue; @@ -882,17 +864,17 @@ public final class ScreenMagnifier implements WindowManagerInternal.Magnificatio new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { - Settings.Secure.putFloat(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale); + Settings.Secure.putFloatForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale, mUserId); return null; } }.execute(); } private float getPersistedScale() { - return Settings.Secure.getFloat(mContext.getContentResolver(), + return Settings.Secure.getFloatForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, - DEFAULT_MAGNIFICATION_SCALE); + DEFAULT_MAGNIFICATION_SCALE, mUserId); } private static boolean isScreenMagnificationAutoUpdateEnabled(Context context) { @@ -915,7 +897,6 @@ public final class ScreenMagnifier implements WindowManagerInternal.Magnificatio public MotionEvent mEvent; public MotionEvent mRawEvent; public int mPolicyFlags; - public long mCachedTimeMillis; public static MotionEventInfo obtain(MotionEvent event, MotionEvent rawEvent, int policyFlags) { @@ -940,7 +921,6 @@ public final class ScreenMagnifier implements WindowManagerInternal.Magnificatio mEvent = MotionEvent.obtain(event); mRawEvent = MotionEvent.obtain(rawEvent); mPolicyFlags = policyFlags; - mCachedTimeMillis = SystemClock.uptimeMillis(); } public void recycle() { @@ -964,7 +944,6 @@ public final class ScreenMagnifier implements WindowManagerInternal.Magnificatio mRawEvent.recycle(); mRawEvent = null; mPolicyFlags = 0; - mCachedTimeMillis = 0; } } diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java index c3f5c43daeb7..f6b32f7a32ec 100644 --- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java @@ -112,12 +112,6 @@ class TouchExplorer implements EventStreamTransformation { // Timeout before trying to decide what the user is trying to do. private final int mDetermineUserIntentTimeout; - // Timeout within which we try to detect a tap. - private final int mTapTimeout; - - // Slop between the down and up tap to be a tap. - private final int mTouchSlop; - // Slop between the first and second tap to be a double tap. private final int mDoubleTapSlop; @@ -142,9 +136,6 @@ class TouchExplorer implements EventStreamTransformation { // Command for delayed sending of touch interaction end events. private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed; - // Command for delayed sending of a long press. - private final PerformLongPressDelayed mPerformLongPressDelayed; - // Command for exiting gesture detection mode after a timeout. private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed; @@ -173,9 +164,6 @@ class TouchExplorer implements EventStreamTransformation { // Handle to the accessibility manager service. private final AccessibilityManagerService mAms; - // Temporary rectangle to avoid instantiation. - private final Rect mTempRect = new Rect(); - // Temporary point to avoid instantiation. private final Point mTempPoint = new Point(); @@ -226,12 +214,9 @@ class TouchExplorer implements EventStreamTransformation { mAms = service; mReceivedPointerTracker = new ReceivedPointerTracker(); mInjectedPointerTracker = new InjectedPointerTracker(); - mTapTimeout = ViewConfiguration.getTapTimeout(); mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout(); - mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); mHandler = new Handler(context.getMainLooper()); - mPerformLongPressDelayed = new PerformLongPressDelayed(); mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed(); mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures); mGestureLibrary.setOrientationStyle(8); @@ -299,7 +284,6 @@ class TouchExplorer implements EventStreamTransformation { // Remove all pending callbacks. mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); - mPerformLongPressDelayed.cancel(); mExitGestureDetectionModeDelayed.cancel(); mSendTouchExplorationEndDelayed.cancel(); mSendTouchInteractionEndDelayed.cancel(); @@ -437,7 +421,6 @@ class TouchExplorer implements EventStreamTransformation { // we resent the delayed callback and wait again. mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); - mPerformLongPressDelayed.cancel(); if (mSendTouchExplorationEndDelayed.isPending()) { mSendTouchExplorationEndDelayed.forceSendAndRemove(); @@ -447,18 +430,7 @@ class TouchExplorer implements EventStreamTransformation { mSendTouchInteractionEndDelayed.forceSendAndRemove(); } - // If we have the first tap, schedule a long press and break - // since we do not want to schedule hover enter because - // the delayed callback will kick in before the long click. - // This would lead to a state transition resulting in long - // pressing the item below the double taped area which is - // not necessary where accessibility focus is. - if (mDoubleTapDetector.firstTapDetected()) { - // We got a tap now post a long press action. - mPerformLongPressDelayed.post(event, policyFlags); - break; - } - if (!mTouchExplorationInProgress) { + if (!mDoubleTapDetector.firstTapDetected() && !mTouchExplorationInProgress) { if (!mSendHoverEnterAndMoveDelayed.isPending()) { // Deliver hover enter with a delay to have a chance // to detect what the user is trying to do. @@ -478,7 +450,6 @@ class TouchExplorer implements EventStreamTransformation { // decide what we will actually do next. mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); - mPerformLongPressDelayed.cancel(); } break; case MotionEvent.ACTION_MOVE: { final int pointerId = receivedTracker.getPrimaryPointerId(); @@ -521,7 +492,6 @@ class TouchExplorer implements EventStreamTransformation { mVelocityTracker.clear(); mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); - mPerformLongPressDelayed.cancel(); mExitGestureDetectionModeDelayed.post(); // Send accessibility event to announce the start // of gesture recognition. @@ -532,28 +502,12 @@ class TouchExplorer implements EventStreamTransformation { // exploring so start sending events. mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); mSendHoverExitDelayed.cancel(); - mPerformLongPressDelayed.cancel(); sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); } break; } } else { - // Cancel the long press if pending and the user - // moved more than the slop. - if (mPerformLongPressDelayed.isPending()) { - final float deltaX = - receivedTracker.getReceivedPointerDownX(pointerId) - - rawEvent.getX(pointerIndex); - final float deltaY = - receivedTracker.getReceivedPointerDownY(pointerId) - - rawEvent.getY(pointerIndex); - final double moveDelta = Math.hypot(deltaX, deltaY); - // The user has moved enough for us to decide. - if (moveDelta > mTouchSlop) { - mPerformLongPressDelayed.cancel(); - } - } if (mTouchExplorationInProgress) { sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, @@ -569,9 +523,7 @@ class TouchExplorer implements EventStreamTransformation { // scheduled sending events. mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); - mPerformLongPressDelayed.cancel(); } else { - mPerformLongPressDelayed.cancel(); if (mTouchExplorationInProgress) { // If the user is touch exploring the second pointer may be // performing a double tap to activate an item without need @@ -620,9 +572,7 @@ class TouchExplorer implements EventStreamTransformation { // scheduled sending events. mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); - mPerformLongPressDelayed.cancel(); } else { - mPerformLongPressDelayed.cancel(); // We are sending events so send exit and gesture // end since we transition to another state. sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); @@ -643,7 +593,6 @@ class TouchExplorer implements EventStreamTransformation { final int pointerId = event.getPointerId(event.getActionIndex()); final int pointerIdBits = (1 << pointerId); - mPerformLongPressDelayed.cancel(); mVelocityTracker.clear(); if (mSendHoverEnterAndMoveDelayed.isPending()) { @@ -1110,6 +1059,7 @@ class TouchExplorer implements EventStreamTransformation { private final GestureDetector mGestureDetector; private boolean mFirstTapDetected; private boolean mDoubleTapDetected; + private int mPolicyFlags; DoubleTapDetector(Context context) { mGestureDetector = new GestureDetector(context, this); @@ -1117,6 +1067,7 @@ class TouchExplorer implements EventStreamTransformation { } public void onMotionEvent(MotionEvent event, int policyFlags) { + mPolicyFlags = policyFlags; switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mDoubleTapDetected = false; @@ -1135,6 +1086,11 @@ class TouchExplorer implements EventStreamTransformation { } @Override + public void onLongPress(MotionEvent e) { + maybeSendLongPress(e, mPolicyFlags); + } + + @Override public boolean onSingleTapUp(MotionEvent event) { mFirstTapDetected = true; return false; @@ -1154,6 +1110,38 @@ class TouchExplorer implements EventStreamTransformation { return true; } + private void maybeSendLongPress(MotionEvent event, int policyFlags) { + if (!mDoubleTapDetected) { + return; + } + + clear(); + + // Pointers should not be zero when running this command. + if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) { + return; + } + + final int pointerIndex = event.getActionIndex(); + final int pointerId = event.getPointerId(pointerIndex); + + Point clickLocation = mTempPoint; + final int result = computeClickLocation(clickLocation); + + if (result == CLICK_LOCATION_NONE) { + return; + } + + mLongPressingPointerId = pointerId; + mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x; + mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y; + + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); + + mCurrentState = STATE_DELEGATING; + sendDownForAllNotInjectedPointers(event, policyFlags); + } + private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) { if (!mDoubleTapDetected) { return; @@ -1169,7 +1157,6 @@ class TouchExplorer implements EventStreamTransformation { // Remove pending event deliveries. mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); - mPerformLongPressDelayed.cancel(); if (mSendTouchExplorationEndDelayed.isPending()) { mSendTouchExplorationEndDelayed.forceSendAndRemove(); @@ -1178,8 +1165,8 @@ class TouchExplorer implements EventStreamTransformation { mSendTouchInteractionEndDelayed.forceSendAndRemove(); } - final int pointerId = event.getPointerId(event.getActionIndex()); - final int pointerIndex = event.findPointerIndex(pointerId); + final int pointerIndex = event.getActionIndex(); + final int pointerId = event.getPointerId(pointerIndex); Point clickLocation = mTempPoint; final int result = computeClickLocation(clickLocation); @@ -1306,65 +1293,6 @@ class TouchExplorer implements EventStreamTransformation { } /** - * Class for delayed sending of long press. - */ - private final class PerformLongPressDelayed implements Runnable { - private MotionEvent mEvent; - private int mPolicyFlags; - - public void post(MotionEvent prototype, int policyFlags) { - mEvent = MotionEvent.obtain(prototype); - mPolicyFlags = policyFlags; - mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout()); - } - - public void cancel() { - if (mEvent != null) { - mHandler.removeCallbacks(this); - clear(); - } - } - - private boolean isPending() { - return mHandler.hasCallbacks(this); - } - - @Override - public void run() { - // Pointers should not be zero when running this command. - if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) { - return; - } - - final int pointerId = mEvent.getPointerId(mEvent.getActionIndex()); - final int pointerIndex = mEvent.findPointerIndex(pointerId); - - Point clickLocation = mTempPoint; - final int result = computeClickLocation(clickLocation); - - if (result == CLICK_LOCATION_NONE) { - return; - } - - mLongPressingPointerId = pointerId; - mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocation.x; - mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocation.y; - - sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); - - mCurrentState = STATE_DELEGATING; - sendDownForAllNotInjectedPointers(mEvent, mPolicyFlags); - clear(); - } - - private void clear() { - mEvent.recycle(); - mEvent = null; - mPolicyFlags = 0; - } - } - - /** * Class for delayed sending of hover enter and move events. */ class SendHoverEnterAndMoveDelayed implements Runnable { diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index f2459852d706..2aa039064410 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -55,7 +55,8 @@ public class GestureLauncherService extends SystemService { * Time in milliseconds in which the power button must be pressed twice so it will be considered * as a camera launch. */ - private static final long CAMERA_POWER_DOUBLE_TAP_TIME_MS = 300; + private static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300; + private static final long CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS = 120; /** The listener that receives the gesture event. */ private final GestureEventListener mGestureListener = new GestureEventListener(); @@ -256,14 +257,16 @@ public class GestureLauncherService extends SystemService { synchronized (this) { doubleTapInterval = event.getEventTime() - mLastPowerDown; if (mCameraDoubleTapPowerEnabled - && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_TIME_MS) { + && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS + && doubleTapInterval > CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS) { launched = true; intercept = interactive; } mLastPowerDown = event.getEventTime(); } if (launched) { - Slog.i(TAG, "Power button double tap gesture detected, launching camera."); + Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval=" + + doubleTapInterval + "ms"); launched = handleCameraLaunchGesture(false /* useWakelock */, StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP); if (launched) { diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 9dad7a181a06..ab1d775e3c32 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -37,6 +37,7 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManagerNative; import android.app.AlertDialog; import android.app.AppGlobals; @@ -280,8 +281,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean mSystemReady; /** - * Id of the currently selected input method. + * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method. + * method. This is to be synchronized with the secure settings keyed with + * {@link Settings.Secure#DEFAULT_INPUT_METHOD}. + * + * <p>This can be transiently {@code null} when the system is re-initializing input method + * settings, e.g., the system locale is just changed.</p> + * + * <p>Note that {@link #mCurId} is used to track which IME is being connected to + * {@link InputMethodManagerService}.</p> + * + * @see #mCurId */ + @Nullable String mCurMethodId; /** @@ -311,9 +323,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub EditorInfo mCurAttribute; /** - * The input method ID of the input method service that we are currently + * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently * connected to or in the process of connecting to. + * + * <p>This can be {@code null} when no input method is connected.</p> + * + * @see #mCurMethodId */ + @Nullable String mCurId; /** @@ -918,7 +935,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub || (newLocale != null && !newLocale.equals(mLastSystemLocale))) { if (!updateOnlyWhenLocaleChanged) { hideCurrentInputLocked(0, null); - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } if (DEBUG) { @@ -1474,7 +1490,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub channel.dispose(); } - void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) { + void unbindCurrentMethodLocked(boolean resetCurrentMethodAndClient, boolean savePosition) { + if (resetCurrentMethodAndClient) { + mCurMethodId = null; + } + if (mVisibleBound) { mContext.unbindService(mVisibleConnection); mVisibleBound = false; @@ -1501,9 +1521,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurId = null; clearCurMethodLocked(); - if (reportToClient && mCurClient != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); + if (resetCurrentMethodAndClient) { + unbindCurrentClientLocked(); } } @@ -1857,13 +1876,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id)); } catch (IllegalArgumentException e) { Slog.w(TAG, "Unknown input method from prefs: " + id, e); - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } mShortcutInputMethodsAndSubtypes.clear(); } else { // There is no longer an input method set, so stop any current one. - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } // Here is not the perfect place to reset the switching controller. Ideally diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bd10c63edbda..617264c29efd 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -20,8 +20,11 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.DOCKED_STACK_ID; +import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.HOME_STACK_ID; import static android.app.ActivityManager.INVALID_STACK_ID; +import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; @@ -8685,7 +8688,32 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found"); return; } - mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode); + // Place the task in the right stack if it isn't there already based on + // the requested bounds. + // The stack transition logic is: + // - a null bounds on a freeform task moves that task to fullscreen + // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves + // that task to freeform + // - otherwise the task is not moved + // Note it's not allowed to resize a home stack task, or a docked task. + int stackId = task.stack.mStackId; + if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID) { + throw new IllegalArgumentException("trying to resizeTask on a " + + "home or docked task"); + } + if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) { + stackId = FULLSCREEN_WORKSPACE_STACK_ID; + } else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) { + stackId = FREEFORM_WORKSPACE_STACK_ID; + } + boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0; + if (stackId != task.stack.mStackId) { + mStackSupervisor.moveTaskToStackUncheckedLocked( + task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask"); + preserveWindow = false; + } + + mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode, preserveWindow); } } finally { Binder.restoreCallingIdentity(ident); @@ -11893,7 +11921,8 @@ public final class ActivityManagerService extends ActivityManagerNative updateCurrentProfileIdsLocked(); mRecentTasks.clear(); - mRecentTasks.addAll(mTaskPersister.restoreTasksLocked()); + mRecentTasks.addAll(mTaskPersister.restoreTasksLocked( + getUserManagerLocked().getUserIds())); mRecentTasks.cleanupLocked(UserHandle.USER_ALL); mTaskPersister.startPersisting(); @@ -20644,9 +20673,6 @@ public final class ActivityManagerService extends ActivityManagerNative // Kill all the processes for the user. forceStopUserLocked(userId, "finish user"); } - - // Explicitly remove the old information in mRecentTasks. - mRecentTasks.removeTasksForUserLocked(userId); } for (int i=0; i<callbacks.size(); i++) { @@ -20665,6 +20691,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } + void onUserRemovedLocked(int userId) { + mRecentTasks.removeTasksForUserLocked(userId); + } + @Override public UserInfo getCurrentUser() { if ((checkCallingPermission(INTERACT_ACROSS_USERS) @@ -20949,6 +20979,13 @@ public final class ActivityManagerService extends ActivityManagerNative return homeActivity == null ? null : homeActivity.realActivity; } } + + @Override + public void onUserRemoved(int userId) { + synchronized (ActivityManagerService.this) { + ActivityManagerService.this.onUserRemovedLocked(userId); + } + } } private final class SleepTokenImpl extends SleepToken { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 6b5f2056ec82..3a368ac59369 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3058,7 +3058,7 @@ public final class ActivityStackSupervisor implements DisplayListener { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } - void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode) { + void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) { if (!task.mResizeable) { Slog.w(TAG, "resizeTask: task " + task + " not resizeable."); return; @@ -3066,7 +3066,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // If this is a forced resize, let it go through even if the bounds is not changing, // as we might need a relayout due to surface size change (to/from fullscreen). - final boolean forced = (resizeMode == RESIZE_MODE_FORCED); + final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0; if (task.mBounds != null && task.mBounds.equals(bounds) && !forced) { // Nothing to do here... return; @@ -3084,25 +3084,14 @@ public final class ActivityStackSupervisor implements DisplayListener { return; } - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId); + // Do not move the task to another stack here. + // This method assumes that the task is already placed in the right stack. + // we do not mess with that decision and we only do the resize! - // The stack of a task is determined by its size (fullscreen vs non-fullscreen). - // Place the task in the right stack if it isn't there already based on the requested - // bounds. - int stackId = task.stack.mStackId; - if (bounds == null && stackId != FULLSCREEN_WORKSPACE_STACK_ID) { - stackId = FULLSCREEN_WORKSPACE_STACK_ID; - } else if (bounds != null - && stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) { - stackId = FREEFORM_WORKSPACE_STACK_ID; - } - final boolean changedStacks = stackId != task.stack.mStackId; - if (changedStacks) { - moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask"); - } + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId); final Configuration overrideConfig = task.updateOverrideConfiguration(bounds); - // This variable holds information whether the configuration didn't change in a signficant + // This variable holds information whether the configuration didn't change in a significant // way and the activity was kept the way it was. If it's false, it means the activity had // to be relaunched due to configuration change. boolean kept = true; @@ -3110,9 +3099,6 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityRecord r = task.topRunningActivityLocked(null); if (r != null) { final ActivityStack stack = task.stack; - final boolean preserveWindow = !changedStacks && - (resizeMode == RESIZE_MODE_USER - || resizeMode == RESIZE_MODE_SYSTEM_SCREEN_ROTATION); kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow); // All other activities must be made visible with their correct configuration. ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS); @@ -3204,7 +3190,7 @@ public final class ActivityStackSupervisor implements DisplayListener { * @param reason Reason the task is been moved. * @return The stack the task was moved to. */ - private ActivityStack moveTaskToStackUncheckedLocked( + ActivityStack moveTaskToStackUncheckedLocked( TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) { final ActivityRecord r = task.getTopActivity(); final boolean wasFocused = isFrontStack(task.stack) && (topRunningActivityLocked() == r); @@ -3261,12 +3247,15 @@ public final class ActivityStackSupervisor implements DisplayListener { // Make sure the task has the appropriate bounds/size for the stack it is in. if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) { - resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM); + resizeTaskLocked(task, stack.mBounds, + RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS); } else if (stackId == FREEFORM_WORKSPACE_STACK_ID && task.mBounds == null && task.mLastNonFullscreenBounds != null) { - resizeTaskLocked(task, task.mLastNonFullscreenBounds, RESIZE_MODE_SYSTEM); + resizeTaskLocked(task, task.mLastNonFullscreenBounds, + RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS); } else if (stackId == DOCKED_STACK_ID) { - resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM); + resizeTaskLocked(task, stack.mBounds, + RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS); } // The task might have already been running and its visibility needs to be synchronized with diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index aa154a7c3850..871331b770e2 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -35,6 +35,7 @@ import android.util.SparseArray; import android.util.Xml; import android.os.Process; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; @@ -330,7 +331,7 @@ public class TaskPersister { return null; } - ArrayList<TaskRecord> restoreTasksLocked() { + ArrayList<TaskRecord> restoreTasksLocked(final int [] validUserIds) { final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>(); ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>(); @@ -362,15 +363,18 @@ public class TaskPersister { if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + task); if (task != null) { - task.isPersistable = true; // XXX Don't add to write queue... there is no reason to write // out the stuff we just read, if we don't write it we will // read the same thing again. //mWriteQueue.add(new TaskWriteQueueItem(task)); - tasks.add(task); final int taskId = task.taskId; - recoveredTaskIds.add(taskId); mStackSupervisor.setNextTaskId(taskId); + // Check if it's a valid user id. Don't add tasks for removed users. + if (ArrayUtils.contains(validUserIds, task.userId)) { + task.isPersistable = true; + tasks.add(task); + recoveredTaskIds.add(taskId); + } } else { Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " + fileToString(taskFile)); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 43f5baab793f..7e14b2b78f0f 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -1193,7 +1193,7 @@ final class TaskRecord { mOverrideConfig = Configuration.EMPTY; } else { mBounds = new Rect(bounds); - if (stack.mStackId != DOCKED_STACK_ID) { + if (stack == null || stack.mStackId != DOCKED_STACK_ID) { mLastNonFullscreenBounds = mBounds; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index e49a7e49822c..c4b57f1945a1 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -501,7 +501,6 @@ public class AudioService extends IAudioService.Stub { private volatile IRingtonePlayer mRingtonePlayer; private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED; - private int mDeviceRotation = Surface.ROTATION_0; // Request to override default use of A2DP for media. private boolean mBluetoothA2dpEnabled; @@ -545,8 +544,6 @@ public class AudioService extends IAudioService.Stub { // If absolute volume is supported in AVRCP device private boolean mAvrcpAbsVolSupported = false; - private AudioOrientationEventListener mOrientationListener; - private static Long mLastDeviceConnectMsgTime = new Long(0); private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate; @@ -669,15 +666,7 @@ public class AudioService extends IAudioService.Stub { } mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false); if (mMonitorRotation) { - mDeviceRotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)) - .getDefaultDisplay().getRotation(); - Log.v(TAG, "monitoring device rotation, initial=" + mDeviceRotation); - - mOrientationListener = new AudioOrientationEventListener(mContext); - mOrientationListener.enable(); - - // initialize rotation in AudioSystem - setRotationForAudioSystem(); + RotationHelper.init(mContext, mAudioHandler); } context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null); @@ -805,7 +794,7 @@ public class AudioService extends IAudioService.Stub { setOrientationForAudioSystem(); } if (mMonitorRotation) { - setRotationForAudioSystem(); + RotationHelper.updateOrientation(); } synchronized (mBluetoothA2dpEnabledLock) { @@ -1058,25 +1047,6 @@ public class AudioService extends IAudioService.Stub { return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex(); } - private class AudioOrientationEventListener - extends OrientationEventListener { - public AudioOrientationEventListener(Context context) { - super(context); - } - - @Override - public void onOrientationChanged(int orientation) { - //Even though we're responding to phone orientation events, - //use display rotation so audio stays in sync with video/dialogs - int newRotation = ((WindowManager) mContext.getSystemService( - Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); - if (newRotation != mDeviceRotation) { - mDeviceRotation = newRotation; - setRotationForAudioSystem(); - } - } - } - /////////////////////////////////////////////////////////////////////////// // IPC methods /////////////////////////////////////////////////////////////////////////// @@ -5066,14 +5036,13 @@ public class AudioService extends IAudioService.Stub { } } else if (action.equals(Intent.ACTION_SCREEN_ON)) { if (mMonitorRotation) { - mOrientationListener.onOrientationChanged(0); //argument is ignored anyway - mOrientationListener.enable(); + RotationHelper.enable(); } AudioSystem.setParameters("screen_state=on"); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { if (mMonitorRotation) { //reduce wakeups (save current) by only listening when display is on - mOrientationListener.disable(); + RotationHelper.disable(); } AudioSystem.setParameters("screen_state=off"); } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { @@ -5322,6 +5291,7 @@ public class AudioService extends IAudioService.Stub { } } + //TODO move to an external "orientation helper" class private void setOrientationForAudioSystem() { switch (mDeviceOrientation) { case Configuration.ORIENTATION_LANDSCAPE: @@ -5345,26 +5315,6 @@ public class AudioService extends IAudioService.Stub { } } - private void setRotationForAudioSystem() { - switch (mDeviceRotation) { - case Surface.ROTATION_0: - AudioSystem.setParameters("rotation=0"); - break; - case Surface.ROTATION_90: - AudioSystem.setParameters("rotation=90"); - break; - case Surface.ROTATION_180: - AudioSystem.setParameters("rotation=180"); - break; - case Surface.ROTATION_270: - AudioSystem.setParameters("rotation=270"); - break; - default: - Log.e(TAG, "Unknown device rotation"); - } - } - - // Handles request to override default use of A2DP for media. // Must be called synchronized on mConnectedDevices public void setBluetoothA2dpOnInt(boolean on) { diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java new file mode 100644 index 000000000000..f03e6c7c545a --- /dev/null +++ b/services/core/java/com/android/server/audio/RotationHelper.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.audio; + +import android.content.Context; +import android.media.AudioSystem; +import android.os.Handler; +import android.util.Log; +import android.view.OrientationEventListener; +import android.view.Surface; +import android.view.WindowManager; + +import com.android.server.policy.WindowOrientationListener; + +/** + * Class to handle device rotation events for AudioService, and forward device rotation + * to the audio HALs through AudioSystem. + * + * The role of this class is to monitor device orientation changes, and upon rotation, + * verify the UI orientation. In case of a change, send the new orientation, in increments + * of 90deg, through AudioSystem. + * + * Note that even though we're responding to device orientation events, we always + * query the display rotation so audio stays in sync with video/dialogs. This is + * done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE. + */ +class RotationHelper { + + private static final String TAG = "AudioService.RotationHelper"; + + private static AudioOrientationListener sOrientationListener; + private static AudioWindowOrientationListener sWindowOrientationListener; + + private static final Object sRotationLock = new Object(); + private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock + + private static Context sContext; + + /** + * post conditions: + * - (sWindowOrientationListener != null) xor (sOrientationListener != null) + * - sWindowOrientationListener xor sOrientationListener is enabled + * - sContext != null + */ + static void init(Context context, Handler handler) { + if (context == null) { + throw new IllegalArgumentException("Invalid null context"); + } + sContext = context; + sWindowOrientationListener = new AudioWindowOrientationListener(context, handler); + sWindowOrientationListener.enable(); + if (!sWindowOrientationListener.canDetectOrientation()) { + // cannot use com.android.server.policy.WindowOrientationListener, revert to public + // orientation API + Log.i(TAG, "Not using WindowOrientationListener, reverting to OrientationListener"); + sWindowOrientationListener.disable(); + sWindowOrientationListener = null; + sOrientationListener = new AudioOrientationListener(context); + sOrientationListener.enable(); + } + } + + static void enable() { + if (sWindowOrientationListener != null) { + sWindowOrientationListener.enable(); + } else { + sOrientationListener.enable(); + } + updateOrientation(); + } + + static void disable() { + if (sWindowOrientationListener != null) { + sWindowOrientationListener.disable(); + } else { + sOrientationListener.disable(); + } + } + + /** + * Query current display rotation and publish the change if any. + */ + static void updateOrientation() { + // Even though we're responding to device orientation events, + // use display rotation so audio stays in sync with video/dialogs + int newRotation = ((WindowManager) sContext.getSystemService( + Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); + synchronized(sRotationLock) { + if (newRotation != sDeviceRotation) { + sDeviceRotation = newRotation; + publishRotation(sDeviceRotation); + } + } + } + + private static void publishRotation(int rotation) { + Log.v(TAG, "publishing device rotation =" + rotation + " (x90deg)"); + switch (rotation) { + case Surface.ROTATION_0: + AudioSystem.setParameters("rotation=0"); + break; + case Surface.ROTATION_90: + AudioSystem.setParameters("rotation=90"); + break; + case Surface.ROTATION_180: + AudioSystem.setParameters("rotation=180"); + break; + case Surface.ROTATION_270: + AudioSystem.setParameters("rotation=270"); + break; + default: + Log.e(TAG, "Unknown device rotation"); + } + } + + /** + * Uses android.view.OrientationEventListener + */ + final static class AudioOrientationListener extends OrientationEventListener { + AudioOrientationListener(Context context) { + super(context); + } + + @Override + public void onOrientationChanged(int orientation) { + updateOrientation(); + } + } + + /** + * Uses com.android.server.policy.WindowOrientationListener + */ + final static class AudioWindowOrientationListener extends WindowOrientationListener { + private static RotationCheckThread sRotationCheckThread; + + AudioWindowOrientationListener(Context context, Handler handler) { + super(context, handler); + } + + public void onProposedRotationChanged(int rotation) { + updateOrientation(); + if (sRotationCheckThread != null) { + sRotationCheckThread.endCheck(); + } + sRotationCheckThread = new RotationCheckThread(); + sRotationCheckThread.beginCheck(); + } + } + + /** + * When com.android.server.policy.WindowOrientationListener report an orientation change, + * the UI may not have rotated yet. This thread polls with gradually increasing delays + * the new orientation. + */ + final static class RotationCheckThread extends Thread { + // how long to wait between each rotation check + private final int[] WAIT_TIMES_MS = { 10, 20, 50, 100, 100, 200, 200, 500 }; + private int mWaitCounter; + private final Object mCounterLock = new Object(); + + RotationCheckThread() { + super("RotationCheck"); + } + + void beginCheck() { + synchronized(mCounterLock) { + mWaitCounter = 0; + } + try { + start(); + } catch (IllegalStateException e) { } + } + + void endCheck() { + synchronized(mCounterLock) { + mWaitCounter = WAIT_TIMES_MS.length; + } + } + + public void run() { + int newRotation; + while (mWaitCounter < WAIT_TIMES_MS.length) { + updateOrientation(); + int waitTimeMs; + synchronized(mCounterLock) { + waitTimeMs = WAIT_TIMES_MS[mWaitCounter]; + mWaitCounter++; + } + try { + sleep(waitTimeMs); + } catch (InterruptedException e) { } + } + } + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f6c937457d33..7b15aad7b6ca 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -27,6 +27,7 @@ import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.AutomaticZenRule; import android.app.IActivityManager; import android.app.INotificationManager; import android.app.ITransientNotification; @@ -101,6 +102,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.Preconditions; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -1172,8 +1174,8 @@ public class NotificationManagerService extends SystemService { // Don't allow client applications to cancel foreground service notis. cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, Binder.getCallingUid() == Process.SYSTEM_UID - ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, - null); + ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, + REASON_NOMAN_CANCEL, null); } @Override @@ -1594,6 +1596,50 @@ public class NotificationManagerService extends SystemService { } @Override + public List<AutomaticZenRule> getAutomaticZenRules() throws RemoteException { + enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules"); + return mZenModeHelper.getAutomaticZenRules(); + } + + @Override + public AutomaticZenRule getAutomaticZenRule(String name) throws RemoteException { + enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule"); + return mZenModeHelper.getAutomaticZenRule(name); + } + + @Override + public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule) + throws RemoteException { + Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null"); + Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null"); + Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null"); + Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null"); + enforcePolicyAccess(Binder.getCallingUid(), "addOrUpdateZenModeRule"); + + return mZenModeHelper.addOrUpdateAutomaticZenRule(automaticZenRule, + "addOrUpdateAutomaticZenRule"); + } + + @Override + public boolean renameAutomaticZenRule(String oldName, String newName) { + Preconditions.checkNotNull(oldName, "oldName is null"); + Preconditions.checkNotNull(newName, "newName is null"); + enforcePolicyAccess(Binder.getCallingUid(), "renameAutomaticZenRule"); + + return mZenModeHelper.renameAutomaticZenRule( + oldName, newName, "renameAutomaticZenRule"); + } + + @Override + public boolean removeAutomaticZenRule(String name) throws RemoteException { + Preconditions.checkNotNull(name, "Name is null"); + // Verify that they can modify zen rules. + enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule"); + + return mZenModeHelper.removeAutomaticZenRule(name, "removeAutomaticZenRule"); + } + + @Override public void setInterruptionFilter(String pkg, int filter) throws RemoteException { enforcePolicyAccess(pkg, "setInterruptionFilter"); final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1); @@ -1641,6 +1687,25 @@ public class NotificationManagerService extends SystemService { message); } + private void enforcePolicyAccess(int uid, String method) { + if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission( + android.Manifest.permission.MANAGE_NOTIFICATIONS)) { + return; + } + boolean accessAllowed = false; + String[] packages = getContext().getPackageManager().getPackagesForUid(uid); + final int packageCount = packages.length; + for (int i = 0; i < packageCount; i++) { + if (checkPolicyAccess(packages[i])) { + accessAllowed = true; + } + } + if (!accessAllowed) { + Slog.w(TAG, "Notification policy access denied calling " + method); + throw new SecurityException("Notification policy access denied"); + } + } + private void enforcePolicyAccess(String pkg, String method) { if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission( android.Manifest.permission.MANAGE_NOTIFICATIONS)) { diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 9131e5eecbf5..4d41e3a9d734 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -21,11 +21,13 @@ import static android.media.AudioAttributes.USAGE_NOTIFICATION; import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import android.app.AppOpsManager; +import android.app.AutomaticZenRule; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.database.ContentObserver; @@ -34,6 +36,7 @@ import android.media.AudioManagerInternal; import android.media.AudioSystem; import android.media.VolumePolicy; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -62,6 +65,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -196,6 +200,121 @@ public class ZenModeHelper { return mZenMode; } + public List<AutomaticZenRule> getAutomaticZenRules() { + List<AutomaticZenRule> rules = new ArrayList<>(); + if (mConfig == null) return rules; + for(ZenRule rule : mConfig.automaticRules.values()) { + if (canManageAutomaticZenRule(rule)) { + rules.add(createAutomaticZenRule(rule)); + } + } + return rules; + } + + public AutomaticZenRule getAutomaticZenRule(String name) { + if (mConfig == null) return null; + for(ZenRule rule : mConfig.automaticRules.values()) { + if (canManageAutomaticZenRule(rule) && rule.name.equals(name)) { + return createAutomaticZenRule(rule); + } + } + return null; + } + + public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) { + if (mConfig == null) return false; + if (DEBUG) { + Log.d(TAG, "addOrUpdateAutomaticZenRule zenRule=" + automaticZenRule + + " reason=" + reason); + } + final ZenModeConfig newConfig = mConfig.copy(); + String ruleId = findMatchingRuleId(newConfig, automaticZenRule.getName()); + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + if (ruleId == null) { + ruleId = newConfig.newRuleId(); + rule.name = automaticZenRule.getName(); + rule.component = automaticZenRule.getOwner(); + } else { + rule = newConfig.automaticRules.get(ruleId); + if (!canManageAutomaticZenRule(rule)) { + throw new SecurityException( + "Cannot update rules not owned by your condition provider"); + } + } + if (rule.enabled != automaticZenRule.isEnabled()) { + rule.snoozing = false; + } + rule.condition = null; + rule.conditionId = automaticZenRule.getConditionId(); + rule.enabled = automaticZenRule.isEnabled(); + rule.zenMode = NotificationManager.zenModeFromInterruptionFilter( + automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF); + newConfig.automaticRules.put(ruleId, rule); + return setConfig(newConfig, reason, true); + } + + public boolean renameAutomaticZenRule(String oldName, String newName, String reason) { + if (mConfig == null) return false; + if (DEBUG) { + Log.d(TAG, "renameAutomaticZenRule oldName=" + oldName + " newName=" + newName + + " reason=" + reason); + } + final ZenModeConfig newConfig = mConfig.copy(); + String ruleId = findMatchingRuleId(newConfig, oldName); + if (ruleId == null) { + return false; + } else { + ZenRule rule = newConfig.automaticRules.get(ruleId); + if (!canManageAutomaticZenRule(rule)) { + throw new SecurityException( + "Cannot update rules not owned by your condition provider"); + } + rule.name = newName; + return setConfig(newConfig, reason, true); + } + } + + public boolean removeAutomaticZenRule(String name, String reason) { + if (mConfig == null) return false; + final ZenModeConfig newConfig = mConfig.copy(); + String ruleId = findMatchingRuleId(newConfig, name); + if (ruleId != null) { + ZenRule rule = newConfig.automaticRules.get(ruleId); + if (canManageAutomaticZenRule(rule)) { + newConfig.automaticRules.remove(ruleId); + if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + name + " reason=" + reason); + } else { + throw new SecurityException( + "Cannot delete rules not owned by your condition provider"); + } + } + return setConfig(newConfig, reason, true); + } + + public boolean canManageAutomaticZenRule(ZenRule rule) { + if (mContext.checkCallingPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS) + == PackageManager.PERMISSION_GRANTED) { + return true; + } else { + String[] packages = mContext.getPackageManager().getPackagesForUid( + Binder.getCallingUid()); + if (packages != null) { + final int packageCount = packages.length; + for (int i = 0; i < packageCount; i++) { + if (packages[i].equals(rule.component.getPackageName())) { + return true; + } + } + } + return false; + } + } + + private AutomaticZenRule createAutomaticZenRule(ZenRule rule) { + return new AutomaticZenRule(rule.name, rule.component, rule.conditionId, + NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled); + } + public void setManualZenMode(int zenMode, Uri conditionId, String reason) { setManualZenMode(zenMode, conditionId, reason, true /*setRingerMode*/); } @@ -225,6 +344,15 @@ public class ZenModeHelper { setConfig(newConfig, reason, setRingerMode); } + private String findMatchingRuleId(ZenModeConfig config, String ruleName) { + for (String ruleId : config.automaticRules.keySet()) { + if (config.automaticRules.get(ruleId).name.equals(ruleName)) { + return ruleId; + } + } + return null; + } + public void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mZenMode="); pw.println(Global.zenModeToString(mZenMode)); @@ -272,7 +400,7 @@ public class ZenModeHelper { } config.manualRule = null; // don't restore the manual rule if (config.automaticRules != null) { - for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) { + for (ZenRule automaticRule : config.automaticRules.values()) { // don't restore transient state from restored automatic rules automaticRule.snoozing = false; automaticRule.condition = null; @@ -319,36 +447,41 @@ public class ZenModeHelper { } private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) { - if (config == null || !config.isValid()) { - Log.w(TAG, "Invalid config in setConfig; " + config); - return false; - } - if (config.user != mUser) { - // simply store away for background users + final long identity = Binder.clearCallingIdentity(); + try { + if (config == null || !config.isValid()) { + Log.w(TAG, "Invalid config in setConfig; " + config); + return false; + } + if (config.user != mUser) { + // simply store away for background users + mConfigs.put(config.user, config); + if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user); + return true; + } + mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config mConfigs.put(config.user, config); - if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user); + if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable()); + ZenLog.traceConfig(reason, mConfig, config); + final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig), + getNotificationPolicy(config)); + mConfig = config; + if (config.equals(mConfig)) { + dispatchOnConfigChanged(); + } + if (policyChanged){ + dispatchOnPolicyChanged(); + } + final String val = Integer.toString(mConfig.hashCode()); + Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val); + if (!evaluateZenMode(reason, setRingerMode)) { + applyRestrictions(); // evaluateZenMode will also apply restrictions if changed + } + mConditions.evaluateConfig(config, true /*processSubscriptions*/); return true; + } finally { + Binder.restoreCallingIdentity(identity); } - mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config - mConfigs.put(config.user, config); - if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable()); - ZenLog.traceConfig(reason, mConfig, config); - final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig), - getNotificationPolicy(config)); - mConfig = config; - if (config.equals(mConfig)) { - dispatchOnConfigChanged(); - } - if (policyChanged){ - dispatchOnPolicyChanged(); - } - final String val = Integer.toString(mConfig.hashCode()); - Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val); - if (!evaluateZenMode(reason, setRingerMode)) { - applyRestrictions(); // evaluateZenMode will also apply restrictions if changed - } - mConditions.evaluateConfig(config, true /*processSubscriptions*/); - return true; } private int getZenModeSetting() { @@ -506,6 +639,7 @@ public class ZenModeHelper { .getString(R.string.zen_mode_default_weeknights_name); rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights); rule1.zenMode = Global.ZEN_MODE_ALARMS; + rule1.component = ScheduleConditionProvider.COMPONENT; config.automaticRules.put(config.newRuleId(), rule1); final ScheduleInfo weekends = new ScheduleInfo(); @@ -519,6 +653,7 @@ public class ZenModeHelper { .getString(R.string.zen_mode_default_weekends_name); rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends); rule2.zenMode = Global.ZEN_MODE_ALARMS; + rule2.component = ScheduleConditionProvider.COMPONENT; config.automaticRules.put(config.newRuleId(), rule2); } @@ -533,6 +668,7 @@ public class ZenModeHelper { rule.name = mContext.getResources().getString(R.string.zen_mode_default_events_name); rule.conditionId = ZenModeConfig.toEventConditionId(events); rule.zenMode = Global.ZEN_MODE_ALARMS; + rule.component = EventConditionProvider.COMPONENT; config.automaticRules.put(config.newRuleId(), rule); } @@ -573,6 +709,7 @@ public class ZenModeHelper { rule.conditionId = ZenModeConfig.toScheduleConditionId(schedule); rule.zenMode = v1.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS : Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + rule.component = ScheduleConditionProvider.COMPONENT; rt.automaticRules.put(rt.newRuleId(), rule); } else { Log.i(TAG, "No existing V1 downtime found, generating default schedules"); diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 0366fff4eee9..cf09b846ad4e 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -140,6 +140,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { private static final String ATTR_APP_ICON = "appIcon"; private static final String ATTR_APP_LABEL = "appLabel"; private static final String ATTR_ORIGINATING_URI = "originatingUri"; + private static final String ATTR_ORIGINATING_UID = "originatingUid"; private static final String ATTR_REFERRER_URI = "referrerUri"; private static final String ATTR_ABI_OVERRIDE = "abiOverride"; private static final String ATTR_VOLUME_UUID = "volumeUuid"; @@ -405,6 +406,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON); params.appLabel = readStringAttribute(in, ATTR_APP_LABEL); params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI); + params.originatingUid = + readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN); params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); @@ -477,6 +480,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName); writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel); writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri); + writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid); writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri); writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 4a473fd42741..a441cb2e511b 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -222,11 +222,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // waived if the installer is the device owner. DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( Context.DEVICE_POLICY_SERVICE); + final boolean isPermissionGranted = + (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid) + == PackageManager.PERMISSION_GRANTED); + final boolean isInstallerRoot = (installerUid == Process.ROOT_UID); + final boolean forcePermissionPrompt = + (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0; mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(installerPackageName); - if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid) - == PackageManager.PERMISSION_GRANTED) - || (installerUid == Process.ROOT_UID) - || mIsInstallerDeviceOwner) { + if ((isPermissionGranted + || isInstallerRoot + || mIsInstallerDeviceOwner) + && !forcePermissionPrompt) { mPermissionsAccepted = true; } else { mPermissionsAccepted = false; @@ -955,7 +961,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (accepted) { // Mark and kick off another install pass - mPermissionsAccepted = true; + synchronized (mLock) { + mPermissionsAccepted = true; + } mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); } else { destroyInternal(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c729e28dae82..2009ccf797ab 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9686,7 +9686,8 @@ public class PackageManagerService extends IPackageManager.Stub { IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user) { final VerificationParams verifParams = new VerificationParams( - null, sessionParams.originatingUri, sessionParams.referrerUri, installerUid, null); + null, sessionParams.originatingUri, sessionParams.referrerUri, + sessionParams.originatingUid, null); verifParams.setInstallerUid(installerUid); final OriginInfo origin; diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index de106a1d0d3c..6386a916d518 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -20,6 +20,7 @@ import android.accounts.Account; import android.annotation.NonNull; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.ActivityManagerNative; import android.app.IStopUserCallback; import android.app.admin.DevicePolicyManager; @@ -64,7 +65,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; -import com.android.server.accounts.AccountManagerService; +import com.android.server.LocalServices; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -242,13 +243,15 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mPackagesLock) { // Prune out any partially created/partially removed users. ArrayList<UserInfo> partials = new ArrayList<UserInfo>(); - for (int i = 0; i < mUsers.size(); i++) { + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { UserInfo ui = mUsers.valueAt(i); if ((ui.partial || ui.guestToRemove) && i != 0) { partials.add(ui); } } - for (int i = 0; i < partials.size(); i++) { + final int partialsSize = partials.size(); + for (int i = 0; i < partialsSize; i++) { UserInfo ui = partials.get(i); Slog.w(LOG_TAG, "Removing partially created user " + ui.id + " (name=" + ui.name + ")"); @@ -272,7 +275,8 @@ public class UserManagerService extends IUserManager.Stub { public UserInfo getPrimaryUser() { checkManageUsersPermission("query users"); synchronized (mPackagesLock) { - for (int i = 0; i < mUsers.size(); i++) { + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { UserInfo ui = mUsers.valueAt(i); if (ui.isPrimary()) { return ui; @@ -287,7 +291,8 @@ public class UserManagerService extends IUserManager.Stub { checkManageUsersPermission("query users"); synchronized (mPackagesLock) { ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size()); - for (int i = 0; i < mUsers.size(); i++) { + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { UserInfo ui = mUsers.valueAt(i); if (ui.partial) { continue; @@ -323,7 +328,8 @@ public class UserManagerService extends IUserManager.Stub { // Probably a dying user return users; } - for (int i = 0; i < mUsers.size(); i++) { + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { UserInfo profile = mUsers.valueAt(i); if (!isProfileOf(user, profile)) { continue; @@ -1010,7 +1016,8 @@ public class UserManagerService extends IUserManager.Stub { serializer.startTag(null, TAG_GUEST_RESTRICTIONS); writeRestrictionsLocked(serializer, mGuestRestrictions); serializer.endTag(null, TAG_GUEST_RESTRICTIONS); - for (int i = 0; i < mUsers.size(); i++) { + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { UserInfo user = mUsers.valueAt(i); serializer.startTag(null, TAG_USER); serializer.attribute(null, ATTR_ID, Integer.toString(user.id)); @@ -1587,6 +1594,9 @@ public class UserManagerService extends IUserManager.Stub { } new Thread() { public void run() { + // Clean up any ActivityManager state + LocalServices.getService(ActivityManagerInternal.class) + .onUserRemoved(userHandle); synchronized (mInstallLock) { synchronized (mPackagesLock) { removeUserStateLocked(userHandle); @@ -1951,14 +1961,15 @@ public class UserManagerService extends IUserManager.Stub { */ private void updateUserIdsLocked() { int num = 0; - for (int i = 0; i < mUsers.size(); i++) { + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { if (!mUsers.valueAt(i).partial) { num++; } } final int[] newUsers = new int[num]; int n = 0; - for (int i = 0; i < mUsers.size(); i++) { + for (int i = 0; i < userSize; i++) { if (!mUsers.valueAt(i).partial) { newUsers[n++] = mUsers.keyAt(i); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index dbc3970cf4cf..c265000f13f5 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2105,6 +2105,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TYPE_WALLPAPER: // wallpaper is at the bottom, though the window manager may move it. return 2; + case TYPE_DOCK_DIVIDER: + return 2; case TYPE_PHONE: return 3; case TYPE_SEARCH_BAR: @@ -2135,56 +2137,54 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TYPE_INPUT_METHOD_DIALOG: // on-screen keyboards and other such input method user interfaces go here. return 13; - case TYPE_DOCK_DIVIDER: - return 14; case TYPE_KEYGUARD_SCRIM: // the safety window that shows behind keyguard while keyguard is starting - return 15; + return 14; case TYPE_STATUS_BAR_SUB_PANEL: - return 16; + return 15; case TYPE_STATUS_BAR: - return 17; + return 16; case TYPE_STATUS_BAR_PANEL: - return 18; + return 17; case TYPE_KEYGUARD_DIALOG: - return 19; + return 18; case TYPE_VOLUME_OVERLAY: // the on-screen volume indicator and controller shown when the user // changes the device volume - return 20; + return 19; case TYPE_SYSTEM_OVERLAY: // the on-screen volume indicator and controller shown when the user // changes the device volume - return 21; + return 20; case TYPE_NAVIGATION_BAR: // the navigation bar, if available, shows atop most things - return 22; + return 21; case TYPE_NAVIGATION_BAR_PANEL: // some panels (e.g. search) need to show on top of the navigation bar - return 23; + return 22; case TYPE_SYSTEM_ERROR: // system-level error dialogs - return 24; + return 23; case TYPE_MAGNIFICATION_OVERLAY: // used to highlight the magnified portion of a display - return 25; + return 24; case TYPE_DISPLAY_OVERLAY: // used to simulate secondary display devices - return 26; + return 25; case TYPE_DRAG: // the drag layer: input for drag-and-drop is associated with this window, // which sits above all other focusable windows - return 27; + return 26; case TYPE_ACCESSIBILITY_OVERLAY: // overlay put by accessibility services to intercept user interaction - return 28; + return 27; case TYPE_SECURE_SYSTEM_OVERLAY: - return 29; + return 28; case TYPE_BOOT_PROGRESS: - return 30; + return 29; case TYPE_POINTER: // the (mouse) pointer layer - return 31; + return 30; } Log.e(TAG, "Unknown window type: " + type); return 2; diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index a64cda60ec1b..cc51d204e98d 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -1032,6 +1032,7 @@ public class AppTransition implements Dump { 0, top - containingFrame.top, 0); set.addAnimation(translate); set.setDuration(DEFAULT_APP_TRANSITION_DURATION); + set.setZAdjustment(Animation.ZORDER_TOP); return set; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index d6d77e9c2608..6b5ecdc70a7b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -126,6 +126,7 @@ class DisplayContent { display.getMetrics(mDisplayMetrics); isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY; mService = service; + initializeDisplayBaseInfo(); mDividerControllerLocked = new DockedStackDividerController(service.mContext, this); } @@ -192,6 +193,21 @@ class DisplayContent { } } + void initializeDisplayBaseInfo() { + synchronized(mDisplaySizeLock) { + // Bootstrap the default logical display from the display manager. + final DisplayInfo newDisplayInfo = + mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId); + if (newDisplayInfo != null) { + mDisplayInfo.copyFrom(newDisplayInfo); + } + mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth; + mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight; + mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi; + mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight); + } + } + void getLogicalDisplayRect(Rect out) { // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked. final int orientation = mDisplayInfo.rotation; @@ -568,12 +584,7 @@ class DisplayContent { } TaskStack getDockedStackLocked() { - for (int i = mStacks.size() - 1; i >= 0; i--) { - TaskStack stack = mStacks.get(i); - if (stack.mStackId == DOCKED_STACK_ID && stack.isVisibleLocked()) { - return stack; - } - } - return null; + final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID); + return (stack != null && stack.isVisibleLocked()) ? stack : null; } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d467b4f4bf4a..1f986dd90b10 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -247,8 +247,32 @@ class Task implements DimLayer.DimLayerUser { return true; } + /** Return true if the current bound can get outputted to the rest of the system as-is. */ + private boolean useCurrentBounds() { + final DisplayContent displayContent = mStack.getDisplayContent(); + if (mFullscreen + || mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID + || mStack.mStackId == DOCKED_STACK_ID + || displayContent == null + || displayContent.getDockedStackLocked() != null) { + return true; + } + return false; + } + + /** Bounds of the task with other system factors taken into consideration. */ void getBounds(Rect out) { - out.set(mBounds); + if (useCurrentBounds()) { + // No need to adjust the output bounds if fullscreen or the docked stack is visible + // since it is already what we want to represent to the rest of the system. + out.set(mBounds); + return; + } + + // The bounds has been adjusted to accommodate for a docked stack, but the docked stack + // is not currently visible. Go ahead a represent it as fullscreen to the rest of the + // system. + mStack.getDisplayContent().getLogicalDisplayRect(out); } void setDragResizing(boolean dragResizing) { @@ -441,10 +465,6 @@ class Task implements DimLayer.DimLayerUser { return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID; } - boolean inDockedWorkspace() { - return mStack != null && mStack.mStackId == DOCKED_STACK_ID; - } - WindowState getTopAppMainWindow() { final int tokensCount = mAppTokens.size(); return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null; @@ -452,7 +472,13 @@ class Task implements DimLayer.DimLayerUser { @Override public boolean isFullscreen() { - return mFullscreen; + if (useCurrentBounds()) { + return mFullscreen; + } + // The bounds has been adjusted to accommodate for a docked stack, but the docked stack + // is not currently visible. Go ahead a represent it as fullscreen to the rest of the + // system. + return true; } @Override diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index df2e5e80b59b..290c2eaf0bf3 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -19,8 +19,8 @@ package com.android.server.wm; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.RESIZE_MODE_FORCED; import static android.app.ActivityManager.RESIZE_MODE_USER; +import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING; @@ -146,13 +146,18 @@ class TaskPositioner implements DimLayer.DimLayerUser { } synchronized (mService.mWindowMap) { mDragEnded = notifyMoveLocked(newX, newY); + mTask.getBounds(mTmpRect); + } + if (!mTmpRect.equals(mWindowDragBounds)) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "wm.TaskPositioner.resizeTask"); + try { + mService.mActivityManager.resizeTask( + mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER); + } catch (RemoteException e) { + } + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.TaskPositioner.resizeTask"); - try { - mService.mActivityManager.resizeTask( - mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER); - } catch(RemoteException e) {} - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } break; case MotionEvent.ACTION_UP: { @@ -171,15 +176,16 @@ class TaskPositioner implements DimLayer.DimLayerUser { } if (mDragEnded) { + final boolean wasResizing = mResizing; synchronized (mService.mWindowMap) { endDragLocked(); } try { - if (mResizing) { + if (wasResizing) { // We were using fullscreen surface during resizing. Request // resizeTask() one last time to restore surface to window size. mService.mActivityManager.resizeTask( - mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_FORCED); + mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED); } if (mCurrentDimSide != CTRL_NONE) { diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 8053fedcebd4..f030b9a23e64 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -25,7 +25,6 @@ import android.annotation.IntDef; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Debug; -import android.os.RemoteException; import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; @@ -59,7 +58,7 @@ public class TaskStack implements DimLayer.DimLayerUser { /** For comparison with DisplayContent bounds. */ private Rect mTmpRect = new Rect(); - private Rect TmpRect2 = new Rect(); + private Rect mTmpRect2 = new Rect(); /** Content limits relative to the DisplayContent this sits in. */ private Rect mBounds = new Rect(); @@ -182,10 +181,40 @@ public class TaskStack implements DimLayer.DimLayerUser { return true; } - void getBounds(Rect out) { + /** Bounds of the stack without adjusting for other factors in the system like visibility + * of docked stack. + * Most callers should be using {@link #getBounds} as it take into consideration other system + * factors. */ + void getRawBounds(Rect out) { out.set(mBounds); } + /** Return true if the current bound can get outputted to the rest of the system as-is. */ + private boolean useCurrentBounds() { + if (mFullscreen + || mStackId == DOCKED_STACK_ID + || mDisplayContent == null + || mDisplayContent.getDockedStackLocked() != null) { + return true; + } + return false; + } + + /** Bounds of the stack with other system factors taken into consideration. */ + void getBounds(Rect out) { + if (useCurrentBounds()) { + // No need to adjust the output bounds if fullscreen or the docked stack is visible + // since it is already what we want to represent to the rest of the system. + out.set(mBounds); + return; + } + + // The bounds has been adjusted to accommodate for a docked stack, but the docked stack + // is not currently visible. Go ahead a represent it as fullscreen to the rest of the + // system. + mDisplayContent.getLogicalDisplayRect(out); + } + void updateDisplayInfo(Rect bounds) { if (mDisplayContent != null) { for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { @@ -196,10 +225,10 @@ public class TaskStack implements DimLayer.DimLayerUser { } else if (mFullscreen) { setBounds(null); } else { - TmpRect2.set(mBounds); + mTmpRect2.set(mBounds); mDisplayContent.rotateBounds( - mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2); - if (setBounds(TmpRect2)) { + mRotation, mDisplayContent.getDisplayInfo().rotation, mTmpRect2); + if (setBounds(mTmpRect2)) { // Post message to inform activity manager of the bounds change simulating // a one-way call. We do this to prevent a deadlock between window manager // lock and activity manager lock been held. @@ -353,14 +382,18 @@ public class TaskStack implements DimLayer.DimLayerUser { mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId()); Rect bounds = null; - final boolean dockedStackExists = mService.mStackIdToStack.get(DOCKED_STACK_ID) != null; - if (mStackId == DOCKED_STACK_ID || (dockedStackExists + final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID); + if (mStackId == DOCKED_STACK_ID || (dockedStack != null && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) { // The existence of a docked stack affects the size of any static stack created since // the docked stack occupies a dedicated region on screen. bounds = new Rect(); displayContent.getLogicalDisplayRect(mTmpRect); - getInitialDockedStackBounds(mTmpRect, bounds, mStackId, + mTmpRect2.setEmpty(); + if (dockedStack != null) { + dockedStack.getRawBounds(mTmpRect2); + } + getInitialDockedStackBounds(mTmpRect, bounds, mStackId, mTmpRect2, mDisplayContent.mDividerControllerLocked.getWidthAdjustment()); } @@ -370,7 +403,7 @@ public class TaskStack implements DimLayer.DimLayerUser { // Attaching a docked stack to the display affects the size of all other static // stacks since the docked stack occupies a dedicated region on screen. // Resize existing static stacks so they are pushed to the side of the docked stack. - resizeNonDockedStacks(!FULLSCREEN); + resizeNonDockedStacks(!FULLSCREEN, mBounds); } } @@ -380,29 +413,49 @@ public class TaskStack implements DimLayer.DimLayerUser { * @param displayRect The bounds of the display the docked stack is on. * @param outBounds Output bounds that should be used for the stack. * @param stackId Id of stack we are calculating the bounds for. + * @param dockedBounds Bounds of the docked stack. * @param adjustment */ - private static void getInitialDockedStackBounds(Rect displayRect, Rect outBounds, int stackId, - int adjustment) { - // Docked stack start off occupying half the screen space. + private static void getInitialDockedStackBounds( + Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int adjustment) { final boolean dockedStack = stackId == DOCKED_STACK_ID; final boolean splitHorizontally = displayRect.width() > displayRect.height(); final boolean topOrLeftCreateMode = WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; - final boolean placeTopOrLeft = (dockedStack && topOrLeftCreateMode) - || (!dockedStack && !topOrLeftCreateMode); + outBounds.set(displayRect); - if (placeTopOrLeft) { + if (dockedStack) { + // The initial bounds of the docked stack when it is created half the screen space and + // its bounds can be adjusted after that. The bounds of all other stacks are adjusted + // to occupy whatever screen space the docked stack isn't occupying. + if (topOrLeftCreateMode) { + if (splitHorizontally) { + outBounds.right = displayRect.centerX() - adjustment; + } else { + outBounds.bottom = displayRect.centerY() - adjustment; + } + } else { + if (splitHorizontally) { + outBounds.left = displayRect.centerX() + adjustment; + } else { + outBounds.top = displayRect.centerY() + adjustment; + } + } + return; + } + + // Other stacks occupy whatever space is left by the docked stack. + if (!topOrLeftCreateMode) { if (splitHorizontally) { - outBounds.right = displayRect.centerX() - adjustment; + outBounds.right = dockedBounds.left - adjustment; } else { - outBounds.bottom = displayRect.centerY() - adjustment; + outBounds.bottom = dockedBounds.top - adjustment; } } else { if (splitHorizontally) { - outBounds.left = displayRect.centerX() + adjustment; + outBounds.left = dockedBounds.right + adjustment; } else { - outBounds.top = displayRect.centerY() + adjustment; + outBounds.top = dockedBounds.bottom + adjustment; } } } @@ -411,11 +464,14 @@ public class TaskStack implements DimLayer.DimLayerUser { * based on the presence of a docked stack. * @param fullscreen If true the stacks will be resized to fullscreen, else they will be * resized to the appropriate size based on the presence of a docked stack. + * @param dockedBounds Bounds of the docked stack. */ - private void resizeNonDockedStacks(boolean fullscreen) { - mDisplayContent.getLogicalDisplayRect(mTmpRect); + private void resizeNonDockedStacks(boolean fullscreen, Rect dockedBounds) { + // Not using mTmpRect because we are posting the object in a message. + final Rect bounds = new Rect(); + mDisplayContent.getLogicalDisplayRect(bounds); if (!fullscreen) { - getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID, + getInitialDockedStackBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds, mDisplayContent.mDividerControllerLocked.getWidth()); } @@ -426,11 +482,8 @@ public class TaskStack implements DimLayer.DimLayerUser { if (otherStackId != DOCKED_STACK_ID && otherStackId >= FIRST_STATIC_STACK_ID && otherStackId <= LAST_STATIC_STACK_ID) { - try { - mService.mActivityManager.resizeStack(otherStackId, mTmpRect); - } catch (RemoteException e) { - // This will not happen since we are in the same process. - } + mService.mH.sendMessage( + mService.mH.obtainMessage(RESIZE_STACK, otherStackId, -1, bounds)); } } } @@ -458,7 +511,7 @@ public class TaskStack implements DimLayer.DimLayerUser { if (mStackId == DOCKED_STACK_ID) { // Docked stack was detached from the display, so we no longer need to restrict the // region of the screen other static stacks occupy. Go ahead and make them fullscreen. - resizeNonDockedStacks(FULLSCREEN); + resizeNonDockedStacks(FULLSCREEN, null); } close(); @@ -527,9 +580,23 @@ public class TaskStack implements DimLayer.DimLayerUser { } } + /** Fullscreen status of the stack without adjusting for other factors in the system like + * visibility of docked stack. + * Most callers should be using {@link #isFullscreen} as it take into consideration other + * system factors. */ + boolean getRawFullscreen() { + return mFullscreen; + } + @Override public boolean isFullscreen() { - return mFullscreen; + if (useCurrentBounds()) { + return mFullscreen; + } + // The bounds has been adjusted to accommodate for a docked stack, but the docked stack + // is not currently visible. Go ahead a represent it as fullscreen to the rest of the + // system. + return true; } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 926bc0f4ef91..74572cfcdee2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -222,6 +222,7 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean HIDE_STACK_CRAWLS = true; static final int LAYOUT_REPEAT_THRESHOLD = 4; + static final boolean PROFILE_ORIENTATION = false; static final boolean localLOGV = DEBUG; @@ -4399,7 +4400,6 @@ public class WindowManagerService extends IWindowManager.Stub mInputMonitor.setUpdateInputWindowsNeededLw(); mWindowPlacerLocked.performSurfacePlacement(); mInputMonitor.updateInputWindowsLw(false /*force*/); - //dump(); } @@ -4484,20 +4484,16 @@ public class WindowManagerService extends IWindowManager.Stub } stack.attachDisplayContent(displayContent); displayContent.attachStack(stack, onTop); - if (stack.mStackId == DOCKED_STACK_ID) { - mH.obtainMessage(H.UPDATE_DOCKED_STACK_DIVIDER, - displayContent).sendToTarget(); - } moveStackWindowsLocked(displayContent); final WindowList windows = displayContent.getWindowList(); for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { windows.get(winNdx).reportResized(); } - if (stack.isFullscreen()) { + if (stack.getRawFullscreen()) { return null; } Rect bounds = new Rect(); - stack.getBounds(bounds); + stack.getRawBounds(bounds); return bounds; } } @@ -4510,11 +4506,6 @@ public class WindowManagerService extends IWindowManager.Stub void detachStackLocked(DisplayContent displayContent, TaskStack stack) { displayContent.detachStack(stack); stack.detachDisplay(); - // We can't directly remove the divider, because only the WM thread can do these operations - // and we can be on AM thread. - if (stack.mStackId == DOCKED_STACK_ID) { - mH.obtainMessage(H.UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget(); - } } public void detachStack(int stackId) { @@ -4620,7 +4611,7 @@ public class WindowManagerService extends IWindowManager.Stub stack.getDisplayContent().layoutNeeded = true; mWindowPlacerLocked.performSurfacePlacement(); } - return stack.isFullscreen(); + return stack.getRawFullscreen(); } } @@ -7107,22 +7098,7 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent != null) { mAnimator.addDisplayLocked(displayId); - synchronized(displayContent.mDisplaySizeLock) { - // Bootstrap the default logical display from the display manager. - final DisplayInfo displayInfo = displayContent.getDisplayInfo(); - DisplayInfo newDisplayInfo = mDisplayManagerInternal.getDisplayInfo(displayId); - if (newDisplayInfo != null) { - displayInfo.copyFrom(newDisplayInfo); - } - displayContent.mInitialDisplayWidth = displayInfo.logicalWidth; - displayContent.mInitialDisplayHeight = displayInfo.logicalHeight; - displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi; - displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth; - displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight; - displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity; - displayContent.mBaseDisplayRect.set(0, 0, - displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight); - } + displayContent.initializeDisplayBaseInfo(); } } } @@ -8361,6 +8337,13 @@ public class WindowManagerService extends IWindowManager.Stub } else if (wtoken != null) { winAnimator.mAnimLayer = w.mLayer + wtoken.mAppAnimator.animLayerAdjustment; + if (wtoken.mReplacingWindow && wtoken.mAnimateReplacingWindow) { + // We know that we will be animating a relaunching window in the near future, + // which will receive a z-order increase. We want the replaced window to + // immediately receive the same treatment, e.g. to be above the dock divider. + w.mLayer += TYPE_LAYER_OFFSET; + winAnimator.mAnimLayer += TYPE_LAYER_OFFSET; + } } else { winAnimator.mAnimLayer = w.mLayer; } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 0b9f2c634db8..60bf57154bc6 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static com.android.server.wm.WindowManagerService.DEBUG_ANIM; import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS; @@ -868,14 +869,18 @@ class WindowStateAnimator { mSurfaceW = width; mSurfaceH = height; - final boolean isHwAccelerated = (attrs.flags & - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; + final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0; final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format; if (!PixelFormat.formatHasAlpha(attrs.format) + // Don't make surface with surfaceInsets opaque as they display a + // translucent shadow. && attrs.surfaceInsets.left == 0 && attrs.surfaceInsets.top == 0 && attrs.surfaceInsets.right == 0 - && attrs.surfaceInsets.bottom == 0) { + && attrs.surfaceInsets.bottom == 0 + // Don't make surface opaque when resizing to reduce the amount of + // artifacts shown in areas the app isn't drawing content to. + && !w.isDragResizing()) { flags |= SurfaceControl.OPAQUE; } @@ -1400,7 +1405,11 @@ class WindowStateAnimator { private void adjustCropToStackBounds(WindowState w, Rect clipRect) { final AppWindowToken appToken = w.mAppToken; - if (appToken != null && appToken.mCropWindowsToStack) { + // We don't apply the the stack bounds to the window that is being replaced, because it was + // living in a different stack. If we suddenly crop it to the new stack bounds, it might + // get cut off. We don't want it to happen, so we let it ignore the stack bounds until it + // gets removed. The window that will replace it will abide them. + if (appToken != null && appToken.mCropWindowsToStack && !appToken.mReplacingWindow) { TaskStack stack = w.getTask().mStack; stack.getBounds(mTmpStackBounds); final int surfaceX = (int) mSurfaceX; @@ -1713,8 +1722,7 @@ class WindowStateAnimator { return false; } final LayoutParams attrs = mWin.getAttrs(); - final boolean isHwAccelerated = (attrs.flags & - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; + final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0; final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format; if (format == mSurfaceFormat) { setOpaqueLocked(!PixelFormat.formatHasAlpha(attrs.format)); diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index d86a8afe1499..df0a1c9d43d5 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -967,6 +967,7 @@ class WindowSurfacePlacer { } mService.mPolicy.finishLayoutLw(); + mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget(); } /** diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java index 982bae0ccb42..53f55cdc847f 100644 --- a/services/net/java/android/net/ip/IpReachabilityMonitor.java +++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java @@ -423,7 +423,7 @@ public class IpReachabilityMonitor { try { byteBuffer = recvKernelReply(); } catch (ErrnoException e) { - Log.w(TAG, "ErrnoException: ", e); + if (stillRunning()) { Log.w(TAG, "ErrnoException: ", e); } break; } final long whenMs = SystemClock.elapsedRealtime(); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index c6c74979f6ef..f4ffe2ee1d27 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -50,11 +50,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi private final File mDeviceOwnerFile; private final File mProfileOwnerBase; - public OwnersTestable(Context context, File dataDir) { + public OwnersTestable(DpmMockContext context) { super(context); - mLegacyFile = new File(dataDir, LEGACY_FILE); - mDeviceOwnerFile = new File(dataDir, DEVICE_OWNER_FILE); - mProfileOwnerBase = new File(dataDir, PROFILE_OWNER_FILE_BASE); + mLegacyFile = new File(context.dataDir, LEGACY_FILE); + mDeviceOwnerFile = new File(context.dataDir, DEVICE_OWNER_FILE); + mProfileOwnerBase = new File(context.dataDir, PROFILE_OWNER_FILE_BASE); } @Override @@ -90,27 +90,15 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi public final File dataDir; - public final File systemUserDataDir; - public final File secondUserDataDir; - private MockInjector(DpmMockContext context, File dataDir) { super(context); this.context = context; this.dataDir = dataDir; - - systemUserDataDir = new File(dataDir, "user0"); - DpmTestUtils.clearDir(dataDir); - - secondUserDataDir = new File(dataDir, "user" + DpmMockContext.CALLER_USER_HANDLE); - DpmTestUtils.clearDir(secondUserDataDir); - - when(context.environment.getUserSystemDirectory( - eq(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(secondUserDataDir); } @Override Owners newOwners() { - return new OwnersTestable(context, dataDir); + return new OwnersTestable(context); } @Override @@ -165,7 +153,7 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi @Override String getDevicePolicyFilePathForSystemUser() { - return systemUserDataDir.getAbsolutePath(); + return context.systemUserDataDir.getAbsolutePath(); } @Override diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 0072f526bd95..5b2379833ad6 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -91,11 +91,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class); admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class); - setUpPackageManagerForAdmin(admin1); - setUpPackageManagerForAdmin(admin2); - setUpPackageManagerForAdmin(admin3); + setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID); + setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID); + setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID); + + setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, + DpmMockContext.CALLER_UID); - setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED); setUpPackageInfo(); setUpUserManager(); } @@ -105,7 +107,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * the actual ResolveInfo for the admin component, but we need to mock PM so it'll return * it for user {@link DpmMockContext#CALLER_USER_HANDLE}. */ - private void setUpPackageManagerForAdmin(ComponentName admin) { + private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid) { final Intent resolveIntent = new Intent(); resolveIntent.setComponent(admin); final List<ResolveInfo> realResolveInfo = @@ -115,32 +117,36 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertNotNull(realResolveInfo); assertEquals(1, realResolveInfo.size()); + // We need to change AI, so set a clone. + realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0))); + // We need to rewrite the UID in the activity info. - realResolveInfo.get(0).activityInfo.applicationInfo.uid = DpmMockContext.CALLER_UID; + realResolveInfo.get(0).activityInfo.applicationInfo.uid = packageUid; doReturn(realResolveInfo).when(mContext.packageManager).queryBroadcastReceivers( MockUtils.checkIntentComponent(admin), eq(PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS), - eq(DpmMockContext.CALLER_USER_HANDLE) - ); + eq(UserHandle.getUserId(packageUid))); } /** * Set up a mock result for {@link IPackageManager#getApplicationInfo} for user * {@link DpmMockContext#CALLER_USER_HANDLE}. */ - private void setUpApplicationInfo(int enabledSetting) throws Exception { - final ApplicationInfo ai = mRealTestContext.getPackageManager().getApplicationInfo( - admin1.getPackageName(), - PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); + private void setUpApplicationInfo(int enabledSetting, int packageUid) throws Exception { + final ApplicationInfo ai = DpmTestUtils.cloneParcelable( + mRealTestContext.getPackageManager().getApplicationInfo( + admin1.getPackageName(), + PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS)); ai.enabledSetting = enabledSetting; + ai.uid = packageUid; doReturn(ai).when(mContext.ipackageManager).getApplicationInfo( eq(admin1.getPackageName()), eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS), - eq(DpmMockContext.CALLER_USER_HANDLE)); + eq(UserHandle.getUserId(packageUid))); } /** @@ -193,16 +199,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { }).when(mContext.userManager).getApplicationRestrictions( anyString(), any(UserHandle.class)); - // System user is always running. - when(mContext.userManager.isUserRunning(MockUtils.checkUserHandle(UserHandle.USER_SYSTEM))) - .thenReturn(true); - - // Set up (default) UserInfo for CALLER_USER_HANDLE. - final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE, - "user" + DpmMockContext.CALLER_USER_HANDLE, 0); - - when(mContext.userManager.getUserInfo(eq(DpmMockContext.CALLER_USER_HANDLE))) - .thenReturn(uh); + // Add the first secondary user. + mContext.addUser(DpmMockContext.CALLER_USER_HANDLE, 0); } private void setAsProfileOwner(ComponentName admin) { @@ -309,7 +307,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Next, add one more admin. // Before doing so, update the application info, now it's enabled. - setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + DpmMockContext.CALLER_UID); dpm.setActiveAdmin(admin2, /* replace =*/ false); @@ -354,6 +353,35 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL"); } + public void testSetActiveAdmin_multiUsers() throws Exception { + + final int ANOTHER_USER_ID = 100; + final int ANOTHER_ADMIN_UID = UserHandle.getUid(ANOTHER_USER_ID, 20456); + + mMockContext.addUser(ANOTHER_USER_ID, 0); // Add one more user. + + // Set up pacakge manager for the other user. + setUpPackageManagerForAdmin(admin2, ANOTHER_ADMIN_UID); + setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, + ANOTHER_ADMIN_UID); + + mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); + + dpm.setActiveAdmin(admin1, /* replace =*/ false); + + mMockContext.binder.callingUid = ANOTHER_ADMIN_UID; + dpm.setActiveAdmin(admin2, /* replace =*/ false); + + + mMockContext.binder.callingUid = DpmMockContext.CALLER_UID; + assertTrue(dpm.isAdminActive(admin1)); + assertFalse(dpm.isAdminActive(admin2)); + + mMockContext.binder.callingUid = ANOTHER_ADMIN_UID; + assertFalse(dpm.isAdminActive(admin1)); + assertTrue(dpm.isAdminActive(admin2)); + } + /** * Test for: * {@link DevicePolicyManager#setActiveAdmin} @@ -400,9 +428,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { // having MANAGE_DEVICE_ADMINS. mContext.callerPermissions.clear(); + // Change the caller, and call into DPMS directly with a different user-id. + mContext.binder.callingUid = 1234567; try { - dpm.removeActiveAdmin(admin1); + dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE); fail("Didn't throw SecurityException"); } catch (SecurityException expected) { } @@ -412,7 +442,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Test for: * {@link DevicePolicyManager#removeActiveAdmin} */ - public void testRemoveActiveAdmin_fromDifferentUserWithMINTERACT_ACROSS_USERS_FULL() { + public void testRemoveActiveAdmin_fromDifferentUserWithINTERACT_ACROSS_USERS_FULL() { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); // Add admin1. @@ -424,8 +454,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Different user, but should work, because caller has proper permissions. mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL); + + // Change the caller, and call into DPMS directly with a different user-id. mContext.binder.callingUid = 1234567; - dpm.removeActiveAdmin(admin1); + + dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE); assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE)); @@ -498,9 +531,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL); - // Call from a process on the system user. + // In this test, change the caller user to "system". mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + // Make sure admin1 is installed on system user. + setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); + setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, + DpmMockContext.CALLER_SYSTEM_USER_UID); + // DO needs to be an DA. dpm.setActiveAdmin(admin1, /* replace =*/ false); @@ -536,8 +574,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Call from a process on the system user. mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; - // DO needs to be an DA. - dpm.setActiveAdmin(admin1, /* replace =*/ false); try { dpm.setDeviceOwner("a.b.c"); fail("Didn't throw IllegalArgumentException"); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java index 325bf9ff0225..b80f3bf8d58d 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java @@ -16,6 +16,7 @@ package com.android.server.devicepolicy; import android.app.admin.DevicePolicyManager; +import android.os.UserHandle; /** * Overrides {@link #DevicePolicyManager} for dependency injection. @@ -31,6 +32,6 @@ public class DevicePolicyManagerTestable extends DevicePolicyManager { @Override public int myUserId() { - return DpmMockContext.CALLER_USER_HANDLE; + return UserHandle.getUserId(dpms.context.binder.callingUid); } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 3b30a37bd2c0..7b36e88c5e29 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.media.IAudioService; import android.os.Bundle; import android.os.Handler; @@ -43,8 +44,10 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; /** * Context used throughout DPMS tests. @@ -58,12 +61,12 @@ public class DpmMockContext extends MockContext { /** * UID corresponding to {@link #CALLER_USER_HANDLE}. */ - public static final int CALLER_UID = UserHandle.PER_USER_RANGE * CALLER_USER_HANDLE + 123; + public static final int CALLER_UID = UserHandle.getUid(CALLER_USER_HANDLE, 20123); /** * UID used when a caller is on the system user. */ - public static final int CALLER_SYSTEM_USER_UID = 123; + public static final int CALLER_SYSTEM_USER_UID = 20321; /** * PID of the caller. @@ -164,6 +167,9 @@ public class DpmMockContext extends MockContext { */ public final Context spiedContext; + public final File dataDir; + public final File systemUserDataDir; + public final MockBinder binder; public final EnvironmentForMock environment; public final SystemPropertiesForMock systemProperties; @@ -184,8 +190,14 @@ public class DpmMockContext extends MockContext { public final List<String> callerPermissions = new ArrayList<>(); - public DpmMockContext(Context context) { + private final ArrayList<UserInfo> mUserInfos = new ArrayList<>(); + + public DpmMockContext(Context context, File dataDir) { realTestContext = context; + + this.dataDir = dataDir; + DpmTestUtils.clearDir(dataDir); + binder = new MockBinder(); environment = mock(EnvironmentForMock.class); systemProperties= mock(SystemPropertiesForMock.class); @@ -205,6 +217,39 @@ public class DpmMockContext extends MockContext { packageManager = spy(context.getPackageManager()); spiedContext = mock(Context.class); + + // Add the system user + systemUserDataDir = addUser(UserHandle.USER_SYSTEM, UserInfo.FLAG_PRIMARY); + + // System user is always running. + when(userManager.isUserRunning(MockUtils.checkUserHandle(UserHandle.USER_SYSTEM))) + .thenReturn(true); + } + + public File addUser(int userId, int flags) { + + // Set up (default) UserInfo for CALLER_USER_HANDLE. + final UserInfo uh = new UserInfo(userId, "user" + userId, flags); + when(userManager.getUserInfo(eq(userId))).thenReturn(uh); + + mUserInfos.add(uh); + when(userManager.getUsers()).thenReturn(mUserInfos); + + // Create a data directory. + final File dir = new File(dataDir, "user" + userId); + DpmTestUtils.clearDir(dir); + + when(environment.getUserSystemDirectory(eq(userId))).thenReturn(dir); + return dir; + } + + /** + * Add multiple users at once. They'll all have flag 0. + */ + public void addUsers(int... userIds) { + for (int userId : userIds) { + addUser(userId, 0); + } } @Override diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java index 6f9f6ab7c5b9..63bf12558dd1 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java @@ -34,10 +34,9 @@ public abstract class DpmTestBase extends AndroidTestCase { super.setUp(); mRealTestContext = super.getContext(); - mMockContext = new DpmMockContext(super.getContext()); - dataDir = new File(mRealTestContext.getCacheDir(), "test-data"); - DpmTestUtils.clearDir(dataDir); + mMockContext = new DpmMockContext( + mRealTestContext, new File(mRealTestContext.getCacheDir(), "test-data")); } @Override diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java index 44a851abe476..7506273accb7 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java @@ -17,6 +17,8 @@ package com.android.server.devicepolicy; import android.os.FileUtils; +import android.os.Parcel; +import android.os.Parcelable; import android.util.Log; import android.util.Printer; @@ -41,6 +43,15 @@ public class DpmTestUtils { return list == null ? 0 : list.size(); } + public static <T extends Parcelable> T cloneParcelable(T source) { + Parcel p = Parcel.obtain(); + p.writeParcelable(source, 0); + p.setDataPosition(0); + final T clone = p.readParcelable(DpmTestUtils.class.getClassLoader()); + p.recycle(); + return clone; + } + public static Printer LOG_PRINTER = new Printer() { @Override public void println(String x) { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java index a07d61520fa4..4a39614aed99 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java @@ -69,22 +69,12 @@ public class OwnersTest extends DpmTestBase { } } - private void addUsersToUserManager(int... userIds) { - final ArrayList<UserInfo> userInfos = new ArrayList<>(); - for (int userId : userIds) { - final UserInfo ui = new UserInfo(); - ui.id = userId; - userInfos.add(ui); - } - when(getContext().userManager.getUsers()).thenReturn(userInfos); - } - public void testUpgrade01() throws Exception { - addUsersToUserManager(10, 11, 20, 21); + getContext().addUsers(10, 11, 20, 21); // First, migrate. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); createLegacyFile(owners.getLegacyConfigFileWithTestOverride(), readAsset("OwnersTest/test01/input.xml")); @@ -111,7 +101,7 @@ public class OwnersTest extends DpmTestBase { // Then re-read and check. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); owners.load(); assertFalse(owners.hasDeviceOwner()); @@ -123,11 +113,11 @@ public class OwnersTest extends DpmTestBase { } public void testUpgrade02() throws Exception { - addUsersToUserManager(10, 11, 20, 21); + getContext().addUsers(10, 11, 20, 21); // First, migrate. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); createLegacyFile(owners.getLegacyConfigFileWithTestOverride(), readAsset("OwnersTest/test02/input.xml")); @@ -156,7 +146,7 @@ public class OwnersTest extends DpmTestBase { // Then re-read and check. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); owners.load(); assertTrue(owners.hasDeviceOwner()); @@ -171,11 +161,11 @@ public class OwnersTest extends DpmTestBase { } public void testUpgrade03() throws Exception { - addUsersToUserManager(10, 11, 20, 21); + getContext().addUsers(10, 11, 20, 21); // First, migrate. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); createLegacyFile(owners.getLegacyConfigFileWithTestOverride(), readAsset("OwnersTest/test03/input.xml")); @@ -212,7 +202,7 @@ public class OwnersTest extends DpmTestBase { // Then re-read and check. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); owners.load(); assertFalse(owners.hasDeviceOwner()); @@ -235,11 +225,11 @@ public class OwnersTest extends DpmTestBase { } public void testUpgrade04() throws Exception { - addUsersToUserManager(10, 11, 20, 21); + getContext().addUsers(10, 11, 20, 21); // First, migrate. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); createLegacyFile(owners.getLegacyConfigFileWithTestOverride(), readAsset("OwnersTest/test04/input.xml")); @@ -281,7 +271,7 @@ public class OwnersTest extends DpmTestBase { // Then re-read and check. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); owners.load(); assertTrue(owners.hasDeviceOwner()); @@ -309,11 +299,11 @@ public class OwnersTest extends DpmTestBase { } public void testUpgrade05() throws Exception { - addUsersToUserManager(10, 11, 20, 21); + getContext().addUsers(10, 11, 20, 21); // First, migrate. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); createLegacyFile(owners.getLegacyConfigFileWithTestOverride(), readAsset("OwnersTest/test05/input.xml")); @@ -341,7 +331,7 @@ public class OwnersTest extends DpmTestBase { // Then re-read and check. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); owners.load(); assertFalse(owners.hasDeviceOwner()); @@ -356,11 +346,11 @@ public class OwnersTest extends DpmTestBase { } public void testUpgrade06() throws Exception { - addUsersToUserManager(10, 11, 20, 21); + getContext().addUsers(10, 11, 20, 21); // First, migrate. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); createLegacyFile(owners.getLegacyConfigFileWithTestOverride(), readAsset("OwnersTest/test06/input.xml")); @@ -387,7 +377,7 @@ public class OwnersTest extends DpmTestBase { // Then re-read and check. { - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); owners.load(); assertFalse(owners.hasDeviceOwner()); @@ -401,9 +391,9 @@ public class OwnersTest extends DpmTestBase { } public void testRemoveExistingFiles() throws Exception { - addUsersToUserManager(10, 11, 20, 21); + getContext().addUsers(10, 11, 20, 21); - final OwnersTestable owners = new OwnersTestable(getContext(), dataDir); + final OwnersTestable owners = new OwnersTestable(getContext()); // First, migrate to create new-style config files. createLegacyFile(owners.getLegacyConfigFileWithTestOverride(), diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java index b458d9b14096..7628c5c24941 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java @@ -188,7 +188,7 @@ public class MultiProducerActivity extends Activity implements OnClickListener { // This call should be done while the rendernode's displaylist is produced. // For simplicity of this test we do this before we kick off the draw. mContent.getLocationInSurface(surfaceOrigin); - mRenderer.setContentOverdrawProtectionBounds(surfaceOrigin[0], surfaceOrigin[1], + mRenderer.setContentDrawBounds(surfaceOrigin[0], surfaceOrigin[1], surfaceOrigin[0] + mContent.getWidth(), surfaceOrigin[1] + mContent.getHeight()); // Determine new position for frame. diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index 3b7bf8549181..f4b1f2cd363f 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -401,7 +401,7 @@ public final class BridgeResources extends Resources { if (xml.isFile()) { // we need to create a pull parser around the layout XML file, and then // give that to our XmlBlockParser - parser = ParserFactory.create(xml); + parser = ParserFactory.create(xml, true); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java index 857e6d03283e..c7b24bcb352d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java @@ -178,7 +178,9 @@ public class FontFamily_Delegate { desiredStyle.mIsItalic = isItalic; FontInfo bestFont = null; int bestMatch = Integer.MAX_VALUE; - for (FontInfo font : mFonts) { + //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation) + for (int i = 0, n = mFonts.size(); i < n; i++) { + FontInfo font = mFonts.get(i); int match = computeMatch(font, desiredStyle); if (match < bestMatch) { bestMatch = match; @@ -415,7 +417,9 @@ public class FontFamily_Delegate { boolean isItalic = fontInfo.mIsItalic; // The list is usually just two fonts big. So iterating over all isn't as bad as it looks. // It's biggest for roboto where the size is 12. - for (FontInfo font : mFonts) { + //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation) + for (int i = 0, n = mFonts.size(); i < n; i++) { + FontInfo font = mFonts.get(i); if (font.mWeight == weight && font.mIsItalic == isItalic) { return false; } diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 65b65ec759d0..a545283ea0ca 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -480,8 +480,10 @@ public class Paint_Delegate { return; } - delegate.mTextSize = textSize; - delegate.updateFontObject(); + if (delegate.mTextSize != textSize) { + delegate.mTextSize = textSize; + delegate.updateFontObject(); + } } @LayoutlibDelegate @@ -503,8 +505,10 @@ public class Paint_Delegate { return; } - delegate.mTextScaleX = scaleX; - delegate.updateFontObject(); + if (delegate.mTextScaleX != scaleX) { + delegate.mTextScaleX = scaleX; + delegate.updateFontObject(); + } } @LayoutlibDelegate @@ -526,8 +530,10 @@ public class Paint_Delegate { return; } - delegate.mTextSkewX = skewX; - delegate.updateFontObject(); + if (delegate.mTextSkewX != skewX) { + delegate.mTextSkewX = skewX; + delegate.updateFontObject(); + } } @LayoutlibDelegate @@ -897,9 +903,12 @@ public class Paint_Delegate { return 0; } - delegate.mTypeface = Typeface_Delegate.getDelegate(typeface); - delegate.mNativeTypeface = typeface; - delegate.updateFontObject(); + Typeface_Delegate typefaceDelegate = Typeface_Delegate.getDelegate(typeface); + if (delegate.mTypeface != typefaceDelegate || delegate.mNativeTypeface != typeface) { + delegate.mTypeface = Typeface_Delegate.getDelegate(typeface); + delegate.mNativeTypeface = typeface; + delegate.updateFontObject(); + } return typeface; } @@ -1214,13 +1223,31 @@ public class Paint_Delegate { mCap = paint.mCap; mJoin = paint.mJoin; mTextAlign = paint.mTextAlign; - mTypeface = paint.mTypeface; - mNativeTypeface = paint.mNativeTypeface; + + boolean needsFontUpdate = false; + if (mTypeface != paint.mTypeface || mNativeTypeface != paint.mNativeTypeface) { + mTypeface = paint.mTypeface; + mNativeTypeface = paint.mNativeTypeface; + needsFontUpdate = true; + } + + if (mTextSize != paint.mTextSize) { + mTextSize = paint.mTextSize; + needsFontUpdate = true; + } + + if (mTextScaleX != paint.mTextScaleX) { + mTextScaleX = paint.mTextScaleX; + needsFontUpdate = true; + } + + if (mTextSkewX != paint.mTextSkewX) { + mTextSkewX = paint.mTextSkewX; + needsFontUpdate = true; + } + mStrokeWidth = paint.mStrokeWidth; mStrokeMiter = paint.mStrokeMiter; - mTextSize = paint.mTextSize; - mTextScaleX = paint.mTextScaleX; - mTextSkewX = paint.mTextSkewX; mXfermode = paint.mXfermode; mColorFilter = paint.mColorFilter; mShader = paint.mShader; @@ -1228,7 +1255,10 @@ public class Paint_Delegate { mMaskFilter = paint.mMaskFilter; mRasterizer = paint.mRasterizer; mHintingMode = paint.mHintingMode; - updateFontObject(); + + if (needsFontUpdate) { + updateFontObject(); + } } private void reset() { @@ -1264,10 +1294,18 @@ public class Paint_Delegate { // Get the fonts from the TypeFace object. List<Font> fonts = mTypeface.getFonts(mFontVariant); + if (fonts.isEmpty()) { + mFonts = Collections.emptyList(); + return; + } + // create new font objects as well as FontMetrics, based on the current text size // and skew info. - ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); - for (Font font : fonts) { + int nFonts = fonts.size(); + ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(nFonts); + //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation) + for (int i = 0; i < nFonts; i++) { + Font font = fonts.get(i); if (font == null) { // If the font is null, add null to infoList. When rendering the text, if this // null is reached, a warning will be logged. diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index f1726ebae4cb..5db1bde5f3f0 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -206,7 +206,7 @@ public final class BridgeInflater extends LayoutInflater { File f = new File(value.getValue()); if (f.isFile()) { try { - XmlPullParser parser = ParserFactory.create(f); + XmlPullParser parser = ParserFactory.create(f, true); BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( parser, bridgeContext, value.isFramework()); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 689e3597720b..b2dc29a90fb1 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -436,7 +436,7 @@ public final class BridgeContext extends Context { // we need to create a pull parser around the layout XML file, and then // give that to our XmlBlockParser try { - XmlPullParser parser = ParserFactory.create(xml); + XmlPullParser parser = ParserFactory.create(xml, true); // set the resource ref to have correct view cookies mBridgeInflater.setResourceReference(resource); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java new file mode 100644 index 000000000000..71e7fd2c5eea --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.layoutlib.bridge.impl; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.annotation.Nullable; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A wrapper around XmlPullParser that can peek forward to inspect if the file is a data-binding + * layout and some parts need to be stripped. + */ +public class LayoutParserWrapper implements XmlPullParser { + + // Data binding constants. + private static final String TAG_LAYOUT = "layout"; + private static final String TAG_DATA = "data"; + private static final String DEFAULT = "default="; + + private final XmlPullParser mDelegate; + + // Storage for peeked values. + private boolean mPeeked; + private int mEventType; + private int mDepth; + private int mNext; + private List<Attribute> mAttributes; + private String mText; + private String mName; + + // Used to end the document before the actual parser ends. + private int mFinalDepth = -1; + private boolean mEndNow; + + public LayoutParserWrapper(XmlPullParser delegate) { + mDelegate = delegate; + } + + public LayoutParserWrapper peekTillLayoutStart() throws IOException, XmlPullParserException { + final int STATE_LAYOUT_NOT_STARTED = 0; // <layout> tag not encountered yet. + final int STATE_ROOT_NOT_STARTED = 1; // the main view root not found yet. + final int STATE_INSIDE_DATA = 2; // START_TAG for <data> found, but not END_TAG. + + int state = STATE_LAYOUT_NOT_STARTED; + int dataDepth = -1; // depth of the <data> tag. Should be two. + while (true) { + int peekNext = peekNext(); + switch (peekNext) { + case START_TAG: + if (state == STATE_LAYOUT_NOT_STARTED) { + if (mName.equals(TAG_LAYOUT)) { + state = STATE_ROOT_NOT_STARTED; + } else { + return this; // no layout tag in the file. + } + } else if (state == STATE_ROOT_NOT_STARTED) { + if (mName.equals(TAG_DATA)) { + state = STATE_INSIDE_DATA; + dataDepth = mDepth; + } else { + mFinalDepth = mDepth; + return this; + } + } + break; + case END_TAG: + if (state == STATE_INSIDE_DATA) { + if (mDepth <= dataDepth) { + state = STATE_ROOT_NOT_STARTED; + } + } + break; + case END_DOCUMENT: + // No layout start found. + return this; + } + // consume the peeked tag. + next(); + } + } + + private int peekNext() throws IOException, XmlPullParserException { + if (mPeeked) { + return mNext; + } + mEventType = mDelegate.getEventType(); + mNext = mDelegate.next(); + if (mEventType == START_TAG) { + int count = mDelegate.getAttributeCount(); + mAttributes = count > 0 ? new ArrayList<Attribute>(count) : + Collections.<Attribute>emptyList(); + for (int i = 0; i < count; i++) { + mAttributes.add(new Attribute(mDelegate.getAttributeNamespace(i), + mDelegate.getAttributeName(i), mDelegate.getAttributeValue(i))); + } + } + mDepth = mDelegate.getDepth(); + mText = mDelegate.getText(); + mName = mDelegate.getName(); + mPeeked = true; + return mNext; + } + + private void reset() { + mAttributes = null; + mText = null; + mName = null; + mPeeked = false; + } + + @Override + public int next() throws XmlPullParserException, IOException { + int returnValue; + int depth; + if (mPeeked) { + returnValue = mNext; + depth = mDepth; + reset(); + } else if (mEndNow) { + return END_DOCUMENT; + } else { + returnValue = mDelegate.next(); + depth = getDepth(); + } + if (returnValue == END_TAG && depth <= mFinalDepth) { + mEndNow = true; + } + return returnValue; + } + + @Override + public int getEventType() throws XmlPullParserException { + return mPeeked ? mEventType : mDelegate.getEventType(); + } + + @Override + public int getDepth() { + return mPeeked ? mDepth : mDelegate.getDepth(); + } + + @Override + public String getName() { + return mPeeked ? mName : mDelegate.getName(); + } + + @Override + public String getText() { + return mPeeked ? mText : mDelegate.getText(); + } + + @Override + public String getAttributeValue(@Nullable String namespace, String name) { + String returnValue = null; + if (mPeeked) { + if (mAttributes == null) { + if (mEventType != START_TAG) { + throw new IndexOutOfBoundsException("getAttributeValue() called when not at START_TAG."); + } else { + return null; + } + } else { + for (Attribute attribute : mAttributes) { + //noinspection StringEquality for nullness check. + if (attribute.name.equals(name) && (attribute.namespace == namespace || + attribute.namespace != null && attribute.namespace.equals(namespace))) { + returnValue = attribute.value; + break; + } + } + } + } else { + returnValue = mDelegate.getAttributeValue(namespace, name); + } + // Check if the value is bound via data-binding, if yes get the default value. + if (returnValue != null && mFinalDepth >= 0 && returnValue.startsWith("@{")) { + // TODO: Improve the detection of default keyword. + int i = returnValue.lastIndexOf(DEFAULT); + return i > 0 ? returnValue.substring(i + DEFAULT.length(), returnValue.length() - 1) + : null; + } + return returnValue; + } + + private static class Attribute { + @Nullable + public final String namespace; + public final String name; + public final String value; + + public Attribute(@Nullable String namespace, String name, String value) { + this.namespace = namespace; + this.name = name; + this.value = value; + } + } + + // Not affected by peeking. + + @Override + public void setFeature(String s, boolean b) throws XmlPullParserException { + mDelegate.setFeature(s, b); + } + + @Override + public void setProperty(String s, Object o) throws XmlPullParserException { + mDelegate.setProperty(s, o); + } + + @Override + public void setInput(InputStream inputStream, String s) throws XmlPullParserException { + mDelegate.setInput(inputStream, s); + } + + @Override + public void setInput(Reader reader) throws XmlPullParserException { + mDelegate.setInput(reader); + } + + @Override + public String getInputEncoding() { + return mDelegate.getInputEncoding(); + } + + @Override + public String getNamespace(String s) { + return mDelegate.getNamespace(s); + } + + @Override + public String getPositionDescription() { + return mDelegate.getPositionDescription(); + } + + @Override + public int getLineNumber() { + return mDelegate.getLineNumber(); + } + + @Override + public String getNamespace() { + return mDelegate.getNamespace(); + } + + @Override + public int getColumnNumber() { + return mDelegate.getColumnNumber(); + } + + // -- We don't care much about the methods that follow. + + @Override + public void require(int i, String s, String s1) throws XmlPullParserException, IOException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public boolean getFeature(String s) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public void defineEntityReplacementText(String s, String s1) throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public Object getProperty(String s) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public int nextToken() throws XmlPullParserException, IOException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public int getNamespaceCount(int i) throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getNamespacePrefix(int i) throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getNamespaceUri(int i) throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public boolean isWhitespace() throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public char[] getTextCharacters(int[] ints) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getPrefix() { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public boolean isEmptyElementTag() throws XmlPullParserException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public int getAttributeCount() { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributeNamespace(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributeName(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributePrefix(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributeType(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public boolean isAttributeDefault(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String getAttributeValue(int i) { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public String nextText() throws XmlPullParserException, IOException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } + + @Override + public int nextTag() throws XmlPullParserException, IOException { + throw new UnsupportedOperationException("Only few parser methods are supported."); + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java index 6e67f593aa53..e273b2cd75cc 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java @@ -53,24 +53,35 @@ public class ParserFactory { @NonNull public static XmlPullParser create(@NonNull File f) throws XmlPullParserException, FileNotFoundException { - InputStream stream = new FileInputStream(f); - return create(stream, f.getName(), f.length()); + return create(f, false); } + public static XmlPullParser create(@NonNull File f, boolean isLayout) + throws XmlPullParserException, FileNotFoundException { + InputStream stream = new FileInputStream(f); + return create(stream, f.getName(), f.length(), isLayout); + } @NonNull public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name) throws XmlPullParserException { - return create(stream, name, -1); + return create(stream, name, -1, false); } @NonNull private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name, - long size) throws XmlPullParserException { + long size, boolean isLayout) throws XmlPullParserException { XmlPullParser parser = instantiateParser(name); stream = readAndClose(stream, name, size); parser.setInput(stream, ENCODING); + if (isLayout) { + try { + return new LayoutParserWrapper(parser).peekTillLayoutStart(); + } catch (IOException e) { + throw new XmlPullParserException(null, parser, e); + } + } return parser; } diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java new file mode 100644 index 000000000000..2c338622301b --- /dev/null +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.layoutlib.bridge.impl; + +import org.junit.Test; +import org.kxml2.io.KXmlParser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.StringReader; + +import static com.android.SdkConstants.NS_RESOURCES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.END_TAG; +import static org.xmlpull.v1.XmlPullParser.START_TAG; + + +public class LayoutParserWrapperTest { + @Test + @SuppressWarnings("StatementWithEmptyBody") // some for loops need to be empty statements. + public void testDataBindingLayout() throws Exception { + LayoutParserWrapper parser = getParserFromString(sDataBindingLayout); + parser.peekTillLayoutStart(); + assertEquals("Expected START_TAG", START_TAG, parser.next()); + assertEquals("RelativeLayout", parser.getName()); + for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT; + next = parser.next()); + assertEquals("Expected START_TAG", START_TAG, parser.getEventType()); + assertEquals("TextView", parser.getName()); + assertEquals("layout_width incorrect for first text view.", "wrap_content", + parser.getAttributeValue(NS_RESOURCES, "layout_width")); + // Ensure that data-binding part is stripped. + assertEquals("Bound attribute android:text incorrect", "World", + parser.getAttributeValue(NS_RESOURCES, "text")); + assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first", + parser.getAttributeValue(NS_RESOURCES, "id")); + for (int next = parser.next(); + (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT; + next = parser.next()); + assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType()); + assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next()); + } + + @Test + @SuppressWarnings("StatementWithEmptyBody") + public void testNonDataBindingLayout() throws Exception { + LayoutParserWrapper parser = getParserFromString(sNonDataBindingLayout); + parser.peekTillLayoutStart(); + assertEquals("Expected START_TAG", START_TAG, parser.next()); + assertEquals("RelativeLayout", parser.getName()); + for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT; + next = parser.next()); + assertEquals("Expected START_TAG", START_TAG, parser.getEventType()); + assertEquals("TextView", parser.getName()); + assertEquals("layout_width incorrect for first text view.", "wrap_content", + parser.getAttributeValue(NS_RESOURCES, "layout_width")); + // Ensure that value isn't modified. + assertEquals("Bound attribute android:text incorrect", "@{user.firstName,default=World}", + parser.getAttributeValue(NS_RESOURCES, "text")); + assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first", + parser.getAttributeValue(NS_RESOURCES, "id")); + for (int next = parser.next(); + (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT; + next = parser.next()); + assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType()); + assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next()); + } + + private static LayoutParserWrapper getParserFromString(String layoutContent) throws + XmlPullParserException { + XmlPullParser parser = new KXmlParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(new StringReader(layoutContent)); + return new LayoutParserWrapper(parser); + } + + private static final String sDataBindingLayout = + //language=XML + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + + " tools:context=\".MainActivity\"\n" + + " tools:showIn=\"@layout/activity_main\">\n" + + "\n" + + " <data>\n" + + "\n" + + " <variable\n" + + " name=\"user\"\n" + + " type=\"com.example.User\" />\n" + + " <variable\n" + + " name=\"activity\"\n" + + " type=\"com.example.MainActivity\" />\n" + + " </data>\n" + + "\n" + + " <RelativeLayout\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:paddingBottom=\"@dimen/activity_vertical_margin\"\n" + + " android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" + + " android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" + + " android:paddingTop=\"@dimen/activity_vertical_margin\"\n" + + " app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n" + + " >\n" + + "\n" + + " <TextView\n" + + " android:id=\"@+id/first\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_alignParentStart=\"true\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:text=\"@{user.firstName,default=World}\" />\n" + + "\n" + + " <TextView\n" + + " android:id=\"@+id/last\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_toEndOf=\"@id/first\"\n" + + " android:layout_toRightOf=\"@id/first\"\n" + + " android:text=\"@{user.lastName,default=Hello}\" />\n" + + "\n" + + " <Button\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_below=\"@id/last\"\n" + + " android:text=\"Submit\"\n" + + " android:onClick=\"@{activity.onClick}\"/>\n" + + " </RelativeLayout>\n" + + "</layout>"; + + private static final String sNonDataBindingLayout = + //language=XML + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:paddingBottom=\"@dimen/activity_vertical_margin\"\n" + + " android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" + + " android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" + + " android:paddingTop=\"@dimen/activity_vertical_margin\"\n" + + " app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n" + + ">\n" + + "\n" + + " <TextView\n" + + " android:id=\"@+id/first\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_alignParentStart=\"true\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:text=\"@{user.firstName,default=World}\" />\n" + + "\n" + + " <TextView\n" + + " android:id=\"@+id/last\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_toEndOf=\"@id/first\"\n" + + " android:layout_toRightOf=\"@id/first\"\n" + + " android:text=\"@{user.lastName,default=Hello}\" />\n" + + "\n" + + " <Button\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_below=\"@id/last\"\n" + + " android:text=\"Submit\"\n" + + " android:onClick=\"@{activity.onClick}\"/>\n" + + "</RelativeLayout>"; +} |