Merge "Simplify DeferredDisplayList construction"
diff --git a/api/current.txt b/api/current.txt
index 758851c..77c64d7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -414,6 +414,7 @@
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contextClickable = 16844007; // 0x10104e7
+ field public static final int contextPopupMenuStyle = 16844032; // 0x1010500
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -1220,6 +1221,7 @@
field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
+ field public static final int textAppearancePopupMenuHeader = 16844033; // 0x1010501
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -4014,6 +4016,23 @@
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);
@@ -5059,15 +5078,20 @@
}
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";
@@ -9211,6 +9235,7 @@
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);
@@ -18131,6 +18156,7 @@
method public boolean importFile(int, java.lang.String);
method public boolean importFile(int, android.os.ParcelFileDescriptor);
method public boolean open(android.hardware.usb.UsbDeviceConnection);
+ method public android.mtp.MtpEvent readEvent(android.os.CancellationSignal);
method public boolean sendObject(int, int, android.os.ParcelFileDescriptor);
method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo);
}
@@ -18142,6 +18168,11 @@
method public final java.lang.String getVersion();
}
+ public class MtpEvent {
+ ctor public MtpEvent();
+ method public int getEventCode();
+ }
+
public final class MtpObjectInfo {
method public final int getAssociationDesc();
method public final int getAssociationType();
@@ -23559,7 +23590,7 @@
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;
@@ -36541,6 +36572,7 @@
method public void setY(float);
method public void setZ(float);
method public boolean showContextMenu();
+ method public boolean showContextMenu(float, float);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
method public void startAnimation(android.view.animation.Animation);
@@ -37007,6 +37039,7 @@
method public void setTransitionGroup(boolean);
method public boolean shouldDelayChildPressedState();
method public boolean showContextMenuForChild(android.view.View);
+ method public boolean showContextMenuForChild(android.view.View, float, float);
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
method public void startLayoutAnimation();
@@ -37128,6 +37161,7 @@
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View);
+ method public abstract boolean showContextMenuForChild(android.view.View, float, float);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
}
@@ -40906,6 +40940,7 @@
method public int getInputMethodMode();
method public int getMaxAvailableHeight(android.view.View);
method public int getMaxAvailableHeight(android.view.View, int);
+ method public int getMaxAvailableHeight(android.view.View, int, boolean);
method public boolean getOverlapAnchor();
method public int getSoftInputMode();
method public int getWidth();
diff --git a/api/system-current.txt b/api/system-current.txt
index 2568240..cff0055 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -506,6 +506,7 @@
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contextClickable = 16844007; // 0x10104e7
+ field public static final int contextPopupMenuStyle = 16844032; // 0x1010500
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -1316,6 +1317,7 @@
field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
+ field public static final int textAppearancePopupMenuHeader = 16844033; // 0x1010501
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -4125,6 +4127,23 @@
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);
@@ -5176,15 +5195,20 @@
}
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";
@@ -9494,6 +9518,7 @@
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);
@@ -19643,6 +19668,7 @@
method public boolean importFile(int, java.lang.String);
method public boolean importFile(int, android.os.ParcelFileDescriptor);
method public boolean open(android.hardware.usb.UsbDeviceConnection);
+ method public android.mtp.MtpEvent readEvent(android.os.CancellationSignal);
method public boolean sendObject(int, int, android.os.ParcelFileDescriptor);
method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo);
}
@@ -19654,6 +19680,11 @@
method public final java.lang.String getVersion();
}
+ public class MtpEvent {
+ ctor public MtpEvent();
+ method public int getEventCode();
+ }
+
public final class MtpObjectInfo {
method public final int getAssociationDesc();
method public final int getAssociationType();
@@ -25511,7 +25542,7 @@
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;
@@ -32862,6 +32893,7 @@
field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
+ field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -38835,6 +38867,7 @@
method public void setY(float);
method public void setZ(float);
method public boolean showContextMenu();
+ method public boolean showContextMenu(float, float);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
method public void startAnimation(android.view.animation.Animation);
@@ -39301,6 +39334,7 @@
method public void setTransitionGroup(boolean);
method public boolean shouldDelayChildPressedState();
method public boolean showContextMenuForChild(android.view.View);
+ method public boolean showContextMenuForChild(android.view.View, float, float);
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
method public void startLayoutAnimation();
@@ -39422,6 +39456,7 @@
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View);
+ method public abstract boolean showContextMenuForChild(android.view.View, float, float);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
}
@@ -43514,6 +43549,7 @@
method public int getInputMethodMode();
method public int getMaxAvailableHeight(android.view.View);
method public int getMaxAvailableHeight(android.view.View, int);
+ method public int getMaxAvailableHeight(android.view.View, int, boolean);
method public boolean getOverlapAnchor();
method public int getSoftInputMode();
method public int getWidth();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 3f0a444..ebf5085 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -22,11 +22,13 @@
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
+import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
@@ -92,6 +94,7 @@
IPackageManager mPm;
IPackageInstaller mInstaller;
IUserManager mUm;
+ IAccountManager mAm;
private WeakHashMap<String, Resources> mResourceCache
= new WeakHashMap<String, Resources>();
@@ -122,9 +125,10 @@
if (args.length < 1) {
return showUsage();
}
-
- mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
+ mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE));
+ mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE));
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+
if (mPm == null) {
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
@@ -1381,6 +1385,8 @@
}
} else if ("--managed".equals(opt)) {
flags |= UserInfo.FLAG_MANAGED_PROFILE;
+ } else if ("--restricted".equals(opt)) {
+ flags |= UserInfo.FLAG_RESTRICTED;
} else {
System.err.println("Error: unknown option " + opt);
showUsage();
@@ -1394,12 +1400,18 @@
}
name = arg;
try {
- UserInfo info = null;
- if (userId < 0) {
+ UserInfo info;
+ if ((flags & UserInfo.FLAG_RESTRICTED) != 0) {
+ // In non-split user mode, userId can only be SYSTEM
+ int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
+ info = mUm.createRestrictedProfile(name, parentUserId);
+ mAm.addSharedAccountsFromParentUser(userId, parentUserId);
+ } else if (userId < 0) {
info = mUm.createUser(name, flags);
} else {
info = mUm.createProfileForUser(name, flags, userId);
}
+
if (info != null) {
System.out.println("Success: created user id " + info.id);
return 1;
@@ -2122,7 +2134,7 @@
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
System.err.println(" pm trim-caches DESIRED_FREE_SPACE [internal|UUID]");
- System.err.println(" pm create-user [--profileOf USER_ID] [--managed] USER_NAME");
+ System.err.println(" pm create-user [--profileOf USER_ID] [--managed] [--restricted] USER_NAME");
System.err.println(" pm remove-user USER_ID");
System.err.println(" pm get-max-users");
System.err.println("");
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index d751f96f..0a7568a 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,6 +16,7 @@
package android.accounts;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.Size;
import android.app.Activity;
@@ -423,6 +424,7 @@
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @NonNull
@RequiresPermission(GET_ACCOUNTS)
public Account[] getAccounts() {
try {
@@ -448,6 +450,7 @@
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @NonNull
@RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsAsUser(int userId) {
try {
@@ -466,6 +469,7 @@
* @param uid the uid of the calling app.
* @return the accounts that are available to this package and user.
*/
+ @NonNull
public Account[] getAccountsForPackage(String packageName, int uid) {
try {
return mService.getAccountsForPackage(packageName, uid, mContext.getOpPackageName());
@@ -483,6 +487,7 @@
* @return An array of {@link Account}, one per matching account. Empty
* (never null) if no accounts of the specified type have been added.
*/
+ @NonNull
public Account[] getAccountsByTypeForPackage(String type, String packageName) {
try {
return mService.getAccountsByTypeForPackage(type, packageName,
@@ -515,12 +520,14 @@
* @return An array of {@link Account}, one per matching account. Empty
* (never null) if no accounts of the specified type have been added.
*/
+ @NonNull
@RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsByType(String type) {
return getAccountsByTypeAsUser(type, Process.myUserHandle());
}
/** @hide Same as {@link #getAccountsByType(String)} but for a specific user. */
+ @NonNull
public Account[] getAccountsByTypeAsUser(String type, UserHandle userHandle) {
try {
return mService.getAccountsAsUser(type, userHandle.getIdentifier(),
@@ -1537,23 +1544,22 @@
}.start();
}
+
/**
- * Adds a shared account from the primary user to a secondary user. Adding the shared account
+ * Adds shared accounts from a parent user to a secondary user. Adding the shared account
* doesn't take effect immediately. When the target user starts up, any pending shared accounts
* are attempted to be copied to the target user from the primary via calls to the
* authenticator.
- * @param account the account to share
- * @param user the target user
- * @return
+ * @param parentUser parent user
+ * @param user target user
* @hide
*/
- public boolean addSharedAccount(final Account account, UserHandle user) {
+ public void addSharedAccountsFromParentUser(UserHandle parentUser, UserHandle user) {
try {
- boolean val = mService.addSharedAccountAsUser(account, user.getIdentifier());
- return val;
+ mService.addSharedAccountsFromParentUser(parentUser.getIdentifier(),
+ user.getIdentifier());
} catch (RemoteException re) {
- // won't ever happen
- throw new RuntimeException(re);
+ throw new IllegalStateException(re);
}
}
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 4378df4..0d95db1 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -74,9 +74,9 @@
String authTokenType);
/* Shared accounts */
- boolean addSharedAccountAsUser(in Account account, int userId);
Account[] getSharedAccountsAsUser(int userId);
boolean removeSharedAccountAsUser(in Account account, int userId);
+ void addSharedAccountsFromParentUser(int parentUserId, int userId);
/* Account renaming. */
void renameAccount(in IAccountManagerResponse response, in Account accountToRename, String newName);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index da9a8c8..5af6504 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -669,6 +669,10 @@
* 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 @@
* 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) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4997dc7..f60250c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3742,6 +3742,12 @@
}
@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 4191dce..61a9a84 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -468,28 +468,51 @@
*/
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 initiated by the user (most likely via a drag action on the
- * window's edge or corner).
+ * that the resize should preserve the window if possible.
* @hide
*/
- public static final int RESIZE_MODE_USER = 1;
+ public static final int RESIZE_MODE_PRESERVE_WINDOW = (0x1 << 0);
/**
* Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
* that the resize should be performed even if the bounds appears unchanged.
* @hide
*/
- public static final int RESIZE_MODE_FORCED = 2;
+ 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_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 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_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 40eb799..373a23f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -55,4 +55,13 @@
* @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/ActivityView.java b/core/java/android/app/ActivityView.java
index 9c0d931..371c923 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -239,6 +239,8 @@
}
mTextureView.setSurfaceTextureListener(null);
+
+ mThread.quit();
}
private void attachToSurfaceWhenReady() {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5544a71..f29dba2 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -126,8 +126,14 @@
@Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
+ return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
+ }
+
+ @Override
+ public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+ throws NameNotFoundException {
try {
- PackageInfo pi = mPM.getPackageInfo(packageName, flags, mContext.getUserId());
+ PackageInfo pi = mPM.getPackageInfo(packageName, flags, userId);
if (pi != null) {
return pi;
}
@@ -1338,7 +1344,7 @@
final VerificationParams verificationParams = new VerificationParams(null, null,
null, VerificationParams.NO_UID, null);
installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
- installerPackageName, verificationParams, null);
+ installerPackageName, verificationParams, null, mContext.getUserId());
}
@Override
@@ -1348,7 +1354,7 @@
final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
null, VerificationParams.NO_UID, manifestDigest);
installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
- installerPackageName, verificationParams, encryptionParams);
+ installerPackageName, verificationParams, encryptionParams, mContext.getUserId());
}
@Override
@@ -1356,15 +1362,23 @@
IPackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
- installerPackageName, verificationParams, encryptionParams);
+ installerPackageName, verificationParams, encryptionParams, mContext.getUserId());
}
@Override
public void installPackage(Uri packageURI, PackageInstallObserver observer,
int flags, String installerPackageName) {
+ installPackageAsUser(packageURI, observer, flags, installerPackageName,
+ mContext.getUserId());
+ }
+
+ @Override
+ public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer, int flags,
+ String installerPackageName, int userId) {
final VerificationParams verificationParams = new VerificationParams(null, null,
null, VerificationParams.NO_UID, null);
- installCommon(packageURI, observer, flags, installerPackageName, verificationParams, null);
+ installCommon(packageURI, observer, flags, installerPackageName, verificationParams, null,
+ userId);
}
@Override
@@ -1375,7 +1389,7 @@
final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
null, VerificationParams.NO_UID, manifestDigest);
installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
- encryptionParams);
+ encryptionParams, mContext.getUserId());
}
@Override
@@ -1383,12 +1397,13 @@
PackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
- encryptionParams);
+ encryptionParams, mContext.getUserId());
}
private void installCommon(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
- VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
+ VerificationParams verificationParams, ContainerEncryptionParams encryptionParams,
+ int userId) {
if (!"file".equals(packageURI.getScheme())) {
throw new UnsupportedOperationException("Only file:// URIs are supported");
}
@@ -1398,17 +1413,22 @@
final String originPath = packageURI.getPath();
try {
- mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
- verificationParams, null);
+ mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
+ verificationParams, null, userId);
} catch (RemoteException ignored) {
}
}
@Override
- public int installExistingPackage(String packageName)
+ public int installExistingPackage(String packageName) throws NameNotFoundException {
+ return installExistingPackageAsUser(packageName, mContext.getUserId());
+ }
+
+ @Override
+ public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
try {
- int res = mPM.installExistingPackageAsUser(packageName, UserHandle.myUserId());
+ int res = mPM.installExistingPackageAsUser(packageName, userId);
if (res == INSTALL_FAILED_INVALID_URI) {
throw new NameNotFoundException("Package " + packageName + " doesn't exist");
}
@@ -1706,8 +1726,14 @@
@Override
public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
+ deletePackageAsUser(packageName, observer, flags, mContext.getUserId());
+ }
+
+ @Override
+ public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
+ int userId) {
try {
- mPM.deletePackageAsUser(packageName, observer, UserHandle.myUserId(), flags);
+ mPM.deletePackageAsUser(packageName, observer, userId, flags);
} catch (RemoteException e) {
// Should never happen!
}
@@ -1812,7 +1838,7 @@
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!
}
@@ -2143,7 +2169,7 @@
}
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/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/core/java/android/app/AutomaticZenRule.aidl
similarity index 68%
rename from packages/SystemUI/res/values-sw600dp-port/dimens.xml
rename to core/java/android/app/AutomaticZenRule.aidl
index 7dc91d1..feb21d6 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/core/java/android/app/AutomaticZenRule.aidl
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2012, The Android Open Source Project
+/**
+ * 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.
@@ -13,9 +12,8 @@
* 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.
-*/
--->
-<resources>
- <!-- Recent Applications parameters -->
- <dimen name="status_bar_recents_app_label_width">140dip</dimen>
-</resources>
+ */
+
+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 0000000..fea5624
--- /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 f78fb47..920fbe9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -30,6 +30,7 @@
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 @@
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 605c006..cbf198b 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -420,6 +420,106 @@
}
/**
+ * 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/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 31d1ab7..5e8ad68 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -90,6 +90,9 @@
public static final int WINDOW_STATE_HIDING = 1;
public static final int WINDOW_STATE_HIDDEN = 2;
+ public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0;
+ public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1;
+
private Context mContext;
private IStatusBarService mService;
private IBinder mToken = new Binder();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e6484e9..a118f16 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2438,6 +2438,17 @@
}
/**
+ * 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/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 689283c..aa4a631 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -533,6 +533,14 @@
File file = scanQueue.remove(0);
String filePath;
try {
+ // Ignore symlinks outright
+ StructStat stat = Os.lstat(file.getPath());
+ if (OsConstants.S_ISLNK(stat.st_mode)) {
+ if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file);
+ continue;
+ }
+
+ // For all other verification, look at the canonicalized path
filePath = file.getCanonicalPath();
// prune this subtree?
@@ -544,11 +552,7 @@
}
// If it's a directory, enqueue its contents for scanning.
- StructStat stat = Os.lstat(filePath);
- if (OsConstants.S_ISLNK(stat.st_mode)) {
- if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file);
- continue;
- } else if (OsConstants.S_ISDIR(stat.st_mode)) {
+ if (OsConstants.S_ISDIR(stat.st_mode)) {
File[] contents = file.listFiles();
if (contents != null) {
for (File entry : contents) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b08db20..670ca80 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3033,6 +3033,39 @@
public static final String EXTRA_PROCESS_TEXT_READONLY =
"android.intent.extra.PROCESS_TEXT_READONLY";
+ /**
+ * Broadcast action: reports when a new thermal event has been reached. When the device
+ * is reaching its maximum temperatue, the thermal level reported
+ * {@hide}
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_THERMAL_EVENT = "android.intent.action.THERMAL_EVENT";
+
+ /** {@hide} */
+ public static final String EXTRA_THERMAL_STATE = "android.intent.extra.THERMAL_STATE";
+
+ /**
+ * Thermal state when the device is normal. This state is sent in the
+ * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@hide}
+ */
+ public static final int EXTRA_THERMAL_STATE_NORMAL = 0;
+
+ /**
+ * Thermal state where the device is approaching its maximum threshold. This state is sent in
+ * the {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@hide}
+ */
+ public static final int EXTRA_THERMAL_STATE_WARNING = 1;
+
+ /**
+ * Thermal state where the device has reached its maximum threshold. This state is sent in the
+ * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@hide}
+ */
+ public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2;
+
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 9341be1..3283005 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -868,6 +868,9 @@
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 @@
/** {@hide} */
public Uri originatingUri;
/** {@hide} */
+ public int originatingUid = UID_UNKNOWN;
+ /** {@hide} */
public Uri referrerUri;
/** {@hide} */
public String abiOverride;
@@ -915,6 +920,7 @@
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 @@
}
/**
+ * 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 @@
}
/** {@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 @@
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 @@
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 c8e9402..82cbbbe 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -422,6 +422,15 @@
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
@@ -2010,7 +2019,7 @@
* {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
* {@link #GET_SIGNATURES}, {@link #GET_UNINSTALLED_PACKAGES} to
* modify the data returned.
- * @return Returns a PackageInfo object containing information about the
+ * @return A PackageInfo object containing information about the
* package. If flag GET_UNINSTALLED_PACKAGES is set and if the
* package is not found in the list of installed applications, the
* package information is retrieved from the list of uninstalled
@@ -2032,6 +2041,46 @@
throws NameNotFoundException;
/**
+ * @hide
+ * Retrieve overall information about an application package that is
+ * installed on the system.
+ * <p>
+ * Throws {@link NameNotFoundException} if a package with the given name can
+ * not be found on the system.
+ *
+ * @param packageName The full name (i.e. com.google.apps.contacts) of the
+ * desired package.
+ * @param flags Additional option flags. Use any combination of
+ * {@link #GET_ACTIVITIES}, {@link #GET_GIDS},
+ * {@link #GET_CONFIGURATIONS}, {@link #GET_INSTRUMENTATION},
+ * {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+ * {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+ * {@link #GET_SIGNATURES}, {@link #GET_UNINSTALLED_PACKAGES} to
+ * modify the data returned.
+ * @param userId The user id.
+ * @return A PackageInfo object containing information about the
+ * package. If flag GET_UNINSTALLED_PACKAGES is set and if the
+ * package is not found in the list of installed applications, the
+ * package information is retrieved from the list of uninstalled
+ * applications (which includes installed applications as well as
+ * applications with data directory i.e. applications which had been
+ * deleted with {@code DONT_DELETE_DATA} flag set).
+ * @see #GET_ACTIVITIES
+ * @see #GET_GIDS
+ * @see #GET_CONFIGURATIONS
+ * @see #GET_INSTRUMENTATION
+ * @see #GET_PERMISSIONS
+ * @see #GET_PROVIDERS
+ * @see #GET_RECEIVERS
+ * @see #GET_SERVICES
+ * @see #GET_SIGNATURES
+ * @see #GET_UNINSTALLED_PACKAGES
+ */
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ public abstract PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+ throws NameNotFoundException;
+
+ /**
* Map from the current package names in use on the device to whatever
* the current canonical name of that package is.
* @param names Array of current names to be mapped.
@@ -3689,6 +3738,31 @@
Uri packageURI, PackageInstallObserver observer,
int flags, String installerPackageName);
+
+ /**
+ * @hide
+ * Install a package. Since this may take a little while, the result will be
+ * posted back to the given observer. An installation will fail if the package named
+ * in the package file's manifest is already installed, or if there's no space
+ * available on the device.
+ * @param packageURI The location of the package file to install. This can be a 'file:' or a
+ * 'content:' URI.
+ * @param observer An observer callback to get notified when the package installation is
+ * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be
+ * called when that happens. This parameter must not be null.
+ * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
+ * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
+ * @param installerPackageName Optional package name of the application that is performing the
+ * installation. This identifies which market the package came from.
+ * @param userId The user id.
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+ public abstract void installPackageAsUser(
+ Uri packageURI, PackageInstallObserver observer, int flags,
+ String installerPackageName, int userId);
+
/**
* Similar to
* {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
@@ -3752,7 +3826,17 @@
* @hide
*/
// @SystemApi
- public abstract int installExistingPackage(String packageName)
+ public abstract int installExistingPackage(String packageName) throws NameNotFoundException;
+
+ /**
+ * If there is already an application with the given package name installed
+ * on the system for other users, also install it for the specified user.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+ public abstract int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException;
/**
@@ -3958,7 +4042,7 @@
* @param observer An observer callback to get notified when the package deletion is
* complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
* called when that happens. observer may be null to indicate that no callback is desired.
- * @param flags - possible values: {@link #DELETE_KEEP_DATA},
+ * @param flags Possible values: {@link #DELETE_KEEP_DATA},
* {@link #DELETE_ALL_USERS}.
*
* @hide
@@ -3968,6 +4052,27 @@
String packageName, IPackageDeleteObserver observer, int flags);
/**
+ * Attempts to delete a package. Since this may take a little while, the result will
+ * be posted back to the given observer. A deletion will fail if the named package cannot be
+ * found, or if the named package is a "system package".
+ * (TODO: include pointer to documentation on "system packages")
+ *
+ * @param packageName The name of the package to delete
+ * @param observer An observer callback to get notified when the package deletion is
+ * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
+ * called when that happens. observer may be null to indicate that no callback is desired.
+ * @param flags Possible values: {@link #DELETE_KEEP_DATA}, {@link #DELETE_ALL_USERS}.
+ * @param userId The user Id
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+ public abstract void deletePackageAsUser(
+ String packageName, IPackageDeleteObserver observer, int flags, int userId);
+
+ /**
* Retrieve the package name of the application that installed a package. This identifies
* which market the package came from.
*
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index e29bd2c..0606e35 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.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.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 @@
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 @@
@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 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 @@
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 @@
}
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 @@
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 @@
}
synchronized (sSync) {
if (mPluralRule != null) {
- mPluralRule = PluralRules.forLocale(config.locale);
+ mPluralRule = PluralRules.forLocale(config.getLocales().getPrimary());
}
}
}
@@ -2049,9 +2048,8 @@
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/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index c373308..cd483b1 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -19,6 +19,7 @@
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 @@
*/
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 @@
// 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/Binder.java b/core/java/android/os/Binder.java
index cfa6164..c4501ba 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -157,7 +157,7 @@
* incoming transaction, then its own UserHandle is returned.
*/
public static final UserHandle getCallingUserHandle() {
- return new UserHandle(UserHandle.getUserId(getCallingUid()));
+ return UserHandle.of(UserHandle.getUserId(getCallingUid()));
}
/**
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index c4f62ba..9fdbec3 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -23,8 +23,11 @@
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/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index aeb5d45..4c19ddd 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -35,6 +35,7 @@
UserInfo createUser(in String name, int flags);
UserInfo createProfileForUser(in String name, int flags, int userHandle);
+ UserInfo createRestrictedProfile(String name, int parentUserId);
void setUserEnabled(int userHandle);
boolean removeUser(int userHandle);
void setUserName(int userHandle, String name);
@@ -48,6 +49,7 @@
UserInfo getUserInfo(int userHandle);
long getUserCreationTime(int userHandle);
boolean isRestricted();
+ boolean canHaveRestrictedProfile(int userId);
int getUserSerialNumber(int userHandle);
int getUserHandle(int userSerialNumber);
Bundle getUserRestrictions(int userHandle);
diff --git a/core/java/android/os/ParcelableParcel.java b/core/java/android/os/ParcelableParcel.java
index 11785f1..5bbe6488 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 @@
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/Process.java b/core/java/android/os/Process.java
index 7234e98..4ac361d0 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -797,8 +797,8 @@
* {@link #myUid()} in that a particular user will have multiple
* distinct apps running under it each with their own uid.
*/
- public static final UserHandle myUserHandle() {
- return new UserHandle(UserHandle.getUserId(myUid()));
+ public static UserHandle myUserHandle() {
+ return UserHandle.of(UserHandle.getUserId(myUid()));
}
/**
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 213e083..796addc 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -17,7 +17,6 @@
package android.os;
import android.annotation.SystemApi;
-import android.util.SparseArray;
import java.io.PrintWriter;
@@ -83,8 +82,6 @@
final int mHandle;
- private static final SparseArray<UserHandle> userHandles = new SparseArray<UserHandle>();
-
/**
* Checks to see if the user id is the same for the two uids, i.e., they belong to the same
* user.
@@ -144,15 +141,8 @@
}
/** @hide */
- public static UserHandle getCallingUserHandle() {
- int userId = getUserId(Binder.getCallingUid());
- UserHandle userHandle = userHandles.get(userId);
- // Intentionally not synchronized to save time
- if (userHandle == null) {
- userHandle = new UserHandle(userId);
- userHandles.put(userId, userHandle);
- }
- return userHandle;
+ public static UserHandle of(int userId) {
+ return userId == USER_SYSTEM ? SYSTEM : new UserHandle(userId);
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 64e2505..d178d20 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -15,10 +15,12 @@
*/
package android.os;
+import android.accounts.AccountManager;
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;
@@ -631,6 +633,19 @@
}
/**
+ * Checks if specified user can have restricted profile.
+ * @hide
+ */
+ public boolean canHaveRestrictedProfile(int userId) {
+ try {
+ return mService.canHaveRestrictedProfile(userId);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not check if user can have restricted profile", re);
+ return false;
+ }
+ }
+
+ /**
* Checks if the calling app is running as a guest user.
* @return whether the caller is a guest user.
* @hide
@@ -927,7 +942,8 @@
}
/**
- * Creates a restricted profile with the specified name.
+ * Creates a restricted profile with the specified name. This method also sets necessary
+ * restrictions and adds shared accounts.
*
* @param name profile's name
* @return UserInfo object for the created user, or null if the user could not be created.
@@ -935,13 +951,14 @@
*/
public UserInfo createRestrictedProfile(String name) {
try {
- if (isSplitSystemUser()) {
- return mService.createProfileForUser(name, UserInfo.FLAG_RESTRICTED,
- UserHandle.getCallingUserId());
- } else {
- return mService.createProfileForUser(name, UserInfo.FLAG_RESTRICTED,
- UserHandle.USER_SYSTEM);
+ UserHandle parentUserHandle = Process.myUserHandle();
+ UserInfo user = mService.createRestrictedProfile(name,
+ parentUserHandle.getIdentifier());
+ if (user != null) {
+ AccountManager.get(mContext).addSharedAccountsFromParentUser(parentUserHandle,
+ UserHandle.of(user.id));
}
+ return user;
} catch (RemoteException e) {
Log.w(TAG, "Could not create a restricted profile", e);
}
@@ -1321,12 +1338,15 @@
}
/**
- * 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;
@@ -1337,8 +1357,8 @@
++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 67f6409..5414f00 100644
--- a/core/java/android/preference/SeekBarPreference.java
+++ b/core/java/android/preference/SeekBarPreference.java
@@ -96,18 +96,15 @@
@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;
}
- return false;
+
+ SeekBar seekBar = (SeekBar) v.findViewById(com.android.internal.R.id.seekbar);
+ if (seekBar == null) {
+ 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 1a83cd5..241e6db 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -19,6 +19,7 @@
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 @@
*/
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 @@
* @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());
- }
- if (paths.size() == 4 && PATH_TREE.equals(paths.get(0))
- && PATH_DOCUMENT.equals(paths.get(2))) {
- 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));
+ }
}
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 225f0cf..d601831 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7689,14 +7689,6 @@
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 6310570..4de903e 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -755,6 +755,10 @@
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 @@
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 1665c75..f2b6041 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 @@
@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 @@
}
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 @@
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 79c4a55..6ece091 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -29,6 +29,7 @@
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 @@
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 8b57d3d..acca3ed 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -185,4 +185,24 @@
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/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 32ef995..7adfa8d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -144,7 +144,6 @@
void setAppStartingWindow(IBinder token, String pkg, int theme,
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
- void setAppWillBeHidden(IBinder token);
void setAppVisibility(IBinder token, boolean visible);
void startAppFreezingScreen(IBinder token, int configChanges);
void stopAppFreezingScreen(IBinder token, boolean force);
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 37312d0..e200bef 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -72,6 +72,9 @@
private static final String TAG = LayoutInflater.class.getSimpleName();
private static final boolean DEBUG = false;
+ /** Empty stack trace used to avoid log spam in re-throw exceptions. */
+ private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
+
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -532,15 +535,14 @@
}
} catch (XmlPullParserException e) {
- InflateException ex = new InflateException(e.getMessage());
- ex.initCause(e);
- throw ex;
+ final InflateException ie = new InflateException(e.getMessage(), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
+ throw ie;
} catch (Exception e) {
- InflateException ex = new InflateException(
- parser.getPositionDescription()
- + ": " + e.getMessage());
- ex.initCause(e);
- throw ex;
+ final InflateException ie = new InflateException(parser.getPositionDescription()
+ + ": " + e.getMessage(), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
+ throw ie;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
@@ -625,27 +627,25 @@
return view;
} catch (NoSuchMethodException e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class "
- + (prefix != null ? (prefix + name) : name));
- ie.initCause(e);
+ final InflateException ie = new InflateException(attrs.getPositionDescription()
+ + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (ClassCastException e) {
// If loaded class is not a View subclass
- InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Class is not a View "
- + (prefix != null ? (prefix + name) : name));
- ie.initCause(e);
+ final InflateException ie = new InflateException(attrs.getPositionDescription()
+ + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (ClassNotFoundException e) {
// If loadClass fails, we should propagate the exception.
throw e;
} catch (Exception e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class "
- + (clazz == null ? "<unknown>" : clazz.getName()));
- ie.initCause(e);
+ final InflateException ie = new InflateException(
+ attrs.getPositionDescription() + ": Error inflating class "
+ + (clazz == null ? "<unknown>" : clazz.getName()), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
@@ -657,8 +657,7 @@
*/
private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
throw new InflateException(attrs.getPositionDescription()
- + ": Class not allowed to be inflated "
- + (prefix != null ? (prefix + name) : name));
+ + ": Class not allowed to be inflated "+ (prefix != null ? (prefix + name) : name));
}
/**
@@ -774,14 +773,14 @@
} catch (ClassNotFoundException e) {
final InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class " + name);
- ie.initCause(e);
+ + ": Error inflating class " + name, e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class " + name);
- ie.initCause(e);
+ + ": Error inflating class " + name, e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2a1d757..2c7a436 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5388,7 +5388,7 @@
protected boolean performButtonActionOnTouchDown(MotionEvent event) {
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE &&
(event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
- showContextMenu(event.getX(), event.getY(), event.getMetaState());
+ showContextMenu(event.getX(), event.getY());
mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT;
return true;
}
@@ -5409,13 +5409,10 @@
*
* @param x The referenced x coordinate.
* @param y The referenced y coordinate.
- * @param metaState The keyboard modifiers that were pressed.
* @return Whether a context menu was displayed.
- *
- * @hide
*/
- public boolean showContextMenu(float x, float y, int metaState) {
- return showContextMenu();
+ public boolean showContextMenu(float x, float y) {
+ return getParent().showContextMenuForChild(this, x, y);
}
/**
@@ -6652,12 +6649,24 @@
}
/**
- * 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}.
+ * 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 delegate instance.
- *
+ * @param delegate the object to which accessibility method calls should be
+ * delegated
* @see AccessibilityDelegate
*/
public void setAccessibilityDelegate(@Nullable AccessibilityDelegate delegate) {
@@ -22280,6 +22289,18 @@
* 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/ViewGroup.java b/core/java/android/view/ViewGroup.java
index bdcd998..475ce2f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -767,6 +767,11 @@
return mParent != null && mParent.showContextMenuForChild(originalView);
}
+ @Override
+ public boolean showContextMenuForChild(View originalView, float x, float y) {
+ return mParent != null && mParent.showContextMenuForChild(originalView, x, y);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 15b86d1..07f1e2c 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -182,6 +182,17 @@
public boolean showContextMenuForChild(View originalView);
/**
+ * Bring up a context menu for the specified view at the given x/y offset from
+ * the top left corner.
+ *
+ * @param originalView
+ * @param x The x offset at which to open the menu
+ * @param y The y offset at which to open the menu
+ * @return true if a context menu was displayed
+ */
+ public boolean showContextMenuForChild(View originalView, float x, float y);
+
+ /**
* Have the parent populate the specified context menu if it has anything to
* add (and then recurse on its parent).
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8403c46..f6c60ed 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6240,6 +6240,11 @@
}
@Override
+ public boolean showContextMenuForChild(View originalView, float x, float y) {
+ return false;
+ }
+
+ @Override
public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
return null;
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0e7089f..5f4e7af 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2016,4 +2016,8 @@
public boolean hasNonClientDecorView() {
return false;
}
+
+ /** @hide */
+ public void setTheme(int resId) {
+ }
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 5724f52..b8faf0c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3080,6 +3080,15 @@
}
private class CheckForLongPress extends WindowRunnnable implements Runnable {
+ private static final int INVALID_COORD = -1;
+ private float mX = INVALID_COORD;
+ private float mY = INVALID_COORD;
+
+ private void setCoords(float x, float y) {
+ mX = x;
+ mY = y;
+ }
+
@Override
public void run() {
final int motionPosition = mMotionPosition;
@@ -3090,7 +3099,11 @@
boolean handled = false;
if (sameWindow() && !mDataChanged) {
- handled = performLongPress(child, longPressPosition, longPressId);
+ if (mX != INVALID_COORD && mY != INVALID_COORD) {
+ handled = performLongPress(child, longPressPosition, longPressId, mX, mY);
+ } else {
+ handled = performLongPress(child, longPressPosition, longPressId);
+ }
}
if (handled) {
mTouchMode = TOUCH_MODE_REST;
@@ -3146,6 +3159,16 @@
boolean performLongPress(final View child,
final int longPressPosition, final long longPressId) {
+ return performLongPress(
+ child,
+ longPressPosition,
+ longPressId,
+ CheckForLongPress.INVALID_COORD,
+ CheckForLongPress.INVALID_COORD);
+ }
+
+ boolean performLongPress(final View child,
+ final int longPressPosition, final long longPressId, float x, float y) {
// CHOICE_MODE_MULTIPLE_MODAL takes over long press.
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
if (mChoiceActionMode == null &&
@@ -3163,7 +3186,11 @@
}
if (!handled) {
mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
- handled = super.showContextMenuForChild(AbsListView.this);
+ if (x != CheckForLongPress.INVALID_COORD && y != CheckForLongPress.INVALID_COORD) {
+ handled = super.showContextMenuForChild(AbsListView.this, x, y);
+ } else {
+ handled = super.showContextMenuForChild(AbsListView.this);
+ }
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
@@ -3178,17 +3205,17 @@
/** @hide */
@Override
- public boolean showContextMenu(float x, float y, int metaState) {
+ public boolean showContextMenu(float x, float y) {
final int position = pointToPosition((int)x, (int)y);
if (position != INVALID_POSITION) {
final long id = mAdapter.getItemId(position);
View child = getChildAt(position - mFirstPosition);
if (child != null) {
mContextMenuInfo = createContextMenuInfo(child, position, id);
- return super.showContextMenuForChild(AbsListView.this);
+ return super.showContextMenuForChild(AbsListView.this, x, y);
}
}
- return super.showContextMenu(x, y, metaState);
+ return super.showContextMenu(x, y);
}
@Override
@@ -3341,6 +3368,7 @@
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
+ mPendingCheckForLongPress.setCoords(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress, longPressTimeout);
} else {
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 68855ff..10aefe4 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -666,7 +666,7 @@
progress += scale * max;
setHotspot(x, (int) event.getY());
- setProgress((int) progress, true);
+ setProgressInternal((int) progress, true, false);
}
/**
@@ -706,9 +706,12 @@
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 fddc40f..f53aa38 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2645,6 +2645,8 @@
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 @@
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 @@
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 @@
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 @@
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/android/widget/MenuPopupWindow.java b/core/java/android/widget/MenuPopupWindow.java
index eac3a0b..1fb62d0 100644
--- a/core/java/android/widget/MenuPopupWindow.java
+++ b/core/java/android/widget/MenuPopupWindow.java
@@ -152,8 +152,16 @@
boolean superVal = super.onHoverEvent(ev);
if (dispatchHover && mHoverListener != null) {
- mHoverListener.onItemHovered(
- ((MenuAdapter) getAdapter()).getAdapterMenu(), position);
+ ListAdapter adapter = getAdapter();
+ MenuAdapter menuAdapter;
+ if (adapter instanceof HeaderViewListAdapter) {
+ menuAdapter = (MenuAdapter) ((HeaderViewListAdapter) adapter)
+ .getWrappedAdapter();
+ } else {
+ menuAdapter = (MenuAdapter) adapter;
+ }
+
+ mHoverListener.onItemHovered(menuAdapter.getAdapterMenu(), position);
}
return superVal;
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f9fa027..7b9de79 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -18,6 +18,7 @@
import com.android.internal.R;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -1449,11 +1450,13 @@
anchor.getLocationOnScreen(mScreenLocation);
onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
(mScreenLocation[1] - yoff - displayFrame.top);
- if (onTop) {
- p.gravity = Gravity.LEFT | Gravity.BOTTOM;
- p.y = root.getHeight() - mDrawingLocation[1] + yoff;
- } else {
- p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ if (!mOverlapAnchor) {
+ if (onTop) {
+ p.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ p.y = root.getHeight() - mDrawingLocation[1] + yoff;
+ } else {
+ p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ }
}
}
@@ -1469,13 +1472,21 @@
p.width = Math.min(p.width, displayFrameWidth);
}
- if (onTop) {
- final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
- if (popupTop < 0) {
- p.y += popupTop;
+ if (mOverlapAnchor) {
+ final int displayFrameHeight = displayFrame.bottom - displayFrame.top;
+ final int bottom = p.y + p.height;
+ if (bottom > displayFrame.bottom) {
+ p.y -= bottom - displayFrameHeight;
}
} else {
- p.y = Math.max(p.y, displayFrame.top);
+ if (onTop) {
+ final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
+ if (popupTop < 0) {
+ p.y += popupTop;
+ }
+ } else {
+ p.y = Math.max(p.y, displayFrame.top);
+ }
}
}
@@ -1494,7 +1505,7 @@
* @return The maximum available height for the popup to be completely
* shown.
*/
- public int getMaxAvailableHeight(View anchor) {
+ public int getMaxAvailableHeight(@NonNull View anchor) {
return getMaxAvailableHeight(anchor, 0);
}
@@ -1509,7 +1520,7 @@
* @return The maximum available height for the popup to be completely
* shown.
*/
- public int getMaxAvailableHeight(View anchor, int yOffset) {
+ public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) {
return getMaxAvailableHeight(anchor, yOffset, false);
}
@@ -1527,22 +1538,29 @@
* bottom decorations
* @return The maximum available height for the popup to be completely
* shown.
- *
- * @hide Pending API council approval.
*/
- public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
+ public int getMaxAvailableHeight(
+ @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
final Rect displayFrame = new Rect();
anchor.getWindowVisibleDisplayFrame(displayFrame);
final int[] anchorPos = mDrawingLocation;
anchor.getLocationOnScreen(anchorPos);
- int bottomEdge = displayFrame.bottom;
+ final int bottomEdge;
if (ignoreBottomDecorations) {
- Resources res = anchor.getContext().getResources();
+ final Resources res = anchor.getContext().getResources();
bottomEdge = res.getDisplayMetrics().heightPixels;
+ } else {
+ bottomEdge = displayFrame.bottom;
}
- final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
+
+ final int distanceToBottom;
+ if (mOverlapAnchor) {
+ distanceToBottom = bottomEdge - anchorPos[1] - yOffset;
+ } else {
+ distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
+ }
final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
// anchorPos[1] is distance from anchor to top of screen
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7ca3339..ca1b211 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -31,6 +31,7 @@
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.Rect;
@@ -55,6 +56,8 @@
import android.widget.AdapterView.OnItemClickListener;
import libcore.util.Objects;
+import com.android.internal.R;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -206,14 +209,22 @@
/** @hide */
public static class OnClickHandler {
+
+ private int mEnterAnimationId;
+
public boolean onClickHandler(View view, PendingIntent pendingIntent,
Intent fillInIntent) {
try {
// TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Context context = view.getContext();
- ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
- 0, 0,
- view.getMeasuredWidth(), view.getMeasuredHeight());
+ ActivityOptions opts;
+ if (mEnterAnimationId != 0) {
+ opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
+ } else {
+ opts = ActivityOptions.makeScaleUpAnimation(view,
+ 0, 0,
+ view.getMeasuredWidth(), view.getMeasuredHeight());
+ }
context.startIntentSender(
pendingIntent.getIntentSender(), fillInIntent,
Intent.FLAG_ACTIVITY_NEW_TASK,
@@ -228,6 +239,10 @@
}
return true;
}
+
+ public void setEnterAnimationId(int enterAnimationId) {
+ mEnterAnimationId = enterAnimationId;
+ }
}
/**
@@ -2761,11 +2776,31 @@
inflater.setFilter(this);
result = inflater.inflate(rvToApply.getLayoutId(), parent, false);
+ loadTransitionOverride(context, handler);
+
rvToApply.performApply(result, parent, handler);
return result;
}
+ private static void loadTransitionOverride(Context context,
+ RemoteViews.OnClickHandler handler) {
+ if (handler != null && context.getResources().getBoolean(
+ com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
+ TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ int windowAnimations = windowStyle.getResourceId(
+ com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+ TypedArray windowAnimationStyle = context.obtainStyledAttributes(
+ windowAnimations, com.android.internal.R.styleable.WindowAnimation);
+ handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
+ com.android.internal.R.styleable.
+ WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
+ windowStyle.recycle();
+ windowAnimationStyle.recycle();
+ }
+ }
+
/**
* Applies all of the actions to the provided view.
*
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2172b5c..f0e216f 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.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 @@
}
@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 ef9d1ce..1710489 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -796,7 +796,7 @@
return false;
}
- boolean shouldAutoLaunchSingleChoice() {
+ boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
return true;
}
@@ -837,18 +837,21 @@
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 b6240e4..c25db65 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -46,6 +46,9 @@
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 e39bf60..64b7768 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 131 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 132 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -5704,6 +5704,8 @@
cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
}
}
+ } else {
+ mCpuClusterSpeed[cluster] = null;
}
}
} else {
@@ -9382,13 +9384,14 @@
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 @@
u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in);
}
}
+ } else {
+ u.mCpuClusterSpeed[cluster] = null;
}
}
} else {
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index db2b41f..13d046e 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -20,6 +20,7 @@
import android.net.LocalSocketAddress;
import android.os.SystemClock;
import android.util.Slog;
+
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -91,32 +92,29 @@
}
}
- public int dexopt(String apkPath, int uid, boolean isPublic,
- String instructionSet, int dexoptNeeded, boolean bootComplete) {
- return dexopt(apkPath, uid, isPublic, "*", instructionSet, dexoptNeeded,
- false, false, null, bootComplete);
+ public int dexopt(String apkPath, int uid, String instructionSet,
+ int dexoptNeeded, int dexFlags) {
+ return dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
+ null /*outputPath*/, dexFlags);
}
- public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
- String instructionSet, int dexoptNeeded, boolean vmSafeMode,
- boolean debuggable, String outputPath, boolean bootComplete) {
+ public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+ int dexoptNeeded, String outputPath, int dexFlags) {
StringBuilder builder = new StringBuilder("dexopt");
builder.append(' ');
builder.append(apkPath);
builder.append(' ');
builder.append(uid);
- builder.append(isPublic ? " 1" : " 0");
builder.append(' ');
builder.append(pkgName);
builder.append(' ');
builder.append(instructionSet);
builder.append(' ');
builder.append(dexoptNeeded);
- builder.append(vmSafeMode ? " 1" : " 0");
- builder.append(debuggable ? " 1" : " 0");
builder.append(' ');
builder.append(outputPath != null ? outputPath : "!");
- builder.append(bootComplete ? " 1" : " 0");
+ builder.append(' ');
+ builder.append(dexFlags);
return execute(builder.toString());
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index eee8b08..aaa89df 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -486,8 +486,8 @@
final int dexoptNeeded = DexFile.getDexOptNeeded(
classPathElement, "*", instructionSet, false /* defer */);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
- instructionSet, dexoptNeeded, false /* boot complete */);
+ installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
+ dexoptNeeded, 0 /*dexFlags*/);
}
}
} catch (IOException ioe) {
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 0000000..4f17c39
--- /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 3353d16..b6d7364 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.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;
@@ -71,6 +73,7 @@
import com.android.internal.view.menu.ListMenuPresenter;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuDialogHelper;
+import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.MenuView;
import com.android.internal.widget.ActionBarContextView;
@@ -274,6 +277,7 @@
private ContextMenuBuilder mContextMenu;
private MenuDialogHelper mContextMenuHelper;
+ private MenuPopupHelper mContextMenuPopupHelper;
private boolean mClosingActionMenu;
private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
@@ -315,6 +319,7 @@
private Rect mOutsets = new Rect();
private boolean mIsStartingWindow;
+ private int mTheme = -1;
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
@@ -335,6 +340,10 @@
mElevation = preservedWindow.getElevation();
mLoadEleveation = false;
mForceDecorInstall = true;
+ // If we're preserving window, carry over the app token from the preserved
+ // window, as we'll be skipping the addView in handleResumeActivity(), and
+ // the token will not be updated as for a new window.
+ getAttributes().token = preservedWindow.getAttributes().token;
}
}
@@ -1123,6 +1132,10 @@
mContextMenuHelper.dismiss();
mContextMenuHelper = null;
}
+ if (mContextMenuPopupHelper != null) {
+ mContextMenuPopupHelper.dismiss();
+ mContextMenuPopupHelper = null;
+ }
}
@Override
@@ -2292,7 +2305,7 @@
}
}
- private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
+ private static final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
/* package */int mDefaultOpacity = PixelFormat.OPAQUE;
@@ -2362,6 +2375,8 @@
private int mRootScrollY = 0;
+ private PhoneWindow mWindow;
+
public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
@@ -2383,7 +2398,7 @@
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
- mBackgroundFallback.draw(mContentRoot, c, mContentParent);
+ mBackgroundFallback.draw(mWindow.mContentRoot, c, mWindow.mContentParent);
}
@Override
@@ -2395,7 +2410,7 @@
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;
@@ -2404,15 +2419,15 @@
// 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) {
@@ -2420,28 +2435,28 @@
}
}
- 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;
@@ -2451,10 +2466,10 @@
// 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) {
@@ -2466,23 +2481,23 @@
@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) {
@@ -2534,7 +2549,7 @@
@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.
@@ -2557,7 +2572,7 @@
int x = (int)event.getX();
int y = (int)event.getY();
if (isOutOfBounds(x, y)) {
- closePanel(mFeatureId);
+ mWindow.closePanel(mFeatureId);
return true;
}
}
@@ -2583,7 +2598,7 @@
if (action == MotionEvent.ACTION_MOVE) {
if (y > (mDownY+30)) {
Log.i(TAG, "Closing!");
- closePanel(mFeatureId);
+ mWindow.closePanel(mFeatureId);
mWatchingForMenu = false;
return true;
}
@@ -2599,7 +2614,7 @@
if (action == MotionEvent.ACTION_DOWN) {
int y = (int)event.getY();
- if (y >= (getHeight()-5) && !hasChildren()) {
+ if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
Log.i(TAG, "Watchiing!");
mWatchingForMenu = true;
}
@@ -2614,7 +2629,7 @@
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;
@@ -2648,8 +2663,8 @@
@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;
}
@@ -2686,7 +2701,7 @@
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);
@@ -2711,7 +2726,8 @@
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) {
@@ -2732,7 +2748,8 @@
}
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) {
@@ -2750,21 +2767,21 @@
}
}
- 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);
}
}
@@ -2776,7 +2793,7 @@
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) {
@@ -2804,12 +2821,12 @@
@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);
}
}
@@ -2825,23 +2842,46 @@
@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 (mWindow.mContextMenu == null) {
+ mWindow.mContextMenu = new ContextMenuBuilder(getContext());
+ mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
+ } else {
+ mWindow.mContextMenu.clearAll();
+ }
+
+ final MenuPopupHelper helper = mWindow.mContextMenu.showPopup(
+ getContext(), originalView, x, y);
+ if (helper != 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.
+ mWindow.mContextMenuPopupHelper.dismiss();
+ }
+ mWindow.mContextMenuPopupHelper = helper;
return helper != null;
}
@@ -2871,14 +2911,15 @@
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.
}
@@ -2903,9 +2944,9 @@
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.
}
@@ -2994,10 +3035,10 @@
}
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;
@@ -3029,14 +3070,14 @@
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);
}
@@ -3053,13 +3094,13 @@
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
@@ -3097,11 +3138,11 @@
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;
@@ -3193,14 +3234,14 @@
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;
@@ -3231,7 +3272,7 @@
// 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);
@@ -3255,14 +3296,14 @@
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
@@ -3344,7 +3385,7 @@
mDefaultOpacity = opacity;
if (mFeatureId < 0) {
- setDefaultWindowFormat(opacity);
+ mWindow.setDefaultWindowFormat(opacity);
}
}
@@ -3354,12 +3395,13 @@
// 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);
}
@@ -3375,8 +3417,8 @@
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();
}
@@ -3388,7 +3430,7 @@
* menu was open. When the activity is recreated, the menu
* should be shown again.
*/
- openPanelsAfterRestore();
+ mWindow.openPanelsAfterRestore();
}
}
@@ -3396,13 +3438,13 @@
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) {
@@ -3417,7 +3459,7 @@
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();
}
@@ -3426,29 +3468,29 @@
@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
@@ -3480,7 +3522,7 @@
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();
@@ -3627,7 +3669,7 @@
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()
@@ -3666,6 +3708,15 @@
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.
*/
@@ -3754,9 +3805,9 @@
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.
}
@@ -3776,7 +3827,20 @@
}
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,
@@ -4145,13 +4209,15 @@
// 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);
}
}
@@ -4173,11 +4239,14 @@
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);
@@ -5352,4 +5421,15 @@
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/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ab3ec98..11ef18b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -74,7 +74,9 @@
/**
* Notifies the status bar that a camera launch gesture has been detected.
+ *
+ * @param source the identifier for the gesture, see {@link StatusBarManager}
*/
- void onCameraLaunchGestureDetected();
+ void onCameraLaunchGestureDetected(int source);
}
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index aa7b34b..293e2ad 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -20,14 +20,16 @@
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnKeyListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.widget.AdapterView;
import android.widget.DropDownListView;
+import android.widget.FrameLayout;
import android.widget.MenuItemHoverListener;
+import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.MenuPopupWindow;
import android.widget.MenuPopupWindow.MenuDropDownListView;
import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;
+import android.widget.TextView;
import com.android.internal.util.Preconditions;
@@ -36,8 +38,8 @@
* side.
* @hide
*/
-final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemClickListener,
- MenuPresenter, OnKeyListener, PopupWindow.OnDismissListener {
+final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKeyListener,
+ PopupWindow.OnDismissListener {
@Retention(RetentionPolicy.SOURCE)
@IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT})
public @interface HorizPosition {}
@@ -96,7 +98,7 @@
int menuIndex = -1;
for (int i = 0; i < mListViews.size(); i++) {
final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(i);
- final MenuAdapter adapter = (MenuAdapter) view.getAdapter();
+ final MenuAdapter adapter = toMenuAdapter(view.getAdapter());
if (adapter.getAdapterMenu() == menu) {
menuIndex = i;
@@ -129,7 +131,7 @@
int nextIndex = mListViews.indexOf(view) + 1;
if (nextIndex < mListViews.size()) {
MenuAdapter nextSubMenuAdapter =
- (MenuAdapter) mListViews.get(nextIndex).getAdapter();
+ toMenuAdapter(mListViews.get(nextIndex).getAdapter());
// Disable exit animation, to prevent overlapping fading out
// submenus.
mPopupWindows.get(nextIndex).setExitTransition(null);
@@ -151,7 +153,7 @@
final MenuDropDownListView nextView =
(MenuDropDownListView) mListViews.get(menuIndex + 1);
- final MenuAdapter nextAdapter = (MenuAdapter) nextView.getAdapter();
+ final MenuAdapter nextAdapter = toMenuAdapter(nextView.getAdapter());
mSubMenuHoverHandler.removeCallbacksAndMessages(null);
mSubMenuHoverHandler.postDelayed(new Runnable() {
@@ -184,7 +186,10 @@
private int mLastPosition;
private List<Integer> mPositions;
private List<int[]> mOffsets;
+ private int mInitXOffset;
+ private int mInitYOffset;
private boolean mForceShowIcon;
+ private boolean mShowTitle;
private Callback mPresenterCallback;
private ViewTreeObserver mTreeObserver;
private PopupWindow.OnDismissListener mOnDismissListener;
@@ -248,7 +253,25 @@
for (int i = 0; i < mPopupWindows.size(); i++) {
MenuPopupWindow popupWindow = mPopupWindows.get(i);
popupWindow.show();
- mListViews.add((DropDownListView) popupWindow.getListView());
+ DropDownListView listView = (DropDownListView) popupWindow.getListView();
+ mListViews.add(listView);
+
+ MenuBuilder menu = toMenuAdapter(listView.getAdapter()).getAdapterMenu();
+ if (i == 0 && mShowTitle && menu.getHeaderTitle() != null) {
+ FrameLayout titleItemView =
+ (FrameLayout) LayoutInflater.from(mContext).inflate(
+ com.android.internal.R.layout.popup_menu_header_item_layout,
+ listView,
+ false);
+ TextView titleView = (TextView) titleItemView.findViewById(
+ com.android.internal.R.id.title);
+ titleView.setText(menu.getHeaderTitle());
+ titleItemView.setEnabled(false);
+ listView.addHeaderView(titleItemView, null, false);
+
+ // Update to show the title.
+ popupWindow.show();
+ }
}
mShownAnchorView = mAnchorView;
@@ -275,12 +298,6 @@
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- MenuAdapter adapter = (MenuAdapter) parent.getAdapter();
- adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
- }
-
- @Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
dismiss();
@@ -381,6 +398,9 @@
y = lastOffset[1] + lastListView.getSelectedView().getTop() -
lastListView.getChildAt(0).getTop();
+ } else {
+ x = mInitXOffset;
+ y = mInitYOffset;
}
popupWindow.setWidth(menuWidth);
@@ -393,7 +413,8 @@
// we deliberately do not yet show the popupWindow, as #show() will do that later.
if (isShowing()) {
popupWindow.show();
- mListViews.add((DropDownListView) popupWindow.getListView());
+ DropDownListView listView = (DropDownListView) popupWindow.getListView();
+ mListViews.add(listView);
}
int[] offsets = {x, y};
@@ -425,8 +446,9 @@
if (dismissedIndex != -1) {
for (int i = dismissedIndex; i < mListViews.size(); i++) {
ListView view = mListViews.get(i);
- MenuAdapter adapter = (MenuAdapter) view.getAdapter();
- adapter.mAdapterMenu.close();
+ ListAdapter adapter = view.getAdapter();
+ MenuAdapter menuAdapter = toMenuAdapter(adapter);
+ menuAdapter.mAdapterMenu.close();
}
}
}
@@ -434,7 +456,7 @@
@Override
public void updateMenuView(boolean cleared) {
for (ListView view : mListViews) {
- ((MenuAdapter) view.getAdapter()).notifyDataSetChanged();
+ toMenuAdapter(view.getAdapter()).notifyDataSetChanged();
}
}
@@ -447,7 +469,7 @@
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
// Don't allow double-opening of the same submenu.
for (ListView view : mListViews) {
- if (((MenuAdapter) view.getAdapter()).mAdapterMenu.equals(subMenu)) {
+ if (toMenuAdapter(view.getAdapter()).mAdapterMenu.equals(subMenu)) {
// Just re-focus that one.
view.requestFocus();
return true;
@@ -471,7 +493,7 @@
for (int i = 0; i < mListViews.size(); i++) {
ListView view = mListViews.get(i);
- MenuAdapter adapter = (MenuAdapter) view.getAdapter();
+ MenuAdapter adapter = toMenuAdapter(view.getAdapter());
if (menuIndex == -1 && menu == adapter.mAdapterMenu) {
menuIndex = i;
@@ -557,4 +579,18 @@
return mListViews.size() > 0 ? mListViews.get(mListViews.size() - 1) : null;
}
+ @Override
+ public void setHorizontalOffset(int x) {
+ mInitXOffset = x;
+ }
+
+ @Override
+ public void setVerticalOffset(int y) {
+ mInitYOffset = y;
+ }
+
+ @Override
+ public void setShowTitle(boolean showTitle) {
+ mShowTitle = showTitle;
+ }
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
index bf44d51..aaa1bf1 100644
--- a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
@@ -17,6 +17,7 @@
package com.android.internal.view.menu;
import android.content.Context;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
import android.util.EventLog;
@@ -93,4 +94,29 @@
return null;
}
+ public MenuPopupHelper showPopup(Context context, View originalView, float x, float y) {
+ if (originalView != null) {
+ // Let relevant views and their populate context listeners populate
+ // the context menu
+ originalView.createContextMenu(this);
+ }
+
+ if (getVisibleItems().size() > 0) {
+ EventLog.writeEvent(50001, 1);
+
+ int location[] = new int[2];
+ originalView.getLocationOnScreen(location);
+
+ final MenuPopupHelper helper = new MenuPopupHelper(
+ context,
+ this,
+ originalView,
+ false /* overflowOnly */,
+ com.android.internal.R.attr.contextPopupMenuStyle);
+ helper.show(Math.round(x), Math.round(y));
+ return helper;
+ }
+
+ return null;
+ }
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java
index b43e8ad..98f5d90 100644
--- a/core/java/com/android/internal/view/menu/MenuPopup.java
+++ b/core/java/com/android/internal/view/menu/MenuPopup.java
@@ -17,10 +17,13 @@
package com.android.internal.view.menu;
import android.content.Context;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
+import android.widget.AdapterView;
import android.widget.FrameLayout;
+import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.PopupWindow;
@@ -30,7 +33,8 @@
*
* @hide
*/
-public abstract class MenuPopup implements ShowableListMenu, MenuPresenter {
+public abstract class MenuPopup implements ShowableListMenu, MenuPresenter,
+ AdapterView.OnItemClickListener {
public abstract void setForceShowIcon(boolean forceShow);
@@ -49,6 +53,18 @@
public abstract void setAnchorView(View anchor);
+ public abstract void setHorizontalOffset(int x);
+
+ public abstract void setVerticalOffset(int y);
+
+ /**
+ * Set whether a title entry should be shown in the popup menu (if a title exists for the
+ * menu).
+ *
+ * @param showTitle
+ */
+ public abstract void setShowTitle(boolean showTitle);
+
/**
* Set a listener to receive a callback when the popup is dismissed.
*
@@ -81,6 +97,16 @@
return 0;
}
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ListAdapter outerAdapter = (ListAdapter) parent.getAdapter();
+ MenuAdapter wrappedAdapter = toMenuAdapter(outerAdapter);
+
+ // Use the position from the outer adapter so that if a header view was added, we don't get
+ // an off-by-1 error in position.
+ wrappedAdapter.mAdapterMenu.performItemAction((MenuItem) outerAdapter.getItem(position), 0);
+ }
+
/**
* Measures the width of the given menu view.
*
@@ -121,4 +147,19 @@
return maxWidth;
}
-}
\ No newline at end of file
+
+ /**
+ * Converts the given ListAdapter originating from a menu, to a MenuAdapter, accounting for
+ * the possibility of the parameter adapter actually wrapping the MenuAdapter. (That could
+ * happen if a header view was added on the menu.)
+ *
+ * @param adapter
+ * @return
+ */
+ protected static MenuAdapter toMenuAdapter(ListAdapter adapter) {
+ if (adapter instanceof HeaderViewListAdapter) {
+ return (MenuAdapter) ((HeaderViewListAdapter) adapter).getWrappedAdapter();
+ }
+ return (MenuAdapter) adapter;
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index ea79983..e674ecc 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -39,7 +39,11 @@
private MenuPopup mPopup;
private int mDropDownGravity = Gravity.NO_GRAVITY;
+ private boolean mForceShowIcon;
+ private boolean mShowTitle;
private Callback mPresenterCallback;
+ private int mInitXOffset;
+ private int mInitYOffset;
public MenuPopupHelper(Context context, MenuBuilder menu) {
this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0);
@@ -81,6 +85,7 @@
}
public void setForceShowIcon(boolean forceShow) {
+ mForceShowIcon = forceShow;
mPopup.setForceShowIcon(forceShow);
}
@@ -99,6 +104,12 @@
}
}
+ public void show(int x, int y) {
+ if (!tryShow(x, y)) {
+ throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
+ }
+ }
+
public ShowableListMenu getPopup() {
return mPopup;
}
@@ -118,10 +129,40 @@
return false;
}
+ mInitXOffset = 0;
+ mInitYOffset = 0;
+ mShowTitle = false;
+
+ showPopup();
+ return true;
+ }
+
+ public boolean tryShow(int x, int y) {
+ if (isShowing()) {
+ return true;
+ }
+
+ if (mAnchorView == null) {
+ return false;
+ }
+
+ mInitXOffset = x;
+ mInitYOffset = y;
+ mShowTitle = true;
+
+ showPopup();
+ return true;
+ }
+
+ private void showPopup() {
mPopup = createMenuPopup();
mPopup.setAnchorView(mAnchorView);
- mPopup.setGravity(mDropDownGravity);
mPopup.setCallback(mPresenterCallback);
+ mPopup.setForceShowIcon(mForceShowIcon);
+ mPopup.setGravity(mDropDownGravity);
+ mPopup.setHorizontalOffset(mInitXOffset);
+ mPopup.setShowTitle(mShowTitle);
+ mPopup.setVerticalOffset(mInitYOffset);
// In order for subclasses of MenuPopupHelper to satisfy the OnDismissedListener interface,
// we must set the listener to this outer Helper rather than to the inner MenuPopup.
@@ -131,7 +172,6 @@
mPopup.addMenu(mMenu);
mPopup.show();
- return true;
}
public void dismiss() {
@@ -149,7 +189,6 @@
return mPopup != null && mPopup.isShowing();
}
-
public void setCallback(MenuPresenter.Callback cb) {
mPresenterCallback = cb;
mPopup.setCallback(cb);
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index 8877f3d..caee0d2 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -6,17 +6,17 @@
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnKeyListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.ViewTreeObserver;
-import android.widget.AdapterView;
+import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.MenuPopupWindow;
import android.widget.PopupWindow;
+import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.PopupWindow.OnDismissListener;
@@ -92,6 +92,10 @@
private int mDropDownGravity = Gravity.NO_GRAVITY;
+ private int mXOffset;
+ private int mYOffset;
+ private boolean mShowTitle;
+
public StandardMenuPopup(Context context, MenuBuilder menu, View anchorView, int popupStyleAttr,
int popupStyleRes, boolean overflowOnly) {
mContext = Preconditions.checkNotNull(context);
@@ -158,8 +162,28 @@
mPopup.setContentWidth(mContentWidth);
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ mPopup.setHorizontalOffset(mXOffset);
+ mPopup.setVerticalOffset(mYOffset);
mPopup.show();
- mPopup.getListView().setOnKeyListener(this);
+
+ ListView listView = mPopup.getListView();
+ listView.setOnKeyListener(this);
+
+ if (mShowTitle && mMenu.getHeaderTitle() != null) {
+ FrameLayout titleItemView =
+ (FrameLayout) LayoutInflater.from(mContext).inflate(
+ com.android.internal.R.layout.popup_menu_header_item_layout,
+ listView,
+ false);
+ TextView titleView = (TextView) titleItemView.findViewById(
+ com.android.internal.R.id.title);
+ titleView.setText(mMenu.getHeaderTitle());
+ titleItemView.setEnabled(false);
+ listView.addHeaderView(titleItemView, null, false);
+
+ // Update to show the title.
+ mPopup.show();
+ }
return true;
}
@@ -178,12 +202,6 @@
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- MenuAdapter adapter = mAdapter;
- adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
- }
-
- @Override
public void addMenu(MenuBuilder menu) {
// No-op: standard implementation has only one menu which is set in the constructor.
}
@@ -288,4 +306,20 @@
public ListView getListView() {
return mPopup.getListView();
}
-}
\ No newline at end of file
+
+
+ @Override
+ public void setHorizontalOffset(int x) {
+ mXOffset = x;
+ }
+
+ @Override
+ public void setVerticalOffset(int y) {
+ mYOffset = y;
+ }
+
+ @Override
+ public void setShowTitle(boolean showTitle) {
+ mShowTitle = showTitle;
+ }
+}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 2a25db6..7bab446 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -73,6 +73,8 @@
// This class is responsible for the public API of the floating toolbar.
// It delegates rendering operations to the FloatingToolbarPopup.
+ public static final String FLOATING_TOOLBAR_TAG = "floating_toolbar";
+
private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
new MenuItem.OnMenuItemClickListener() {
@Override
@@ -1460,8 +1462,10 @@
}
private static ViewGroup createContentContainer(Context context) {
- return (ViewGroup) LayoutInflater.from(context)
+ ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
.inflate(R.layout.floating_popup_container, null);
+ contentContainer.setTag(FLOATING_TOOLBAR_TAG);
+ return contentContainer;
}
private static PopupWindow createPopupWindow(View content) {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index f7e9add..60ef4a4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1078,7 +1078,7 @@
long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId);
final long now = SystemClock.elapsedRealtime();
- if (deadline < now) {
+ if (deadline < now && deadline != 0) {
// timeout expired
setLong(LOCKOUT_ATTEMPT_DEADLINE, 0, userId);
setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0, userId);
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 6ab306c..56cf921 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -57,7 +57,8 @@
* </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 {
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;
@@ -112,7 +113,7 @@
}
@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 +225,7 @@
boolean invisible = isFillingScreen() || !mShowDecor;
View caption = getChildAt(0);
caption.setVisibility(invisible ? GONE : VISIBLE);
+ caption.setOnTouchListener(this);
mVisible = !invisible;
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 75da27e..20d00b0 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -1,6 +1,5 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += -DHAVE_CONFIG_H -DKHTML_NO_EXCEPTIONS -DGKWQ_NO_JAVA
LOCAL_CFLAGS += -DNO_SUPPORT_JS_BINDING -DQT_NO_WHEELEVENT -DKHTML_NO_XBL
@@ -259,6 +258,9 @@
# <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp
LOCAL_C_INCLUDES += bionic/libc/private
+# AndroidRuntime.h depends on nativehelper/jni.h
+LOCAL_EXPORT_C_INCLUDE_DIRS := libnativehelper/include
+
LOCAL_MODULE:= libandroid_runtime
# -Wno-unknown-pragmas: necessary for Clang as the GL bindings need to turn
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 520dc4f..223dae0 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -44,6 +44,7 @@
#include "TypefaceImpl.h"
#include <vector>
+#include <memory>
// temporary for debugging
#include <Caches.h>
@@ -569,137 +570,9 @@
return descent - ascent + leading;
}
- static jfloat measureText_CIII(JNIEnv* env, jobject jpaint, jcharArray text, jint index, jint count,
- jint bidiFlags) {
- NPE_CHECK_RETURN_ZERO(env, jpaint);
- NPE_CHECK_RETURN_ZERO(env, text);
-
- size_t textLength = env->GetArrayLength(text);
- if ((index | count) < 0 || (size_t)(index + count) > textLength) {
- doThrowAIOOBE(env);
- return 0;
- }
- if (count == 0) {
- return 0;
- }
-
- Paint* paint = getNativePaint(env, jpaint);
- const jchar* textArray = env->GetCharArrayElements(text, NULL);
- jfloat result = 0;
-
- Layout layout;
- TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
- MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + index, 0, count,
- count);
- result = layout.getAdvance();
- env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
- return result;
- }
-
- static jfloat measureText_StringIII(JNIEnv* env, jobject jpaint, jstring text, jint start, jint end,
- jint bidiFlags) {
- NPE_CHECK_RETURN_ZERO(env, jpaint);
- NPE_CHECK_RETURN_ZERO(env, text);
-
- size_t textLength = env->GetStringLength(text);
- int count = end - start;
- if ((start | count) < 0 || (size_t)end > textLength) {
- doThrowAIOOBE(env);
- return 0;
- }
- if (count == 0) {
- return 0;
- }
-
- const jchar* textArray = env->GetStringChars(text, NULL);
- Paint* paint = getNativePaint(env, jpaint);
- jfloat width = 0;
-
- Layout layout;
- TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
- // Only the substring is used for measurement, so no additional context is passed in. This
- // behavior is consistent between char[] and String specializations.
- MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + start, 0, count, count);
- width = layout.getAdvance();
-
- env->ReleaseStringChars(text, textArray);
- return width;
- }
-
- static jfloat measureText_StringI(JNIEnv* env, jobject jpaint, jstring text, jint bidiFlags) {
- NPE_CHECK_RETURN_ZERO(env, jpaint);
- NPE_CHECK_RETURN_ZERO(env, text);
-
- size_t textLength = env->GetStringLength(text);
- if (textLength == 0) {
- return 0;
- }
-
- const jchar* textArray = env->GetStringChars(text, NULL);
- Paint* paint = getNativePaint(env, jpaint);
- jfloat width = 0;
-
- Layout layout;
- TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
- MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray, 0, textLength, textLength);
- width = layout.getAdvance();
-
- env->ReleaseStringChars(text, textArray);
- return width;
- }
-
- static int dotextwidths(JNIEnv* env, Paint* paint, TypefaceImpl* typeface, const jchar text[], int count,
- jfloatArray widths, jint bidiFlags) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- NPE_CHECK_RETURN_ZERO(env, text);
-
- if (count < 0 || !widths) {
- doThrowAIOOBE(env);
- return 0;
- }
- if (count == 0) {
- return 0;
- }
- size_t widthsLength = env->GetArrayLength(widths);
- if ((size_t)count > widthsLength) {
- doThrowAIOOBE(env);
- return 0;
- }
-
- AutoJavaFloatArray autoWidths(env, widths, count);
- jfloat* widthsArray = autoWidths.ptr();
-
- Layout layout;
- MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, 0, count, count);
- layout.getAdvances(widthsArray);
-
- return count;
- }
-
- static jint getTextWidths___CIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray text,
- jint index, jint count, jint bidiFlags, jfloatArray widths) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
- const jchar* textArray = env->GetCharArrayElements(text, NULL);
- count = dotextwidths(env, paint, typeface, textArray + index, count, widths, bidiFlags);
- env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
- JNI_ABORT);
- return count;
- }
-
- static jint getTextWidths__StringIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring text,
- jint start, jint end, jint bidiFlags, jfloatArray widths) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
- const jchar* textArray = env->GetStringChars(text, NULL);
- int count = dotextwidths(env, paint, typeface, textArray + start, end - start, widths, bidiFlags);
- env->ReleaseStringChars(text, textArray);
- return count;
- }
-
- static jfloat doTextRunAdvances(JNIEnv *env, Paint *paint, TypefaceImpl* typeface, const jchar *text,
- jint start, jint count, jint contextCount, jboolean isRtl,
- jfloatArray advances, jint advancesIndex) {
+ 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);
@@ -712,50 +585,45 @@
}
if (advances) {
size_t advancesLength = env->GetArrayLength(advances);
- if ((size_t)count > advancesLength) {
+ if ((size_t)(count + advancesIndex) > advancesLength) {
doThrowAIOOBE(env);
return 0;
}
}
- jfloat* advancesArray = new jfloat[count];
- jfloat totalAdvance = 0;
-
- int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
Layout layout;
- MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, start, count, contextCount);
- layout.getAdvances(advancesArray);
- totalAdvance = layout.getAdvance();
-
+ MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, start, count,
+ contextCount);
if (advances != NULL) {
- env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray);
+ std::unique_ptr<jfloat> advancesArray(new jfloat[count]);
+ layout.getAdvances(advancesArray.get());
+ env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
}
- delete [] advancesArray;
- return totalAdvance;
+ return layout.getAdvance();
}
- static jfloat getTextRunAdvances___CIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+ static jfloat getTextAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
jlong typefaceHandle,
jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
- jboolean isRtl, jfloatArray advances, jint advancesIndex) {
+ jint bidiFlags, jfloatArray advances, jint advancesIndex) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
jchar* textArray = env->GetCharArrayElements(text, NULL);
- jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextIndex,
- index - contextIndex, count, contextCount, isRtl, advances, advancesIndex);
+ jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex,
+ index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
return result;
}
- static jfloat getTextRunAdvances__StringIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+ static jfloat getTextAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
jlong typefaceHandle,
- jstring text, jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl,
+ jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint bidiFlags,
jfloatArray advances, jint advancesIndex) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
- jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextStart,
- start - contextStart, end - start, contextEnd - contextStart, isRtl,
+ jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart,
+ start - contextStart, end - start, contextEnd - contextStart, bidiFlags,
advances, advancesIndex);
env->ReleaseStringChars(text, textArray);
return result;
@@ -1160,18 +1028,13 @@
(void*)PaintGlue::getFontMetrics},
{"getFontMetricsInt", "!(Landroid/graphics/Paint$FontMetricsInt;)I",
(void*)PaintGlue::getFontMetricsInt},
- {"native_measureText","([CIII)F", (void*) PaintGlue::measureText_CIII},
- {"native_measureText","(Ljava/lang/String;I)F", (void*) PaintGlue::measureText_StringI},
- {"native_measureText","(Ljava/lang/String;III)F", (void*) PaintGlue::measureText_StringIII},
+
{"native_breakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
{"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
- {"native_getTextWidths","(JJ[CIII[F)I", (void*) PaintGlue::getTextWidths___CIII_F},
- {"native_getTextWidths","(JJLjava/lang/String;III[F)I",
- (void*) PaintGlue::getTextWidths__StringIII_F},
- {"native_getTextRunAdvances","(JJ[CIIIIZ[FI)F",
- (void*) PaintGlue::getTextRunAdvances___CIIIIZ_FI},
- {"native_getTextRunAdvances","(JJLjava/lang/String;IIIIZ[FI)F",
- (void*) PaintGlue::getTextRunAdvances__StringIIIIZ_FI},
+ {"native_getTextAdvances","(JJ[CIIIII[FI)F",
+ (void*) PaintGlue::getTextAdvances___CIIIII_FI},
+ {"native_getTextAdvances","(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",
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index a151e00..83f76ea 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -117,9 +117,17 @@
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 @@
{"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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 28dac39..148e7c1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -65,6 +65,7 @@
<protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
<protected-broadcast android:name="android.intent.action.REBOOT" />
<protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
+ <protected-broadcast android:name="android.intent.action.THERMAL_EVENT" />
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
<protected-broadcast android:name="android.intent.action.USER_ADDED" />
<protected-broadcast android:name="android.intent.action.USER_REMOVED" />
@@ -1714,7 +1715,7 @@
android:protectionLevel="signature|privileged" />
<!-- Allows applications to change network connectivity state.
- <p>Protection level: normal
+ <p>Protection level: signature
-->
<permission android:name="android.permission.CHANGE_NETWORK_STATE"
android:description="@string/permdesc_changeNetworkState"
@@ -2529,6 +2530,12 @@
android:label="@string/permlab_access_notification_policy"
android:protectionLevel="normal" />
+ <!-- Allows modification of do not disturb rules and policies. Only allowed for system
+ processes.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_NOTIFICATIONS"
+ android:protectionLevel="signature" />
+
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
diff --git a/core/res/res/color/btn_colored_text_material.xml b/core/res/res/color/btn_colored_text_material.xml
index 23d05a9..c80fea6 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/layout/popup_menu_header_item_layout.xml b/core/res/res/layout/popup_menu_header_item_layout.xml
new file mode 100644
index 0000000..5879f85
--- /dev/null
+++ b/core/res/res/layout/popup_menu_header_item_layout.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/dropdownListPreferredItemHeight"
+ android:minWidth="196dip"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/textAppearancePopupMenuHeader"
+ android:layout_gravity="center_vertical"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:textAlignment="viewStart" />
+
+</FrameLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index a08f040..2f1627b 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-cs/strings.xml b/core/res/res/values-cs/strings.xml
index cd6bb4b..7b05612 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -231,7 +231,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakty"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"přístup ke kontaktům"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"přístup k poloze tohoto zařízení"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"přístup k poloze tohoto zařízení"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendář"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"přístup ke kalendáři"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index e718935..60f0502 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -493,8 +493,8 @@
<string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"Dette giver indehaveren mulighed for at knytte sig til det øverste grænsefladeniveau for et mobilselskabs beskedtjeneste. Dette bør ikke være nødvendigt i normale apps."</string>
<string name="permlab_bindCarrierServices" msgid="3233108656245526783">"knytte til tjenester fra mobilselskabet"</string>
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Tillader, at brugeren knytter sig til tjenester fra mobilselskabet. Dette bør aldrig være nødvendigt for almindelige apps."</string>
- <string name="permlab_access_notification_policy" msgid="4247510821662059671">"Adgang til Vil ikke forstyrres"</string>
- <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Giver appen tilladelse til at læse og skrive konfigurationen af Vil ikke forstyrres."</string>
+ <string name="permlab_access_notification_policy" msgid="4247510821662059671">"have adgang til Forstyr ikke"</string>
+ <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Giver appen tilladelse til at læse og skrive konfigurationen af Forstyr ikke."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Angiv regler for adgangskoder"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Kontrollér længden samt tilladte tegn i adgangskoder og pinkoder til skærmlåsen."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Overvåg forsøg på oplåsning af skærm"</string>
@@ -1471,10 +1471,10 @@
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Indtil du slår denne indstilling fra"</string>
- <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Indtil du slår \"Vil ikke forstyrres\" fra"</string>
+ <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Indtil du slår \"Forstyr ikke\" fra"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="2821479483960330739">"Skjul"</string>
- <string name="zen_mode_feature_name" msgid="5254089399895895004">"Vil ikke forstyrres"</string>
+ <string name="zen_mode_feature_name" msgid="5254089399895895004">"Forstyr ikke"</string>
<string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"Nedetid"</string>
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Hverdagsaften"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 2c7a64d..8e7193a 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -227,23 +227,23 @@
<string name="user_owner_label" msgid="2804351898001038951">"Pertsonalak"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"Lana"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktuak"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"Atzitu kontaktuak"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"kontaktuak atzitzeko"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Kokapena"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"Atzitu gailuaren kokapena"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"gailuaren kokapena atzitzeko"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Egutegia"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"Atzitu egutegia"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"egutegia atzitzeko"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS mezuak"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"Bidali eta ikusi SMS mezuak"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS mezuak bidaltzeko eta ikusteko"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Memoria"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"Atzitu gailuko argazkiak, multimedia-elementuak eta fitxategiak"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"gailuko argazkiak, multimedia-elementuak eta fitxategiak atzitzeko"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofonoa"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"Grabatu audioa"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"audioa grabatzeko"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"Atera argazkiak eta grabatu bideoak"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"argazkiak ateratzeko eta bideoak grabatzeko"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefonoa"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"Egin eta kudeatu telefono-deiak"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"telefono-deiak egiteko eta kudeatzeko"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Gorputz-sentsoreak"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"Atzitu bizi-konstanteei buruzko sentsore-datuak"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"bizi-konstanteei buruzko sentsore-datuak atzitzeko"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Eskuratu leihoko edukia"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Arakatu irekita daukazun leihoko edukia."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Aktibatu ukipen bidez arakatzeko eginbidea"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 6c03159..45d9dc3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -144,14 +144,14 @@
<string name="httpErrorOk" msgid="1191919378083472204">"تأیید"</string>
<string name="httpError" msgid="7956392511146698522">"خطایی در شبکه وجود داشت."</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"URL پیدا نشد."</string>
- <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"طرح کلی تأیید اعتبار سایت پشتیبانی نمیشود."</string>
+ <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"طرح کلی تأیید اعتبار سایت پشتیبانی نمیشود."</string>
<string name="httpErrorAuth" msgid="1435065629438044534">"تأیید اعتبار انجام نشد."</string>
<string name="httpErrorProxyAuth" msgid="1788207010559081331">"تأیید اعتبار از طریق سرور پروکسی انجام نشد."</string>
<string name="httpErrorConnect" msgid="8714273236364640549">"اتصال به سرور انجام نشد."</string>
<string name="httpErrorIO" msgid="2340558197489302188">"برقراری ارتباط با سرور ممکن نبود. بعداً دوباره امتحان کنید."</string>
<string name="httpErrorTimeout" msgid="4743403703762883954">"زمان اتصال به سرور تمام شده است."</string>
<string name="httpErrorRedirectLoop" msgid="8679596090392779516">"این صفحه دارای تعداد بسیار زیادی تغییر مسیر سرور است."</string>
- <string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"پروتکل پشتیبانی نمیشود."</string>
+ <string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"پروتکل پشتیبانی نمیشود."</string>
<string name="httpErrorFailedSslHandshake" msgid="96549606000658641">"اتصال امن ایجاد نشد."</string>
<string name="httpErrorBadUrl" msgid="3636929722728881972">"بدلیل نامعتبر بودن URL، باز کردن صفحه ممکن نیست."</string>
<string name="httpErrorFile" msgid="2170788515052558676">"دسترسی به فایل انجام نشد."</string>
@@ -781,13 +781,13 @@
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"به برنامه اجازه میدهد تا سابقه یا نشانکهای ذخیره شده مرورگر در تلویزیون شما را تغییر دهد. شاید به برنامه اجازه دهد تا دادههای «مرورگر» را پاک کند یا تغییر دهد. توجه: این مجوز شاید توسط مرورگرهای شخص ثالث یا سایر برنامهها با قابلیتهای مرور وب اجرا شود."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"به برنامه اجازه میدهد سابقه مرورگر یا نشانکهای ذخیره شده در تلفن شما را اصلاح کند. این ویژگی ممکن است به برنامه اجازه دهد دادههای مرورگر را حذف یا اصلاح کند. توجه: این مجوز ممکن است توسط مرورگرهای شخص ثالث یا سایر برنامههای دارای قابلیت مرور وب قابل اجرا نباشد."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"تنظیم یک هشدار"</string>
- <string name="permdesc_setAlarm" msgid="316392039157473848">"به برنامه اجازه میدهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامههای ساعت زنگدار نمیتوانند این ویژگی را اعمال کنند."</string>
+ <string name="permdesc_setAlarm" msgid="316392039157473848">"به برنامه اجازه میدهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامههای ساعت زنگدار نمیتوانند این ویژگی را اعمال کنند."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"افزودن پست صوتی"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه میدهد تا پیامها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"به برنامه اجازه میدهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامههای مخرب میتوانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایتهای وب کتابخانه بفرستند."</string>
<string name="save_password_message" msgid="767344687139195790">"میخواهید مرورگر این رمز ورود را به خاطر داشته باشد؟"</string>
- <string name="save_password_notnow" msgid="6389675316706699758">"الآن نه"</string>
+ <string name="save_password_notnow" msgid="6389675316706699758">"اکنون نه"</string>
<string name="save_password_remember" msgid="6491879678996749466">"به خاطر سپردن"</string>
<string name="save_password_never" msgid="8274330296785855105">"هیچوقت"</string>
<string name="open_permission_deny" msgid="7374036708316629800">"شما اجازه بازکردن این صفحه را ندارید."</string>
@@ -894,16 +894,16 @@
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"پیشفرض را در تنظیمات سیستم> برنامهها> مورد دانلود شده پاک کنید."</string>
<string name="chooseActivity" msgid="7486876147751803333">"انتخاب عملکرد"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"انتخاب برنامه برای دستگاه USB"</string>
- <string name="noApplications" msgid="2991814273936504689">"هیچ برنامهای نمیتواند این کار را انجام دهد."</string>
+ <string name="noApplications" msgid="2991814273936504689">"هیچ برنامهای نمیتواند این کار را انجام دهد."</string>
<string name="aerr_title" msgid="1905800560317137752"></string>
<string name="aerr_application" msgid="932628488013092776">"متأسفانه، <xliff:g id="APPLICATION">%1$s</xliff:g> متوقف شده است."</string>
<string name="aerr_process" msgid="4507058997035697579">"متأسفانه، پردازش <xliff:g id="PROCESS">%1$s</xliff:g> متوقف شده است."</string>
<string name="aerr_process_silence" msgid="4226685530196000222">"تا راهاندازی مجدد، خرابیها از <xliff:g id="PROCESS">%1$s</xliff:g> نادیده گرفته شوند."</string>
<string name="anr_title" msgid="4351948481459135709"></string>
- <string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> پاسخ نمیدهد.\n\nآیا میخواهید آنرا ببندید؟"</string>
- <string name="anr_activity_process" msgid="5776209883299089767">"فعالیت <xliff:g id="ACTIVITY">%1$s</xliff:g> پاسخ نمیدهد.\n\nآیا میخواهید آن را ببندید؟"</string>
- <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="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> پاسخ نمیدهد.\n\nآیا میخواهید آنرا ببندید؟"</string>
+ <string name="anr_activity_process" msgid="5776209883299089767">"فعالیت <xliff:g id="ACTIVITY">%1$s</xliff:g> پاسخ نمیدهد.\n\nآیا میخواهید آن را ببندید؟"</string>
+ <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="wait" msgid="7147118217226317732">"منتظر بمانید"</string>
@@ -1006,14 +1006,14 @@
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"همیشه غیرمجاز"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"سیم کارت برداشته شد"</string>
<string name="sim_removed_message" msgid="5450336489923274918">"تا زمانی که با یک سیمکارت معتبر دوباره راهاندازی نکنید شبکه تلفن همراه غیر قابل دسترس خواهد بود."</string>
- <string name="sim_done_button" msgid="827949989369963775">"انجام شد"</string>
+ <string name="sim_done_button" msgid="827949989369963775">"تمام"</string>
<string name="sim_added_title" msgid="3719670512889674693">"سیم کارت اضافه شد"</string>
<string name="sim_added_message" msgid="7797975656153714319">"برای دسترسی به شبکه تلفن همراه، دستگاهتان را مجدداً راهاندازی کنید."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"راهاندازی مجدد"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"تنظیم زمان"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"تاریخ تنظیم"</string>
<string name="date_time_set" msgid="5777075614321087758">"تنظیم"</string>
- <string name="date_time_done" msgid="2507683751759308828">"انجام شد"</string>
+ <string name="date_time_done" msgid="2507683751759308828">"تمام"</string>
<string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"جدید: "</font></string>
<string name="perms_description_app" msgid="5139836143293299417">"ارائه شده توسط <xliff:g id="APP_NAME">%1$s</xliff:g> ."</string>
<string name="no_permissions" msgid="7283357728219338112">"مجوزی لازم نیست"</string>
@@ -1085,7 +1085,7 @@
<string name="ime_action_search" msgid="658110271822807811">"جستجو"</string>
<string name="ime_action_send" msgid="2316166556349314424">"ارسال"</string>
<string name="ime_action_next" msgid="3138843904009813834">"بعدی"</string>
- <string name="ime_action_done" msgid="8971516117910934605">"انجام شد"</string>
+ <string name="ime_action_done" msgid="8971516117910934605">"تمام"</string>
<string name="ime_action_previous" msgid="1443550039250105948">"قبلی"</string>
<string name="ime_action_default" msgid="2840921885558045721">"اجرا کردن"</string>
<string name="dial_number_using" msgid="5789176425167573586">"شماره گیری \nبا استفاده از <xliff:g id="NUMBER">%s</xliff:g>"</string>
@@ -1131,7 +1131,7 @@
<item quantity="one"><xliff:g id="INDEX">%d</xliff:g> از <xliff:g id="TOTAL">%d</xliff:g></item>
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> از <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
- <string name="action_mode_done" msgid="7217581640461922289">"انجام شد"</string>
+ <string name="action_mode_done" msgid="7217581640461922289">"تمام"</string>
<string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"در حال پاک کردن حافظهٔ USB..."</string>
<string name="progress_erasing" product="default" msgid="6596988875507043042">"در حال پاک کردن کارت SD..."</string>
<string name="share" msgid="1778686618230011964">"اشتراکگذاری"</string>
@@ -1173,7 +1173,7 @@
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"لغو"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
- <string name="keyboardview_keycode_done" msgid="1992571118466679775">"انجام شد"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"تمام"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"تغییر حالت"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
@@ -1416,7 +1416,7 @@
<string name="immersive_cling_title" msgid="8394201622932303336">"مشاهده در حالت تمام صفحه"</string>
<string name="immersive_cling_description" msgid="3482371193207536040">"برای خروج، انگشتتان را از بالای صفحه به پایین بکشید."</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"متوجه شدم"</string>
- <string name="done_label" msgid="2093726099505892398">"انجام شد"</string>
+ <string name="done_label" msgid="2093726099505892398">"تمام"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"لغزنده دایرهای ساعت"</string>
<string name="minute_picker_description" msgid="8606010966873791190">"لغزنده دایرهای دقیقه"</string>
<string name="select_hours" msgid="6043079511766008245">"انتخاب ساعت"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 7b5ca29..c32add0 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acceder aos teus contactos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Localización"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"acceso á localización deste dispositivo"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"acceder á localización deste dispositivo"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder ao teu calendario"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 9392997..6281abf 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -229,8 +229,8 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"સંપર્કો"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"તમારા સંપર્કોને ઍક્સેસ કરો"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"સ્થાન"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરો"</string>
- <string name="permgrouplab_calendar" msgid="5863508437783683902">"કૅલેન્ડર"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની"</string>
+ <string name="permgrouplab_calendar" msgid="5863508437783683902">"કેલેન્ડર"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS સંદેશા મોકલો અને જોવાની"</string>
@@ -326,14 +326,14 @@
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"એપ્લિકેશનને ઇનકમિંગ અને આઉટગોઇંગ કૉલ્સ વિશેનાં ડેટા સહિત, તમારા ફોનના કૉલ લૉગને સંશોધિત કરવાની મંજૂરી આપે છે. દુર્ભાવનાપૂર્ણ એપ્લિકેશનો આનો ઉપયોગ તમારા કૉલ લૉગને કાઢી નાખવા અથવા સંશોધિત માટે કરી શકે છે."</string>
<string name="permlab_bodySensors" msgid="4871091374767171066">"બૉડી સેન્સર્સ (જેમ કે હાર્ટ રેટ મૉનિટર્સ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"એપ્લિકેશનને તમારી હૃદય ગતિ જેવી તમારી શારીરિક સ્થિતિને મૉનિટર કરતાં સેન્સર્સથી ડેટા ઍક્સેસ કરવાની મંજૂરી આપે છે."</string>
- <string name="permlab_readCalendar" msgid="5972727560257612398">"કૅલેન્ડર ઇવેન્ટ્સ વત્તા ગોપનીયતા માહિતી વાંચો"</string>
- <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કૅલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કૅલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
- <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કૅલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
- <string name="permlab_writeCalendar" msgid="8438874755193825647">"કૅલેન્ડર ઇવેન્ટ્સ ઉમેરો અથવા સંશોધિત કરો અને માલિકની જાણ બહાર અતિથિઓને ઇમેઇલ મોકલો"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કૅલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કૅલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કૅલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
+ <string name="permlab_readCalendar" msgid="5972727560257612398">"કેલેન્ડર ઇવેન્ટ્સ વત્તા ગોપનીયતા માહિતી વાંચો"</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર સંગ્રહિત તમામ કેલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર સંગ્રહિત તમામ કેલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
+ <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર સંગ્રહિત તમામ કેલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
+ <string name="permlab_writeCalendar" msgid="8438874755193825647">"કેલેન્ડર ઇવેન્ટ્સ ઉમેરો અથવા સંશોધિત કરો અને માલિકની જાણ બહાર અતિથિઓને ઇમેઇલ મોકલો"</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરો"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"એપ્લિકેશનને વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને GPS અથવા અન્ય સ્થાન સ્રોતોના ઓપરેશનમાં દખલ કરવાની મંજૂરી આપી શકે છે."</string>
<string name="permlab_accessFineLocation" msgid="1191898061965273372">"નિશ્ચિત સ્થાન (GPS અને નેટવર્ક-આધારિત)"</string>
diff --git a/core/res/res/values-it-watch/strings.xml b/core/res/res/values-it-watch/strings.xml
index c348e48..aa6a4be 100644
--- a/core/res/res/values-it-watch/strings.xml
+++ b/core/res/res/values-it-watch/strings.xml
@@ -29,7 +29,7 @@
<string name="permgrouplab_storagewear" msgid="1003807594193602313">"accedere a foto, contenuti multimediali e file sull\'orologio"</string>
<string name="permgrouplab_microphonewear" msgid="1047561180980891136">"registrare audio"</string>
<string name="permgrouplab_camerawear" msgid="4543951283103407017">"scattare foto e registrare video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"fare e gestire telefonate"</string>
+ <string name="permgrouplab_phonewear" msgid="134365036753766126">"eseguire e gestire le telefonate"</string>
<string name="permgrouplab_sensorswear" msgid="1429324744329327663">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
<string name="permlab_statusBarServicewear" msgid="2469402818964691034">"avere la funzione di barra di stato"</string>
<string name="permlab_bodySensorswear" msgid="7857941041202791873">"accedere ai sensori (come il cardiofrequenzimetro)"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4271287..72b1c54 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -239,11 +239,11 @@
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfono"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"registrare audio"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Fotocamera"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"acquisire foto e registrare video"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"scattare foto e registrare video"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefono"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"eseguire e gestire le telefonate"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Sensori per il corpo"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accedere ai dati dei sensori sui tuoi parametri vitali"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperare contenuti della finestra"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Esaminare i contenuti di una finestra con cui interagisci."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Attivare Esplora al tocco"</string>
@@ -272,7 +272,7 @@
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Consente all\'applicazione di leggere i messaggi cell broadcast ricevuti dal dispositivo. Gli avvisi cell broadcast vengono trasmessi in alcune località per avvertire di eventuali situazioni di emergenza. Le applicazioni dannose potrebbero interferire con il rendimento o con il funzionamento del dispositivo quando si riceve un messaggio cell broadcast di emergenza."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lettura feed sottoscritti"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Consente all\'applicazione di ottenere dettagli sui feed attualmente sincronizzati."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"invio e lettura di SMS"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"inviare e visualizzare SMS"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Consente all\'applicazione di inviare messaggi SMS. Ciò potrebbe comportare costi imprevisti. Applicazioni dannose potrebbero generare dei costi inviando messaggi senza la tua conferma."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"lettura messaggi di testo personali (SMS o MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Consente all\'applicazione di leggere i messaggi SMS memorizzati sul tablet o sulla scheda SIM. Ciò consente all\'applicazione di leggere tutti i messaggi SMS, indipendentemente dai contenuti o dal livello di riservatezza."</string>
@@ -342,7 +342,7 @@
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Consente all\'applicazione di ottenere la tua posizione approssimativa. Questa posizione viene ottenuta da servizi di localizzazione utilizzando fonti di geolocalizzazione delle reti come ripetitori di telefonia mobile e Wi-Fi. Questi servizi di localizzazione devono essere attivi e disponibili sul dispositivo per poter essere utilizzati dall\'applicazione. Le applicazioni potrebbero utilizzare questa autorizzazione per stabilire la tua posizione approssimativa."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifica impostazioni audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Consente all\'applicazione di modificare le impostazioni audio globali, come il volume e quale altoparlante viene utilizzato per l\'uscita."</string>
- <string name="permlab_recordAudio" msgid="3876049771427466323">"registrazione audio"</string>
+ <string name="permlab_recordAudio" msgid="3876049771427466323">"registrare audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Consente all\'applicazione di registrare audio con il microfono. Questa autorizzazione consente all\'applicazione di registrare audio in qualsiasi momento senza la tua conferma."</string>
<string name="permlab_sim_communication" msgid="1180265879464893029">"comunicazione SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Consente all\'app di inviare comandi alla SIM. Questo è molto pericoloso."</string>
diff --git a/core/res/res/values-ja-watch/strings.xml b/core/res/res/values-ja-watch/strings.xml
index 3248041..82c3ffd 100644
--- a/core/res/res/values-ja-watch/strings.xml
+++ b/core/res/res/values-ja-watch/strings.xml
@@ -37,7 +37,7 @@
<string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"おおよその位置情報(ネットワーク基地局)へのアクセス"</string>
<string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIMへのコマンド送信"</string>
<string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ネットワークへのフルアクセス"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"プロフィールの所有者と端末の所有者の管理"</string>
+ <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"プロファイルの所有者と端末の所有者の管理"</string>
<string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX状態の変更"</string>
<string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Androidビーム転送のステータスの受信"</string>
<string name="permlab_route_media_outputwear" msgid="8737024341474587192">"メディア出力のルーティング"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 545b963..135c7ea 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -282,8 +282,8 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAPメッセージの受信と処理をアプリに許可します。これにより、アプリが端末に届いたメッセージを表示することなく監視または削除できるようになります。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"実行中のアプリの取得"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"現在実行中または最近実行したタスクに関する情報の取得をアプリに許可します。これにより、その端末でどのアプリを使用しているかをアプリから識別できるようになる可能性があります。"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"プロフィールの所有者と端末の所有者の管理"</string>
- <string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"プロフィールの所有者と端末の所有者の設定をアプリに許可します。"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"プロファイルの所有者と端末の所有者の管理"</string>
+ <string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"プロファイルの所有者と端末の所有者の設定をアプリに許可します。"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"実行中のアプリの順序変更"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"タスクをフォアグラウンドやバックグラウンドに移動することをアプリに許可します。これにより、アプリがユーザーからの入力なしでこの処理を実行する可能性があります。"</string>
<string name="permlab_enableCarMode" msgid="5684504058192921098">"運転モードの有効化"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index d39ee05..7af4df0 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контактілер"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"контактілерге кіру"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Орын"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"бұл құрылғының орнына кіру"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"бұл құрылғының орналасқан жерін көру"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнтізбе"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"күнтізбеге кіру"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index e7e43f0..f20551d 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Байланыштар"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"байланыштарыңызга уруксат"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Жайгашкан жер"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"бул түзмөктүн жайгашкан жерине кирүү"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"бул түзмөктүн жайгашкан жери тууралуу дайындарды көрүү"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнбарак"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"жылнаамаңызды пайдалануу"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index f10fb19..a41be56 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -227,23 +227,23 @@
<string name="user_owner_label" msgid="2804351898001038951">"Лични"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"Работа"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"пристапи до контактите"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"пристапува до контактите"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"да пристапува до локацијата на овој уред"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"пристапува до локацијата на овој уред"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"пристапи до календарот"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"пристапува до календарот"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"СМС"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"испраќа и прикажува СМС-пораки"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Меморија"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"пристапува до фотографии, медиуми и датотеки на уредот"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"пристапува до фотографии, аудио-видео и датотеки на уредот"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снимај аудио"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снима аудио"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Фотоапарат"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"фотографирај и снимај видео"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"фотографира и снима видео"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Телефон"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"повикувај и управувај со телефонски повици"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"упатува и управува со телефонски повици"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Телесни сензори"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"пристапи до податоците од сензорите за виталните знаци"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"пристапува до податоците од сензорите за виталните знаци"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Врати содржина на прозорец"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Провери ја содржината на прозорецот со кој се комуницира."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Вклучи „Истражувај со допир“"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index a547ca7..41528d8 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"အဆက်အသွယ်များ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"သင့် အဆက်အသွယ်များအား ဝင်ရောက်သုံးရန်"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"တည်နေရာ"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"စက်ပစ္စည်း၏ တည်နေရာကို အသုံးပြုမည်"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"ဤစက်ပစ္စည်း၏ တည်နေရာကို ရယူ"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ပြက္ခဒိန်"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"သင့်ပြက္ခဒိန်အား ဝင်ရောက်သုံးရန်"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"စာတိုစနစ်"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d203a6a..2f6c8bf 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -227,23 +227,23 @@
<string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatos"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acessar seus contatos"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acesse seus contatos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acessar sua agenda"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"enviar e ver mensagens SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do seu dispositivo"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do dispositivo"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"grave áudio"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefone"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"fazer e gerenciar chamadas telefônicas"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"faça e gerencie chamadas telefônicas"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporais"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acessar dados do sensor sobre seus sinais vitais"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acesse dados do sensor sobre seus sinais vitais"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar cont. da janela"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecionar o conteúdo da janela com que você está interagindo."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Ativar Explorar por toque"</string>
@@ -272,7 +272,7 @@
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds inscritos"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite que o app obtenha detalhes sobre os feeds sincronizados no momento."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"enviar e ver mensagens SMS"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"envie e veja mensagens SMS"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que o app envie mensagens SMS. Isso pode resultar em cobranças inesperadas. Apps maliciosos podem gerar custos através do envio de mensagens sem sua confirmação."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"ler suas mensagens de texto (SMS ou MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que o app leia mensagens SMS armazenadas no tablet ou cartão SIM. Isso permite que o app leia todas as mensagens SMS, independentemente de seu conteúdo ou confidencialidade."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d203a6a..2f6c8bf 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -227,23 +227,23 @@
<string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatos"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acessar seus contatos"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acesse seus contatos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acessar sua agenda"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"enviar e ver mensagens SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do seu dispositivo"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do dispositivo"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"grave áudio"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefone"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"fazer e gerenciar chamadas telefônicas"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"faça e gerencie chamadas telefônicas"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporais"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acessar dados do sensor sobre seus sinais vitais"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acesse dados do sensor sobre seus sinais vitais"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar cont. da janela"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecionar o conteúdo da janela com que você está interagindo."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Ativar Explorar por toque"</string>
@@ -272,7 +272,7 @@
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds inscritos"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite que o app obtenha detalhes sobre os feeds sincronizados no momento."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"enviar e ver mensagens SMS"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"envie e veja mensagens SMS"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que o app envie mensagens SMS. Isso pode resultar em cobranças inesperadas. Apps maliciosos podem gerar custos através do envio de mensagens sem sua confirmação."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"ler suas mensagens de texto (SMS ou MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que o app leia mensagens SMS armazenadas no tablet ou cartão SIM. Isso permite que o app leia todas as mensagens SMS, independentemente de seu conteúdo ou confidencialidade."</string>
diff --git a/core/res/res/values-ro-watch/strings.xml b/core/res/res/values-ro-watch/strings.xml
index e412cad..9a8e4e0 100644
--- a/core/res/res/values-ro-watch/strings.xml
+++ b/core/res/res/values-ro-watch/strings.xml
@@ -22,15 +22,15 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplic. <xliff:g id="NUMBER_0">%1$d</xliff:g> din <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Senzori"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"să acceseze persoanele de contact"</string>
+ <string name="permgrouplab_contactswear" msgid="2340286500790908344">"acceseze persoanele de contact"</string>
<string name="permgrouplab_locationwear" msgid="6275317222482780209">"să acceseze locația acestui ceas"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"să acceseze calendarul"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"să trimită și să vadă mesajele SMS"</string>
+ <string name="permgrouplab_calendarwear" msgid="441900844045065081">"acceseze calendarul"</string>
+ <string name="permgrouplab_smswear" msgid="6849506550342974220">"trimită și să vadă mesajele SMS"</string>
<string name="permgrouplab_storagewear" msgid="1003807594193602313">"să acceseze fotografiile, conținutul media și fișierele de pe ceas"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"să înregistreze conținut audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"să fotografieze și să înregistreze videoclipuri"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"să inițieze să și gestioneze apeluri telefonice"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"să acceseze datele de la senzori despre semnele vitale"</string>
+ <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"înregistreze sunet"</string>
+ <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fotografieze și să înregistreze imagini"</string>
+ <string name="permgrouplab_phonewear" msgid="134365036753766126">"inițieze să și gestioneze apeluri telefonice"</string>
+ <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"acceseze datele de la senzori despre semnele vitale"</string>
<string name="permlab_statusBarServicewear" msgid="2469402818964691034">"să fie bara de stare"</string>
<string name="permlab_bodySensorswear" msgid="7857941041202791873">"să acceseze senzorii corporali (cum ar fi monitoarele cardiace)"</string>
<string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"să acceseze locația exactă (bazată pe GPS și pe rețea)"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d215d4c..5062e76 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -135,7 +135,7 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Se preferă conexiunea Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="5920549484600758786">"Se preferă conexiunea mobilă"</string>
<string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Numai Wi-Fi"</string>
- <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecţionată"</string>
+ <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecționată"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> după <xliff:g id="TIME_DELAY">{2}</xliff:g> (de) secunde"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecţionat"</string>
@@ -232,19 +232,19 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Locație"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acceseze locația acestui dispozitiv"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"accesează calendarul"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceseze calendarul"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"trimite și vede mesajele SMS"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"trimită și să vadă mesajele SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Stocare"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"acceseze fotografiile, conținutul media și fișierele de pe dispozitiv"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfonul"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"înregistreze conținut audio"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"înregistreze sunet"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Camera foto"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotografieze și să înregistreze videoclipuri"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotografieze și să înregistreze imagini"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefon"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"inițieze și să gestioneze apeluri telefonice"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Senzori corporali"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accesează datele înregistrate de senzori despre semnele vitale"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acceseze datele de la senzori despre semnele vitale"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperează conținutul ferestrei"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspectează conținutul unei ferestre cu care interacționați."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Activează funcția Explorați prin atingere"</string>
@@ -270,11 +270,11 @@
<string name="permlab_receiveMms" msgid="1821317344668257098">"primeşte mesaje text (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"citeşte mesajele cu transmisie celulară"</string>
- <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situaţiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanţa sau funcţionarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanţa sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"citire feeduri abonat"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite aplicației să obţină detalii despre feedurile sincronizate în prezent."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"trimite și vede mesajele SMS"</string>
- <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea determina apariţia unor taxe neaşteptate. Aplicaţiile rău intenţionate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"trimită și să vadă mesajele SMS"</string>
+ <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea determina apariția unor taxe neaşteptate. Aplicaţiile rău intenţionate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"citeşte mesajele text (SMS sau MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite aplicației să citească mesajele SMS stocate pe tabletă sau pe cardul SIM. În acest fel, aplicația poate citi toate mesajele SMS, indiferent de conţinutul sau de gradul de confidenţialitate al acestora."</string>
<string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Permite aplicației să citească mesajele SMS stocate pe televizor sau pe cardul SIM. Cu această permisiune, aplicația poate citi toate mesajele SMS, indiferent de conținutul sau de gradul de confidențialitate al acestora."</string>
@@ -292,11 +292,11 @@
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"închide alte aplicații"</string>
<string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Permite aplicației să oprească procesele derulate în fundal de alte aplicații. Acest lucru poate face ca respectivele aplicații să nu mai ruleze."</string>
<string name="permlab_systemAlertWindow" msgid="3543347980839518613">"suprapune elemente vizuale peste alte aplicații"</string>
- <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permite aplicației să suprapună elemente vizuale peste alte aplicații sau părţi ale interfeţei cu utilizatorul. Acestea pot interfera cu utilizarea de către dvs. a interfeţei în orice aplicație sau pot schimba ceea ce credeţi că vedeţi în alte aplicații."</string>
+ <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permite aplicației să suprapună elemente vizuale peste alte aplicații sau părți ale interfeței cu utilizatorul. Acestea pot interfera cu utilizarea de către dvs. a interfeței în orice aplicație sau pot schimba ceea ce credeți că vedeţi în alte aplicații."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"rulare continuă a aplicației"</string>
- <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Permite aplicației să declare persistente în memorie anumite părţi ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcţionarea tabletei."</string>
+ <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Permite aplicației să declare persistente în memorie anumite părți ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcționarea tabletei."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Permite aplicației să declare persistente în memorie anumite părți ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcționarea televizorului."</string>
- <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Permite aplicației să declare persistente în memorie anumite părţi ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcţionarea telefonului."</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Permite aplicației să declare persistente în memorie anumite părți ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcționarea telefonului."</string>
<string name="permlab_getPackageSize" msgid="7472921768357981986">"măsurare spaţiu de stocare al aplicației"</string>
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"Permite aplicației să preia dimensiunile codului, ale datelor și ale memoriei cache"</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"modifică setări de sistem"</string>
@@ -310,17 +310,17 @@
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Permite aplicației să trimită mesaje difuzate persistente, care se păstrează după terminarea difuzării mesajului. Utilizarea excesivă a acestei funcții poate să încetinească sau să destabilizeze televizorul, determinându-l să utilizeze prea multă memorie."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Permite aplicației să trimită mesaje difuzate persistente, care se păstrează după terminarea difuzării mesajului. Utilizarea excesivă a acestei funcții poate să încetinească sau să destabilizeze telefonul, determinându-l să utilizeze prea multă memorie."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"citeşte agenda"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Permite aplicației să citească datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără ştirea dvs."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Permite aplicației să citească datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără știrea dvs."</string>
<string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Permite aplicației să citească datele despre persoanele de contact salvate pe televizor, inclusiv frecvența cu care ați apelat, ați trimis e-mailuri sau ați comunicat în alte moduri cu anumite persoane. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău-intenționate pot permite accesul la datele de contact fără cunoștința dvs."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Permite aplicației să citească datele despre persoanele din agenda stocată pe telefon, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără ştirea dvs."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Permite aplicației să citească datele despre persoanele din agenda stocată pe telefon, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără știrea dvs."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"modifică agenda"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
<string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Permite aplicației să modifice datele despre persoanele de contact salvate pe televizor, inclusiv frecvența cu care ați apelat, ați trimis e-mailuri sau ați comunicat în alte moduri cu anumite persoane de contact. Cu această permisiune, aplicația poate șterge datele de contact."</string>
- <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe telefon, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe telefon, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"citeşte jurnalul de apeluri"</string>
- <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Permite aplicației să citească jurnalul de apeluri al tabletei, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără ştirea dvs."</string>
+ <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Permite aplicației să citească jurnalul de apeluri al tabletei, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără știrea dvs."</string>
<string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Permite aplicației să citească jurnalul de apeluri al televizorului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune, aplicațiile pot să salveze datele din jurnalul de apeluri, iar aplicațiile rău-intenționate pot permite accesul la datele din jurnalul de apeluri fără cunoștința dvs."</string>
- <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Permite aplicației să citească jurnalul de apeluri al telefonului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără ştirea dvs."</string>
+ <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Permite aplicației să citească jurnalul de apeluri al telefonului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără știrea dvs."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"scrie jurnalul de apeluri"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite aplicației să modifice jurnalul de apeluri al tabletei dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite aplicației să modifice jurnalul de apeluri al televizorului, inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău-intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul de apeluri."</string>
@@ -331,10 +331,10 @@
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite aplicației să citească toate evenimentele din calendar stocate pe tabletă, inclusiv cele ale prietenilor sau colegilor. Acest lucru poate permite aplicației să distribuie sau să salveze datele din calendar, indiferent dacă acestea sunt confidenţiale sau sensibile."</string>
<string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Permite aplicației să citească toate evenimentele din calendar stocate pe televizor, inclusiv cele ale prietenilor sau colegilor. Cu această permisiune, aplicația poate să permită accesul la datele din calendar sau să le salveze, indiferent dacă acestea sunt confidențiale sau sensibile."</string>
<string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Permite aplicației să citească toate evenimentele din calendar stocate pe telefon, inclusiv cele ale prietenilor sau colegilor. Acest lucru poate permite aplicației să distribuie sau să salveze datele din calendar, indiferent dacă acestea sunt confidenţiale sau sensibile."</string>
- <string name="permlab_writeCalendar" msgid="8438874755193825647">"adăugarea sau modificarea evenimentelor din calendar și trimiterea de e-mailuri invitaţilor fără ştirea proprietarului"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe tabletă, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără ştirea proprietarilor."</string>
+ <string name="permlab_writeCalendar" msgid="8438874755193825647">"adăugarea sau modificarea evenimentelor din calendar și trimiterea de e-mailuri invitaților fără știrea proprietarului"</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe tabletă, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără știrea proprietarilor."</string>
<string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe televizor, inclusiv pe cele ale prietenilor sau ale colegilor. Cu această permisiune, aplicația poate să trimită mesaje care par că vin din partea proprietarilor calendarului sau să modifice evenimentele fără cunoștința acestora."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe telefon, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără ştirea proprietarilor."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe telefon, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără știrea proprietarilor."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesare comenzi suplimentare ale furnizorului locației"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite aplicației să acceseze comenzi suplimentare pentru furnizorul locației. Aplicația ar putea să utilizeze această permisiune pentru a influența operațiile GPS sau ale altor surse de locații."</string>
<string name="permlab_accessFineLocation" msgid="1191898061965273372">"locaţia exactă (bazată pe rețea și GPS)"</string>
@@ -343,7 +343,7 @@
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite aplicației să obţină locaţia dvs. aproximativă. Această locație este dedusă de serviciile de localizare utilizând surse de localizare prin rețele, cum ar fi cele prin turn de celule și Wi-Fi. Pentru a fi utilizate de aplicație, aceste servicii de localizare trebuie să fie activate și disponibile pe dispozitivul dvs. Aplicaţiile pot utiliza această permisiune pentru a determina locaţia dvs. aproximativă."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modificare setări audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite aplicației să modifice setările audio globale, cum ar fi volumul și difuzorul care este utilizat pentru ieșire."</string>
- <string name="permlab_recordAudio" msgid="3876049771427466323">"înregistrare audio"</string>
+ <string name="permlab_recordAudio" msgid="3876049771427466323">"înregistreze sunet"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite aplicației să efectueze înregistrări audio cu ajutorul microfonului. Cu această permisiune aplicația efectuează oricând înregistrări audio fără confirmare."</string>
<string name="permlab_sim_communication" msgid="1180265879464893029">"comunicare cu cardul SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite aplicației să trimită comenzi pe cardul SIM. Această permisiune este foarte periculoasă."</string>
@@ -354,11 +354,11 @@
<string name="permlab_flashlight" msgid="2155920810121984215">"control lanternă"</string>
<string name="permdesc_flashlight" msgid="6522284794568368310">"Permite aplicației să controleze lanterna."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"apelare directă numere de telefon"</string>
- <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite aplicației să apeleze numere de telefon fără intervenţia dvs. Acest lucru poate determina apariţia unor taxe sau a unor apeluri neaşteptate. Cu această permisiune aplicația nu poate apela numerele de urgenţă. Aplicaţiile rău intenţionate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string>
+ <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite aplicației să apeleze numere de telefon fără intervenţia dvs. Acest lucru poate determina apariția unor taxe sau a unor apeluri neaşteptate. Cu această permisiune aplicația nu poate apela numerele de urgenţă. Aplicaţiile rău intenţionate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string>
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"accesează serviciul de apelare IMS"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția dvs."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"citeşte starea și identitatea telefonului"</string>
- <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabileşte numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanţă conectat printr-un apel."</string>
+ <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanţă conectat printr-un apel."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"împiedicarea computerului tablet PC să intre în repaus"</string>
<string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"împiedică intrarea televizorului în stare de inactivitate"</string>
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"împiedicare intrare telefon în repaus"</string>
@@ -756,7 +756,7 @@
<string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Rămâneți în această pagină"</string>
<string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nSigur doriți să părăsiți această pagină?"</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirmați"</string>
- <string name="double_tap_toast" msgid="4595046515400268881">"Sfat: măriţi și micşoraţi prin dublă atingere."</string>
+ <string name="double_tap_toast" msgid="4595046515400268881">"Sfat: măriți și micșorați prin dublă atingere."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Automat"</string>
<string name="setup_autofill" msgid="7103495070180590814">"Conf.Compl.auto."</string>
<string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string>
@@ -767,7 +767,7 @@
<string name="autofill_postal_code" msgid="4696430407689377108">"Cod poştal"</string>
<string name="autofill_state" msgid="6988894195520044613">"Stat"</string>
<string name="autofill_zip_code" msgid="8697544592627322946">"Cod ZIP"</string>
- <string name="autofill_county" msgid="237073771020362891">"Judeţ"</string>
+ <string name="autofill_county" msgid="237073771020362891">"Județ"</string>
<string name="autofill_island" msgid="4020100875984667025">"Insulă"</string>
<string name="autofill_district" msgid="8400735073392267672">"District"</string>
<string name="autofill_department" msgid="5343279462564453309">"Departament"</string>
@@ -776,11 +776,11 @@
<string name="autofill_area" msgid="3547409050889952423">"Zonă"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"Emirat"</string>
<string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"citeşte marcajele și istoricul web"</string>
- <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Permite aplicației să citească istoricul tuturor adreselor URL accesate de Browser și toate marcajele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Permite aplicației să citească istoricul tuturor adreselor URL accesate de Browser și toate marcajele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacități de navigare pe web."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"scrie în marcajele și în istoricul web"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe tabletă. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe tabletă. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacități de navigare pe web."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Permite aplicației să modifice istoricul sau marcajele browserului stocate pe televizor. Cu această permisiune, aplicația poate șterge sau modifica datele din browser. Notă: această permisiune nu poate fi aplicată de browsere terță parte sau de alte aplicații cu capacități de navigare pe web."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe telefon. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe telefon. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacități de navigare pe web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"setează o alarmă"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite aplicației să seteze o alarmă într-o aplicație de ceas cu alarmă instalată. Este posibil ca unele aplicații de ceas cu alarmă să nu implementeze această funcție."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"adăugare mesagerie vocală"</string>
@@ -803,11 +803,11 @@
<string name="searchview_description_search" msgid="6749826639098512120">"Căutați"</string>
<string name="searchview_description_query" msgid="5911778593125355124">"Interogare de căutare"</string>
<string name="searchview_description_clear" msgid="1330281990951833033">"Ștergeți interogarea"</string>
- <string name="searchview_description_submit" msgid="2688450133297983542">"Trimiteţi interogarea"</string>
+ <string name="searchview_description_submit" msgid="2688450133297983542">"Trimiteți interogarea"</string>
<string name="searchview_description_voice" msgid="2453203695674994440">"Căutare vocală"</string>
- <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Activați Exploraţi prin atingere?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Exploraţi prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu tableta."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Exploraţi prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu telefonul."</string>
+ <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Activați Explorați prin atingere?"</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu tableta."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu telefonul."</string>
<string name="oneMonthDurationPast" msgid="7396384508953779925">"cu 1 lună în urmă"</string>
<string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Cu mai mult de 1 lună în urmă"</string>
<plurals name="last_num_days" formatted="false" msgid="5104533550723932025">
@@ -918,7 +918,7 @@
<string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g> a fost lansată iniţial."</string>
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scară"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Afişaţi întotdeauna"</string>
- <string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reactivaţi acest mod din Setări de sistem > Aplicații > Descărcate."</string>
+ <string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reactivați acest mod din Setări de sistem > Aplicații > Descărcate."</string>
<string name="smv_application" msgid="3307209192155442829">"Aplicaţia <xliff:g id="APPLICATION">%1$s</xliff:g> (procesul <xliff:g id="PROCESS">%2$s</xliff:g>) a încălcat propria politică StrictMode."</string>
<string name="smv_process" msgid="5120397012047462446">"Procesul <xliff:g id="PROCESS">%1$s</xliff:g> a încălcat propria politică StrictMode."</string>
<string name="android_upgrading_title" msgid="1584192285441405746">"Android trece la o versiune superioară..."</string>
@@ -1002,10 +1002,10 @@
<string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> trimite un număr mare de mesaje SMS. Permiteți acestei aplicații să trimită în continuare mesaje?"</string>
<string name="sms_control_yes" msgid="3663725993855816807">"Permiteți"</string>
<string name="sms_control_no" msgid="625438561395534982">"Refuzaţi"</string>
- <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> intenţionează să trimită un mesaj la <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string>
+ <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> intenționează să trimită un mesaj la <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string>
<string name="sms_short_code_details" msgid="5873295990846059400">"Acest lucru "<b>"poate genera costuri"</b>" în contul dvs. mobil."</string>
<string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Acest lucru va genera costuri în contul dvs. mobil."</b></string>
- <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Trimiteţi"</string>
+ <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Trimiteți"</string>
<string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"Anulați"</string>
<string name="sms_short_code_remember_choice" msgid="5289538592272218136">"Doresc să se reţină opţiunea"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"Puteți modifica ulterior în Setări > Aplicații"</string>
@@ -1080,8 +1080,8 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Se formatează…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nu este introdus"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nu s-a găsit nicio activitate potrivită."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Direcţionează rezultatele media"</string>
- <string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite unei aplicații să direcţioneze rezultate media către alte dispozitive externe."</string>
+ <string name="permlab_route_media_output" msgid="1642024455750414694">"Direcționează rezultatele media"</string>
+ <string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite unei aplicații să direcționeze rezultate media către alte dispozitive externe."</string>
<string name="permlab_readInstallSessions" msgid="6165432407628065939">"Citirea sesiunilor de instalare"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite unei aplicații accesul la citirea sesiunilor de instalare. Aceasta poate vedea detalii despre instalările de pachete active."</string>
<string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicită instalarea pachetelor"</string>
@@ -1124,7 +1124,7 @@
<string name="upload_file" msgid="2897957172366730416">"Alegeţi un fişier"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nu au fost găsite fișiere"</string>
<string name="reset" msgid="2448168080964209908">"Resetaţi"</string>
- <string name="submit" msgid="1602335572089911941">"Trimiteţi"</string>
+ <string name="submit" msgid="1602335572089911941">"Trimiteți"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Mod Maşină activat"</string>
<string name="car_mode_disable_notification_message" msgid="8035230537563503262">"Atingeți pentru a ieşi din modul Maşină."</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"Tethering sau hotspot activ"</string>
@@ -1160,21 +1160,21 @@
<string name="choose_account_label" msgid="5655203089746423927">"Alegeţi un cont"</string>
<string name="add_account_label" msgid="2935267344849993553">"Adăugaţi un cont"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"Adăugaţi un cont"</string>
- <string name="number_picker_increment_button" msgid="2412072272832284313">"Creşteţi"</string>
+ <string name="number_picker_increment_button" msgid="2412072272832284313">"Creșteți"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"Reduceţi"</string>
<string name="number_picker_increment_scroll_mode" msgid="3073101067441638428">"Atingeți și țineți apăsat <xliff:g id="VALUE">%s</xliff:g>."</string>
<string name="number_picker_increment_scroll_action" msgid="9101473045891835490">"Glisaţi în sus pentru a creşte și în jos pentru a reduce."</string>
- <string name="time_picker_increment_minute_button" msgid="8865885114028614321">"Creşteţi valoarea pentru minute"</string>
+ <string name="time_picker_increment_minute_button" msgid="8865885114028614321">"Creșteți valoarea pentru minute"</string>
<string name="time_picker_decrement_minute_button" msgid="6246834937080684791">"Reduceţi valoarea pentru minute"</string>
- <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"Creşteţi valoarea pentru oră"</string>
+ <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"Creșteți valoarea pentru oră"</string>
<string name="time_picker_decrement_hour_button" msgid="1377479863429214792">"Reduceţi valoarea pentru oră"</string>
<string name="time_picker_increment_set_pm_button" msgid="4147590696151230863">"Setaţi valoarea PM"</string>
<string name="time_picker_decrement_set_am_button" msgid="8302140353539486752">"Setaţi valoarea AM"</string>
- <string name="date_picker_increment_month_button" msgid="5369998479067934110">"Creşteţi valoarea pentru lună"</string>
+ <string name="date_picker_increment_month_button" msgid="5369998479067934110">"Creșteți valoarea pentru lună"</string>
<string name="date_picker_decrement_month_button" msgid="1832698995541726019">"Reduceţi valoarea pentru lună"</string>
- <string name="date_picker_increment_day_button" msgid="7130465412308173903">"Creşteţi valoarea pentru zi"</string>
+ <string name="date_picker_increment_day_button" msgid="7130465412308173903">"Creșteți valoarea pentru zi"</string>
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduceţi valoarea pentru zi"</string>
- <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creşteţi valoarea pentru an"</string>
+ <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creșteți valoarea pentru an"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceţi valoarea pentru an"</string>
<string name="date_picker_prev_month_button" msgid="2858244643992056505">"Luna trecută"</string>
<string name="date_picker_next_month_button" msgid="5559507736887605055">"Luna viitoare"</string>
@@ -1245,7 +1245,7 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tabletă"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Căşti"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Căști"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Difuz. dispozit. andocare"</string>
<string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f1deb22..0621bbe 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-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index d5bafd25..582f912 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"සම්බන්ධතා"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ඔබේ සම්බන්ධතාවලට පිවිසෙන්න"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ස්ථානය"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"මෙම උපාංගයේ ස්ථානය ප්රවේශ කිරීම"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"මෙම උපාංගයේ ස්ථානයට ප්රවේශ කරන්න"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"දින දර්ශනය"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"ඔබේ දින දර්ශනයට පිවිසෙන්න"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"කෙටි පණිවිඩ"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index cf87e2a..c5ca3c2 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -993,7 +993,7 @@
<string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct ni bilo mogoče zagnati."</string>
<string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct je vklopljen"</string>
<string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dotaknite se za nastavitve"</string>
- <string name="accept" msgid="1645267259272829559">"Sprejmi"</string>
+ <string name="accept" msgid="1645267259272829559">"Sprejmem"</string>
<string name="decline" msgid="2112225451706137894">"Zavrni"</string>
<string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Povabilo je poslano"</string>
<string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"Povabilo za povezavo"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 86f9d11..7df0f86 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktet"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"qasu te kontaktet e tua"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Vendndodhja"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"qasu te vendndodhja e kësaj pajisjeje"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"qasjen te vendndodhja e kësaj pajisjeje"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendari"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"qasu te kalendari yt"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index a3a4b32..5bb2d78 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -230,21 +230,21 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"приступи контактима"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"приступ локацији овог уређаја"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"приступи локацији овог уређаја"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"приступи календару"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"шаље и прегледа SMS поруке"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Складиште"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"приступи сликама, медијима и датотекама на уређају"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"приступа сликама, медијима и датотекама на уређају"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снимање аудио снимака"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снима аудио снимке"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"снимање слика и видео снимака"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"снима слике и видео снимке"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Телефон"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"упућивање телефонских позива и управљање њима"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"упућује телефонске позиве и управља њима"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Сензори за тело"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"приступ подацима сензора о виталним функцијама"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"приступа подацима сензора о виталним функцијама"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Преузима садржај прозора"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Проверава садржај прозора са којим остварујете интеракцију."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Укључи Истраживања додиром"</string>
@@ -273,7 +273,7 @@
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Омогућава апликацији да чита поруке инфо сервиса које уређај прима. Упозорења инфо сервиса се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на учинак или ометају функционисање уређаја када се прими порука инфо сервиса о хитном случају."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"читање пријављених фидова"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Дозвољава апликацији да преузима детаље о тренутно синхронизованим фидовима."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"шаљи и прегледај SMS поруке"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"шаље и прегледа SMS поруке"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Дозвољава апликацији да шаље SMS поруке. Ово може да доведе до неочекиваних трошкова. Злонамерне апликације могу да шаљу поруке без ваше потврде, што може да изазове трошкове."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"читање текстуалних порука (SMS или MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Дозвољава апликацији да чита SMS поруке ускладиштене на таблету или SIM картици. Ово омогућава апликацији да чита све SMS поруке, без обзира на садржај или поверљивост."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 237afe7..29fd6a1 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -853,7 +853,7 @@
<string name="Midnight" msgid="5630806906897892201">"Midnatt"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="6876518925844129331">"Välj alla"</string>
+ <string name="selectAll" msgid="6876518925844129331">"Markera allt"</string>
<string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
<string name="copy" msgid="2681946229533511987">"Kopiera"</string>
<string name="paste" msgid="5629880836805036433">"Klistra in"</string>
@@ -1190,7 +1190,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Fler alternativ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="4891916833657929263">"Internminne"</string>
+ <string name="storage_internal" msgid="4891916833657929263">"lagring"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kort (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-enhet"</string>
@@ -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-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 6445f6a..d367887 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -231,7 +231,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Anwani"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ifikie anwani zako"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Mahali"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"fikia mahali kilipo kifaa hiki"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"ifikie mahali kilipo kifaa hiki"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"fikia kalenda yako"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-uz-rUZ-watch/strings.xml b/core/res/res/values-uz-rUZ-watch/strings.xml
index 0fe54a1..44af51d 100644
--- a/core/res/res/values-uz-rUZ-watch/strings.xml
+++ b/core/res/res/values-uz-rUZ-watch/strings.xml
@@ -22,15 +22,15 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>dan <xliff:g id="NUMBER_0">%1$d</xliff:g> ilova."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensorlar"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"kontaktlarga kirishga ruxsat berish"</string>
+ <string name="permgrouplab_contactswear" msgid="2340286500790908344">"kontaktlarga kirish"</string>
<string name="permgrouplab_locationwear" msgid="6275317222482780209">"mazkur soatning joylashgan joyini ko‘rishga ruxsat berish"</string>
<string name="permgrouplab_calendarwear" msgid="441900844045065081">"taqvim ma’lumotlariga kirish"</string>
<string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS xabarlarni yuborish va ko‘rish"</string>
<string name="permgrouplab_storagewear" msgid="1003807594193602313">"Soatingizdagi rasmlar, media va fayllarga kirish"</string>
<string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ovoz yozib olish"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"rasmga tushirish va videoga olish"</string>
+ <string name="permgrouplab_camerawear" msgid="4543951283103407017">"suratga olish va video yozib olish"</string>
<string name="permgrouplab_phonewear" msgid="134365036753766126">"telefon qo‘ng‘iroqlarini amalga oshirish va boshqarish"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"asosiy belgilaringiz haqidagi sezgich ma’lumotlaridan foydalanishga ruxsat"</string>
+ <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"organizm holati haqidagi sezgich ma’lumotlariga kirish"</string>
<string name="permlab_statusBarServicewear" msgid="2469402818964691034">"holat qatorida ko‘rinishi"</string>
<string name="permlab_bodySensorswear" msgid="7857941041202791873">"tana sezgichlari (m-n, yurak urishi sensori) ma’lumotlaridan foydalanishga ruxsat"</string>
<string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"aniq joylashuv (GPS va tarmoqqa asoslanib) ma’lumotlaridan foydalanishga ruxsat"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 325ad91..b7ae992 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -231,19 +231,19 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Joylashuv"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"qurilmaning joylashuvi haqidagi ma’lumotlarga kirish"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Taqvim"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvimga kirish"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvim ma’lumotlariga kirish"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS xabarlarni yuborish va ko‘rish"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Xotira"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"qurilmangizdagi rasm, media va fayllarga kirish"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"qurilmangizdagi rasm, multimedia va fayllarga kirish"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"ovoz yozib olish"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"rasm va videoga olish"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"suratga olish va video yozib olish"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefon"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"telefon qo‘ng‘iroqlarini amalga oshirish va boshqarish"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Tana sezgichlari"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"asosiy belgilaringiz haqidagi sezgich ma’lumotlariga kirish"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"organizm holati haqidagi sezgich ma’lumotlariga kirish"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Oynadagi kontentni o‘qiydi"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Joriy oynadagi kontent mazmunini aniqlaydi."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Teginib o‘rganish xizmatini yoqadi"</string>
@@ -342,7 +342,7 @@
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Ilovaga sizning taxminiy joylashuvingizni topishga ruxsat beradi. Ushbu joylashuv Wi-Fi va uyali tarmoq antennalari kabi tarmoq joylashuv manbalaridan foydlanuvchi joylashuv xizmatlari orqali aniqlanadi. Ushbu joylashuv xizmatlari yoqib qo‘yilgan bo‘lishi va qurilmangizdagi ilovaga ulardan foydalanish uchun mavjud bo‘lishi kerak. Ilovalar bundan foydalanib, sizning taxminiy joylashuvingizni aniqlaydi."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio sozlamalaringizni o‘zgartirish"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ilovalarga tovush va ovoz chiqarish uchun foydalaniladigan karnay kabi global audio sozlamalarini o‘zgartirish uchun ruxsat beradi."</string>
- <string name="permlab_recordAudio" msgid="3876049771427466323">"audioni yozib olish"</string>
+ <string name="permlab_recordAudio" msgid="3876049771427466323">"ovoz yozib olish"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Ilovaga mikrofon yordamida audio yozish uchun ruxsat beradi. Bu huquq ilovaga ruxsatingizsiz audio fayllarni yozib olishga ruxsat beradi."</string>
<string name="permlab_sim_communication" msgid="1180265879464893029">"sim orqali ulanish"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Dasturga SIM kartaga buyruqlar jo‘natishga ruxsat beradi. Bu juda ham xavfli."</string>
@@ -797,9 +797,9 @@
<string name="menu_space_shortcut_label" msgid="2410328639272162537">"space"</string>
<string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
<string name="menu_delete_shortcut_label" msgid="3658178007202748164">"delete"</string>
- <string name="search_go" msgid="8298016669822141719">"Izlash"</string>
+ <string name="search_go" msgid="8298016669822141719">"Qidirish"</string>
<string name="search_hint" msgid="1733947260773056054">"Qidirish…"</string>
- <string name="searchview_description_search" msgid="6749826639098512120">"Izlash"</string>
+ <string name="searchview_description_search" msgid="6749826639098512120">"Qidirish"</string>
<string name="searchview_description_query" msgid="5911778593125355124">"Qidiruv so‘rovi"</string>
<string name="searchview_description_clear" msgid="1330281990951833033">"So‘rovni tozalash"</string>
<string name="searchview_description_submit" msgid="2688450133297983542">"So‘rov yaratish"</string>
@@ -1082,7 +1082,7 @@
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Masshtabni o‘zgartirish uchun ikki marta bosing"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidjet qo‘shilmadi."</string>
<string name="ime_action_go" msgid="8320845651737369027">"O‘tish"</string>
- <string name="ime_action_search" msgid="658110271822807811">"Izlash"</string>
+ <string name="ime_action_search" msgid="658110271822807811">"Qidirish"</string>
<string name="ime_action_send" msgid="2316166556349314424">"Jo‘natish"</string>
<string name="ime_action_next" msgid="3138843904009813834">"Keyingi"</string>
<string name="ime_action_done" msgid="8971516117910934605">"Tayyor"</string>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 41b05ea..919519e 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -54,4 +54,7 @@
<!-- Do not show the message saying USB is connected in charging mode. -->
<bool name="config_usbChargingMessage">false</bool>
+
+ <!-- Use a custom transition for RemoteViews. -->
+ <bool name="config_overrideRemoteViewsActivityTransition">true</bool>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index dc96df4..a6eb68b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -162,6 +162,9 @@
<!-- Text color, typeface, size, and style for small text inside of a popup menu. -->
<attr name="textAppearanceSmallPopupMenu" format="reference" />
+ <!-- Text color, typeface, size, and style for header text inside of a popup menu. -->
+ <attr name="textAppearancePopupMenuHeader" format="reference" />
+
<!-- The underline color and thickness for easy correct suggestion -->
<attr name="textAppearanceEasyCorrectSuggestion" format="reference" />
@@ -729,6 +732,8 @@
<attr name="listPopupWindowStyle" format="reference" />
<!-- Default PopupMenu style. -->
<attr name="popupMenuStyle" format="reference" />
+ <!-- Default context menu PopupMenu style. -->
+ <attr name="contextPopupMenuStyle" format="reference" />
<!-- Default StackView style. -->
<attr name="stackViewStyle" format="reference" />
@@ -2150,6 +2155,13 @@
(which is exiting the screen). The wallpaper remains
static behind the animation. -->
<attr name="wallpaperIntraCloseExitAnimation" format="reference" />
+
+ <!-- When opening a new activity from a RemoteViews, this is the
+ animation that is run on the next activity (which is entering the
+ screen). Requires config_overrideRemoteViewsActivityTransition to
+ be true. -->
+ <attr name="activityOpenRemoteViewsEnterAnimation" format="reference" />
+
</declare-styleable>
<!-- ============================= -->
diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml
index c29fec6..9d1dda5 100644
--- a/core/res/res/values/colors_holo.xml
+++ b/core/res/res/values/colors_holo.xml
@@ -79,8 +79,7 @@
<eat-comment />
<color name="holo_primary_dark">#ff000000</color>
- <color name="holo_primary">#ffe6e6e6</color>
- <color name="holo_primary_light">#ffffffff</color>
+ <color name="holo_primary">#ff222222</color>
<color name="holo_control_activated">@color/holo_blue_light</color>
<color name="holo_control_normal">#39cccccc</color>
<color name="holo_button_pressed">#59f0f0f0</color>
@@ -88,8 +87,7 @@
<color name="holo_light_primary_dark">#ff000000</color>
<color name="holo_light_primary">#ffe6e6e6</color>
- <color name="holo_light_primary_light">#ffffffff</color>
- <color name="holo_light_control_activated">@color/holo_control_activated</color>
+ <color name="holo_light_control_activated">@color/holo_blue_light</color>
<color name="holo_light_control_normal">#dacccccc</color>
<color name="holo_light_button_pressed">#66666666</color>
<color name="holo_light_button_normal">#b3cccccc</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9b1a613..24d760f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2214,6 +2214,10 @@
<bool name="config_defaultWindowFeatureOptionsPanel">true</bool>
<bool name="config_defaultWindowFeatureContextMenu">true</bool>
+ <!-- If true, the transition for a RemoteViews is read from a resource instead of using the
+ default scale-up transition. -->
+ <bool name="config_overrideRemoteViewsActivityTransition">false</bool>
+
<!-- This config is used to check if the carrier requires converting destination
number before sending out a SMS.
Formats for this configuration as below:
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 55bea9e..96a81d1 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -72,6 +72,7 @@
<dimen name="text_size_title_material_toolbar">20dp</dimen>
<dimen name="text_size_subtitle_material_toolbar">16dp</dimen>
<dimen name="text_size_menu_material">16sp</dimen>
+ <dimen name="text_size_menu_header_material">14sp</dimen>
<dimen name="text_size_body_2_material">14sp</dimen>
<dimen name="text_size_body_1_material">14sp</dimen>
<dimen name="text_size_caption_material">12sp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 89d9bab..f4d0b39 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2674,6 +2674,8 @@
<public type="attr" name="buttonGravity" />
<public type="attr" name="collapseIcon" />
<public type="attr" name="level" />
+ <public type="attr" name="contextPopupMenuStyle" />
+ <public type="attr" name="textAppearancePopupMenuHeader" />
<public type="style" name="Theme.Material.DayNight" />
<public type="style" name="Theme.Material.DayNight.DarkActionBar" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 611b171..38a1693 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -307,6 +307,10 @@
<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">
+ <item name="fontFamily">@string/font_family_title_material</item>
+ <item name="textSize">@dimen/text_size_menu_header_material</item>
+ </style>
<style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" />
@@ -863,6 +867,11 @@
<item name="dropDownHorizontalOffset">-4dip</item>
</style>
+ <style name="Widget.Material.ContextPopupMenu" parent="Widget.Material.ListPopupWindow">
+ <item name="overlapAnchor">true</item>
+ <item name="popupEnterTransition">@null</item>
+ </style>
+
<style name="Widget.Material.ActionButton">
<item name="background">?attr/actionBarItemBackground</item>
<item name="paddingStart">12dp</item>
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index c6052ff..05835e7 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -18,6 +18,7 @@
<style name="Animation.Micro.Activity" parent="Animation.Material.Activity">
<item name="activityOpenEnterAnimation">@anim/slide_in_micro</item>
+ <item name="activityOpenRemoteViewsEnterAnimation">@anim/slide_in_micro</item>
<item name="activityOpenExitAnimation">@null</item>
<item name="activityCloseEnterAnimation">@null</item>
<item name="activityCloseExitAnimation">@anim/slide_out_micro</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b5efbfd..06de81d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1290,6 +1290,7 @@
<java-symbol type="layout" name="number_picker" />
<java-symbol type="layout" name="permissions_package_list_item" />
<java-symbol type="layout" name="popup_menu_item_layout" />
+ <java-symbol type="layout" name="popup_menu_header_item_layout" />
<java-symbol type="layout" name="remote_views_adapter_default_loading_view" />
<java-symbol type="layout" name="search_bar" />
<java-symbol type="layout" name="search_dropdown_item_icons_2line" />
@@ -2191,6 +2192,7 @@
<java-symbol type="bool" name="config_sms_force_7bit_encoding" />
<java-symbol type="bool" name="config_defaultWindowFeatureOptionsPanel" />
<java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
+ <java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" />
<java-symbol type="layout" name="simple_account_item" />
<java-symbol type="array" name="config_sms_convert_destination_number_support" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 3010190..59dfc92 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -93,6 +93,7 @@
<item name="textAppearanceLargePopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Large</item>
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Small</item>
+ <item name="textAppearancePopupMenuHeader">@style/TextAppearance.Material.Widget.PopupMenu.Header</item>
<!-- Button styles -->
<item name="buttonStyle">@style/Widget.Material.Button</item>
@@ -283,6 +284,7 @@
<item name="stackViewStyle">@style/Widget.Material.StackView</item>
<item name="activityChooserViewStyle">@style/Widget.Material.ActivityChooserView</item>
<item name="fragmentBreadCrumbsStyle">@style/Widget.Material.FragmentBreadCrumbs</item>
+ <item name="contextPopupMenuStyle">@style/Widget.Material.ContextPopupMenu</item>
<!-- Preference styles -->
<item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
@@ -449,6 +451,7 @@
<item name="textAppearanceLargePopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Large</item>
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Small</item>
+ <item name="textAppearancePopupMenuHeader">@style/TextAppearance.Material.Widget.PopupMenu.Header</item>
<!-- Button styles -->
<item name="buttonStyle">@style/Widget.Material.Light.Button</item>
@@ -640,6 +643,7 @@
<item name="stackViewStyle">@style/Widget.Material.Light.StackView</item>
<item name="activityChooserViewStyle">@style/Widget.Material.Light.ActivityChooserView</item>
<item name="fragmentBreadCrumbsStyle">@style/Widget.Material.FragmentBreadCrumbs</item>
+ <item name="contextPopupMenuStyle">@style/Widget.Material.ContextPopupMenu</item>
<!-- Preference styles -->
<item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index ce9ae02..4db1d9a 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -18,8 +18,12 @@
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
+import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.longPressAndDragOnText;
+import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
+import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
import static android.widget.espresso.TextViewAssertions.hasSelection;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.pressKey;
@@ -59,6 +63,7 @@
getActivity();
final String helloWorld = "Hello world!";
+ onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
@@ -68,10 +73,39 @@
}
@SmallTest
+ public void testLongPressToSelect() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello Kirk!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ longPressOnTextAtIndex(helloWorld.indexOf("Kirk")));
+
+ onView(withId(R.id.textview)).check(hasSelection("Kirk"));
+ }
+
+ @SmallTest
+ public void testLongPressEmptySpace() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello big round sun!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ // Move cursor somewhere else
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("big")));
+ // Long-press at end of line.
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(helloWorld.length()));
+
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(helloWorld.length()));
+ }
+
+ @SmallTest
public void testLongPressAndDragToSelect() throws Exception {
getActivity();
final String helloWorld = "Hello little handsome boy!";
+ onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
onView(withId(R.id.textview)).perform(
longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!")));
@@ -80,14 +114,60 @@
}
@SmallTest
+ public void testDoubleTapToSelect() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello SuetYi!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ doubleClickOnTextAtIndex(helloWorld.indexOf("SuetYi")));
+
+ onView(withId(R.id.textview)).check(hasSelection("SuetYi"));
+ }
+
+ @SmallTest
public void testDoubleTapAndDragToSelect() throws Exception {
getActivity();
final String helloWorld = "Hello young beautiful girl!";
+ onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
onView(withId(R.id.textview)).perform(
doubleTapAndDragOnText(helloWorld.indexOf("young"), helloWorld.indexOf(" girl!")));
onView(withId(R.id.textview)).check(hasSelection("young beautiful"));
}
+
+ @SmallTest
+ public void testSelectBackwordsByTouch() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello king of the Jungle!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ doubleTapAndDragOnText(helloWorld.indexOf(" Jungle!"), helloWorld.indexOf("king")));
+
+ onView(withId(R.id.textview)).check(hasSelection("king of the"));
+ }
+
+ @SmallTest
+ public void testToolbarAppearsAfterSelection() throws Exception {
+ getActivity();
+
+ // It'll be nice to check that the toolbar is not visible (or does not exist) here
+ // I can't currently find a way to do this. I'll get to it later.
+
+ final String text = "Toolbar appears after selection.";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+ onView(withId(R.id.textview)).perform(
+ longPressOnTextAtIndex(text.indexOf("appears")));
+
+ // It takes the toolbar less than 100ms to start to animate into screen.
+ // Ideally, we'll wait using the UiController, but I guess this works for now.
+ Thread.sleep(100);
+ assertFloatingToolbarIsDisplayed(getActivity());
+ }
}
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
new file mode 100644
index 0000000..fc01d84
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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.widget.espresso;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
+import android.app.Activity;
+import com.android.internal.widget.FloatingToolbar;
+
+/**
+ * Espresso utility methods for the floating toolbar.
+ */
+public class FloatingToolbarEspressoUtils {
+
+
+ private FloatingToolbarEspressoUtils() {}
+
+ /**
+ * Asserts that the floating toolbar is displayed on screen.
+ *
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertFloatingToolbarIsDisplayed(Activity activity) {
+ onView(withTagValue(is((Object) FloatingToolbar.FLOATING_TOOLBAR_TAG)))
+ .inRoot(withDecorView(not(is(activity.getWindow().getDecorView()))))
+ .check(matches(isDisplayed()));
+ }
+
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 7e4735b..835b1b9 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -52,6 +52,36 @@
}
/**
+ * Returns an action that double-clicks on text at an index on the TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param index The index of the TextView's text to double-click on.
+ */
+ public static ViewAction doubleClickOnTextAtIndex(int index) {
+ return actionWithAssertions(
+ new GeneralClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.FINGER));
+ }
+
+ /**
+ * Returns an action that long presses on text at an index on the TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param index The index of the TextView's text to long press on.
+ */
+ public static ViewAction longPressOnTextAtIndex(int index) {
+ return actionWithAssertions(
+ new GeneralClickAction(Tap.LONG, new TextCoordinates(index), Press.FINGER));
+ }
+
+ /**
* Returns an action that long presses then drags on text from startIndex to endIndex on the
* TextView.<br>
* <br>
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
index dce3182..37c7425 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
@@ -47,7 +47,7 @@
* @param selection The expected selection.
*/
public static ViewAssertion hasSelection(String selection) {
- return new TextSelectionAssertion(is(selection));
+ return hasSelection(is(selection));
}
/**
@@ -66,6 +66,53 @@
}
/**
+ * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at
+ * a specified index.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a text view displayed on screen
+ * <ul>
+ *
+ * @param index The expected index.
+ */
+ public static ViewAssertion hasInsertionPointerAtIndex(int index) {
+ return hasInsertionPointerAtIndex(is(index));
+ }
+
+ /**
+ * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at
+ * a specified index.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a text view displayed on screen
+ * <ul>
+ *
+ * @param index A matcher representing the expected index.
+ */
+ public static ViewAssertion hasInsertionPointerAtIndex(final Matcher<Integer> index) {
+ return new ViewAssertion() {
+ @Override
+ public void check(View view, NoMatchingViewException exception) {
+ if (view instanceof TextView) {
+ TextView textView = (TextView) view;
+ int selectionStart = textView.getSelectionStart();
+ int selectionEnd = textView.getSelectionEnd();
+ try {
+ assertThat(selectionStart, index);
+ assertThat(selectionEnd, index);
+ } catch (IndexOutOfBoundsException e) {
+ throw new AssertionFailedError(e.getMessage());
+ }
+ } else {
+ throw new AssertionFailedError("TextView not found");
+ }
+ }
+ };
+ }
+
+ /**
* A {@link ViewAssertion} to check the selected text in a {@link TextView}.
*/
private static final class TextSelectionAssertion implements ViewAssertion {
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index 9dc0ed1..22ad0c9 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -665,7 +665,7 @@
<li>Added support for a Collapse icon description in the {@link android.support.v7.widget.Toolbar}
class.</li>
<li>Updated the {@link android.support.v7.widget.SearchView} widget to support displaying
- the {@link android.support.v7.mediarouter.R.attr#commitIcon}. </li>
+ the {@link android.support.v7.appcompat.R.attr#commitIcon}. </li>
<li>Removed the <code>buttonGravity</code> attribute from the
{@link android.support.v7.widget.Toolbar} class. </li>
</ul>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 6582b7e..58de87a 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1526,18 +1526,18 @@
return 0f;
}
if (!mHasCompatScaling) {
- return (float) Math.ceil(native_measureText(text, index, count, mBidiFlags));
+ return (float) Math.ceil(native_getTextAdvances(mNativePaint, mNativeTypeface, text,
+ index, count, index, count, mBidiFlags, null, 0));
}
final float oldSize = getTextSize();
- setTextSize(oldSize*mCompatScaling);
- float w = native_measureText(text, index, count, mBidiFlags);
+ setTextSize(oldSize * mCompatScaling);
+ float w = native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index,
+ count, mBidiFlags, null, 0);
setTextSize(oldSize);
return (float) Math.ceil(w*mInvCompatScaling);
}
- private native float native_measureText(char[] text, int index, int count, int bidiFlags);
-
/**
* Return the width of the text.
*
@@ -1558,18 +1558,17 @@
return 0f;
}
if (!mHasCompatScaling) {
- return (float) Math.ceil(native_measureText(text, start, end, mBidiFlags));
+ return (float) Math.ceil(native_getTextAdvances(mNativePaint, mNativeTypeface, text,
+ start, end, start, end, mBidiFlags, null, 0));
}
-
final float oldSize = getTextSize();
- setTextSize(oldSize*mCompatScaling);
- float w = native_measureText(text, start, end, mBidiFlags);
+ setTextSize(oldSize * mCompatScaling);
+ float w = native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start,
+ end, mBidiFlags, null, 0);
setTextSize(oldSize);
- return (float) Math.ceil(w*mInvCompatScaling);
+ return (float) Math.ceil(w * mInvCompatScaling);
}
- private native float native_measureText(String text, int start, int end, int bidiFlags);
-
/**
* Return the width of the text.
*
@@ -1580,23 +1579,9 @@
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
-
- if (text.length() == 0) {
- return 0f;
- }
-
- if (!mHasCompatScaling) {
- return (float) Math.ceil(native_measureText(text, mBidiFlags));
- }
- final float oldSize = getTextSize();
- setTextSize(oldSize*mCompatScaling);
- float w = native_measureText(text, mBidiFlags);
- setTextSize(oldSize);
- return (float) Math.ceil(w*mInvCompatScaling);
+ return measureText(text, 0, text.length());
}
- private native float native_measureText(String text, int bidiFlags);
-
/**
* Return the width of the text.
*
@@ -1795,17 +1780,20 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_getTextWidths(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, widths);
+ native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
+ mBidiFlags, widths, 0);
+ return count;
}
final float oldSize = getTextSize();
- setTextSize(oldSize*mCompatScaling);
- int res = native_getTextWidths(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, widths);
+ setTextSize(oldSize * mCompatScaling);
+ native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
+ mBidiFlags, widths, 0);
setTextSize(oldSize);
- for (int i=0; i<res; i++) {
+ for (int i = 0; i < count; i++) {
widths[i] *= mInvCompatScaling;
}
- return res;
+ return count;
}
/**
@@ -1860,7 +1848,7 @@
* @param end The end of the text slice to measure
* @param widths array to receive the advance widths of the characters.
* Must be at least a large as the text.
- * @return the number of unichars in the specified text.
+ * @return the number of code units in the specified text.
*/
public int getTextWidths(String text, int start, int end, float[] widths) {
if (text == null) {
@@ -1877,17 +1865,20 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_getTextWidths(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, widths);
+ native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
+ mBidiFlags, widths, 0);
+ return end - start;
}
final float oldSize = getTextSize();
- setTextSize(oldSize*mCompatScaling);
- int res = native_getTextWidths(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, widths);
+ setTextSize(oldSize * mCompatScaling);
+ native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
+ mBidiFlags, widths, 0);
setTextSize(oldSize);
- for (int i=0; i<res; i++) {
+ for (int i = 0; i < end - start; i++) {
widths[i] *= mInvCompatScaling;
}
- return res;
+ return end - start;
}
/**
@@ -1896,7 +1887,7 @@
* @param text The text to measure
* @param widths array to receive the advance widths of the characters.
* Must be at least a large as the text.
- * @return the number of unichars in the specified text.
+ * @return the number of code units in the specified text.
*/
public int getTextWidths(String text, float[] widths) {
return getTextWidths(text, 0, text.length(), widths);
@@ -1929,14 +1920,16 @@
return 0f;
}
if (!mHasCompatScaling) {
- return native_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count,
- contextIndex, contextCount, isRtl, advances, advancesIndex);
+ return native_getTextAdvances(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_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count,
- contextIndex, contextCount, isRtl, advances, advancesIndex);
+ float res = native_getTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
+ contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
+ advancesIndex);
setTextSize(oldSize);
if (advances != null) {
@@ -2039,7 +2032,6 @@
*/
public float getTextRunAdvances(String text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
-
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2056,14 +2048,16 @@
}
if (!mHasCompatScaling) {
- return native_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end,
- contextStart, contextEnd, isRtl, advances, advancesIndex);
+ return native_getTextAdvances(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_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end,
- contextStart, contextEnd, isRtl, advances, advancesIndex);
+ float totalAdvance = native_getTextAdvances(mNativePaint, mNativeTypeface, text, start,
+ end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
+ advancesIndex);
setTextSize(oldSize);
if (advances != null) {
@@ -2511,21 +2505,16 @@
private static native void native_setTextLocale(long native_object,
String locale);
- private static native int native_getTextWidths(long native_object, long native_typeface,
- char[] text, int index, int count, int bidiFlags, float[] widths);
- private static native int native_getTextWidths(long native_object, long native_typeface,
- String text, int start, int end, int bidiFlags, float[] widths);
-
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_getTextRunAdvances(long native_object, long native_typeface,
+ private static native float native_getTextAdvances(long native_object, long native_typeface,
char[] text, int index, int count, int contextIndex, int contextCount,
- boolean isRtl, float[] advances, int advancesIndex);
- private static native float native_getTextRunAdvances(long native_object, long native_typeface,
+ int bidiFlags, float[] advances, int advancesIndex);
+ private static native float native_getTextAdvances(long native_object, long native_typeface,
String text, int start, int end, int contextStart, int contextEnd,
- boolean isRtl, float[] advances, int advancesIndex);
+ int bidiFlags, float[] advances, int advancesIndex);
private native int native_getTextRunCursor(long native_object, char[] text,
int contextStart, int contextLength, int dir, int offset, int cursorOpt);
diff --git a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
new file mode 100644
index 0000000..6763dd1
--- /dev/null
+++ b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.graphics;
+
+import android.test.AndroidTestCase;
+
+public class PaintTest extends AndroidTestCase {
+ public void testGetTextRunAdvances() {
+ {
+ // LTR
+ String text = "abcdef";
+ assertGetTextRunAdvances(text, 0, text.length(), 0, text.length(), false, true);
+ assertGetTextRunAdvances(text, 1, text.length() - 1, 0, text.length(), false, false);
+ }
+ {
+ // RTL
+ final String text =
+ "\u0645\u0627\u0020\u0647\u064A\u0020\u0627\u0644\u0634" +
+ "\u0641\u0631\u0629\u0020\u0627\u0644\u0645\u0648\u062D" +
+ "\u062F\u0629\u0020\u064A\u0648\u0646\u064A\u0643\u0648" +
+ "\u062F\u061F";
+ assertGetTextRunAdvances(text, 0, text.length(), 0, text.length(), true, true);
+ assertGetTextRunAdvances(text, 1, text.length() - 1, 0, text.length(), true, false);
+ }
+ }
+
+ private void assertGetTextRunAdvances(String str, int start, int end,
+ int contextStart, int contextEnd, boolean isRtl, boolean compareWithOtherMethods) {
+ Paint p = new Paint();
+
+ final int count = end - start;
+ final float[][] advanceArrays = new float[4][count];
+
+ final float advance = p.getTextRunAdvances(str, start, end, contextStart, contextEnd,
+ isRtl, advanceArrays[0], 0);
+
+ char chars[] = str.toCharArray();
+ final float advance_c = p.getTextRunAdvances(chars, start, count, contextStart,
+ contextEnd - contextStart, isRtl, advanceArrays[1], 0);
+ assertEquals(advance, advance_c, 1.0f);
+
+ for (int c = 1; c < count; ++c) {
+ final float firstPartAdvance = p.getTextRunAdvances(str, start, start + c,
+ contextStart, contextEnd, isRtl, advanceArrays[2], 0);
+ final float secondPartAdvance = p.getTextRunAdvances(str, start + c, end,
+ contextStart, contextEnd, isRtl, advanceArrays[2], c);
+ assertEquals(advance, firstPartAdvance + secondPartAdvance, 1.0f);
+
+
+ final float firstPartAdvance_c = p.getTextRunAdvances(chars, start, c,
+ contextStart, contextEnd - contextStart, isRtl, advanceArrays[3], 0);
+ final float secondPartAdvance_c = p.getTextRunAdvances(chars, start + c,
+ count - c, contextStart, contextEnd - contextStart, isRtl,
+ advanceArrays[3], c);
+ assertEquals(advance, firstPartAdvance_c + secondPartAdvance_c, 1.0f);
+ assertEquals(firstPartAdvance, firstPartAdvance_c, 1.0f);
+ assertEquals(secondPartAdvance, secondPartAdvance_c, 1.0f);
+
+ for (int i = 1; i < advanceArrays.length; i++) {
+ for (int j = 0; j < count; j++) {
+ assertEquals(advanceArrays[0][j], advanceArrays[i][j], 1.0f);
+ }
+ }
+
+ // Compare results with measureText, getRunAdvance, and getTextWidths.
+ if (compareWithOtherMethods && start == contextStart && end == contextEnd) {
+ assertEquals(advance, p.measureText(str, start, end), 1.0f);
+ assertEquals(advance, p.getRunAdvance(
+ str, start, end, contextStart, contextEnd, isRtl, end), 1.0f);
+
+ final float[] widths = new float[count];
+ p.getTextWidths(str, start, end, widths);
+ for (int i = 0; i < count; i++) {
+ assertEquals(advanceArrays[0][i], widths[i], 1.0f);
+ }
+ }
+ }
+ }
+
+ public void testGetTextRunAdvances_invalid() {
+ Paint p = new Paint();
+ String text = "test";
+
+ try {
+ p.getTextRunAdvances((String)null, 0, 0, 0, 0, false, null, 0);
+ fail("Should throw an IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ p.getTextRunAdvances((CharSequence)null, 0, 0, 0, 0, false, null, 0);
+ fail("Should throw an IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ p.getTextRunAdvances((char[])null, 0, 0, 0, 0, false, null, 0);
+ fail("Should throw an IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
+ new float[text.length() - 1], 0);
+ fail("Should throw an IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ try {
+ p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
+ new float[text.length()], 1);
+ fail("Should throw an IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ // 0 > contextStart
+ try {
+ p.getTextRunAdvances(text, 0, text.length(), -1, text.length(), false, null, 0);
+ fail("Should throw an IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ // contextStart > start
+ try {
+ p.getTextRunAdvances(text, 0, text.length(), 1, text.length(), false, null, 0);
+ fail("Should throw an IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ // start > end
+ try {
+ p.getTextRunAdvances(text, 1, 0, 0, text.length(), false, null, 0);
+ fail("Should throw an IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ // end > contextEnd
+ try {
+ p.getTextRunAdvances(text, 0, text.length(), 0, text.length() - 1, false, null, 0);
+ fail("Should throw an IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ // contextEnd > text.length
+ try {
+ p.getTextRunAdvances(text, 0, text.length(), 0, text.length() + 1, false, null, 0);
+ fail("Should throw an IndexOutOfBoundsException.");
+ } 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 b0f69cb..018572c 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -539,7 +539,11 @@
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;
}
@@ -583,7 +587,8 @@
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;
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index ce304a0..7873fbd 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"
@@ -176,7 +177,7 @@
* 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/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h
deleted file mode 100644
index 4ff9a42..0000000
--- 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 54ad60e..4b8f81e 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.util.Log;
+import android.util.MathUtils;
import java.util.Calendar;
import java.util.Collections;
@@ -332,7 +333,14 @@
}
// 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 ee4edee..a3e2abd 100644
--- a/media/java/android/media/SRTRenderer.java
+++ b/media/java/android/media/SRTRenderer.java
@@ -165,7 +165,6 @@
return;
}
- final int _ = 0;
for (Cue cue : activeCues) {
TextTrackCue ttc = (TextTrackCue) cue;
@@ -184,7 +183,8 @@
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/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index 3cd157e..95cb520 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -18,6 +18,8 @@
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
/**
@@ -47,7 +49,7 @@
/**
* Opens the MTP device. Once the device is open it takes ownership of the
- * {@link android.hardware.usb.UsbDeviceConnection}.
+ * {@link android.hardware.usb.UsbDeviceConnection}.
* The connection will be closed when you call {@link #close()}
* The connection will also be closed if this method fails.
*
@@ -278,6 +280,38 @@
return native_send_object_info(info);
}
+ /**
+ * Reads an event from the device. It blocks the current thread until it gets an event.
+ * It throws OperationCanceledException if it is cancelled by signal.
+ *
+ * @param signal signal for cancellation
+ * @return obtained event
+ */
+ public MtpEvent readEvent(CancellationSignal signal) {
+ final int handle = native_submit_event_request();
+
+ if (handle < 0) {
+ throw new IllegalStateException("Other thread is reading an event.");
+ }
+
+ if (signal != null) {
+ signal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
+ @Override
+ public void onCancel() {
+ native_discard_event_request(handle);
+ }
+ });
+ }
+
+ try {
+ return native_reap_event_request(handle);
+ } finally {
+ if (signal != null) {
+ signal.setOnCancelListener(null);
+ }
+ }
+ }
+
// used by the JNI code
private long mNativeContext;
@@ -297,4 +331,7 @@
private native boolean native_import_file(int objectHandle, int fd);
private native boolean native_send_object(int objectHandle, int size, int fd);
private native MtpObjectInfo native_send_object_info(MtpObjectInfo info);
+ private native int native_submit_event_request();
+ private native MtpEvent native_reap_event_request(int handle);
+ private native void native_discard_event_request(int handle);
}
diff --git a/media/java/android/mtp/MtpEvent.java b/media/java/android/mtp/MtpEvent.java
new file mode 100644
index 0000000..0fa7d93
--- /dev/null
+++ b/media/java/android/mtp/MtpEvent.java
@@ -0,0 +1,31 @@
+/*
+ * 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.mtp;
+
+/**
+ * This class encapsulates information about a MTP event.
+ */
+public class MtpEvent {
+ private int mEventCode;
+
+ /**
+ * Returns event code of MTP event.
+ *
+ * @return event code
+ */
+ public int getEventCode() { return mEventCode; }
+}
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index f11329c..3f4d183 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -46,10 +46,14 @@
jclass clazz_deviceInfo;
jclass clazz_storageInfo;
jclass clazz_objectInfo;
+jclass clazz_event;
+jclass clazz_io_exception;
+jclass clazz_operation_canceled_exception;
jmethodID constructor_deviceInfo;
jmethodID constructor_storageInfo;
jmethodID constructor_objectInfo;
+jmethodID constructor_event;
// MtpDeviceInfo fields
static jfieldID field_deviceInfo_manufacturer;
@@ -86,6 +90,9 @@
static jfieldID field_objectInfo_dateModified;
static jfieldID field_objectInfo_keywords;
+// MtpEvent fields
+static jfieldID field_event_eventCode;
+
MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice)
{
return (MtpDevice*)env->GetLongField(javaDevice, field_context);
@@ -488,6 +495,42 @@
return result;
}
+static jint android_mtp_MtpDevice_submit_event_request(JNIEnv *env, jobject thiz)
+{
+ MtpDevice* const device = get_device_from_object(env, thiz);
+ if (!device) {
+ env->ThrowNew(clazz_io_exception, "");
+ return -1;
+ }
+ return device->submitEventRequest();
+}
+
+static jobject android_mtp_MtpDevice_reap_event_request(JNIEnv *env, jobject thiz, jint seq)
+{
+ MtpDevice* const device = get_device_from_object(env, thiz);
+ if (!device) {
+ env->ThrowNew(clazz_io_exception, "");
+ return NULL;
+ }
+ const int eventCode = device->reapEventRequest(seq);
+ if (eventCode <= 0) {
+ env->ThrowNew(clazz_operation_canceled_exception, "");
+ return NULL;
+ }
+ jobject result = env->NewObject(clazz_event, constructor_event);
+ env->SetIntField(result, field_event_eventCode, eventCode);
+ return result;
+}
+
+static void android_mtp_MtpDevice_discard_event_request(JNIEnv *env, jobject thiz, jint seq)
+{
+ MtpDevice* const device = get_device_from_object(env, thiz);
+ if (!device) {
+ return;
+ }
+ device->discardEventRequest(seq);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -513,7 +556,11 @@
{"native_import_file", "(II)Z",(void *)android_mtp_MtpDevice_import_file_to_fd},
{"native_send_object", "(III)Z",(void *)android_mtp_MtpDevice_send_object},
{"native_send_object_info", "(Landroid/mtp/MtpObjectInfo;)Landroid/mtp/MtpObjectInfo;",
- (void *)android_mtp_MtpDevice_send_object_info}
+ (void *)android_mtp_MtpDevice_send_object_info},
+ {"native_submit_event_request", "()I", (void *)android_mtp_MtpDevice_submit_event_request},
+ {"native_reap_event_request", "(I)Landroid/mtp/MtpEvent;",
+ (void *)android_mtp_MtpDevice_reap_event_request},
+ {"native_discard_event_request", "(I)V", (void *)android_mtp_MtpDevice_discard_event_request},
};
int register_android_mtp_MtpDevice(JNIEnv *env)
@@ -703,6 +750,23 @@
}
clazz_objectInfo = (jclass)env->NewGlobalRef(clazz);
+ clazz = env->FindClass("android/mtp/MtpEvent");
+ if (clazz == NULL) {
+ ALOGE("Can't find android/mtp/MtpEvent");
+ return -1;
+ }
+ constructor_event = env->GetMethodID(clazz, "<init>", "()V");
+ if (constructor_event == NULL) {
+ ALOGE("Can't find android/mtp/MtpEvent constructor");
+ return -1;
+ }
+ field_event_eventCode = env->GetFieldID(clazz, "mEventCode", "I");
+ if (field_event_eventCode == NULL) {
+ ALOGE("Can't find MtpObjectInfo.mEventCode");
+ return -1;
+ }
+ clazz_event = (jclass)env->NewGlobalRef(clazz);
+
clazz = env->FindClass("android/mtp/MtpDevice");
if (clazz == NULL) {
ALOGE("Can't find android/mtp/MtpDevice");
@@ -713,6 +777,18 @@
ALOGE("Can't find MtpDevice.mNativeContext");
return -1;
}
+ clazz = env->FindClass("java/io/IOException");
+ if (clazz == NULL) {
+ ALOGE("Can't find java.io.IOException");
+ return -1;
+ }
+ clazz_io_exception = (jclass)env->NewGlobalRef(clazz);
+ clazz = env->FindClass("android/os/OperationCanceledException");
+ if (clazz == NULL) {
+ ALOGE("Can't find android.os.OperationCanceledException");
+ return -1;
+ }
+ clazz_operation_canceled_exception = (jclass)env->NewGlobalRef(clazz);
return AndroidRuntime::registerNativeMethods(env,
"android/mtp/MtpDevice", gMethods, NELEM(gMethods));
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
index 1b684bb..175f730 100644
--- a/native/graphics/jni/Android.mk
+++ b/native/graphics/jni/Android.mk
@@ -31,7 +31,7 @@
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-# TODO: This is to work around b/19059885. Remove after root cause is fixed
+# TODO: This is to work around b/24465209. Remove after root cause is fixed
LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
include $(BUILD_SHARED_LIBRARY)
diff --git a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
index 969ec599..6a7dbbd 100644
--- a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"Copiere de rezervă completă"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Restabilire completă"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unei copii de rezervă complete a tuturor datelor pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu aţi solicitat dvs. copierea de rezervă, nu permiteți ca operaţiunea să continue."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unei copii de rezervă complete a tuturor datelor pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu aţi solicitat dvs. copierea de rezervă, nu permiteți ca operațiunea să continue."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Creaţi copii de rezervă pentru datele dvs."</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Nu creaţi copii de rezervă"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu dvs. aţi solicitat această restabilire, nu permiteți continuarea operaţiunii. Acest proces va înlocui toate datele existente în prezent pe dispozitiv!"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu dvs. aţi solicitat această restabilire, nu permiteți continuarea operațiunii. Acest proces va înlocui toate datele existente în prezent pe dispozitiv!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Restabiliţi datele dvs."</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"Nu restabiliţi"</string>
<string name="current_password_text" msgid="8268189555578298067">"Introduceţi mai jos parola actuală pentru copia de rezervă:"</string>
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index ac6f950..295cb80 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -3,6 +3,7 @@
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.REMOVE_TASKS" />
+ <uses-permission android:name="android.permission.REORDER_TASKS" />
<application
android:name=".DocumentsApplication"
@@ -49,8 +50,8 @@
</activity>
<activity
- android:name=".FilesActivity"
- android:theme="@style/FilesTheme"
+ android:name=".LauncherActivity"
+ android:theme="@android:style/Theme.NoDisplay"
android:icon="@drawable/ic_files_app"
android:label="@string/files_label"
android:enabled="@bool/productivity_device">
@@ -58,6 +59,17 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
+ </activity>
+
+ <activity
+ android:name=".FilesActivity"
+ android:theme="@style/FilesTheme"
+ android:icon="@drawable/ic_files_app"
+ android:label="@string/files_label"
+ android:documentLaunchMode="intoExisting">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
<intent-filter>
<action android:name="android.provider.action.BROWSE_DOCUMENT_ROOT" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml
index dec4e92..0dac0d5 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">
+ 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: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">
+ </LinearLayout>
- <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" />
+ <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">
- <FrameLayout
- android:id="@+id/container_roots"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
+ <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" />
- </LinearLayout>
+ <FrameLayout
+ android:id="@+id/container_roots"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
-</android.support.v4.widget.DrawerLayout>
+ </LinearLayout>
+
+ </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 eba9af4..403c667 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">
+ 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>
-
- <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"
+ <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 20c3232..c5a5745 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 7df152f..673a254 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -26,6 +26,7 @@
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
@@ -56,11 +57,18 @@
android:icon="@drawable/ic_menu_view_list"
android:showAsAction="never" />
<item
+ android:id="@+id/menu_new_window"
+ android:title="@string/menu_new_window"
+ android:alphabeticShortcut="n"
+ android:showAsAction="never"
+ android:visible="false" />
+ <item
android:id="@+id/menu_paste_from_clipboard"
android:title="@string/menu_paste_from_clipboard"
android:alphabeticShortcut="v"
android:showAsAction="never"
android:visible="false" />
+ <!-- Copy action is defined in mode_directory.xml -->
<item
android:id="@+id/menu_advanced"
android:showAsAction="never"
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index 0053f22..c2b6dbc 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Kies"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopieer"</string>
<string name="button_move" msgid="2202666023104202232">"Skuif"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Maak toe"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Probeer weer"</string>
<string name="sort_name" msgid="9183560467917256779">"Volgens naam"</string>
<string name="sort_date" msgid="586080032956151448">"Volgens datum gewysig"</string>
<string name="sort_size" msgid="3350681319735474741">"Volgens grootte"</string>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index eb88830..b82fa93 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"ምረጥ"</string>
<string name="button_copy" msgid="8706475544635021302">"ቅዳ"</string>
<string name="button_move" msgid="2202666023104202232">"አንቀሳቀስ"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"አሰናብት"</string>
+ <string name="button_retry" msgid="4392027584153752797">"እንደገና ይሞክሩ"</string>
<string name="sort_name" msgid="9183560467917256779">"በስም"</string>
<string name="sort_date" msgid="586080032956151448">"በተለወጠበት ቀን"</string>
<string name="sort_size" msgid="3350681319735474741">"በመጠን"</string>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index bf464b6..c7c9ef2 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"تحديد"</string>
<string name="button_copy" msgid="8706475544635021302">"نسخ"</string>
<string name="button_move" msgid="2202666023104202232">"نقل"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"إزالة"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"بحسب الاسم"</string>
<string name="sort_date" msgid="586080032956151448">"بحسب تاريخ التعديل"</string>
<string name="sort_size" msgid="3350681319735474741">"بحسب الحجم"</string>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index 0bd01a5..8c79ceb 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Seçin"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
<string name="button_move" msgid="2202666023104202232">"Köçürün"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Rədd edin"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Ad üzrə"</string>
<string name="sort_date" msgid="586080032956151448">"Tarix üzrə dəyişmiş"</string>
<string name="sort_size" msgid="3350681319735474741">"Ölçü üzrə"</string>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index 669145e..fdf57d0 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Избиране"</string>
<string name="button_copy" msgid="8706475544635021302">"Копиране"</string>
<string name="button_move" msgid="2202666023104202232">"Преместване"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Отхвърляне"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"По име"</string>
<string name="sort_date" msgid="586080032956151448">"По дата на промяната"</string>
<string name="sort_size" msgid="3350681319735474741">"По размер"</string>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 240ba6c..7caf57f 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"নির্বাচন করুন"</string>
<string name="button_copy" msgid="8706475544635021302">"অনুলিপি করুন"</string>
<string name="button_move" msgid="2202666023104202232">"সরান"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"খারিজ করুন"</string>
+ <string name="button_retry" msgid="4392027584153752797">"আবার চেষ্টা করুন"</string>
<string name="sort_name" msgid="9183560467917256779">"নামের দ্বারা"</string>
<string name="sort_date" msgid="586080032956151448">"পরিবর্তনের তারিখ দ্বারা"</string>
<string name="sort_size" msgid="3350681319735474741">"আকার অনুযায়ী"</string>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index 30accda..ab365ce 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Selecciona"</string>
<string name="button_copy" msgid="8706475544635021302">"Copia"</string>
<string name="button_move" msgid="2202666023104202232">"Desplaça"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignora"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Per nom"</string>
<string name="sort_date" msgid="586080032956151448">"Per data de modificació"</string>
<string name="sort_size" msgid="3350681319735474741">"Per mida"</string>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index cb1d973..62b313c 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Vybrat"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopírovat"</string>
<string name="button_move" msgid="2202666023104202232">"Přesunout"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Zavřít"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Zkusit znovu"</string>
<string name="sort_name" msgid="9183560467917256779">"Podle názvu"</string>
<string name="sort_date" msgid="586080032956151448">"Podle data úpravy"</string>
<string name="sort_size" msgid="3350681319735474741">"Podle velikosti"</string>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index f12737c..c3d7cdd 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Vælg"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiér"</string>
<string name="button_move" msgid="2202666023104202232">"Flyt"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Luk"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Prøv igen"</string>
<string name="sort_name" msgid="9183560467917256779">"Efter navn"</string>
<string name="sort_date" msgid="586080032956151448">"Efter ændringsdato"</string>
<string name="sort_size" msgid="3350681319735474741">"Efter størrelse"</string>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index a7be4db..f88b5c4 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Auswählen"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopieren"</string>
<string name="button_move" msgid="2202666023104202232">"Verschieben"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Schließen"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Erneut versuchen"</string>
<string name="sort_name" msgid="9183560467917256779">"Nach Name"</string>
<string name="sort_date" msgid="586080032956151448">"Nach Änderungsdatum"</string>
<string name="sort_size" msgid="3350681319735474741">"Nach Größe"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index 82155a8..dcca46c 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Επιλογή"</string>
<string name="button_copy" msgid="8706475544635021302">"Αντιγραφή"</string>
<string name="button_move" msgid="2202666023104202232">"Μετακίνηση"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Παράβλεψη"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Δοκιμάστε ξανά"</string>
<string name="sort_name" msgid="9183560467917256779">"Κατά όνομα"</string>
<string name="sort_date" msgid="586080032956151448">"Κατά ημερομηνία τροποποίησης"</string>
<string name="sort_size" msgid="3350681319735474741">"Κατά μέγεθος"</string>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index c609047..26fd30f 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"select"</string>
<string name="button_copy" msgid="8706475544635021302">"Copy"</string>
<string name="button_move" msgid="2202666023104202232">"Move"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dismiss"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Try again"</string>
<string name="sort_name" msgid="9183560467917256779">"By name"</string>
<string name="sort_date" msgid="586080032956151448">"By date modified"</string>
<string name="sort_size" msgid="3350681319735474741">"By size"</string>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index c609047..26fd30f 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"select"</string>
<string name="button_copy" msgid="8706475544635021302">"Copy"</string>
<string name="button_move" msgid="2202666023104202232">"Move"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dismiss"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Try again"</string>
<string name="sort_name" msgid="9183560467917256779">"By name"</string>
<string name="sort_date" msgid="586080032956151448">"By date modified"</string>
<string name="sort_size" msgid="3350681319735474741">"By size"</string>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index c609047..26fd30f 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"select"</string>
<string name="button_copy" msgid="8706475544635021302">"Copy"</string>
<string name="button_move" msgid="2202666023104202232">"Move"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dismiss"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Try again"</string>
<string name="sort_name" msgid="9183560467917256779">"By name"</string>
<string name="sort_date" msgid="586080032956151448">"By date modified"</string>
<string name="sort_size" msgid="3350681319735474741">"By size"</string>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 5936d83..6e805b9 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Seleccionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Descartar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Reintentar"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nombre"</string>
<string name="sort_date" msgid="586080032956151448">"Por fecha de modificación"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 2ed67dd..8fa1d2e 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Seleccionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Reintentar"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nombre"</string>
<string name="sort_date" msgid="586080032956151448">"Por fecha de modificación"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index e32936f..7008885 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Vali"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopeeri"</string>
<string name="button_move" msgid="2202666023104202232">"Teisalda"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Loobu"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Nime järgi"</string>
<string name="sort_date" msgid="586080032956151448">"Muutmiskuupäeva järgi"</string>
<string name="sort_size" msgid="3350681319735474741">"Suuruse järgi"</string>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index a1b6fc2..17d7c66 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Hautatu"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiatu"</string>
<string name="button_move" msgid="2202666023104202232">"Mugitu"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Baztertu"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Izenaren arabera"</string>
<string name="sort_date" msgid="586080032956151448">"Aldatze-dataren arabera"</string>
<string name="sort_size" msgid="3350681319735474741">"Tamainaren arabera"</string>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index bbbfe52..c4f6d58 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -22,7 +22,7 @@
<string name="title_save" msgid="2433679664882857999">"ذخیره در"</string>
<string name="menu_create_dir" msgid="5947289605844398389">"ایجاد پوشه"</string>
<string name="menu_grid" msgid="6878021334497835259">"نمای جدولی"</string>
- <string name="menu_list" msgid="7279285939892417279">"نمای فهرستوار"</string>
+ <string name="menu_list" msgid="7279285939892417279">"نمای فهرستی"</string>
<string name="menu_sort" msgid="7677740407158414452">"مرتبسازی براساس"</string>
<string name="menu_search" msgid="3816712084502856974">"جستجو"</string>
<string name="menu_settings" msgid="6008033148948428823">"تنظیمات"</string>
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"انتخاب"</string>
<string name="button_copy" msgid="8706475544635021302">"کپی"</string>
<string name="button_move" msgid="2202666023104202232">"انتقال"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"نپذیرفتن"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"براساس نام"</string>
<string name="sort_date" msgid="586080032956151448">"براساس تاریخ اصلاح"</string>
<string name="sort_size" msgid="3350681319735474741">"براساس اندازه"</string>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index fd289a7..3e87300 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Valitse"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopioi"</string>
<string name="button_move" msgid="2202666023104202232">"Siirrä"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Hylkää"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Nimen mukaan"</string>
<string name="sort_date" msgid="586080032956151448">"Muokkauspäivän mukaan"</string>
<string name="sort_size" msgid="3350681319735474741">"Koon mukaan"</string>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 342fc97..3e82f34 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Sélectionner"</string>
<string name="button_copy" msgid="8706475544635021302">"Copier"</string>
<string name="button_move" msgid="2202666023104202232">"Déplacer"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Faire disparaître"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
<string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
<string name="sort_size" msgid="3350681319735474741">"Par taille"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 7434e2f..0512ab1 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Sélectionner"</string>
<string name="button_copy" msgid="8706475544635021302">"Copier"</string>
<string name="button_move" msgid="2202666023104202232">"Déplacer"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Fermer"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Réessayer"</string>
<string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
<string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
<string name="sort_size" msgid="3350681319735474741">"Par taille"</string>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index b74018d..a1cd614 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Seleccionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificación"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 58384c9..059fb2c 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"પસંદ કરો"</string>
<string name="button_copy" msgid="8706475544635021302">"કૉપિ કરો"</string>
<string name="button_move" msgid="2202666023104202232">"ખસેડો"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"છોડી દો"</string>
+ <string name="button_retry" msgid="4392027584153752797">"ફરીથી પ્રયત્ન કરો"</string>
<string name="sort_name" msgid="9183560467917256779">"નામ દ્વારા"</string>
<string name="sort_date" msgid="586080032956151448">"સંશોધન તારીખ દ્વારા"</string>
<string name="sort_size" msgid="3350681319735474741">"કદ દ્વારા"</string>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index b42c69c..8e33a00 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"चुनें"</string>
<string name="button_copy" msgid="8706475544635021302">"कॉपी करें"</string>
<string name="button_move" msgid="2202666023104202232">"ले जाएं"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ख़ारिज करें"</string>
+ <string name="button_retry" msgid="4392027584153752797">"पुनः प्रयास करें"</string>
<string name="sort_name" msgid="9183560467917256779">"नाम के अनुसार"</string>
<string name="sort_date" msgid="586080032956151448">"बदलाव के दिनांक के अनुसार"</string>
<string name="sort_size" msgid="3350681319735474741">"आकार के अनुसार"</string>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 575c7ff..4774753 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Odaberi"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
<string name="button_move" msgid="2202666023104202232">"Premjesti"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Odbaci"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Po nazivu"</string>
<string name="sort_date" msgid="586080032956151448">"Po datumu izmjene"</string>
<string name="sort_size" msgid="3350681319735474741">"Po veličini"</string>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index 2d2e3c8..7a521ec 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Kiválasztás"</string>
<string name="button_copy" msgid="8706475544635021302">"Másolás"</string>
<string name="button_move" msgid="2202666023104202232">"Áthelyezés"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Elvetés"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Név szerint"</string>
<string name="sort_date" msgid="586080032956151448">"Módosítás dátuma szerint"</string>
<string name="sort_size" msgid="3350681319735474741">"Méret szerint"</string>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 0a4e7a3..95d73f0 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Ընտրել"</string>
<string name="button_copy" msgid="8706475544635021302">"Պատճենել"</string>
<string name="button_move" msgid="2202666023104202232">"Տեղափոխել"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Փակել"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Ըստ անվան"</string>
<string name="sort_date" msgid="586080032956151448">"Ըստ փոփոխման ամսաթվի"</string>
<string name="sort_size" msgid="3350681319735474741">"Ըստ չափի"</string>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 59aadf1..63d415c 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Pilih"</string>
<string name="button_copy" msgid="8706475544635021302">"Salin"</string>
<string name="button_move" msgid="2202666023104202232">"Pindahkan"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Tutup"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Coba Lagi"</string>
<string name="sort_name" msgid="9183560467917256779">"Menurut nama"</string>
<string name="sort_date" msgid="586080032956151448">"Menurut tanggal diubah"</string>
<string name="sort_size" msgid="3350681319735474741">"Menurut ukuran"</string>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index a0f4987..0c0e47f 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Velja"</string>
<string name="button_copy" msgid="8706475544635021302">"Afrita"</string>
<string name="button_move" msgid="2202666023104202232">"Færa"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Hunsa"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Reyna aftur"</string>
<string name="sort_name" msgid="9183560467917256779">"Eftir heiti"</string>
<string name="sort_date" msgid="586080032956151448">"Eftir breytingadags."</string>
<string name="sort_size" msgid="3350681319735474741">"Eftir stærð"</string>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index ce837da..de129a7 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Seleziona"</string>
<string name="button_copy" msgid="8706475544635021302">"Copia"</string>
<string name="button_move" msgid="2202666023104202232">"Sposta"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignora"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Riprova"</string>
<string name="sort_name" msgid="9183560467917256779">"Per nome"</string>
<string name="sort_date" msgid="586080032956151448">"Per data di modifica"</string>
<string name="sort_size" msgid="3350681319735474741">"Per dimensioni"</string>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index 89e6403..dbe4630 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"בחר"</string>
<string name="button_copy" msgid="8706475544635021302">"העתק"</string>
<string name="button_move" msgid="2202666023104202232">"העבר"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"הסר"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"לפי שם"</string>
<string name="sort_date" msgid="586080032956151448">"לפי תאריך שינוי"</string>
<string name="sort_size" msgid="3350681319735474741">"לפי גודל"</string>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 92e1023..45f0ea1 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"選択"</string>
<string name="button_copy" msgid="8706475544635021302">"コピー"</string>
<string name="button_move" msgid="2202666023104202232">"移動"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"表示しない"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"名前順"</string>
<string name="sort_date" msgid="586080032956151448">"更新日順"</string>
<string name="sort_size" msgid="3350681319735474741">"サイズ順"</string>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index 9a324ba..114e73c 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"არჩევა"</string>
<string name="button_copy" msgid="8706475544635021302">"კოპირება"</string>
<string name="button_move" msgid="2202666023104202232">"გადაადგილება"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"დახურვა"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"სახელით"</string>
<string name="sort_date" msgid="586080032956151448">"ცვლილების თარიღით"</string>
<string name="sort_size" msgid="3350681319735474741">"ზომით"</string>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index 66f69f5..af123c6 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Таңдау"</string>
<string name="button_copy" msgid="8706475544635021302">"Көшіру"</string>
<string name="button_move" msgid="2202666023104202232">"Жылжыту"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Өшіру"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Атауы бойынша"</string>
<string name="sort_date" msgid="586080032956151448">"Өзгертілген мерзімі бойынша"</string>
<string name="sort_size" msgid="3350681319735474741">"Өлшемі бойынша"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 5249d97..8f3feae 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"ជ្រើស"</string>
<string name="button_copy" msgid="8706475544635021302">"ចម្លង"</string>
<string name="button_move" msgid="2202666023104202232">"ផ្លាស់ទី"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"បដិសេធ"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"តាមឈ្មោះ"</string>
<string name="sort_date" msgid="586080032956151448">"តាមកាលបរិច្ឆេទបានកែប្រែ"</string>
<string name="sort_size" msgid="3350681319735474741">"តាមទំហំ"</string>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index d5eda84..826cf1e 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"ಆಯ್ಕೆಮಾಡು"</string>
<string name="button_copy" msgid="8706475544635021302">"ನಕಲಿಸು"</string>
<string name="button_move" msgid="2202666023104202232">"ಸರಿಸು"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ವಜಾಗೊಳಿಸಿ"</string>
+ <string name="button_retry" msgid="4392027584153752797">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="sort_name" msgid="9183560467917256779">"ಹೆಸರಿನ ಪ್ರಕಾರ"</string>
<string name="sort_date" msgid="586080032956151448">"ಮಾರ್ಪಡಿಸಿರುವ ದಿನಾಂಕದ ಪ್ರಕಾರ"</string>
<string name="sort_size" msgid="3350681319735474741">"ಗಾತ್ರದ ಪ್ರಕಾರ"</string>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index 77a0df6..322b43b 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"선택"</string>
<string name="button_copy" msgid="8706475544635021302">"복사"</string>
<string name="button_move" msgid="2202666023104202232">"이동"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"닫기"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"이름순"</string>
<string name="sort_date" msgid="586080032956151448">"수정된 날짜순"</string>
<string name="sort_size" msgid="3350681319735474741">"크기순"</string>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index 14a6331..c8aa69e 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Тандоо"</string>
<string name="button_copy" msgid="8706475544635021302">"Көчүрүү"</string>
<string name="button_move" msgid="2202666023104202232">"Жылдыруу"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Этибарга албоо"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Аты боюнча"</string>
<string name="sort_date" msgid="586080032956151448">"Өзгөртүлгөн күнү боюнча"</string>
<string name="sort_size" msgid="3350681319735474741">"Өлчөмү боюнча"</string>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index 2d099f4..dedfa05 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"ເລືອກ"</string>
<string name="button_copy" msgid="8706475544635021302">"ສຳເນົາ"</string>
<string name="button_move" msgid="2202666023104202232">"ຍ້າຍ"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ປິດໄວ້"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"ຕາມຊື່"</string>
<string name="sort_date" msgid="586080032956151448">"ຕາມວັນທີທີ່ແກ້ໄຂ"</string>
<string name="sort_size" msgid="3350681319735474741">"ຕາມຂະໜາດ"</string>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 7b458add..0c8b443 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Pasirinkti"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopijuoti"</string>
<string name="button_move" msgid="2202666023104202232">"Perkelti"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Atsisakyti"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Pagal pavadinimą"</string>
<string name="sort_date" msgid="586080032956151448">"Pagal keitimo datą"</string>
<string name="sort_size" msgid="3350681319735474741">"Pagal dydį"</string>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index 44909ce..5c79554 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Atlasīt"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopēt"</string>
<string name="button_move" msgid="2202666023104202232">"Pārvietot"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Noraidīt"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Pēc nosaukuma"</string>
<string name="sort_date" msgid="586080032956151448">"Pēc pārveidošanas datuma"</string>
<string name="sort_size" msgid="3350681319735474741">"Pēc lieluma"</string>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index 7408d0b..b2cb9f1 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Избери"</string>
<string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
<string name="button_move" msgid="2202666023104202232">"Премести"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Отфрли"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"По име"</string>
<string name="sort_date" msgid="586080032956151448">"Изменети по датум"</string>
<string name="sort_size" msgid="3350681319735474741">"По големина"</string>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index af21db9..07efa66 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"തിരഞ്ഞെടുക്കുക"</string>
<string name="button_copy" msgid="8706475544635021302">"പകര്ത്തുക"</string>
<string name="button_move" msgid="2202666023104202232">"നീക്കുക"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ഡിസ്മിസ് ചെയ്യുക"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"പേര് പ്രകാരം"</string>
<string name="sort_date" msgid="586080032956151448">"പരിഷ്ക്കരിച്ച തീയതി പ്രകാരം"</string>
<string name="sort_size" msgid="3350681319735474741">"വലുപ്പം പ്രകാരം"</string>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index 1475e08..d45778c 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Сонгох"</string>
<string name="button_copy" msgid="8706475544635021302">"Хуулах"</string>
<string name="button_move" msgid="2202666023104202232">"Зөөх"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Алгасах"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Нэрээр"</string>
<string name="sort_date" msgid="586080032956151448">"Өөрчлөгдсөн огноогоор"</string>
<string name="sort_size" msgid="3350681319735474741">"Хэмжээгээр"</string>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index b7654881..8d70143 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"निवडा"</string>
<string name="button_copy" msgid="8706475544635021302">"कॉपी करा"</string>
<string name="button_move" msgid="2202666023104202232">"हलवा"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"डिसमिस करा"</string>
+ <string name="button_retry" msgid="4392027584153752797">"पुन्हा प्रयत्न करा"</string>
<string name="sort_name" msgid="9183560467917256779">"नावानुसार"</string>
<string name="sort_date" msgid="586080032956151448">"सुधारित केलेल्या तारखेनुसार"</string>
<string name="sort_size" msgid="3350681319735474741">"आकारानुसार"</string>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 4682957..2250109 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Pilih"</string>
<string name="button_copy" msgid="8706475544635021302">"Salin"</string>
<string name="button_move" msgid="2202666023104202232">"Alihkan"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Tolak"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Mengikut nama"</string>
<string name="sort_date" msgid="586080032956151448">"Mengikut tarikh diubah"</string>
<string name="sort_size" msgid="3350681319735474741">"Mengikut saiz"</string>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index a422898..480c27b 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"ရွေးရန်"</string>
<string name="button_copy" msgid="8706475544635021302">"ကူးယူရန်"</string>
<string name="button_move" msgid="2202666023104202232">"ရွေ့မည်"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ပယ်ရန်"</string>
+ <string name="button_retry" msgid="4392027584153752797">"ထပ် စမ်းကြည့်ပါ"</string>
<string name="sort_name" msgid="9183560467917256779">"အမည်ဖြင့်"</string>
<string name="sort_date" msgid="586080032956151448">"ပြင်ဆင်မှု ရက်စွဲဖြင့်"</string>
<string name="sort_size" msgid="3350681319735474741">"အရွယ်အစားဖြင့်"</string>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index 5bcc50f..48355aa 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Velg"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiér"</string>
<string name="button_move" msgid="2202666023104202232">"Flytt"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Avvis"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Prøv på nytt"</string>
<string name="sort_name" msgid="9183560467917256779">"Etter navn"</string>
<string name="sort_date" msgid="586080032956151448">"Etter endringsdato"</string>
<string name="sort_size" msgid="3350681319735474741">"Etter størrelse"</string>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index b121035..53942c1 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"चयन गर्नुहोस्"</string>
<string name="button_copy" msgid="8706475544635021302">"प्रतिलिपि बनाउनुहोस्"</string>
<string name="button_move" msgid="2202666023104202232">"सार्नुहोस्"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"खारेज गर्नुहोस्"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"नाम अनुसार"</string>
<string name="sort_date" msgid="586080032956151448">"परिमार्जित मिति अनुसार"</string>
<string name="sort_size" msgid="3350681319735474741">"आकार अनुसार"</string>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index 88ef0bf..d3e6bbe 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selecteren"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiëren"</string>
<string name="button_move" msgid="2202666023104202232">"Verplaatsen"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Sluiten"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Opnieuw proberen"</string>
<string name="sort_name" msgid="9183560467917256779">"Op naam"</string>
<string name="sort_date" msgid="586080032956151448">"Op aanpassingsdatum"</string>
<string name="sort_size" msgid="3350681319735474741">"Op grootte"</string>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index a55fc27..3b14396 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"ਚੁਣੋ"</string>
<string name="button_copy" msgid="8706475544635021302">"ਕਾਪੀ ਕਰੋ"</string>
<string name="button_move" msgid="2202666023104202232">"ਮੂਵ ਕਰੋ"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ਬਰਖਾਸਤ ਕਰੋ"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"ਨਾਮ ਮੁਤਾਬਕ"</string>
<string name="sort_date" msgid="586080032956151448">"ਤਾਰੀਖ ਮੁਤਾਬਕ ਸੰਸ਼ੋਧਿਤ"</string>
<string name="sort_size" msgid="3350681319735474741">"ਆਕਾਰ ਮੁਤਾਬਕ"</string>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 0099e5a..3e4ef68 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Wybierz"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiuj"</string>
<string name="button_move" msgid="2202666023104202232">"Przenieś"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Zamknij"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Według nazwy"</string>
<string name="sort_date" msgid="586080032956151448">"Według daty edycji"</string>
<string name="sort_size" msgid="3350681319735474741">"Według rozmiaru"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index 18506dc..ca984cf 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selecionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dispensar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Tentar novamente"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamanho"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index adf6ec9..ab67358 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selecionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Tentar novamente"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamanho"</string>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index 18506dc..ca984cf 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selecionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dispensar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Tentar novamente"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamanho"</string>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index 1b046a3..e927b78 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selectați"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiați"</string>
<string name="button_move" msgid="2202666023104202232">"Mutați"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Închideți"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Încercați din nou"</string>
<string name="sort_name" msgid="9183560467917256779">"După nume"</string>
<string name="sort_date" msgid="586080032956151448">"După data modificării"</string>
<string name="sort_size" msgid="3350681319735474741">"După dimensiune"</string>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index 84ae160..cdf20ca 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Выбрать"</string>
<string name="button_copy" msgid="8706475544635021302">"Копировать"</string>
<string name="button_move" msgid="2202666023104202232">"Переместить"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Скрыть"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"По названию"</string>
<string name="sort_date" msgid="586080032956151448">"По дате изменения"</string>
<string name="sort_size" msgid="3350681319735474741">"По размеру"</string>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index d9d5818..138f8a6 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"තෝරන්න"</string>
<string name="button_copy" msgid="8706475544635021302">"පිටපත් කිරීම"</string>
<string name="button_move" msgid="2202666023104202232">"ගෙන යන්න"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ඉවතලන්න"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"නමින්"</string>
<string name="sort_date" msgid="586080032956151448">"වෙනස් කරන ලද දිනයෙන්"</string>
<string name="sort_size" msgid="3350681319735474741">"ප්රමාණය මගින්"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index e0ff5fc..4310819 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Vybrať"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopírovať"</string>
<string name="button_move" msgid="2202666023104202232">"Presunúť"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Odmietnuť"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Podľa názvu"</string>
<string name="sort_date" msgid="586080032956151448">"Podľa dátumu zmeny"</string>
<string name="sort_size" msgid="3350681319735474741">"Podľa veľkosti"</string>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index 83e5124..c9982a7 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Izberi"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
<string name="button_move" msgid="2202666023104202232">"Premik"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Opusti"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Po imenu"</string>
<string name="sort_date" msgid="586080032956151448">"Po datumu spremembe"</string>
<string name="sort_size" msgid="3350681319735474741">"Po velikosti"</string>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index 981daf2..f1ee1bc 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Zgjidh"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopjo"</string>
<string name="button_move" msgid="2202666023104202232">"Zhvendos"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Largoje"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Sipas emrit"</string>
<string name="sort_date" msgid="586080032956151448">"Sipas datës së modifikimit"</string>
<string name="sort_size" msgid="3350681319735474741">"Sipas madhësisë"</string>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index bd0e9af..c5116c2 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Изабери"</string>
<string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
<string name="button_move" msgid="2202666023104202232">"Премести"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Одбаци"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Према имену"</string>
<string name="sort_date" msgid="586080032956151448">"Према датуму измене"</string>
<string name="sort_size" msgid="3350681319735474741">"Према величини"</string>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index 2569e00..06e6514 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Välj"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiera"</string>
<string name="button_move" msgid="2202666023104202232">"Flytta"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ta bort permanent"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Försök igen"</string>
<string name="sort_name" msgid="9183560467917256779">"Efter namn"</string>
<string name="sort_date" msgid="586080032956151448">"Efter ändringsdatum"</string>
<string name="sort_size" msgid="3350681319735474741">"Efter storlek"</string>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index ce186d7..79bae1f 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Teua"</string>
<string name="button_copy" msgid="8706475544635021302">"Nakili"</string>
<string name="button_move" msgid="2202666023104202232">"Hamisha"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ondoa"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Jaribu Tena"</string>
<string name="sort_name" msgid="9183560467917256779">"Kwa jina"</string>
<string name="sort_date" msgid="586080032956151448">"Kwa tarehe viliporekebishwa"</string>
<string name="sort_size" msgid="3350681319735474741">"Kwa ukubwa"</string>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index b7b9c09..117aabc 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"தேர்ந்தெடு"</string>
<string name="button_copy" msgid="8706475544635021302">"நகலெடு"</string>
<string name="button_move" msgid="2202666023104202232">"நகர்த்து"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"நிராகரி"</string>
+ <string name="button_retry" msgid="4392027584153752797">"மீண்டும் முயற்சிக்கவும்"</string>
<string name="sort_name" msgid="9183560467917256779">"பெயரின்படி"</string>
<string name="sort_date" msgid="586080032956151448">"திருத்தப்பட்ட தேதியின்படி"</string>
<string name="sort_size" msgid="3350681319735474741">"அளவின்படி"</string>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index 7a81486..21b7f58 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"ఎంచుకోండి"</string>
<string name="button_copy" msgid="8706475544635021302">"కాపీ చేయి"</string>
<string name="button_move" msgid="2202666023104202232">"తరలించు"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"తీసివేయి"</string>
+ <string name="button_retry" msgid="4392027584153752797">"మళ్లీ ప్రయత్నించు"</string>
<string name="sort_name" msgid="9183560467917256779">"పేరు ద్వారా"</string>
<string name="sort_date" msgid="586080032956151448">"సవరించిన తేదీ ద్వారా"</string>
<string name="sort_size" msgid="3350681319735474741">"పరిమాణం ద్వారా"</string>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 87c0a73..8a49708 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"เลือก"</string>
<string name="button_copy" msgid="8706475544635021302">"คัดลอก"</string>
<string name="button_move" msgid="2202666023104202232">"ย้าย"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ปิด"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"ตามชื่อ"</string>
<string name="sort_date" msgid="586080032956151448">"ตามวันที่ที่ปรับเปลี่ยน"</string>
<string name="sort_size" msgid="3350681319735474741">"ตามขนาด"</string>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index eaef936..5c0dc12 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Pumili"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopyahin"</string>
<string name="button_move" msgid="2202666023104202232">"Ilipat"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"I-dismiss"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Ayon sa pangalan"</string>
<string name="sort_date" msgid="586080032956151448">"Ayon sa petsa ng pagbago"</string>
<string name="sort_size" msgid="3350681319735474741">"Ayon sa laki"</string>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index 8c0596f..092b38e 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Seç"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
<string name="button_move" msgid="2202666023104202232">"Taşı"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Kapat"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Ada göre"</string>
<string name="sort_date" msgid="586080032956151448">"Değişiklik tarihine göre"</string>
<string name="sort_size" msgid="3350681319735474741">"Boyuta göre"</string>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index 9bbc59a..dff37c3 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Вибрати"</string>
<string name="button_copy" msgid="8706475544635021302">"Копіювати"</string>
<string name="button_move" msgid="2202666023104202232">"Перемістити"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Закрити"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Повторити спробу"</string>
<string name="sort_name" msgid="9183560467917256779">"За назвою"</string>
<string name="sort_date" msgid="586080032956151448">"За датою змінення"</string>
<string name="sort_size" msgid="3350681319735474741">"За розміром"</string>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 0c12aa1..82822ec 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"منتخب کریں"</string>
<string name="button_copy" msgid="8706475544635021302">"کاپی کریں"</string>
<string name="button_move" msgid="2202666023104202232">"منتقل کریں"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"برخاست کریں"</string>
+ <string name="button_retry" msgid="4392027584153752797">"دوبارہ کوشش کریں"</string>
<string name="sort_name" msgid="9183560467917256779">"نام کے لحاظ سے"</string>
<string name="sort_date" msgid="586080032956151448">"ترمیم کی تاریخ کے لحاظ سے"</string>
<string name="sort_size" msgid="3350681319735474741">"سائز کے لحاظ سے"</string>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index ec91885..d0cfacc 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -24,7 +24,7 @@
<string name="menu_grid" msgid="6878021334497835259">"Katak ko‘rinishida"</string>
<string name="menu_list" msgid="7279285939892417279">"Ro‘yxat ko‘rinishida"</string>
<string name="menu_sort" msgid="7677740407158414452">"Saralash"</string>
- <string name="menu_search" msgid="3816712084502856974">"Izlash"</string>
+ <string name="menu_search" msgid="3816712084502856974">"Qidirish"</string>
<string name="menu_settings" msgid="6008033148948428823">"Sozlamalar"</string>
<string name="menu_open" msgid="432922957274920903">"Ochish"</string>
<string name="menu_save" msgid="2394743337684426338">"Saqlash"</string>
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Tanlash"</string>
<string name="button_copy" msgid="8706475544635021302">"Nusxalash"</string>
<string name="button_move" msgid="2202666023104202232">"Ko‘chirib o‘tkazish"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"O‘chirish"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Nomi bo‘yicha"</string>
<string name="sort_date" msgid="586080032956151448">"Tahrir sanasi bo‘yicha"</string>
<string name="sort_size" msgid="3350681319735474741">"Hajmi bo‘yicha"</string>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index a652ecc..4583362 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Chọn"</string>
<string name="button_copy" msgid="8706475544635021302">"Sao chép"</string>
<string name="button_move" msgid="2202666023104202232">"Di chuyển"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Loại bỏ"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Theo tên"</string>
<string name="sort_date" msgid="586080032956151448">"Theo ngày sửa đổi"</string>
<string name="sort_size" msgid="3350681319735474741">"Theo kích thước"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index b0f5480..d577e14 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"选择"</string>
<string name="button_copy" msgid="8706475544635021302">"复制"</string>
<string name="button_move" msgid="2202666023104202232">"移动"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"关闭"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"按名称"</string>
<string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
<string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index 220b716..3d44047 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"選取"</string>
<string name="button_copy" msgid="8706475544635021302">"複製"</string>
<string name="button_move" msgid="2202666023104202232">"移動"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"按名稱"</string>
<string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
<string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index 9c21aa5..aaad98c 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"選取"</string>
<string name="button_copy" msgid="8706475544635021302">"複製"</string>
<string name="button_move" msgid="2202666023104202232">"移動"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"依名稱"</string>
<string name="sort_date" msgid="586080032956151448">"依修改日期"</string>
<string name="sort_size" msgid="3350681319735474741">"依大小"</string>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index 5ce9f12..8f20cc4 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Khetha"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopisha"</string>
<string name="button_move" msgid="2202666023104202232">"Hambisa"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Cashisa"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Zama futhi"</string>
<string name="sort_name" msgid="9183560467917256779">"Ngegama"</string>
<string name="sort_date" msgid="586080032956151448">"Ngedethi yokuguqula"</string>
<string name="sort_size" msgid="3350681319735474741">"Ngosayizi"</string>
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index ed7820b..ce5b174 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <bool name="productivity_device">true</bool>
+ <bool name="productivity_device">false</bool>
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index a12edf3..a4acb60 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -54,6 +54,8 @@
<!-- Menu item title that moves the selected documents [CHAR LIMIT=24] -->
<string name="menu_move">Move to\u2026</string>
+ <!-- Menu item title that creates a new window in the activity [CHAR LIMIT=24] -->
+ <string name="menu_new_window">New window</string>
<!-- Menu item title that copies the selected documents to clipboard [CHAR LIMIT=24] -->
<string name="menu_copy_to_clipboard">Copy</string>
<!-- Menu item title that pastes files from the clipboard [CHAR LIMIT=24] -->
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index c8ec4dc..ab0f666 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_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;
@@ -75,10 +76,11 @@
RootsCache mRoots;
SearchManager mSearchManager;
DrawerController mDrawer;
+ boolean mProductivityDevice;
+ private final String mTag;
@LayoutRes
private int mLayoutId;
- private final String mTag;
private DirectoryContainerView mDirectoryContainer;
public abstract void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings);
@@ -99,6 +101,7 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ mProductivityDevice = getResources().getBoolean(R.bool.productivity_device);
mState = (icicle != null)
? icicle.<State>getParcelable(EXTRA_STATE)
: buildState();
@@ -148,12 +151,10 @@
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);
+ sortSize.setVisible(mState.showSize); // Only sort by size when visible
+ fileSize.setVisible(!mState.showSize);
+ grid.setVisible(mState.derivedMode != State.MODE_GRID);
+ list.setVisible(mState.derivedMode != State.MODE_LIST);
advanced.setVisible(!mState.showAdvanced);
settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
@@ -183,12 +184,10 @@
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);
@@ -208,6 +207,7 @@
switch (item.getItemId()) {
case R.id.menu_advanced:
case R.id.menu_file_size:
+ case R.id.menu_new_window:
break;
default:
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
@@ -286,8 +286,8 @@
}
void openDirectory(DocumentInfo doc) {
- getDisplayState().stack.push(doc);
- getDisplayState().stackTouched = true;
+ mState.stack.push(doc);
+ mState.stackTouched = true;
onCurrentDirectoryChanged(ANIM_DOWN);
}
@@ -364,16 +364,15 @@
}
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();
}
@@ -386,7 +385,7 @@
* Set state sort order based on explicit user action.
*/
void setUserSortOrder(int sortOrder) {
- getDisplayState().userSortOrder = sortOrder;
+ mState.userSortOrder = sortOrder;
DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
}
@@ -394,7 +393,7 @@
* Set state mode based on explicit user action.
*/
void setUserMode(int mode) {
- getDisplayState().userMode = mode;
+ mState.userMode = mode;
DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
}
@@ -408,7 +407,7 @@
@Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
- state.putParcelable(EXTRA_STATE, getDisplayState());
+ state.putParcelable(EXTRA_STATE, mState);
}
@Override
@@ -417,16 +416,15 @@
}
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() {
@@ -467,9 +465,8 @@
// 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) {
@@ -499,9 +496,8 @@
@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);
}
}
@@ -513,7 +509,9 @@
@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
@@ -525,7 +523,7 @@
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) {
@@ -536,13 +534,13 @@
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;
}
}
@@ -553,7 +551,7 @@
@Override
protected void onPostExecute(Void result) {
if (isDestroyed()) return;
- getDisplayState().restored = true;
+ mState.restored = true;
onCurrentDirectoryChanged(ANIM_NONE);
onStackRestored(mRestoredStack, mExternal);
}
@@ -597,10 +595,9 @@
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);
}
@@ -617,13 +614,12 @@
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
@@ -711,13 +707,12 @@
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()) {
@@ -743,7 +738,7 @@
mMenu.setVisible(visible);
if (!visible) {
- getDisplayState().currentSearch = null;
+ mState.currentSearch = null;
}
}
@@ -761,7 +756,7 @@
}
boolean isSearching() {
- return getDisplayState().currentSearch != null;
+ return mState.currentSearch != null;
}
boolean isExpanded() {
@@ -776,7 +771,7 @@
return false;
}
- getDisplayState().currentSearch = null;
+ mState.currentSearch = null;
onCurrentDirectoryChanged(ANIM_NONE);
return false;
}
@@ -795,7 +790,7 @@
mIgnoreNextCollapse = false;
return true;
}
- getDisplayState().currentSearch = null;
+ mState.currentSearch = null;
onCurrentDirectoryChanged(ANIM_NONE);
return true;
}
@@ -803,7 +798,7 @@
@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 f1492dc7..362052c 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.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.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 @@
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 @@
* @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,
+ Shared.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 @@
}
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 @@
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 @@
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 e408e6e..9f44516 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.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.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 @@
// Navigate into newly created child
mActivity.onDirectoryCreated(result);
} else {
- Toast.makeText(mActivity, R.string.create_error, Toast.LENGTH_SHORT).show();
+ Shared.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 5eacf21..b3ce103 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -91,7 +91,6 @@
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;
@@ -424,7 +423,7 @@
}
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));
}
@@ -805,8 +804,8 @@
mModel.markForDeletion(selected);
- Activity activity = getActivity();
- Snackbar.make(this.getView(), message, Snackbar.LENGTH_LONG)
+ final Activity activity = getActivity();
+ Shared.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
.setAction(
R.string.undo,
new android.view.View.OnClickListener() {
@@ -821,11 +820,11 @@
mModel.undoDeletion();
} else {
mModel.finalizeDeletion(
- new Runnable() {
+ new Model.DeletionListener() {
@Override
- public void run() {
- Snackbar.make(
- DirectoryFragment.this.getView(),
+ public void onError() {
+ Shared.makeSnackbar(
+ activity,
R.string.toast_failed_delete,
Snackbar.LENGTH_LONG)
.show();
@@ -1245,9 +1244,11 @@
private void copyDocuments(final List<DocumentInfo> docs, final DocumentInfo destination) {
if (!canCopy(docs, destination)) {
- Toast.makeText(
+ Shared.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 +1298,10 @@
void onDocumentsReady(List<DocumentInfo> docs) {
mClipper.clipDocuments(docs);
Activity activity = getActivity();
- Toast.makeText(activity,
+ Shared.makeSnackbar(activity,
activity.getResources().getQuantityString(
R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
- Toast.LENGTH_SHORT).show();
+ Snackbar.LENGTH_SHORT).show();
}
}.execute(items);
}
@@ -1864,9 +1865,9 @@
* @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 +1877,16 @@
*/
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 +1920,29 @@
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 4658fe3..ced03cc 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.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.WindowManager;
import android.widget.BaseAdapter;
import android.widget.Spinner;
-import android.widget.Toast;
import android.widget.Toolbar;
import com.android.documentsui.RecentsProvider.RecentColumns;
@@ -510,7 +510,7 @@
} 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 +611,8 @@
if (result != null) {
onTaskFinished(result);
} else {
- Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
- .show();
+ Shared.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 ea0c18a..120f610 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -66,7 +66,7 @@
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 7e9531b..f6a5131 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -19,6 +19,7 @@
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
import static com.android.documentsui.Shared.DEBUG;
import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkState;
import android.app.Activity;
import android.app.FragmentManager;
@@ -29,7 +30,10 @@
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;
@@ -37,7 +41,6 @@
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;
@@ -84,32 +87,49 @@
mDrawer = DrawerController.create(this);
RootsFragment.show(getFragmentManager(), null);
- if (!mState.restored) {
- Uri rootUri = getIntent().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) {
- 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 Intent intent = getIntent();
- 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);
}
}
@@ -125,7 +145,7 @@
// 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;
}
@@ -207,16 +227,25 @@
public boolean onPrepareOptionsMenu(Menu menu) {
boolean shown = super.onPrepareOptionsMenu(menu);
- final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
+ menu.findItem(R.id.menu_file_size).setVisible(true);
+ menu.findItem(R.id.menu_advanced).setVisible(true);
+
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);
- pasteFromCb.setVisible(true);
+ 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;
@@ -224,17 +253,30 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- final int id = item.getItemId();
- if (id == R.id.menu_paste_from_clipboard) {
- DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
- dir = DirectoryFragment.get(getFragmentManager());
- dir.pasteFromClipboard();
- return true;
+ switch (item.getItemId()) {
+ case R.id.menu_create_dir:
+ checkState(canCreateDirectory());
+ showCreateDirectoryDialog();
+ return true;
+ case R.id.menu_new_window:
+ createNewWindow();
+ return true;
+ case R.id.menu_paste_from_clipboard:
+ DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
+ dir = DirectoryFragment.get(getFragmentManager());
+ dir.pasteFromClipboard();
+ return true;
}
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();
@@ -305,7 +347,7 @@
try {
startActivity(intent);
} catch (ActivityNotFoundException ex2) {
- Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
+ Shared.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
}
}
@@ -317,19 +359,13 @@
dir = DirectoryFragment.get(getFragmentManager());
dir.selectAllFiles();
return true;
- case KeyEvent.KEYCODE_N:
- if (event.isShiftPressed() && canCreateDirectory()) {
- showCreateDirectoryDialog();
- return true;
- }
case KeyEvent.KEYCODE_C:
// TODO: Should be statically bound using alphabeticShortcut. See b/21330356.
dir = DirectoryFragment.get(getFragmentManager());
dir.copySelectedToClipboard();
- // TODO: Cancel action mode in directory fragment.
}
- return super.onKeyUp(keyCode, event);
+ return super.onKeyShortcut(keyCode, event);
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
new file mode 100644
index 0000000..c29937d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
@@ -0,0 +1,93 @@
+/*
+ * 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.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.AppTask;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Provides FilesActivity task grouping support. This allows multiple FilesActivities to be
+ * launched (a behavior imparted by way of {@code documentLaunchMode="intoExisting"} and
+ * our use of pseudo document {@link Uri}s. This also lets us move an existing task
+ * to the foreground when a suitable task exists.
+ *
+ * Requires that {@code documentLaunchMode="intoExisting"} be set on target activity.
+ *
+ */
+public class LauncherActivity extends Activity {
+
+ public static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ List<AppTask> tasks = activities.getAppTasks();
+
+ AppTask raiseTask = null;
+ for (AppTask task : tasks) {
+ Uri taskUri = task.getTaskInfo().baseIntent.getData();
+ if (taskUri != null && isLaunchUri(taskUri)) {
+ raiseTask = task;
+ }
+ }
+
+ if (raiseTask == null) {
+ launchFilesTask();
+ } else {
+ raiseFilesTask(activities, raiseTask.getTaskInfo());
+ }
+
+ finish();
+ }
+
+ private void launchFilesTask() {
+ Intent intent = createLaunchIntent(this);
+ startActivity(intent);
+ }
+
+ private void raiseFilesTask(ActivityManager activities, RecentTaskInfo task) {
+ activities.moveTaskToFront(task.id, 0);
+ }
+
+ static Intent createLaunchIntent(Context context) {
+ Intent intent = new Intent(context, FilesActivity.class);
+ intent.setData(buildLaunchUri());
+ return intent;
+ }
+
+ private static Uri buildLaunchUri() {
+ return new Uri.Builder()
+ .authority(LAUNCH_CONTROL_AUTHORITY)
+ .fragment(String.valueOf(System.currentTimeMillis()))
+ .build();
+ }
+
+ static boolean isLaunchUri(@Nullable Uri uri) {
+ return uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority());
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
index 4754899..ed7333d 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.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;
@@ -184,7 +184,8 @@
try {
startActivity(view);
} catch (ActivityNotFoundException ex2) {
- Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
+ Shared.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT)
+ .show();
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index 9c884d4..e06d6a5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -16,14 +16,18 @@
package com.android.documentsui;
-import android.content.Context;
+import static com.android.internal.util.Preconditions.checkNotNull;
-/**
- * @hide
- */
+import android.app.Activity;
+import android.content.Context;
+import android.support.design.widget.Snackbar;
+import android.view.View;
+
+/** @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.
@@ -31,4 +35,14 @@
public static final String getQuantityString(Context context, int resourceId, int quantity) {
return context.getResources().getQuantityString(resourceId, quantity, quantity);
}
+
+ public static final Snackbar makeSnackbar(Activity activity, int messageId, int duration) {
+ return 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/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index bbffad3..4306a0e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -44,14 +44,14 @@
public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
public boolean allowMultiple;
- public boolean forceSize ;
+ public boolean forceSize;
public boolean showSize;
- public boolean localOnly ;
- public boolean forceAdvanced ;
- public boolean showAdvanced ;
- public boolean stackTouched ;
- public boolean restored ;
- public boolean directoryCopy ;
+ public boolean localOnly;
+ public boolean forceAdvanced;
+ public boolean showAdvanced;
+ public boolean stackTouched;
+ public boolean restored;
+ public boolean directoryCopy;
/** Transfer mode for file copy/move operations. */
public int transferMode;
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
index 568e9e4..fc42c3b 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 @@
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 1895a6e..98ffb77 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.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 @@
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 @@
// Undo the deletion
model.undoDeletion();
assertEquals(ITEM_COUNT, model.getItemCount());
-
}
// Tests that the right things are marked for deletion.
@@ -125,6 +119,15 @@
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 @@
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 25d4ed4..2447469 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.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 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.List;
import java.util.Set;
-public class MultiSelectManagerTest {
+public class MultiSelectManagerTest extends AndroidTestCase {
private static final List<String> items;
static {
@@ -54,7 +53,6 @@
private TestCallback mCallback;
private EventHelper mEventHelper;
- @Before
public void setUp() throws Exception {
mAdapter = new TestAdapter(items);
mCallback = new TestCallback();
@@ -63,65 +61,61 @@
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 @@
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 @@
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 @@
}
- @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 @@
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 @@
assertSelection(20);
}
- @Test
- public void provisionaSelection() {
+ public void testProvisionalSelection() {
Selection s = mManager.getSelection();
assertSelection();
@@ -298,6 +282,7 @@
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 @@
}
@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 87d7e15..aa50b48 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 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 @@
});
}
- @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 @@
assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
- @Test
public void testSelectionRightOfItems() {
setUp(20, 4);
model.startSelection(new Point(viewWidth - 1, 10));
@@ -78,7 +74,6 @@
assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
- @Test
public void testSelectionAboveItems() {
setUp(20, 4);
model.startSelection(new Point(10, 0));
@@ -87,7 +82,6 @@
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 @@
assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
- @Test
public void testVerticalSelectionBetweenItems() {
setUp(20, 4);
model.startSelection(new Point(106, 0));
@@ -105,7 +98,6 @@
assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
- @Test
public void testHorizontalSelectionBetweenItems() {
setUp(20, 4);
model.startSelection(new Point(0, 105));
@@ -114,7 +106,6 @@
assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
- @Test
public void testGrowingAndShrinkingSelection() {
setUp(20, 4);
model.startSelection(new Point(0, 0));
@@ -145,7 +136,6 @@
assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
- @Test
public void testSelectionMovingAroundOrigin() {
setUp(16, 4);
model.startSelection(new Point(210, 210));
@@ -160,7 +150,6 @@
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 51b542b..eddf4ef 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 @@
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 @@
selection.add(9);
}
- @Test
- public void add() {
+ public void testAdd() {
// We added in setUp.
assertEquals(3, selection.size());
assertContains(3);
@@ -44,29 +43,25 @@
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 @@
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 @@
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 @@
assertContains(19);
}
- @Test
- public void expandAfter() {
+ public void testExpandAfter() {
selection.expand(10, 10);
assertEquals(3, selection.size());
assertContains(3);
@@ -122,8 +111,7 @@
assertContains(9);
}
- @Test
- public void expandSplit() {
+ public void testExpandSplit() {
selection.expand(5, 10);
assertEquals(3, selection.size());
assertContains(3);
@@ -131,8 +119,7 @@
assertContains(19);
}
- @Test
- public void expandEncompased() {
+ public void testExpandEncompased() {
selection.expand(2, 10);
assertEquals(3, selection.size());
assertContains(13);
@@ -140,8 +127,7 @@
assertContains(19);
}
- @Test
- public void collapseBefore() {
+ public void testCollapseBefore() {
selection.collapse(0, 2);
assertEquals(3, selection.size());
assertContains(1);
@@ -149,8 +135,7 @@
assertContains(7);
}
- @Test
- public void collapseAfter() {
+ public void testCollapseAfter() {
selection.collapse(10, 10);
assertEquals(3, selection.size());
assertContains(3);
@@ -158,8 +143,7 @@
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 be3f251..0000000
--- 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 393771a..18335b6 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.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 @@
@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/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 7cc7413..af7f691 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -22,12 +22,14 @@
import android.hardware.usb.UsbManager;
import android.mtp.MtpConstants;
import android.mtp.MtpDevice;
+import android.mtp.MtpEvent;
import android.mtp.MtpObjectInfo;
+import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -188,7 +190,13 @@
}
}
- private MtpDevice getDevice(int deviceId) throws IOException {
+ @VisibleForTesting
+ MtpEvent readEvent(int deviceId, CancellationSignal signal) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ return device.readEvent(signal);
+ }
+
+ private synchronized MtpDevice getDevice(int deviceId) throws IOException {
final MtpDevice device = mDevices.get(deviceId);
if (device == null) {
throw new IOException("USB device " + deviceId + " is not opened.");
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
index 2c1f115..5547771 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
@@ -16,35 +16,101 @@
package com.android.mtp;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
import android.test.InstrumentationTestCase;
+import java.io.IOException;
import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+@RealDeviceTest
public class MtpManagerTest extends InstrumentationTestCase {
- @RealDeviceTest
- public void testBasic() throws Exception {
- final UsbDevice usbDevice = findDevice();
- final MtpManager manager = new MtpManager(getContext());
- manager.openDevice(usbDevice.getDeviceId());
- waitForStorages(manager, usbDevice.getDeviceId());
- manager.closeDevice(usbDevice.getDeviceId());
+ private static final String ACTION_USB_PERMISSION =
+ "com.android.mtp.USB_PERMISSION";
+ private static final int TIMEOUT_MS = 1000;
+ UsbManager mUsbManager;
+ MtpManager mManager;
+ UsbDevice mUsbDevice;
+ int mRequest;
+
+ @Override
+ public void setUp() throws Exception {
+ mUsbManager = getContext().getSystemService(UsbManager.class);
+ mUsbDevice = findDevice();
+ mManager = new MtpManager(getContext());
+ mManager.openDevice(mUsbDevice.getDeviceId());
+ waitForStorages(mManager, mUsbDevice.getDeviceId());
+ }
+
+ @Override
+ public void tearDown() throws IOException {
+ mManager.closeDevice(mUsbDevice.getDeviceId());
+ }
+
+ public void testCancelEvent() throws Exception {
+ final CancellationSignal signal = new CancellationSignal();
+ final Thread thread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ mManager.readEvent(mUsbDevice.getDeviceId(), signal);
+ } catch (OperationCanceledException | IOException e) {
+ show(e.getMessage());
+ }
+ }
+ };
+ thread.start();
+ Thread.sleep(TIMEOUT_MS);
+ signal.cancel();
+ thread.join(TIMEOUT_MS);
+ }
+
+ private void requestPermission(UsbDevice device) throws InterruptedException {
+ if (mUsbManager.hasPermission(device)) {
+ return;
+ }
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ latch.countDown();
+ getInstrumentation().getTargetContext().unregisterReceiver(this);
+ }
+ };
+ getInstrumentation().getTargetContext().registerReceiver(
+ receiver, new IntentFilter(ACTION_USB_PERMISSION));
+ mUsbManager.requestPermission(device, PendingIntent.getBroadcast(
+ getInstrumentation().getTargetContext(),
+ 0 /* requstCode */,
+ new Intent(ACTION_USB_PERMISSION),
+ 0 /* flags */));
+ latch.await();
+ assertTrue(mUsbManager.hasPermission(device));
}
private UsbDevice findDevice() throws InterruptedException {
- final UsbManager usbManager = getContext().getSystemService(UsbManager.class);
while (true) {
- final HashMap<String,UsbDevice> devices = usbManager.getDeviceList();
+ final HashMap<String,UsbDevice> devices = mUsbManager.getDeviceList();
if (devices.size() == 0) {
show("Wait for devices.");
Thread.sleep(1000);
continue;
}
final UsbDevice device = devices.values().iterator().next();
- final UsbDeviceConnection connection = usbManager.openDevice(device);
+ requestPermission(device);
+ final UsbDeviceConnection connection = mUsbManager.openDevice(device);
+ if (connection == null) {
+ fail("Cannot open USB connection.");
+ }
for (int i = 0; i < device.getInterfaceCount(); i++) {
// Since the test runs real environment, we need to call claim interface with
// force = true to rob interfaces from other applications.
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java
index 9641ad7..22daaf29 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java
@@ -17,7 +17,10 @@
package com.android.mtp;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
@interface RealDeviceTest {}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
index 9824d28..a243375 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
@@ -45,8 +45,11 @@
}
private void show(String tag, Test test, Throwable t) {
+ String message = "";
+ if (t != null && t.getMessage() != null) {
+ message = t.getMessage();
+ }
TestResultActivity.show(
- getContext(),
- String.format("[%s] %s %s", tag, test.toString(), t != null ? t.getMessage() : ""));
+ getContext(), String.format("[%s] %s %s", tag, test.toString(), message));
}
}
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index 8e41a34..f88eca5 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -46,7 +46,7 @@
<string name="savetopdf_button" msgid="2976186791686924743">"PDF sifatida saqlash"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"Chop qilish tanlamalari yoyildi"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"Chop qilish tanlamalari yig‘ildi"</string>
- <string name="search" msgid="5421724265322228497">"Izlash"</string>
+ <string name="search" msgid="5421724265322228497">"Qidirish"</string>
<string name="all_printers_label" msgid="3178848870161526399">"Barcha printerlar"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"Xizmat qo‘shish"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Izlash oynasi ko‘rsatildi"</string>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 1cd2908..c324abd 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 ee296d9..d4e428e 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 @@
}
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 @@
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/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 93dc889..8fc7ad0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -206,7 +206,7 @@
android:resumeWhilePausing="true"
android:screenOrientation="behind"
android:resizeableActivity="true"
- android:theme="@style/config_recents_activity_theme">
+ android:theme="@style/RecentsTheme.Wallpaper">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
</intent-filter>
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 47e24e8..368f9f7 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 0000000..204af7e
--- /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 0000000..c877f06
--- /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/drawable/vector_drawable_place_dock_bottom.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml
new file mode 100644
index 0000000..f11b690
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<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="#FF0000FF"
+ android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,10.0l16.0,0.0l0.0,10.0L4.0,20.0L4.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml
new file mode 100644
index 0000000..79ade42
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<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="#FF0000FF"
+ android:pathData="M24.0,0.0L0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0zM14.0,4.0l0.0,16.0L4.0,20.0L4.0,4.0L14.0,4.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml
new file mode 100644
index 0000000..49c2a38
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<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="#FF0000FF"
+ android:pathData="M0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0L0.0,24.0zM10.0,20.0L10.0,4.0l10.0,0.0l0.0,16.0L10.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml
new file mode 100644
index 0000000..c3abec2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<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="#FF0000FF"
+ android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM20.0,14.0L4.0,14.0L4.0,4.0l16.0,0.0L20.0,14.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
index a718d4d..0a4c086 100644
--- a/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
+ android:id="@+id/place_dock_left"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_left" />
+ <Button
+ android:id="@+id/place_dock_right"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_right" />
+ <Button
android:id="@+id/place_left"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
index 250f53d..bf5207a 100644
--- a/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
+ android:id="@+id/place_dock_top"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_top" />
+ <Button
+ android:id="@+id/place_dock_bottom"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_bottom" />
+ <Button
android:id="@+id/place_top"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
index 26c9b1a..6e92afc 100644
--- a/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
+ android:id="@+id/place_dock_left"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_left" />
+ <Button
+ android:id="@+id/place_dock_right"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_right" />
+ <Button
android:id="@+id/place_left"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
index e180daa..faa5f4b 100644
--- a/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
+ android:id="@+id/place_dock_top"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_top" />
+ <Button
+ android:id="@+id/place_dock_bottom"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_bottom" />
+ <Button
android:id="@+id/place_top"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 1473f24..c7c7f1a 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 7ab6af9..f49f5bc 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 bd02650..aaf20cc 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 a0d7d2b..0efc82b 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 a14d0fc..6805779 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 e819d54..c0ffe2c 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 cc37162..c955f34 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>
@@ -406,7 +408,7 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> és el diàleg de volum"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Toca per restaurar l\'original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estàs utilitzant el perfil professional"</string>
- <string name="system_ui_tuner" msgid="708224127392452018">"Configurador de la IU del sistema"</string>
+ <string name="system_ui_tuner" msgid="708224127392452018">"Personalitzador d\'interfície d\'usuari"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostra el percentatge de la bateria inserit"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostra el percentatge del nivell de bateria dins de la icona de la barra d\'estat quan no s\'estigui carregant"</string>
<string name="quick_settings" msgid="10042998191725428">"Configuració ràpida"</string>
@@ -428,12 +430,12 @@
<string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"Zona Wi-Fi"</string>
<string name="accessibility_managed_profile" msgid="6613641363112584120">"Perfil professional"</string>
<string name="tuner_warning_title" msgid="7094689930793031682">"Diversió per a uns quants, però no per a tothom"</string>
- <string name="tuner_warning" msgid="8730648121973575701">"El Configurador de la IU del sistema presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
+ <string name="tuner_warning" msgid="8730648121973575701">"El Personalitzador d\'interfície d\'usuari presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
<string name="tuner_persistent_warning" msgid="8597333795565621795">"És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
<string name="got_it" msgid="2239653834387972602">"D\'acord"</string>
- <string name="tuner_toast" msgid="603429811084428439">"Enhorabona! El Configurador de la IU del sistema s\'ha afegit a Configuració."</string>
+ <string name="tuner_toast" msgid="603429811084428439">"Enhorabona! El Personalitzador d\'interfície d\'usuari s\'ha afegit a Configuració."</string>
<string name="remove_from_settings" msgid="8389591916603406378">"Treu de Configuració"</string>
- <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vols treure el Configurador de la UI del sistema de Configuració i deixar d\'utilitzar-ne totes les funcions?"</string>
+ <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vols suprimir el Personalitzador d\'interfície d\'usuari de Configuració i deixar d\'utilitzar-ne totes les funcions?"</string>
<string name="activity_not_found" msgid="348423244327799974">"L\'aplicació no està instal·lada al dispositiu"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostra els segons del rellotge"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra els segons del rellotge a la barra d\'estat. Això pot afectar la durada de la bateria."</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index a73947f..18fd994 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 6f49187..5a2dd33 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -183,12 +183,12 @@
<string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"Flytilstand er slået til."</string>
<string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Flytilstand er slået fra."</string>
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Flytilstand er slået til."</string>
- <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"\"Vil ikke forstyrres\" er slået til, kun prioritet."</string>
- <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"\"Vil ikke forstyrres\" er slået til, total stilhed."</string>
- <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"\"Vil ikke forstyrres\" er slået til, kun alarmer."</string>
- <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"\"Vil ikke forstyrres\" er slået fra."</string>
- <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"\"Vil ikke forstyrres\" er slået fra."</string>
- <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"\"Vil ikke forstyrres\" er slået til."</string>
+ <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"\"Forstyr ikke\" er slået til, kun prioritet."</string>
+ <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"\"Forstyr ikke\" er slået til, total stilhed."</string>
+ <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"\"Forstyr ikke\" er slået til, kun alarmer."</string>
+ <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"\"Forstyr ikke\" er slået fra."</string>
+ <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"\"Forstyr ikke\" er slået fra."</string>
+ <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"\"Forstyr ikke\" er slået til."</string>
<string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"Bluetooth er slået fra."</string>
<string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"Bluetooth er slået til."</string>
<string name="accessibility_quick_settings_bluetooth_connecting" msgid="6953242966685343855">"Opretter forbindelse til Bluetooth."</string>
@@ -236,7 +236,7 @@
<string name="dessert_case" msgid="1295161776223959221">"Dessertcase"</string>
<string name="start_dreams" msgid="7219575858348719790">"Dagdrøm"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
- <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Vil ikke forstyrres"</string>
+ <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Forstyr ikke"</string>
<string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Kun prioritet"</string>
<string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Kun Alarmer"</string>
<string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Total stilhed"</string>
@@ -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 a659077..a8143a4 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 bcfc458..6031d3b 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 fb781d4..bef7661 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 fb781d4..bef7661 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 fb781d4..bef7661 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-en/donottranslate.xml b/packages/SystemUI/res/values-en/donottranslate.xml
new file mode 100644
index 0000000..9f04e1f
--- /dev/null
+++ b/packages/SystemUI/res/values-en/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time_fast</item>
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time_slowly</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 10834a2..1590265 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 37c81f4..082e762 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 f4218a3..4add147 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 0e5532c..4f5c9f2 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 7593f46..6fa7e1c 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -275,7 +275,7 @@
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"برگردان رنگها"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"حالت تصحیح رنگ"</string>
<string name="quick_settings_more_settings" msgid="326112621462813682">"تنظیمات بیشتر"</string>
- <string name="quick_settings_done" msgid="3402999958839153376">"انجام شد"</string>
+ <string name="quick_settings_done" msgid="3402999958839153376">"تمام"</string>
<string name="quick_settings_connected" msgid="1722253542984847487">"متصل"</string>
<string name="quick_settings_connecting" msgid="47623027419264404">"در حال اتصال..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"اتصال به اینترنت با تلفن همراه"</string>
@@ -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 6c4a029..655cf31 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 07a2fc3..2f2c331 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 0aa45ae..9f34431 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 ec64f22..df90ef4 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 e873c95..42ee460 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 83ac46e..86f536d 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 b5d9931..043e7ad 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 79390a6..e2b0812 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 9f48b26..f8fc232 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 60024115..7dbcda9 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 e8e0530..b28b6fd 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 8ee5f07..54e6f56 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>
@@ -354,7 +356,7 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"Rimuovere l\'utente?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Tutte le app e i dati di questo utente verranno eliminati."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Rimuovi"</string>
- <string name="battery_saver_notification_title" msgid="237918726750955859">"Risparmio batteria attivo"</string>
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"Risparmio energetico attivo"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"Riduce le prestazioni e i dati in background"</string>
<string name="battery_saver_notification_action_text" msgid="109158658238110382">"Disattiva risparmio energetico"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenuti nascosti"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 44058a3..c959feb 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 a73f4c1..29476d8 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -327,10 +327,12 @@
<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>
- <string name="accessibility_multi_user_switch_quick_contact" msgid="3020367729287990475">"プロフィールを表示"</string>
+ <string name="accessibility_multi_user_switch_quick_contact" msgid="3020367729287990475">"プロファイルを表示"</string>
<string name="user_add_user" msgid="5110251524486079492">"ユーザーを追加"</string>
<string name="user_new_user_name" msgid="426540612051178753">"新しいユーザー"</string>
<string name="guest_nickname" msgid="8059989128963789678">"ゲスト"</string>
@@ -364,10 +366,10 @@
<string name="media_projection_action_text" msgid="8470872969457985954">"今すぐ開始"</string>
<string name="empty_shade_text" msgid="708135716272867002">"通知はありません"</string>
<string name="device_owned_footer" msgid="3802752663326030053">"端末が監視されている可能性があります"</string>
- <string name="profile_owned_footer" msgid="8021888108553696069">"プロフィールが監視されている可能性があります"</string>
+ <string name="profile_owned_footer" msgid="8021888108553696069">"プロファイルが監視されている可能性があります"</string>
<string name="vpn_footer" msgid="2388611096129106812">"ネットワークが監視されている可能性があります"</string>
<string name="monitoring_title_device_owned" msgid="7121079311903859610">"端末の監視"</string>
- <string name="monitoring_title_profile_owned" msgid="6790109874733501487">"プロフィールの監視"</string>
+ <string name="monitoring_title_profile_owned" msgid="6790109874733501487">"プロファイルの監視"</string>
<string name="monitoring_title" msgid="169206259253048106">"ネットワーク監視"</string>
<string name="disable_vpn" msgid="4435534311510272506">"VPNを無効にする"</string>
<string name="disconnect_vpn" msgid="1324915059568548655">"VPNを切断"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index f1035a8..d83c9e9 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 8b82e5f..5c066b3 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 b8db00f..4c469b0 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 316c04f..4215a91 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 d7f1543..8ee5946 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 65f91a2..ce0afd4 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-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index d608e25..f7e2344 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -20,10 +20,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
- <!-- Whether we're using the tablet-optimized recents interface (we use this
- value at runtime for some things) -->
- <integer name="status_bar_recents_bg_gradient_degrees">90</integer>
-
<!-- The maximum number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">2</integer>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index a9e7735..6ef1ada 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -19,28 +19,6 @@
<!-- thickness (width) of the navigation bar on phones that require it -->
<dimen name="navigation_bar_size">@*android:dimen/navigation_bar_width</dimen>
- <!-- Recent Applications parameters -->
- <!-- How far the thumbnail for a recent app appears from left edge -->
- <dimen name="status_bar_recents_thumbnail_left_margin">0dp</dimen>
- <!-- How far the thumbnail for a recent app appears from top edge -->
- <dimen name="status_bar_recents_thumbnail_top_margin">28dp</dimen>
- <!-- Padding for text descriptions -->
- <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
- <!-- Width of application label text -->
- <dimen name="status_bar_recents_app_label_width">156dip</dimen>
- <!-- Left margin of application label text -->
- <dimen name="status_bar_recents_app_label_left_margin">12dip</dimen>
- <!-- Margin between recents container and glow on the right -->
- <dimen name="status_bar_recents_right_glow_margin">0dip</dimen>
- <!-- Padding between recents items -->
- <dimen name="status_bar_recents_item_padding">2dip</dimen>
- <!-- Where to place the app icon over the thumbnail -->
- <dimen name="status_bar_recents_app_icon_left_margin">8dp</dimen>
- <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
-
- <!-- The side padding for the task stack as a percentage of the width. -->
- <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.26</item>
-
<!-- Standard notification width + gravity -->
<dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen>
<integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 50b4522..b45395e 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 9082778..194668b 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 5fb4bb0..2b3be58 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 8571b3f..912c30f 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 4b11962..c85ecc7 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 d1dbd6d..6b29aff 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 2b9d953..9d518a0 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 01f7edf..10dca3e 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 d0eaccf..adfd9f8 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 e582039..ecfb7f2 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 c3dc703..d997aff 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 79f3333..c6bab06 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 ef3d2f3..74e5fcb 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 8c2c5c4..e04fd71 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 3803b81..2a8e3ab 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 12609c7..8453e5b 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 3803b81..2a8e3ab 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 bb8f2c3..d85e791 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 9609f40..39d8617 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 9e78834..51bb047 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 2e41b29..2886e36 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 4dffcfa..e325ef1 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 20c3426..5bcf941 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 1dcde3a..522b2af 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 0abeb1e..d91335c 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 d000004..1cf4293 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-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 3a62ad9..f084bc2 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -16,12 +16,6 @@
*/
-->
<resources>
- <!-- Recent Applications parameters -->
- <dimen name="status_bar_recents_app_label_width">190dip</dimen>
-
- <!-- The side padding for the task stack as a percentage of the width. -->
- <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item>
-
<fraction name="keyguard_clock_y_fraction_max">37%</fraction>
<fraction name="keyguard_clock_y_fraction_min">20%</fraction>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 83477c0..4f6d209 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -32,9 +32,4 @@
<!-- Set to true to enable the user switcher on the keyguard. -->
<bool name="config_keyguardUserSwitcher">true</bool>
-
- <!-- Transposes the search bar layout in landscape. -->
- <bool name="recents_has_transposed_search_bar">true</bool>
- <!-- Transposes the nav bar in landscape (only used for purposes of layout). -->
- <bool name="recents_has_transposed_nav_bar">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 1af4ab1..49dbac2 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -32,10 +32,6 @@
<!-- The width of the view containing the menu/ime navigation bar icons -->
<dimen name="navigation_extra_key_width">48dip</dimen>
- <!-- Size of application thumbnail -->
- <dimen name="status_bar_recents_thumbnail_width">200dp</dimen>
- <dimen name="status_bar_recents_thumbnail_height">177dp</dimen>
-
<!-- Minimum fraction of the screen that should be taken up by the notification panel. -->
<item type="dimen" name="notification_panel_min_height_frac">40%</item>
@@ -43,12 +39,6 @@
<!-- On tablets this is just the close_handle_height -->
<dimen name="peek_height">@dimen/close_handle_height</dimen>
- <!-- The side padding for the task stack as a percentage of the width. -->
- <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.075</item>
-
- <!-- The height of the search bar space. -->
- <dimen name="recents_search_bar_space_height">72dp</dimen>
-
<!-- The fraction of the screen height where the clock on the Keyguard has its center. The
max value is used when no notifications are displaying, and the min value is when the
highest possible number of notifications are showing. -->
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index fbeadcd..64e2760e 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -22,27 +22,8 @@
<resources>
<integer name="status_bar_config_maxNotificationIcons">5</integer>
- <!-- Whether we're using the tablet-optimized recents interface (we use this
- value at runtime for some things) -->
- <bool name="config_recents_interface_for_tablets">true</bool>
-
- <!-- Whether recents thumbnails should stretch in both x and y to fill their
- ImageView -->
- <bool name="config_recents_thumbnail_image_fits_to_xy">true</bool>
-
- <!-- Min alpha % that recent items will fade to while being dismissed -->
- <integer name="config_recent_item_min_alpha">0</integer>
-
- <!-- Transposes the search bar layout in landscape -->
- <bool name="recents_transpose_search_layout_with_orientation">false</bool>
-
<!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
card. -->
<integer name="keyguard_max_notification_count">5</integer>
-
- <!-- Transposes the search bar layout in landscape. -->
- <bool name="recents_has_transposed_search_bar">false</bool>
- <!-- Transposes the nav bar in landscape (only used for purposes of layout). -->
- <bool name="recents_has_transposed_nav_bar">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index dd158c2..7cee381 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -29,42 +29,9 @@
<!-- Bottom margin (from display edge) for status bar panels -->
<dimen name="panel_float">56dp</dimen>
- <!-- Recent Applications parameters -->
- <!-- How far the thumbnail for a recent app appears from left edge -->
- <dimen name="status_bar_recents_thumbnail_left_margin">28dp</dimen>
- <!-- Upper width limit for application icon -->
- <dimen name="status_bar_recents_app_icon_max_width">64dp</dimen>
- <!-- Upper height limit for application icon -->
- <dimen name="status_bar_recents_app_icon_max_height">64dp</dimen>
-
- <!-- Size of application icon -->
- <dimen name="status_bar_recents_thumbnail_width">208dp</dimen>
- <dimen name="status_bar_recents_thumbnail_height">130dp</dimen>
-
- <!-- Width of recents panel -->
- <dimen name="status_bar_recents_width">600dp</dimen>
- <!-- Padding for text descriptions -->
- <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
- <!-- Size of application label text -->
- <dimen name="status_bar_recents_app_label_text_size">18dip</dimen>
- <!-- Size of application description text -->
- <dimen name="status_bar_recents_app_description_text_size">18dip</dimen>
- <!-- Width of application label text -->
- <dimen name="status_bar_recents_app_label_width">97dip</dimen>
- <!-- Left margin for application label -->
- <dimen name="status_bar_recents_app_label_left_margin">16dip</dimen>
- <!-- Size of fading edge for text -->
- <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
- <!-- Size of fading edge for scrolling -->
- <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
-
<!-- The radius of the rounded corners on a task view. -->
<dimen name="recents_task_view_rounded_corners_radius">3dp</dimen>
- <!-- Where to place the app icon over the thumbnail -->
- <dimen name="status_bar_recents_app_icon_left_margin">0dp</dimen>
- <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
-
<!-- The fraction of the screen height where the clock on the Keyguard has its center. The
max value is used when no notifications are displaying, and the min value is when the
highest possible number of notifications are showing. -->
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index 7b1ded3..670bccc 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 06762bd..bbd9979 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 52c7af7..2486519 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 66056d1..3d1243e 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 49edb57..4dadfbc 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 9dde801..d158dbe 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 a4ef16a..f8d8c71 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 04ec78b..0186809 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -82,7 +82,7 @@
<string name="accessibility_home" msgid="8217216074895377641">"Uyga"</string>
<string name="accessibility_menu" msgid="316839303324695949">"Menyu"</string>
<string name="accessibility_recent" msgid="5208608566793607626">"Umumiy nazar"</string>
- <string name="accessibility_search_light" msgid="1103867596330271848">"Izlash"</string>
+ <string name="accessibility_search_light" msgid="1103867596330271848">"Qidirish"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
<string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Ovozli yordam"</string>
@@ -303,7 +303,7 @@
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g>da to‘ladi"</string>
<string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"Quvvat olmayapti"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Tarmoq nazorat\nostida bo‘lishi mumkin"</string>
- <string name="description_target_search" msgid="3091587249776033139">"Izlash"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Qidirish"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun yuqoriga suring."</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun chapga suring."</string>
<string name="zen_priority_introduction" msgid="3070506961866919502">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq uyg‘otkich signallari, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan qo‘ng‘iroqlar bundan mustasno."</string>
@@ -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 460d57a..49b5d1d 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 4d70f24..93ed5ca 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 f7cac90..2196111 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 369172f3..fe4fe06 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 2a14a9f..2f068af 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/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index da21084..f40f0d9 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -24,11 +24,8 @@
<color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
<color name="system_bar_background_transparent">#00000000</color>
<color name="notification_panel_solid_background">#ff000000</color>
- <drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable>
- <color name="status_bar_recents_app_label_color">#ffffffff</color>
<drawable name="status_bar_notification_row_background_color">#ff090909</drawable>
<color name="notification_list_shadow_top">#80000000</color>
- <drawable name="recents_callout_line">#99ffffff</drawable>
<drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
<color name="batterymeter_frame_color">#4DFFFFFF</color><!-- 30% white -->
<color name="batterymeter_charge_color">#FFFFFFFF</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 8da1148..1d19589 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -20,15 +20,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
-
- <!-- Whether we're using the tablet-optimized recents interface (we use this
- value at runtime for some things) -->
- <bool name="config_recents_interface_for_tablets">false</bool>
-
- <!-- Whether recents thumbnails should stretch in both x and y to fill their
- ImageView -->
- <bool name="config_recents_thumbnail_image_fits_to_xy">false</bool>
-
<!-- Whether recents should use hardware layers for its taskviews. This flag can be enabled
for devices where the java drawing of round rects may be slow -->
<bool name="config_recents_use_hardware_layers">false</bool>
@@ -46,9 +37,6 @@
certain GPU's and thus can be turned off with only minimal visual impact. -->
<bool name="config_notifications_round_rect_clipping">true</bool>
- <!-- The theme to use for RecentsActivity. -->
- <item type="style" name="config_recents_activity_theme">@style/RecentsTheme.Wallpaper</item>
-
<!-- Control whether status bar should distinguish HSPA data icon form UMTS
data icon on devices -->
<bool name="config_hspa_data_distinguishable">false</bool>
@@ -92,10 +80,6 @@
<!-- The length of the vibration when the notification pops open. -->
<integer name="one_finger_pop_duration_ms">10</integer>
- <!-- Whether we're using the tablet-optimized recents interface (we use this
- value at runtime for some things) -->
- <integer name="status_bar_recents_bg_gradient_degrees">90</integer>
-
<!-- decay duration (from size_max -> size), in ms -->
<integer name="navigation_bar_deadzone_hold">333</integer>
<integer name="navigation_bar_deadzone_decay">333</integer>
@@ -205,12 +189,6 @@
<!-- The delay to enforce between each alt-tab key press. -->
<integer name="recents_alt_tab_key_delay">200</integer>
- <!-- Transposes the search bar layout in landscape. -->
- <bool name="recents_has_transposed_search_bar">true</bool>
-
- <!-- Transposes the nav bar in landscape (only used for purposes of layout). -->
- <bool name="recents_has_transposed_nav_bar">true</bool>
-
<!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
<integer name="recents_svelte_level">0</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 96a77256..abfd863 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -16,45 +16,6 @@
*/
-->
<resources>
- <!-- Recent Applications parameters -->
- <!-- Upper width limit for application icon -->
- <dimen name="status_bar_recents_app_icon_max_width">48dp</dimen>
- <!-- Upper height limit for application icon -->
- <dimen name="status_bar_recents_app_icon_max_height">48dp</dimen>
-
- <!-- Size of application thumbnail -->
- <dimen name="status_bar_recents_thumbnail_width">164dp</dimen>
- <dimen name="status_bar_recents_thumbnail_height">145dp</dimen>
- <dimen name="status_bar_recents_thumbnail_bg_padding">4dp</dimen>
-
- <!-- Size of application label text -->
- <dimen name="status_bar_recents_app_label_text_size">14dip</dimen>
- <!-- Size of application description text -->
- <dimen name="status_bar_recents_app_description_text_size">14dip</dimen>
- <!-- Size of fading edge for text -->
- <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
- <!-- Size of fading edge for scrolling -->
- <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
- <!-- Margin between recents container and glow on the right -->
- <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
- <!-- How far the thumbnail for a recent app appears from left edge -->
- <dimen name="status_bar_recents_thumbnail_left_margin">20dp</dimen>
- <!-- Padding for text descriptions -->
- <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
- <!-- Width of application label text -->
- <dimen name="status_bar_recents_app_label_width">88dip</dimen>
- <!-- Left margin of application label text -->
- <dimen name="status_bar_recents_app_label_left_margin">0dip</dimen>
- <!-- Padding between recents items -->
- <dimen name="status_bar_recents_item_padding">0dip</dimen>
- <!-- When recents first appears, how far the icon and label of the primary activity
- travel -->
- <dimen name="status_bar_recents_app_icon_translate_distance">35dip</dimen>
-
- <!-- Where to place the app icon over the thumbnail -->
- <dimen name="status_bar_recents_app_icon_left_margin">0dp</dimen>
- <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
-
<!-- Amount to offset bottom of notification peek window from top of status bar. -->
<dimen name="peek_window_y_offset">-12dp</dimen>
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index 351a1fd..30ff704 100644
--- a/packages/SystemUI/res/values/donottranslate.xml
+++ b/packages/SystemUI/res/values/donottranslate.xml
@@ -20,4 +20,10 @@
<!-- Date format for display: should match the lockscreen in /policy. -->
<string name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</string>
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time</item>
+
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time</item>
+
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index db1e688..d567e97 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -793,6 +793,12 @@
<!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]-->
<string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+ <!-- Indication on the keyguard that is shown when the device is charging rapidly. Should match keyguard_plugged_in_charging_fast [CHAR LIMIT=40]-->
+ <string name="keyguard_indication_charging_time_fast">Charging rapidly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
+ <!-- Indication on the keyguard that is shown when the device is charging slowly. Should match keyguard_plugged_in_charging_slowly [CHAR LIMIT=40]-->
+ <string name="keyguard_indication_charging_time_slowly">Charging slowly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
<!-- Related to user switcher --><skip/>
<!-- Accessibility label for the button that opens the user switcher. -->
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 0000000..3eb1271
--- /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
old mode 100755
new mode 100644
index 95b58e5..6cb8da4
--- 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";
+public class BatteryMeterView extends ImageView implements BatteryController.BatteryStateChangeCallback {
- 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 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,326 +38,14 @@
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);
-
- 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 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);
- }
-
- @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();
- }
-
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- // TODO: Use this callback instead of own broadcast receiver.
- }
-
- @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;
- }
-
- private void updateShowPercent() {
- mShowPercent = 0 != Settings.System.getInt(getContext().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;
- 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);
- }
- }
+ setImageDrawable(mDrawable);
}
@Override
@@ -428,116 +53,37 @@
return false;
}
- private boolean mDemoMode;
- private BatteryTracker mDemoTracker = new BatteryTracker();
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mBatteryController.addStateChangedCallback(this);
+ mDrawable.startListening();
+ }
@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();
- }
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mBatteryController.removeStateChangedCallback(this);
+ mDrawable.stopListening();
}
- 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);
- }
- });
- }
- }
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ setContentDescription(
+ getContext().getString(R.string.accessibility_battery_level, level));
}
- private final class SettingObserver extends ContentObserver {
- public SettingObserver() {
- super(new Handler());
- }
+ @Override
+ public void onPowerSaveChanged() {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
- updateShowPercent();
- postInvalidate();
- }
}
+ public void setBatteryController(BatteryController mBatteryController) {
+ this.mBatteryController = mBatteryController;
+ mDrawable.setBatteryController(mBatteryController);
+ }
+
+ public void setDarkIntensity(float f) {
+ mDrawable.setDarkIntensity(f);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 33f6564..6207324 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -34,6 +34,8 @@
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
+import com.android.systemui.classifier.FalsingManager;
+
public class SwipeHelper implements Gefingerpoken {
static final String TAG = "com.android.systemui.SwipeHelper";
private static final boolean DEBUG = false;
@@ -67,6 +69,7 @@
private Handler mHandler;
private int mSwipeDirection;
private VelocityTracker mVelocityTracker;
+ private FalsingManager mFalsingManager;
private float mInitialTouchPos;
private boolean mDragging;
@@ -97,6 +100,7 @@
android.R.interpolator.fast_out_linear_in);
mFalsingThreshold = context.getResources().getDimensionPixelSize(
R.dimen.swipe_helper_falsing_threshold);
+ mFalsingManager = FalsingManager.getInstance(context);
}
public void setLongPressListener(LongPressListener listener) {
@@ -449,8 +453,13 @@
boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
(Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
(velocity > 0) == (getTranslation(mCurrAnimView) > 0);
- boolean falsingDetected = mCallback.isAntiFalsingNeeded()
- && !mTouchAboveFalsingThreshold;
+ boolean falsingDetected = mCallback.isAntiFalsingNeeded();
+
+ if (mFalsingManager.isClassiferEnabled()) {
+ falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
+ } else {
+ falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
+ }
boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
&& !falsingDetected && (childSwipedFastEnough || childSwipedFarEnough)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
similarity index 62%
rename from packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
rename to packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
index c74339b..a6ebc0b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
@@ -32,17 +32,26 @@
* previously calculated angle. Then it calculates the variance of the differences from a stroke.
* To the differences there is artificially added value 0.0 and the difference between the first
* angle and PI (angles are in radians). It helps with strokes which have few points and punishes
- * more strokes which are not smooth. This classifier also tries to split the stroke into two parts
- * int the place in which the biggest angle is. It calculates the angle variance of the two parts
- * and sums them up. The reason the classifier is doing this, is because some human swipes at the
- * beginning go for a moment in one direction and then they rapidly change direction for the rest
- * of the stroke (like a tick). The final result is the minimum of angle variance of the whole
- * stroke and the sum of angle variances of the two parts split up.
+ * more strokes which are not smooth.
+ *
+ * This classifier also tries to split the stroke into two parts in the place in which the biggest
+ * angle is. It calculates the angle variance of the two parts and sums them up. The reason the
+ * classifier is doing this, is because some human swipes at the beginning go for a moment in one
+ * direction and then they rapidly change direction for the rest of the stroke (like a tick). The
+ * final result is the minimum of angle variance of the whole stroke and the sum of angle variances
+ * of the two parts split up. The classifier tries the tick option only if the first part is
+ * shorter than the second part.
+ *
+ * Additionally, the classifier classifies the angles as left angles (those angles which value is
+ * in [0.0, PI - ANGLE_DEVIATION) interval), straight angles
+ * ([PI - ANGLE_DEVIATION, PI + ANGLE_DEVIATION] interval) and right angles
+ * ((PI + ANGLE_DEVIATION, 2 * PI) interval) and then calculates the percentage of angles which are
+ * in the same direction (straight angles can be left angels or right angles)
*/
-public class AnglesVarianceClassifier extends StrokeClassifier {
+public class AnglesClassifier extends StrokeClassifier {
private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
- public AnglesVarianceClassifier(ClassifierData classifierData) {
+ public AnglesClassifier(ClassifierData classifierData) {
mClassifierData = classifierData;
}
@@ -66,10 +75,14 @@
@Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
- return AnglesVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance());
+ Data data = mStrokeMap.get(stroke);
+ return AnglesVarianceEvaluator.evaluate(data.getAnglesVariance())
+ + AnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
}
private static class Data {
+ private final float ANGLE_DEVIATION = (float) Math.PI / 20.0f;
+
private List<Point> mLastThreePoints = new ArrayList<>();
private float mFirstAngleVariance;
private float mPreviousAngle;
@@ -80,6 +93,12 @@
private float mSecondSum;
private float mCount;
private float mSecondCount;
+ private float mFirstLength;
+ private float mLength;
+ private float mAnglesCount;
+ private float mLeftAngles;
+ private float mRightAngles;
+ private float mStraightAngles;
public Data() {
mFirstAngleVariance = 0.0f;
@@ -88,6 +107,8 @@
mSumSquares = mSecondSumSquares = 0.0f;
mSum = mSecondSum = 0.0f;
mCount = mSecondCount = 1.0f;
+ mLength = mFirstLength = 0.0f;
+ mAnglesCount = mLeftAngles = mRightAngles = mStraightAngles = 0.0f;
}
public void addPoint(Point point) {
@@ -95,6 +116,9 @@
// Repetitions are being ignored so that proper angles are calculated.
if (mLastThreePoints.isEmpty()
|| !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(point)) {
+ if (!mLastThreePoints.isEmpty()) {
+ mLength += mLastThreePoints.get(mLastThreePoints.size() - 1).dist(point);
+ }
mLastThreePoints.add(point);
if (mLastThreePoints.size() == 4) {
mLastThreePoints.remove(0);
@@ -102,6 +126,15 @@
float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
mLastThreePoints.get(2));
+ mAnglesCount++;
+ if (angle < Math.PI - ANGLE_DEVIATION) {
+ mLeftAngles++;
+ } else if (angle <= Math.PI + ANGLE_DEVIATION) {
+ mStraightAngles++;
+ } else {
+ mRightAngles++;
+ }
+
float difference = angle - mPreviousAngle;
// If this is the biggest angle of the stroke so then we save the value of
@@ -109,6 +142,7 @@
// variance of the second part.
if (mBiggestAngle < angle) {
mBiggestAngle = angle;
+ mFirstLength = mLength;
mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount);
mSecondSumSquares = 0.0f;
mSecondSum = 0.0f;
@@ -132,9 +166,19 @@
}
public float getAnglesVariance() {
- return Math.min(getAnglesVariance(mSumSquares, mSum, mCount),
- mFirstAngleVariance + getAnglesVariance(mSecondSumSquares, mSecondSum,
- mSecondCount));
+ float anglesVariance = getAnglesVariance(mSumSquares, mSum, mCount);
+ if (mFirstLength < mLength / 2f) {
+ anglesVariance = Math.min(anglesVariance, mFirstAngleVariance
+ + getAnglesVariance(mSecondSumSquares, mSecondSum, mSecondCount));
+ }
+ return anglesVariance;
+ }
+
+ public float getAnglesPercentage() {
+ if (mAnglesCount == 0.0f) {
+ return 1.0f;
+ }
+ return (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount;
}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
new file mode 100644
index 0000000..a0ceb29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
@@ -0,0 +1,27 @@
+/*
+ * 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.classifier;
+
+public class AnglesPercentageEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 1.00) evaluation++;
+ if (value < 0.95) evaluation++;
+ if (value < 0.90) evaluation++;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
index 649279d..c83c74f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
@@ -28,12 +28,10 @@
public class ClassifierData {
private SparseArray<Stroke> mCurrentStrokes = new SparseArray<>();
private ArrayList<Stroke> mEndingStrokes = new ArrayList<>();
- private float mXdpi;
- private float mYdpi;
+ private final float mDpi;
- public ClassifierData(float xdpi, float ydpi) {
- mXdpi = xdpi;
- mYdpi = ydpi;
+ public ClassifierData(float dpi) {
+ mDpi = dpi;
}
public void update(MotionEvent event) {
@@ -46,7 +44,7 @@
for (int i = 0; i < event.getPointerCount(); i++) {
int id = event.getPointerId(i);
if (mCurrentStrokes.get(id) == null) {
- mCurrentStrokes.put(id, new Stroke(event.getEventTimeNano(), mXdpi, mYdpi));
+ mCurrentStrokes.put(id, new Stroke(event.getEventTimeNano(), mDpi));
}
mCurrentStrokes.get(id).addPoint(event.getX(i), event.getY(i),
event.getEventTimeNano());
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
new file mode 100644
index 0000000..299d0e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
@@ -0,0 +1,34 @@
+/*
+ * 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.classifier;
+
+/**
+ * A classifier which looks at the general direction of a stroke and evaluates it depending on
+ * the type of action that takes place.
+ */
+public class DirectionClassifier extends StrokeClassifier {
+ public DirectionClassifier(ClassifierData classifierData) {
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ Point firstPoint = stroke.getPoints().get(0);
+ Point lastPoint = stroke.getPoints().get(stroke.getPoints().size() - 1);
+ return DirectionEvaluator.evaluate(lastPoint.x - firstPoint.x, lastPoint.y - firstPoint.y,
+ type);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java
new file mode 100644
index 0000000..e20b1ca6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java
@@ -0,0 +1,54 @@
+/*
+ * 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.classifier;
+
+public class DirectionEvaluator {
+ public static float evaluate(float xDiff, float yDiff, int type) {
+ float falsingEvaluation = 5.5f;
+ boolean vertical = Math.abs(yDiff) >= Math.abs(xDiff);
+ switch (type) {
+ case Classifier.QUICK_SETTINGS:
+ case Classifier.NOTIFICATION_DRAG_DOWN:
+ if (!vertical || yDiff <= 0.0) {
+ return falsingEvaluation;
+ }
+ break;
+ case Classifier.NOTIFICATION_DISMISS:
+ if (vertical) {
+ return falsingEvaluation;
+ }
+ break;
+ case Classifier.UNLOCK:
+ if (!vertical || yDiff >= 0.0) {
+ return falsingEvaluation;
+ }
+ break;
+ case Classifier.LEFT_AFFORDANCE:
+ if (xDiff < 0.0 && yDiff > 0.0) {
+ return falsingEvaluation;
+ }
+ break;
+ case Classifier.RIGHT_AFFORDANCE:
+ if (xDiff > 0.0 && yDiff > 0.0) {
+ return falsingEvaluation;
+ }
+ default:
+ break;
+ }
+ return 0.0f;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index c68fff8..735a7c4 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -39,7 +39,11 @@
public class FalsingManager implements SensorEventListener {
private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
- private static final int[] SENSORS = new int[] {
+ private static final int[] CLASSIFIER_SENSORS = new int[] {
+ Sensor.TYPE_PROXIMITY,
+ };
+
+ private static final int[] COLLECTOR_SENSORS = new int[] {
Sensor.TYPE_ACCELEROMETER,
Sensor.TYPE_GYROSCOPE,
Sensor.TYPE_PROXIMITY,
@@ -113,7 +117,17 @@
private void onSessionStart() {
mBouncerOn = false;
mSessionActive = true;
- for (int sensorType : SENSORS) {
+
+ if (mHumanInteractionClassifier.isEnabled()) {
+ registerSensors(CLASSIFIER_SENSORS);
+ }
+ if (mDataCollector.isEnabled()) {
+ registerSensors(COLLECTOR_SENSORS);
+ }
+ }
+
+ private void registerSensors(int [] sensors) {
+ for (int sensorType : sensors) {
Sensor s = mSensorManager.getDefaultSensor(sensorType);
if (s != null) {
mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
@@ -121,6 +135,10 @@
}
}
+ public boolean isClassiferEnabled() {
+ return mHumanInteractionClassifier.isEnabled();
+ }
+
private boolean isEnabled() {
return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
index 6ef805c..a7a5694 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -23,8 +23,10 @@
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.DisplayMetrics;
import android.view.MotionEvent;
+import java.util.ArrayDeque;
import java.util.ArrayList;
/**
@@ -32,6 +34,7 @@
*/
public class HumanInteractionClassifier extends Classifier {
private static final String HIC_ENABLE = "HIC_enable";
+ private static final float FINGER_DISTANCE = 0.1f;
private static HumanInteractionClassifier sInstance = null;
private final Handler mHandler = new Handler();
@@ -39,11 +42,13 @@
private ArrayList<StrokeClassifier> mStrokeClassifiers = new ArrayList<>();
private ArrayList<GestureClassifier> mGestureClassifiers = new ArrayList<>();
+ private ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>();
private final int mStrokeClassifiersSize;
private final int mGestureClassifiersSize;
+ private final float mDpi;
private HistoryEvaluator mHistoryEvaluator;
- private boolean mEnableClassifier = false;
+ private boolean mEnableClassifier = true;
private int mCurrentType = Classifier.GENERIC;
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@@ -55,18 +60,24 @@
private HumanInteractionClassifier(Context context) {
mContext = context;
- mClassifierData = new ClassifierData(mContext.getResources().getDisplayMetrics().xdpi,
- mContext.getResources().getDisplayMetrics().ydpi);
+ DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+
+ // If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
+ // were to be used separately. Due negligible differences in xdpi and ydpi we can just
+ // take the average.
+ mDpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
+ mClassifierData = new ClassifierData(mDpi);
mHistoryEvaluator = new HistoryEvaluator();
- mStrokeClassifiers.add(new AnglesVarianceClassifier(mClassifierData));
+ mStrokeClassifiers.add(new AnglesClassifier(mClassifierData));
mStrokeClassifiers.add(new SpeedClassifier(mClassifierData));
mStrokeClassifiers.add(new DurationCountClassifier(mClassifierData));
mStrokeClassifiers.add(new EndPointRatioClassifier(mClassifierData));
mStrokeClassifiers.add(new EndPointLengthClassifier(mClassifierData));
mStrokeClassifiers.add(new AccelerationClassifier(mClassifierData));
- mStrokeClassifiers.add(new SpeedVarianceClassifier(mClassifierData));
+ mStrokeClassifiers.add(new SpeedAnglesClassifier(mClassifierData));
mStrokeClassifiers.add(new LengthCountClassifier(mClassifierData));
+ mStrokeClassifiers.add(new DirectionClassifier(mClassifierData));
mGestureClassifiers.add(new PointerCountClassifier(mClassifierData));
mGestureClassifiers.add(new ProximityClassifier(mClassifierData));
@@ -101,42 +112,73 @@
@Override
public void onTouchEvent(MotionEvent event) {
- if (mEnableClassifier) {
- mClassifierData.update(event);
+ if (!mEnableClassifier) {
+ return;
+ }
- for (int i = 0; i < mStrokeClassifiersSize; i++) {
- mStrokeClassifiers.get(i).onTouchEvent(event);
- }
+ // If the user is dragging down the notification, he might want to drag it down
+ // enough to see the content, read it for a while and then lift the finger to open
+ // the notification. This kind of motion scores very bad in the Classifier so the
+ // MotionEvents which are close to the current position of the finger are not
+ // sent to the classifiers until the finger moves far enough. When the finger if lifted
+ // up, the last MotionEvent which was far enough from the finger is set as the final
+ // MotionEvent and sent to the Classifiers.
+ if (mCurrentType == Classifier.NOTIFICATION_DRAG_DOWN) {
+ mBufferedEvents.add(MotionEvent.obtain(event));
+ Point pointEnd = new Point(event.getX() / mDpi, event.getY() / mDpi);
- for (int i = 0; i < mGestureClassifiersSize; i++) {
- mGestureClassifiers.get(i).onTouchEvent(event);
- }
-
- int size = mClassifierData.getEndingStrokes().size();
- for (int i = 0; i < size; i++) {
- Stroke stroke = mClassifierData.getEndingStrokes().get(i);
- float evaluation = 0.0f;
- for (int j = 0; j < mStrokeClassifiersSize; j++) {
- evaluation += mStrokeClassifiers.get(j).getFalseTouchEvaluation(
- mCurrentType, stroke);
- }
- mHistoryEvaluator.addStroke(evaluation);
+ while (pointEnd.dist(new Point(mBufferedEvents.getFirst().getX() / mDpi,
+ mBufferedEvents.getFirst().getY() / mDpi)) > FINGER_DISTANCE) {
+ addTouchEvent(mBufferedEvents.getFirst());
+ mBufferedEvents.remove();
}
int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- float evaluation = 0.0f;
- for (int i = 0; i < mGestureClassifiersSize; i++) {
- evaluation += mGestureClassifiers.get(i).getFalseTouchEvaluation(mCurrentType);
- }
- mHistoryEvaluator.addGesture(evaluation);
- setType(Classifier.GENERIC);
+ if (action == MotionEvent.ACTION_UP) {
+ mBufferedEvents.getFirst().setAction(MotionEvent.ACTION_UP);
+ addTouchEvent(mBufferedEvents.getFirst());
+ mBufferedEvents.clear();
}
-
- mClassifierData.cleanUp(event);
+ } else {
+ addTouchEvent(event);
}
}
+ private void addTouchEvent(MotionEvent event) {
+ mClassifierData.update(event);
+
+ for (int i = 0; i < mStrokeClassifiersSize; i++) {
+ mStrokeClassifiers.get(i).onTouchEvent(event);
+ }
+
+ for (int i = 0; i < mGestureClassifiersSize; i++) {
+ mGestureClassifiers.get(i).onTouchEvent(event);
+ }
+
+ int size = mClassifierData.getEndingStrokes().size();
+ for (int i = 0; i < size; i++) {
+ Stroke stroke = mClassifierData.getEndingStrokes().get(i);
+ float evaluation = 0.0f;
+ for (int j = 0; j < mStrokeClassifiersSize; j++) {
+ evaluation += mStrokeClassifiers.get(j).getFalseTouchEvaluation(
+ mCurrentType, stroke);
+ }
+ mHistoryEvaluator.addStroke(evaluation);
+ }
+
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ float evaluation = 0.0f;
+ for (int i = 0; i < mGestureClassifiersSize; i++) {
+ evaluation += mGestureClassifiers.get(i).getFalseTouchEvaluation(mCurrentType);
+ }
+ mHistoryEvaluator.addGesture(evaluation);
+ setType(Classifier.GENERIC);
+ }
+
+ mClassifierData.cleanUp(event);
+ }
+
@Override
public void onSensorChanged(SensorEvent event) {
for (int i = 0; i < mStrokeClassifiers.size(); i++) {
@@ -149,7 +191,10 @@
}
public boolean isFalseTouch() {
- return mHistoryEvaluator.getEvaluation() >= 5.0f;
+ if (mEnableClassifier) {
+ return mHistoryEvaluator.getEvaluation() >= 5.0f;
+ }
+ return false;
}
public boolean isEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
index 1ea467b..cedf467 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
@@ -17,8 +17,11 @@
package com.android.systemui.classifier;
/**
- * A classifier which looks at the ratio between the duration of the stroke and its number of
- * points.
+ * A classifier which looks at the ratio between the length of the stroke and its number of
+ * points. The number of points is subtracted by 2 because the UP event comes in with some delay
+ * and it should not influence the ratio and also strokes which are long and have a small number
+ * of points are punished more (these kind of strokes are usually bad ones and they tend to score
+ * well in other classifiers).
*/
public class LengthCountClassifier extends StrokeClassifier {
public LengthCountClassifier(ClassifierData classifierData) {
@@ -26,6 +29,7 @@
@Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
- return LengthCountEvaluator.evaluate(stroke.getTotalLength() / stroke.getCount());
+ return LengthCountEvaluator.evaluate(stroke.getTotalLength()
+ / Math.max(1.0f, stroke.getCount() - 2));
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
index 68f163d1..dac7a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
@@ -23,10 +23,11 @@
public class LengthCountEvaluator {
public static float evaluate(float value) {
float evaluation = 0.0f;
- if (value < 0.07) evaluation++;
+ if (value < 0.09) evaluation++;
if (value < 0.05) evaluation++;
if (value < 0.02) evaluation++;
if (value > 0.6) evaluation++;
+ if (value > 0.9) evaluation++;
if (value > 1.2) evaluation++;
return evaluation;
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java
rename to packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
index 9a30fe1..d544a3d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
@@ -28,14 +28,17 @@
* A classifier which for each point from a stroke, it creates a point on plane with coordinates
* (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE)
* and then it calculates the angle variance of these points like the class
- * {@link AnglesVarianceClassifier} (without splitting it into two parts). The classifier ignores
+ * {@link AnglesClassifier} (without splitting it into two parts). The classifier ignores
* the last point of a stroke because the UP event comes in with some delay and this ruins the
- * smoothness of this curve
+ * smoothness of this curve. Additionally, the classifier classifies calculates the percentage of
+ * angles which value is in [PI - ANGLE_DEVIATION, 2* PI) interval. The reason why the classifier
+ * does that is because the speed of a good stroke is most often increases, so most of these angels
+ * should be in this interval.
*/
-public class SpeedVarianceClassifier extends StrokeClassifier {
+public class SpeedAnglesClassifier extends StrokeClassifier {
private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
- public SpeedVarianceClassifier(ClassifierData classifierData) {
+ public SpeedAnglesClassifier(ClassifierData classifierData) {
mClassifierData = classifierData;
}
@@ -64,12 +67,15 @@
@Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
- return SpeedVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance());
+ Data data = mStrokeMap.get(stroke);
+ return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance())
+ + SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
}
private static class Data {
private final float DURATION_SCALE = 1e8f;
private final float LENGTH_SCALE = 1.0f;
+ private final float ANGLE_DEVIATION = (float) Math.PI / 10.0f;
private List<Point> mLastThreePoints = new ArrayList<>();
private Point mPreviousPoint;
@@ -78,6 +84,8 @@
private float mSum;
private float mCount;
private float mDist;
+ private float mAnglesCount;
+ private float mAcceleratingAngles;
public Data() {
mPreviousPoint = null;
@@ -86,6 +94,7 @@
mSum = 0.0f;
mCount = 1.0f;
mDist = 0.0f;
+ mAnglesCount = mAcceleratingAngles = 0.0f;
}
public void addPoint(Point point) {
@@ -108,6 +117,11 @@
float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
mLastThreePoints.get(2));
+ mAnglesCount++;
+ if (angle >= (float) Math.PI - ANGLE_DEVIATION) {
+ mAcceleratingAngles++;
+ }
+
float difference = angle - mPreviousAngle;
mSum += difference;
mSumSquares += difference * difference;
@@ -120,5 +134,12 @@
public float getAnglesVariance() {
return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
}
+
+ public float getAnglesPercentage() {
+ if (mAnglesCount == 0.0f) {
+ return 1.0f;
+ }
+ return (mAcceleratingAngles) / mAnglesCount;
+ }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
new file mode 100644
index 0000000..2a45fa3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
@@ -0,0 +1,27 @@
+/*
+ * 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.classifier;
+
+public class SpeedAnglesPercentageEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 1.00) evaluation++;
+ if (value < 0.95) evaluation++;
+ if (value < 0.90) evaluation++;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
index 49e6fb8..fb04d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
@@ -29,18 +29,16 @@
private long mStartTimeNano;
private long mEndTimeNano;
private float mLength;
- private float mXdpi;
- private float mYdpi;
+ private final float mDpi;
- public Stroke(long eventTimeNano, float xdpi, float ydpi) {
- mXdpi = xdpi;
- mYdpi = ydpi;
+ public Stroke(long eventTimeNano, float dpi) {
+ mDpi = dpi;
mStartTimeNano = mEndTimeNano = eventTimeNano;
}
public void addPoint(float x, float y, long eventTimeNano) {
mEndTimeNano = eventTimeNano;
- Point point = new Point(x / mXdpi, y / mYdpi, eventTimeNano - mStartTimeNano);
+ Point point = new Point(x / mDpi, y / mDpi, eventTimeNano - mStartTimeNano);
if (!mPoints.isEmpty()) {
mLength += mPoints.get(mPoints.size() - 1).dist(point);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 3b3593b..e562682 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.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 @@
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 08cdc1e..e575923 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 @@
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 84b05d0..f676ea3 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 @@
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 0000000..8f9655d
--- /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 0000000..3675f02
--- /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 0000000..3c5ab8d
--- /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 cbccaf8..a78351a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -38,7 +38,6 @@
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -75,6 +74,8 @@
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";
@@ -164,7 +165,6 @@
static RecentsTaskLoadPlan sInstanceLoadPlan;
static Recents sInstance;
- LayoutInflater mInflater;
SystemServicesProxy mSystemServicesProxy;
Handler mHandler;
TaskStackListenerImpl mTaskStackListener;
@@ -176,12 +176,14 @@
// Task launching
RecentsConfiguration mConfig;
+ Rect mSearchBarBounds = new Rect();
Rect mTaskStackBounds = new Rect();
- Rect mSystemInsets = new Rect();
+ Rect mLastTaskViewBounds = new Rect();
TaskViewTransform mTmpTransform = new TaskViewTransform();
int mStatusBarHeight;
int mNavBarHeight;
int mNavBarWidth;
+ int mTaskBarHeight;
// Header (for transition)
TaskViewHeader mHeaderBar;
@@ -229,11 +231,11 @@
if (sInstance == null) {
sInstance = this;
}
+ Resources res = mContext.getResources();
RecentsTaskLoader.initialize(mContext);
- mInflater = LayoutInflater.from(mContext);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
mSystemServicesProxy = new SystemServicesProxy(mContext);
mHandler = new Handler();
- mTaskStackBounds = new Rect();
mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
// Register the task stack listener
@@ -241,7 +243,7 @@
mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
// Only the owner has the callback to update the SysUI visibility flags, so all non-owner
- // instances of AlternateRecentsComponent needs to notify the owner when the visibility
+ // instances of RecentsComponent needs to notify the owner when the visibility
// changes.
if (mSystemServicesProxy.isForegroundUserSystem()) {
mProxyBroadcastReceiver = new RecentsOwnerEventProxyReceiver();
@@ -254,8 +256,16 @@
// Initialize some static datastructures
TaskStackViewLayoutAlgorithm.initializeCurve();
- // Load the header bar layout
- reloadHeaderBarLayout();
+ // Initialize the static configuration resources
+ mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy);
+ mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
+ mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
+ mTaskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
+ mDummyStackView = new TaskStackView(mContext, new TaskStack());
+ mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
+ null, false);
+ reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
// When we start, preload the data associated with the previous recent tasks.
// We can use a new plan since the caches will be the same.
@@ -273,6 +283,7 @@
@Override
public void onBootCompleted() {
mBootCompleted = true;
+ reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
}
/** Shows the Recents. */
@@ -293,7 +304,7 @@
mTriggeredFromAltTab = triggeredFromAltTab;
try {
- startRecentsActivity();
+ showRecentsActivity();
} catch (ActivityNotFoundException e) {
Console.logRawError("Failed to launch RecentAppsIntent", e);
}
@@ -488,54 +499,52 @@
void configurationChanged() {
// Don't reuse task stack views if the configuration changes
mCanReuseTaskStackViews = false;
- // Reload the header bar layout
- reloadHeaderBarLayout();
+ mConfig.updateOnConfigurationChange();
}
- /** Prepares the header bar layout. */
- void reloadHeaderBarLayout() {
- Resources res = mContext.getResources();
+ /**
+ * Prepares the header bar layout for the next transition, if the task view bounds has changed
+ * since the last call, it will attempt to re-measure and layout the header bar to the new size.
+ *
+ * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
+ * is not already bound (can be expensive)
+ */
+ void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) {
Rect windowRect = mSystemServicesProxy.getWindowRect();
- mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
- mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
- mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
- mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
- mConfig.updateOnConfigurationChange();
- Rect searchBarBounds = new Rect();
- // Try and pre-emptively bind the search widget on startup to ensure that we
- // have the right thumbnail bounds to animate to.
- // Note: We have to reload the widget id before we get the task stack bounds below
- if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
- mConfig.getSearchBarBounds(windowRect,
- mStatusBarHeight, searchBarBounds);
+ // Update the configuration for the current state
+ mConfig.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect());
+
+ if (tryAndBindSearchWidget) {
+ // Try and pre-emptively bind the search widget on startup to ensure that we
+ // have the right thumbnail bounds to animate to.
+ // Note: We have to reload the widget id before we get the task stack bounds below
+ if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
+ mConfig.getSearchBarBounds(windowRect,
+ mStatusBarHeight, mSearchBarBounds);
+ }
}
mConfig.getAvailableTaskStackBounds(windowRect,
- mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), searchBarBounds,
- mTaskStackBounds);
- if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
- mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
- } else {
- mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
- }
+ mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+ mSearchBarBounds, mTaskStackBounds);
+ int systemBarBottomInset = mConfig.hasTransposedNavBar ? 0 : mNavBarHeight;
- // Inflate the header bar layout so that we can rebind and draw it for the transition
- TaskStack stack = new TaskStack();
- mDummyStackView = new TaskStackView(mContext, stack);
+ // Rebind the header bar and draw it for the transition
TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
Rect taskStackBounds = new Rect(mTaskStackBounds);
- taskStackBounds.bottom -= mSystemInsets.bottom;
+ taskStackBounds.bottom -= systemBarBottomInset;
algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
- Rect taskViewSize = algo.getUntransformedTaskViewSize();
- int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
- synchronized (mHeaderBarLock) {
- mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
- false);
- mHeaderBar.measure(
- View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
- // TODO: may not be needed
- mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
+ Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
+ if (!taskViewBounds.equals(mLastTaskViewBounds)) {
+ mLastTaskViewBounds.set(taskViewBounds);
+
+ int taskViewWidth = taskViewBounds.width();
+ synchronized (mHeaderBarLock) {
+ mHeaderBar.measure(
+ View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
+ mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+ }
}
}
@@ -560,17 +569,17 @@
return;
} else {
// Otherwise, start the recents activity
- startRecentsActivity(topTask, isTopTaskHome.value);
+ showRecentsActivity(topTask, isTopTaskHome.value);
}
}
- /** Starts the recents activity if it is not already running */
- void startRecentsActivity() {
+ /** Shows the recents activity if it is not already running */
+ void showRecentsActivity() {
// Check if the top task is in the home stack, and start the recents activity
ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
MutableBoolean isTopTaskHome = new MutableBoolean(true);
if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
- startRecentsActivity(topTask, isTopTaskHome.value);
+ showRecentsActivity(topTask, isTopTaskHome.value);
}
}
@@ -732,30 +741,18 @@
return mTmpTransform;
}
- /** Starts the recents activity */
- void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
+ /** Shows the recents activity */
+ void showRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
+
+ // Update the header bar if necessary
+ reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
if (sInstanceLoadPlan == null) {
// Create a new load plan if onPreloadRecents() was never triggered
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
- // Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
- // For multi-stack we need to figure out where each of the tasks are going.
- if (mConfig.multiWindowEnabled) {
- loader.preloadTasks(sInstanceLoadPlan, true);
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
- mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
- TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
- mDummyStackView.computeStackVisibilityReport();
- ActivityOptions opts = getUnknownTransitionActivityOptions();
- startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
- false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
- return;
- }
-
if (!sInstanceLoadPlan.hasTasks()) {
loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
}
@@ -774,7 +771,7 @@
ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
mDummyStackView);
if (opts != null) {
- startAlternateRecentsActivity(topTask, opts, false /* fromHome */,
+ startRecentsActivity(topTask, opts, false /* fromHome */,
false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
} else {
// Fall through below to the non-thumbnail transition
@@ -794,12 +791,12 @@
boolean fromSearchHome = (homeActivityPackage != null) &&
homeActivityPackage.equals(searchWidgetPackage);
ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
- startAlternateRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
+ startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
false /* fromThumbnail */, stackVr);
} else {
// Otherwise we do the normal fade from an unknown source
ActivityOptions opts = getUnknownTransitionActivityOptions();
- startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
+ startRecentsActivity(topTask, opts, true /* fromHome */,
false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
}
}
@@ -807,19 +804,20 @@
}
/** Starts the recents activity */
- void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
- TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
+ void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
+ TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
// Update the configuration based on the launch options
- mConfig.launchedFromHome = fromSearchHome || fromHome;
- mConfig.launchedFromSearchHome = fromSearchHome;
- mConfig.launchedFromAppWithThumbnail = fromThumbnail;
- mConfig.launchedToTaskId = (topTask != null) ? topTask.id : -1;
- mConfig.launchedWithAltTab = mTriggeredFromAltTab;
- mConfig.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
- mConfig.launchedNumVisibleTasks = vr.numVisibleTasks;
- mConfig.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
- mConfig.launchedHasConfigurationChanged = false;
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ launchState.launchedFromHome = fromSearchHome || fromHome;
+ launchState.launchedFromSearchHome = fromSearchHome;
+ launchState.launchedFromAppWithThumbnail = fromThumbnail;
+ launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+ launchState.launchedWithAltTab = mTriggeredFromAltTab;
+ launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
+ launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
+ launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
+ launchState.launchedHasConfigurationChanged = false;
Intent intent = new Intent(sToggleRecentsAction);
intent.setClassName(sRecentsPackage, sRecentsActivity);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index c53e573..be99641 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -25,21 +25,18 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.UserHandle;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewStub;
-import android.widget.Toast;
-
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
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;
@@ -57,7 +54,10 @@
public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
+ public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
+
RecentsConfiguration mConfig;
+ RecentsPackageMonitor mPackageMonitor;
long mLastTabKeyEventTime;
// Top level views
@@ -182,18 +182,19 @@
}
// Start loading tasks according to the load plan
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
if (!plan.hasTasks()) {
- loader.preloadTasks(plan, mConfig.launchedFromHome);
+ loader.preloadTasks(plan, launchState.launchedFromHome);
}
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
- loadOpts.runningTaskId = mConfig.launchedToTaskId;
- loadOpts.numVisibleTasks = mConfig.launchedNumVisibleTasks;
- loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;
+ loadOpts.runningTaskId = launchState.launchedToTaskId;
+ loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+ loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, plan, loadOpts);
TaskStack stack = plan.getTaskStack();
- mConfig.launchedWithNoRecentTasks = !plan.hasTasks();
- if (!mConfig.launchedWithNoRecentTasks) {
+ launchState.launchedWithNoRecentTasks = !plan.hasTasks();
+ if (!launchState.launchedWithNoRecentTasks) {
mRecentsView.setTaskStack(stack);
}
@@ -204,19 +205,19 @@
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
ActivityOptions.makeCustomAnimation(this,
- mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
+ launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
R.anim.recents_to_launcher_enter,
- mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
+ launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
R.anim.recents_to_launcher_exit));
// Mark the task that is the launch target
int launchTaskIndexInStack = 0;
- if (mConfig.launchedToTaskId != -1) {
+ if (launchState.launchedToTaskId != -1) {
ArrayList<Task> tasks = stack.getTasks();
int taskCount = tasks.size();
for (int j = 0; j < taskCount; j++) {
Task t = tasks.get(j);
- if (t.key.id == mConfig.launchedToTaskId) {
+ if (t.key.id == launchState.launchedToTaskId) {
t.isLaunchTarget = true;
launchTaskIndexInStack = tasks.size() - j - 1;
break;
@@ -225,7 +226,7 @@
}
// Update the top level view's visibilities
- if (mConfig.launchedWithNoRecentTasks) {
+ if (launchState.launchedWithNoRecentTasks) {
if (mEmptyView == null) {
mEmptyView = mEmptyViewStub.inflate();
}
@@ -246,13 +247,13 @@
mScrimViews.prepareEnterRecentsAnimation();
// Keep track of whether we launched from the nav bar button or via alt-tab
- if (mConfig.launchedWithAltTab) {
+ if (launchState.launchedWithAltTab) {
MetricsLogger.count(this, "overview_trigger_alttab", 1);
} else {
MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
}
// Keep track of whether we launched from an app or from home
- if (mConfig.launchedFromAppWithThumbnail) {
+ if (launchState.launchedFromAppWithThumbnail) {
MetricsLogger.count(this, "overview_source_app", 1);
// If from an app, track the stack index of the app in the stack (for affiliated tasks)
MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
@@ -266,6 +267,7 @@
/** Dismisses recents if we are already visible and the intent is to toggle the recents view */
boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// If we currently have filtered stacks, then unfilter those first
@@ -274,7 +276,7 @@
// If we have a focused Task, launch that Task now
if (mRecentsView.launchFocusedTask()) return true;
// If we launched from Home, then return to Home
- if (mConfig.launchedFromHome) {
+ if (launchState.launchedFromHome) {
dismissRecentsToHomeRaw(true);
return true;
}
@@ -324,7 +326,9 @@
// initialized
RecentsTaskLoader.initialize(this);
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
- mConfig = RecentsConfiguration.reinitialize(this, ssp);
+ 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);
@@ -337,7 +341,7 @@
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
- mScrimViews = new SystemBarScrimViews(this, mConfig);
+ mScrimViews = new SystemBarScrimViews(this);
// Bind the search app widget when we first start up
mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
@@ -358,6 +362,7 @@
@Override
protected void onStart() {
super.onStart();
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SystemServicesProxy ssp = loader.getSystemServicesProxy();
@@ -371,7 +376,7 @@
registerReceiver(mServiceBroadcastReceiver, filter);
// Register any broadcast receivers for the task loader
- loader.registerReceivers(this, mRecentsView);
+ mPackageMonitor.register(this);
// Update the recent tasks
updateRecentsTasks();
@@ -379,12 +384,13 @@
// If this is a new instance from a configuration change, then we have to manually trigger
// the enter animation state, or if recents was relaunched by AM, without going through
// the normal mechanisms
- boolean wasLaunchedByAm = !mConfig.launchedFromHome && !mConfig.launchedFromAppWithThumbnail;
- if (mConfig.launchedHasConfigurationChanged || wasLaunchedByAm) {
+ boolean wasLaunchedByAm = !launchState.launchedFromHome &&
+ !launchState.launchedFromAppWithThumbnail;
+ if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
onEnterAnimationTriggered();
}
- if (!mConfig.launchedHasConfigurationChanged) {
+ if (!launchState.launchedHasConfigurationChanged) {
mRecentsView.disableLayersForOneFrame();
}
}
@@ -402,6 +408,7 @@
protected void onStop() {
super.onStop();
MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SystemServicesProxy ssp = loader.getSystemServicesProxy();
Recents.notifyVisibilityChanged(this, ssp, false);
@@ -413,17 +420,17 @@
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
// wait on the system to send a signal that was never queued.
- mConfig.launchedFromHome = false;
- mConfig.launchedFromSearchHome = false;
- mConfig.launchedFromAppWithThumbnail = false;
- mConfig.launchedToTaskId = -1;
- mConfig.launchedWithAltTab = false;
- mConfig.launchedHasConfigurationChanged = false;
+ launchState.launchedFromHome = false;
+ launchState.launchedFromSearchHome = false;
+ launchState.launchedFromAppWithThumbnail = false;
+ launchState.launchedToTaskId = -1;
+ launchState.launchedWithAltTab = false;
+ launchState.launchedHasConfigurationChanged = false;
}
@Override
@@ -475,8 +482,9 @@
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_TAB: {
+ int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
- mLastTabKeyEventTime) > mConfig.altTabKeyDelay;
+ mLastTabKeyEventTime) > altTabKeyDelay;
if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
// Focus the next task in the stack
final boolean backward = event.isShiftPressed();
@@ -514,9 +522,6 @@
@Override
public void onBackPressed() {
- // Test mode where back does not do anything
- if (mConfig.debugModeEnabled) return;
-
// Dismiss Recents to the focused Task or Home
dismissRecentsToFocusedTaskOrHome(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
new file mode 100644
index 0000000..e2e0e918
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+/**
+ * The launch state of the RecentsActivity.
+ *
+ * TODO: We will be refactoring this out RecentsConfiguration.
+ * Current Constraints:
+ * - needed in onStart() before onNewIntent()
+ * - needs to be reset when Recents is hidden
+ * - needs to be computed in Recents component
+ * - needs to be accessible by views
+ */
+public class RecentsActivityLaunchState {
+
+ public RecentsConfiguration mConfig;
+
+ public boolean launchedWithAltTab;
+ public boolean launchedWithNoRecentTasks;
+ public boolean launchedFromAppWithThumbnail;
+ public boolean launchedFromHome;
+ public boolean launchedFromSearchHome;
+ public boolean launchedReuseTaskStackViews;
+ public boolean launchedHasConfigurationChanged;
+ public int launchedToTaskId;
+ public int launchedNumVisibleTasks;
+ public int launchedNumVisibleThumbnails;
+
+ RecentsActivityLaunchState(RecentsConfiguration config) {
+ mConfig = config;
+ }
+
+ /** Called when the configuration has changed, and we want to reset any configuration specific
+ * members. */
+ public void updateOnConfigurationChange() {
+ // Reset this flag on configuration change to ensure that we recreate new task views
+ launchedReuseTaskStackViews = false;
+ // Set this flag to indicate that the configuration has changed since Recents last launched
+ launchedHasConfigurationChanged = true;
+ }
+
+ /** Returns whether the status bar scrim should be animated when shown for the first time. */
+ public boolean shouldAnimateStatusBarScrim() {
+ return launchedFromHome;
+ }
+
+ /** Returns whether the status bar scrim should be visible. */
+ public boolean hasStatusBarScrim() {
+ return !launchedWithNoRecentTasks;
+ }
+
+ /** Returns whether the nav bar scrim should be animated when shown for the first time. */
+ public boolean shouldAnimateNavBarScrim() {
+ return true;
+ }
+
+ /** Returns whether the nav bar scrim should be visible. */
+ public boolean hasNavBarScrim() {
+ // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
+ return !launchedWithNoRecentTasks && mConfig.hasTransposedNavBar;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index b41b5e7..52b9521 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -16,27 +16,29 @@
package com.android.systemui.recents;
-import android.app.ActivityManager;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.SystemServicesProxy;
-
-/** A static Recents configuration for the current context
- * NOTE: We should not hold any references to a Context from a static instance */
+/**
+ * Application resources that can be retrieved from the application context and are not specifically
+ * tied to the current activity.
+ */
public class RecentsConfiguration {
static RecentsConfiguration sInstance;
- static int sPrevConfigurationHashCode;
+
+ private static final int LARGE_SCREEN_MIN_DP = 600;
+ private static final int XLARGE_SCREEN_MIN_DP = 720;
+
+ // Variables that are used for global calculations
+ private static final float STACK_SIDE_PADDING_PHONES_PCT = 0.03333f;
+ private static final float STACK_SIZE_PADDING_TABLETS_PCT = 0.075f;
+ private static final float STACK_SIZE_PADDING_LARGE_TABLETS_PCT = 0.15f;
+ private static final int SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS = 64;
+ private static final int SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS = 72;
/** Levels of svelte in increasing severity/austerity. */
// No svelting.
@@ -50,123 +52,81 @@
// Disable all thumbnail loading.
public static final int SVELTE_DISABLE_LOADING = 3;
- /** Interpolators */
- public Interpolator fastOutSlowInInterpolator;
- public Interpolator fastOutLinearInInterpolator;
- public Interpolator linearOutSlowInInterpolator;
- public Interpolator quintOutInterpolator;
+ // Launch states
+ public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState(this);
- /** Filtering */
- public int filteringCurrentViewsAnimDuration;
- public int filteringNewViewsAnimDuration;
-
- /** Insets */
- public Rect systemInsets = new Rect();
- public Rect displayRect = new Rect();
-
- /** Layout */
- boolean isLandscape;
+ // TODO: Values determined by the current context, needs to be refactored into something that is
+ // agnostic of the activity context, but still calculable from the Recents component for
+ // the transition into recents
boolean hasTransposedSearchBar;
boolean hasTransposedNavBar;
-
- /** Loading */
- public int maxNumTasksToLoad;
-
- /** Search bar */
- public int searchBarSpaceHeightPx;
-
- /** Task stack */
- public int taskStackScrollDuration;
- public int taskStackMaxDim;
- public int taskStackTopPaddingPx;
- public int dismissAllButtonSizePx;
public float taskStackWidthPaddingPct;
- public float taskStackOverscrollPct;
- /** Transitions */
- public int transitionEnterFromAppDelay;
- public int transitionEnterFromHomeDelay;
-
- /** Task view animation and styles */
- public int taskViewEnterFromAppDuration;
- public int taskViewEnterFromHomeDuration;
- public int taskViewEnterFromHomeStaggerDelay;
- public int taskViewExitToAppDuration;
- public int taskViewExitToHomeDuration;
- public int taskViewRemoveAnimDuration;
- public int taskViewRemoveAnimTranslationXPx;
- public int taskViewTranslationZMinPx;
- public int taskViewTranslationZMaxPx;
- public int taskViewRoundedCornerRadiusPx;
- public int taskViewHighlightPx;
- public int taskViewAffiliateGroupEnterOffsetPx;
- public float taskViewThumbnailAlpha;
-
- /** Task bar colors */
- public int taskBarViewDefaultBackgroundColor;
- public int taskBarViewLightTextColor;
- public int taskBarViewDarkTextColor;
- public int taskBarViewHighlightColor;
- public float taskBarViewAffiliationColorMinAlpha;
-
- /** Task bar size & animations */
- public int taskBarHeight;
- public int taskBarDismissDozeDelaySeconds;
-
- /** Nav bar scrim */
- public int navBarScrimEnterDuration;
-
- /** Launch states */
- public boolean launchedWithAltTab;
- public boolean launchedWithNoRecentTasks;
- public boolean launchedFromAppWithThumbnail;
- public boolean launchedFromHome;
- public boolean launchedFromSearchHome;
- public boolean launchedReuseTaskStackViews;
- public boolean launchedHasConfigurationChanged;
- public int launchedToTaskId;
- public int launchedNumVisibleTasks;
- public int launchedNumVisibleThumbnails;
+ // Since the positions in Recents has to be calculated globally (before the RecentsActivity
+ // starts), we need to calculate some resource values ourselves, instead of relying on framework
+ // resources.
+ public final boolean isLargeScreen;
+ public final boolean isXLargeScreen;
+ public final int smallestWidth;
/** Misc **/
public boolean useHardwareLayers;
- public int altTabKeyDelay;
public boolean fakeShadows;
+ public int svelteLevel;
+ public int searchBarSpaceHeightPx;
/** Dev options and global settings */
public boolean multiWindowEnabled;
public boolean lockToAppEnabled;
- public boolean developerOptionsEnabled;
- public boolean debugModeEnabled;
- public int svelteLevel;
/** Private constructor */
- private RecentsConfiguration(Context context) {
- // Properties that don't have to be reloaded with each configuration change can be loaded
- // here.
+ private RecentsConfiguration(Context context, SystemServicesProxy ssp) {
+ // Load only resources that can not change after the first load either through developer
+ // settings or via multi window
+ Context appContext = context.getApplicationContext();
+ Resources res = appContext.getResources();
+ useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
+ fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
+ svelteLevel = res.getInteger(R.integer.recents_svelte_level);
- // Interpolators
- fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
- fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_linear_in);
- linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.linear_out_slow_in);
- quintOutInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.decelerate_quint);
+ float density = context.getResources().getDisplayMetrics().density;
+ smallestWidth = ssp.getDeviceSmallestWidth();
+ isLargeScreen = smallestWidth >= (int) (density * LARGE_SCREEN_MIN_DP);
+ isXLargeScreen = smallestWidth >= (int) (density * XLARGE_SCREEN_MIN_DP);
+ searchBarSpaceHeightPx = isLargeScreen ?
+ (int) (density * SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS) :
+ (int) (density * SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS);
+ if (isLargeScreen) {
+ taskStackWidthPaddingPct = STACK_SIZE_PADDING_TABLETS_PCT;
+ } else if (isXLargeScreen) {
+ taskStackWidthPaddingPct = STACK_SIZE_PADDING_LARGE_TABLETS_PCT;
+ } else {
+ taskStackWidthPaddingPct = STACK_SIDE_PADDING_PHONES_PCT;
+ }
+ }
+
+ /**
+ * Updates the configuration based on the current state of the system
+ */
+ void update(Context context, SystemServicesProxy ssp, Rect windowRect) {
+ // Only update resources that can change after the first load, either through developer
+ // settings or via multi window
+ lockToAppEnabled = ssp.getSystemSetting(context,
+ Settings.System.LOCK_TO_APP_ENABLED) != 0;
+ multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
+
+ // Recompute some values based on the given state, since we can not rely on the resource
+ // system to get certain values.
+ boolean isLandscape = windowRect.width() > windowRect.height();
+ hasTransposedNavBar = isLandscape && isLargeScreen && !isXLargeScreen;
+ hasTransposedSearchBar = isLandscape && isLargeScreen && !isXLargeScreen;
}
/** Updates the configuration to the current context */
- public static RecentsConfiguration reinitialize(Context context, SystemServicesProxy ssp) {
+ public static RecentsConfiguration initialize(Context context, SystemServicesProxy ssp) {
if (sInstance == null) {
- sInstance = new RecentsConfiguration(context);
+ sInstance = new RecentsConfiguration(context, ssp);
}
- int configHashCode = context.getResources().getConfiguration().hashCode();
- if (sPrevConfigurationHashCode != configHashCode) {
- sInstance.update(context);
- sPrevConfigurationHashCode = configHashCode;
- }
- sInstance.updateOnReinitialize(context, ssp);
return sInstance;
}
@@ -175,145 +135,18 @@
return sInstance;
}
- /** Updates the state, given the specified context */
- void update(Context context) {
- Resources res = context.getResources();
- DisplayMetrics dm = res.getDisplayMetrics();
-
- // Debug mode
- debugModeEnabled = Prefs.getBoolean(context, Prefs.Key.DEBUG_MODE_ENABLED,
- false /* defaultValue */);
- if (debugModeEnabled) {
- Console.Enabled = true;
- }
-
- // Layout
- isLandscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
- hasTransposedSearchBar = res.getBoolean(R.bool.recents_has_transposed_search_bar);
- hasTransposedNavBar = res.getBoolean(R.bool.recents_has_transposed_nav_bar);
-
- // Insets
- displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
-
- // Filtering
- filteringCurrentViewsAnimDuration =
- res.getInteger(R.integer.recents_filter_animate_current_views_duration);
- filteringNewViewsAnimDuration =
- res.getInteger(R.integer.recents_filter_animate_new_views_duration);
-
- // Loading
- maxNumTasksToLoad = ActivityManager.getMaxRecentTasksStatic();
-
- // Search Bar
- searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-
- // Task stack
- taskStackScrollDuration =
- res.getInteger(R.integer.recents_animate_task_stack_scroll_duration);
- taskStackWidthPaddingPct = res.getFloat(R.dimen.recents_stack_width_padding_percentage);
- taskStackOverscrollPct = res.getFloat(R.dimen.recents_stack_overscroll_percentage);
- taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
- taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
- dismissAllButtonSizePx = res.getDimensionPixelSize(R.dimen.recents_dismiss_all_button_size);
-
- // Transition
- transitionEnterFromAppDelay =
- res.getInteger(R.integer.recents_enter_from_app_transition_duration);
- transitionEnterFromHomeDelay =
- res.getInteger(R.integer.recents_enter_from_home_transition_duration);
-
- // Task view animation and styles
- taskViewEnterFromAppDuration =
- res.getInteger(R.integer.recents_task_enter_from_app_duration);
- taskViewEnterFromHomeDuration =
- res.getInteger(R.integer.recents_task_enter_from_home_duration);
- taskViewEnterFromHomeStaggerDelay =
- res.getInteger(R.integer.recents_task_enter_from_home_stagger_delay);
- taskViewExitToAppDuration =
- res.getInteger(R.integer.recents_task_exit_to_app_duration);
- taskViewExitToHomeDuration =
- res.getInteger(R.integer.recents_task_exit_to_home_duration);
- taskViewRemoveAnimDuration =
- res.getInteger(R.integer.recents_animate_task_view_remove_duration);
- taskViewRemoveAnimTranslationXPx =
- res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x);
- taskViewRoundedCornerRadiusPx =
- res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
- taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
- taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
- taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
- taskViewAffiliateGroupEnterOffsetPx =
- res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset);
- taskViewThumbnailAlpha = res.getFloat(R.dimen.recents_task_view_thumbnail_alpha);
-
- // Task bar colors
- taskBarViewDefaultBackgroundColor = context.getColor(
- R.color.recents_task_bar_default_background_color);
- taskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
- taskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
- taskBarViewHighlightColor = context.getColor(R.color.recents_task_bar_highlight_color);
- taskBarViewAffiliationColorMinAlpha = res.getFloat(
- R.dimen.recents_task_affiliation_color_min_alpha_percentage);
-
- // Task bar size & animations
- taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
- taskBarDismissDozeDelaySeconds =
- res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds);
-
- // Nav bar scrim
- navBarScrimEnterDuration =
- res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
-
- // Misc
- useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
- altTabKeyDelay = res.getInteger(R.integer.recents_alt_tab_key_delay);
- fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
- svelteLevel = res.getInteger(R.integer.recents_svelte_level);
- }
-
- /** Updates the system insets */
- public void updateSystemInsets(Rect insets) {
- systemInsets.set(insets);
- }
-
- /** Updates the states that need to be re-read whenever we re-initialize. */
- void updateOnReinitialize(Context context, SystemServicesProxy ssp) {
- // Check if the developer options are enabled
- developerOptionsEnabled = ssp.getGlobalSetting(context,
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
- lockToAppEnabled = ssp.getSystemSetting(context,
- Settings.System.LOCK_TO_APP_ENABLED) != 0;
- multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
+ /**
+ * Returns the activity launch state.
+ * TODO: This will be refactored out of RecentsConfiguration.
+ */
+ public RecentsActivityLaunchState getLaunchState() {
+ return mLaunchState;
}
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
- // Reset this flag on configuration change to ensure that we recreate new task views
- launchedReuseTaskStackViews = false;
- // Set this flag to indicate that the configuration has changed since Recents last launched
- launchedHasConfigurationChanged = true;
- }
-
- /** Returns whether the status bar scrim should be animated when shown for the first time. */
- public boolean shouldAnimateStatusBarScrim() {
- return launchedFromHome;
- }
-
- /** Returns whether the status bar scrim should be visible. */
- public boolean hasStatusBarScrim() {
- return !launchedWithNoRecentTasks;
- }
-
- /** Returns whether the nav bar scrim should be animated when shown for the first time. */
- public boolean shouldAnimateNavBarScrim() {
- return true;
- }
-
- /** Returns whether the nav bar scrim should be visible. */
- public boolean hasNavBarScrim() {
- // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
- return !launchedWithNoRecentTasks && (!hasTransposedNavBar || !isLandscape);
+ mLaunchState.updateOnConfigurationChange();
}
/**
@@ -322,14 +155,17 @@
*/
public void getAvailableTaskStackBounds(Rect windowBounds, int topInset,
int rightInset, Rect searchBarBounds, Rect taskStackBounds) {
- if (isLandscape && hasTransposedSearchBar) {
- // In landscape, the search bar appears on the left, but we overlay it on top
- taskStackBounds.set(windowBounds.left, windowBounds.top + topInset,
- windowBounds.right - rightInset, windowBounds.bottom);
+ if (hasTransposedNavBar) {
+ // In landscape phones, the search bar appears on the left, but we overlay it on top
+ int swInset = getInsetToSmallestWidth(windowBounds.right - rightInset -
+ windowBounds.left);
+ taskStackBounds.set(windowBounds.left + swInset, windowBounds.top + topInset,
+ windowBounds.right - swInset - rightInset, windowBounds.bottom);
} else {
// In portrait, the search bar appears on the top (which already has the inset)
- taskStackBounds.set(windowBounds.left, searchBarBounds.bottom,
- windowBounds.right, windowBounds.bottom);
+ int swInset = getInsetToSmallestWidth(windowBounds.right - windowBounds.left);
+ taskStackBounds.set(windowBounds.left + swInset, searchBarBounds.bottom,
+ windowBounds.right - swInset, windowBounds.bottom);
}
}
@@ -340,8 +176,8 @@
public void getSearchBarBounds(Rect windowBounds, int topInset, Rect searchBarSpaceBounds) {
// Return empty rects if search is not enabled
int searchBarSize = searchBarSpaceHeightPx;
- if (isLandscape && hasTransposedSearchBar) {
- // In landscape, the search bar appears on the left
+ if (hasTransposedSearchBar) {
+ // In landscape phones, the search bar appears on the left
searchBarSpaceBounds.set(windowBounds.left, windowBounds.top + topInset,
windowBounds.left + searchBarSize, windowBounds.bottom);
} else {
@@ -350,4 +186,14 @@
windowBounds.right, windowBounds.top + topInset + searchBarSize);
}
}
+
+ /**
+ * Constrain the width of the landscape stack to the smallest width of the device.
+ */
+ private int getInsetToSmallestWidth(int availableWidth) {
+ if (availableWidth > smallestWidth) {
+ return (availableWidth - smallestWidth) / 2;
+ }
+ return 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 703c7d2..59df293 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -16,17 +16,18 @@
package com.android.systemui.recents;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.FragmentManager;
-import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
+import android.widget.Toast;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
@@ -50,10 +51,18 @@
private static final int PLACE_BOTTOM_LEFT = 7;
private static final int PLACE_BOTTOM_RIGHT = 8;
private static final int PLACE_FULL = 9;
+ private static final int PLACE_DOCK_LEFT = 10;
+ private static final int PLACE_DOCK_RIGHT = 11;
+ private static final int PLACE_DOCK_TOP = 12;
+ private static final int PLACE_DOCK_BOTTOM = 13;
// The button resource ID combined with the arrangement command.
private static final int[][] BUTTON_DEFINITIONS =
- {{R.id.place_left, PLACE_LEFT},
+ {{R.id.place_dock_left, PLACE_DOCK_LEFT},
+ {R.id.place_dock_right, PLACE_DOCK_RIGHT},
+ {R.id.place_dock_top, PLACE_DOCK_TOP},
+ {R.id.place_dock_bottom, PLACE_DOCK_BOTTOM},
+ {R.id.place_left, PLACE_LEFT},
{R.id.place_right, PLACE_RIGHT},
{R.id.place_top, PLACE_TOP},
{R.id.place_bottom, PLACE_BOTTOM},
@@ -72,6 +81,12 @@
private Rect[] mBounds = {new Rect(), new Rect(), new Rect(), new Rect()};
private Task[] mTasks = {null, null, null, null};
+ /**
+ * Called by FragmentManager
+ */
+ public RecentsResizeTaskDialog() {
+ }
+
public RecentsResizeTaskDialog(FragmentManager mgr, RecentsActivity activity) {
mFragmentManager = mgr;
mRecentsActivity = activity;
@@ -82,13 +97,11 @@
void showResizeTaskDialog(Task mainTask, RecentsView rv) {
mTasks[0] = mainTask;
mRecentsView = rv;
-
- show(mFragmentManager, TAG);
+ showAllowingStateLoss(mFragmentManager, TAG);
}
/** Creates a new resize-task dialog. */
- private void createResizeTaskDialog(final Context context, LayoutInflater inflater,
- AlertDialog.Builder builder) {
+ private void createResizeTaskDialog(LayoutInflater inflater, AlertDialog.Builder builder) {
builder.setTitle(R.string.recents_caption_resize);
mResizeTaskDialogContent =
inflater.inflate(R.layout.recents_task_resize_dialog, null, false);
@@ -100,7 +113,17 @@
b.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v) {
- placeTasks(action);
+ switch (action) {
+ case PLACE_DOCK_LEFT:
+ case PLACE_DOCK_RIGHT:
+ case PLACE_DOCK_TOP:
+ case PLACE_DOCK_BOTTOM:
+ placeDockTasks(action);
+ break;
+ default:
+ placeTasks(action);
+ break;
+ }
}
});
}
@@ -109,7 +132,7 @@
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- dismiss();
+ dismissAllowingStateLoss();
}
});
@@ -118,7 +141,7 @@
/** Helper function to place window(s) on the display according to an arrangement request. */
private void placeTasks(int arrangement) {
- Rect rect = mSsp.getWindowRect();
+ Rect rect = mSsp.getDisplayRect();
for (int i = 0; i < mBounds.length; ++i) {
mBounds[i].set(rect);
if (i != 0) {
@@ -193,7 +216,7 @@
break;
case PLACE_FULL:
// Nothing to change.
- mBounds[0] = null;
+ mBounds[0] = new Rect();
break;
}
@@ -207,7 +230,7 @@
}
// Get rid of the dialog.
- dismiss();
+ dismissAllowingStateLoss();
mRecentsActivity.dismissRecentsToHomeWithoutTransitionAnimation();
// In debug mode, we force all task to be resizeable regardless of the
@@ -229,12 +252,44 @@
}
}
+ /**
+ * Helper function to place docked window(s) on the display according to an arrangement request.
+ */
+ private void placeDockTasks(int arrangement) {
+ int createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ switch (arrangement) {
+ case PLACE_DOCK_LEFT:
+ createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ break;
+ case PLACE_DOCK_TOP:
+ createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ break;
+ case PLACE_DOCK_RIGHT:
+ createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ break;
+ case PLACE_DOCK_BOTTOM:
+ createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ break;
+ }
+
+ // Dismiss the dialog before trying to launch the task
+ dismissAllowingStateLoss();
+
+ if (mTasks[0].key.stackId != ActivityManager.DOCKED_STACK_ID) {
+ int taskId = mTasks[0].key.id;
+ mSsp.setTaskResizeable(taskId);
+ mSsp.dockTask(taskId, createMode);
+ mRecentsView.launchTask(mTasks[0], null);
+ } else {
+ Toast.makeText(getContext(), "Already docked", Toast.LENGTH_SHORT);
+ }
+ }
+
@Override
public Dialog onCreateDialog(Bundle args) {
- final Context context = this.getActivity();
LayoutInflater inflater = getActivity().getLayoutInflater();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- createResizeTaskDialog(context, inflater, builder);
+ createResizeTaskDialog(inflater, builder);
return builder.create();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index cbf5c05..231843e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -39,7 +39,6 @@
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.systemui.R;
import java.util.ArrayList;
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 0000000..4addfa5
--- /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/PackagesChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
new file mode 100644
index 0000000..3b68574
--- /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 5790ca6..b6d25f5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -55,18 +55,18 @@
import android.provider.Settings;
import android.util.Log;
import android.util.MutableBoolean;
+import android.util.MutableFloat;
+import android.util.MutableInt;
import android.util.Pair;
-import android.util.SparseArray;
+import android.util.Size;
import android.view.Display;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-
import com.android.internal.app.AssistUtils;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsConfiguration;
import java.io.IOException;
import java.util.ArrayList;
@@ -282,6 +282,30 @@
}
}
+ /**
+ * Resizes the given task to the new bounds.
+ */
+ public void resizeTask(int taskId, Rect bounds) {
+ if (mIam == null) return;
+
+ try {
+ mIam.resizeTask(taskId, bounds, ActivityManager.RESIZE_MODE_FORCED);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /** Docks a task to the side of the screen. */
+ public void dockTask(int taskId, int createMode) {
+ if (mIam == null) return;
+
+ try {
+ mIam.moveTaskToDockedStack(taskId, createMode, true);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Returns the focused stack id. */
public int getFocusedStack() {
if (mIam == null) return -1;
@@ -637,7 +661,32 @@
}
/**
- * Returns the window rect.
+ * Returns the smallest width/height.
+ */
+ public int getDeviceSmallestWidth() {
+ if (mWm == null) return 0;
+
+ Point smallestSizeRange = new Point();
+ Point largestSizeRange = new Point();
+ mWm.getDefaultDisplay().getCurrentSizeRange(smallestSizeRange, largestSizeRange);
+ return smallestSizeRange.x;
+ }
+
+ /**
+ * Returns the display rect.
+ */
+ public Rect getDisplayRect() {
+ Rect displayRect = new Rect();
+ if (mWm == null) return displayRect;
+
+ Point p = new Point();
+ mWm.getDefaultDisplay().getRealSize(p);
+ displayRect.set(0, 0, p.x, p.y);
+ return displayRect;
+ }
+
+ /**
+ * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
*/
public Rect getWindowRect() {
Rect windowRect = new Rect();
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 e48e5f0..8f9a293 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.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 @@
* 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 @@
} 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 @@
@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 @@
// 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/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 649cb4d..6ef7253 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -23,7 +23,6 @@
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Log;
-import android.util.SparseArray;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -76,7 +75,7 @@
* An optimization to preload the raw list of tasks.
*/
public synchronized void preloadRawTasks(boolean isTopTaskHome) {
- mRawTasks = mSystemServicesProxy.getRecentTasks(mConfig.maxNumTasksToLoad,
+ mRawTasks = mSystemServicesProxy.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
Collections.reverse(mRawTasks);
@@ -125,7 +124,7 @@
activityLabel, mSystemServicesProxy, res);
Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
mSystemServicesProxy, res, infoHandle, false);
- int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig);
+ int activityColor = loader.getActivityPrimaryColor(t.taskDescription, res);
// Update the activity info cache
if (!hadCachedActivityInfo && infoHandle.info != null) {
@@ -153,7 +152,7 @@
// Initialize the stacks
mStack = new TaskStack();
mStack.setTasks(stackTasks);
- mStack.createAffiliatedGroupings(mConfig);
+ mStack.createAffiliatedGroupings(mContext);
}
/**
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 ad25c85..39bef81 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -27,7 +27,6 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
-
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
@@ -248,7 +247,7 @@
}
/* Recents task loader
- * NOTE: We should not hold any references to a Context from a static instance */
+ * NOTE: We should not hold any references to non-application Context from a static instance */
public class RecentsTaskLoader {
private static final String TAG = "RecentsTaskLoader";
@@ -263,8 +262,6 @@
TaskResourceLoadQueue mLoadQueue;
TaskResourceLoader mLoader;
- RecentsPackageMonitor mPackageMonitor;
-
int mMaxThumbnailCacheSize;
int mMaxIconCacheSize;
int mNumVisibleTasksLoaded;
@@ -294,7 +291,6 @@
// Initialize the proxy, cache and loaders
mSystemServicesProxy = new SystemServicesProxy(context);
- mPackageMonitor = new RecentsPackageMonitor();
mLoadQueue = new TaskResourceLoadQueue();
mApplicationIconCache = new DrawableLruCache(iconCacheSize);
mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
@@ -438,12 +434,11 @@
}
/** Returns the activity's primary color. */
- public int getActivityPrimaryColor(ActivityManager.TaskDescription td,
- RecentsConfiguration config) {
+ public int getActivityPrimaryColor(ActivityManager.TaskDescription td, Resources res) {
if (td != null && td.getPrimaryColor() != 0) {
return td.getPrimaryColor();
}
- return config.taskBarViewDefaultBackgroundColor;
+ return res.getColor(R.color.recents_task_bar_default_background_color);
}
/** Returns the size of the app icon cache. */
@@ -521,17 +516,6 @@
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/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 515e578..20d9203 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,11 +16,12 @@
package com.android.systemui.recents.model;
+import android.content.Context;
import android.graphics.Color;
import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.NamedCounter;
import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.R;
import java.util.ArrayList;
import java.util.Collections;
@@ -367,7 +368,7 @@
/**
* Temporary: This method will simulate affiliation groups by
*/
- public void createAffiliatedGroupings(RecentsConfiguration config) {
+ public void createAffiliatedGroupings(Context context) {
if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
// Sort all tasks by increasing firstActiveTime of the task
@@ -452,7 +453,8 @@
tasksMap.put(t.key, t);
}
// Update the task colors for each of the groups
- float minAlpha = config.taskBarViewAffiliationColorMinAlpha;
+ float minAlpha = context.getResources().getFloat(
+ R.dimen.recents_task_affiliation_color_min_alpha_percentage);
int taskGroupCount = mGroups.size();
for (int i = 0; i < taskGroupCount; i++) {
TaskGrouping group = mGroups.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
index 41adbed..682fd8f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
@@ -89,7 +89,8 @@
mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mCornerShadowPaint.setStyle(Paint.Style.FILL);
mCornerShadowPaint.setDither(true);
- mCornerRadius = config.taskViewRoundedCornerRadiusPx;
+ mCornerRadius = resources.getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius);
mCardBounds = new RectF();
mEdgeShadowPaint = new Paint(mCornerShadowPaint);
}
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 fab8b3a..68faccc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -38,8 +38,9 @@
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManagerGlobal;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
@@ -58,8 +59,7 @@
* 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";
@@ -83,6 +83,9 @@
TaskStackView mTaskStackView;
RecentsAppWidgetHostView mSearchBar;
RecentsViewCallbacks mCb;
+ Interpolator mFastOutSlowInInterpolator;
+
+ Rect mSystemInsets = new Rect();
public RecentsView(Context context) {
super(context);
@@ -100,6 +103,8 @@
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
}
/** Sets the callbacks */
@@ -109,7 +114,7 @@
/** Set/get the bsp root node */
public void setTaskStack(TaskStack stack) {
- if (mConfig.launchedReuseTaskStackViews) {
+ if (mConfig.getLaunchState().launchedReuseTaskStackViews) {
if (mTaskStackView != null) {
// If onRecentsHidden is not triggered, we need to the stack view again here
mTaskStackView.reset();
@@ -184,7 +189,8 @@
for (int j = 0; j < taskViewCount; j++) {
TaskView tv = taskViews.get(j);
if (tv.getTask() == task) {
- onTaskViewClicked(mTaskStackView, tv, stack, task, false, true, taskBounds);
+ onTaskViewClicked(mTaskStackView, tv, stack, task, false, taskBounds != null,
+ taskBounds);
return true;
}
}
@@ -277,7 +283,7 @@
// Get the search bar bounds and measure the search bar layout
Rect searchBarSpaceBounds = new Rect();
if (mSearchBar != null) {
- mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mConfig.systemInsets.top,
+ mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top,
searchBarSpaceBounds);
mSearchBar.measure(
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
@@ -285,8 +291,8 @@
}
Rect taskStackBounds = new Rect();
- mConfig.getAvailableTaskStackBounds(new Rect(0, 0, width, height), mConfig.systemInsets.top,
- mConfig.systemInsets.right, searchBarSpaceBounds, taskStackBounds);
+ mConfig.getAvailableTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+ mSystemInsets.right, searchBarSpaceBounds, taskStackBounds);
if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
mTaskStackView.setTaskStackBounds(taskStackBounds);
mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
@@ -305,7 +311,7 @@
Rect searchBarSpaceBounds = new Rect();
if (mSearchBar != null) {
mConfig.getSearchBarBounds(measuredRect,
- mConfig.systemInsets.top, searchBarSpaceBounds);
+ mSystemInsets.top, searchBarSpaceBounds);
mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
}
@@ -317,8 +323,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- // Update the configuration with the latest system insets and trigger a relayout
- mConfig.updateSystemInsets(insets.getSystemWindowInsets());
+ mSystemInsets.set(insets.getSystemWindowInsets());
requestLayout();
return insets.consumeSystemWindowInsets();
}
@@ -547,7 +552,7 @@
// outside the display rect (to ensure we don't animate from too far away)
sourceView = stackView;
offsetX = transform.rect.left;
- offsetY = mConfig.displayRect.height();
+ offsetY = getMeasuredHeight();
} else {
sourceView = tv.mThumbnailView;
}
@@ -578,22 +583,15 @@
}
postDrawHeaderThumbnailTransitionRunnable(stackView, tv, offsetX, offsetY, stackScroll,
animStartedListener);
- if (mConfig.multiWindowEnabled) {
- opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
- R.anim.recents_from_unknown_enter,
- R.anim.recents_from_unknown_exit,
- sourceView.getHandler(), animStartedListener);
- } else {
- opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
- Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(),
- offsetX, offsetY, transform.rect.width(), transform.rect.height(),
- sourceView.getHandler(), animStartedListener);
- }
+ opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
+ Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(),
+ offsetX, offsetY, transform.rect.width(), transform.rect.height(),
+ sourceView.getHandler(), animStartedListener);
} else {
opts = ActivityOptions.makeBasic();
}
if (boundsValid) {
- opts.setBounds(bounds);
+ opts.setBounds(bounds.isEmpty() ? null : bounds);
}
final ActivityOptions launchOpts = opts;
final boolean screenPinningRequested = (animStartedListener == null) && lockToTask;
@@ -699,11 +697,13 @@
public void onTaskStackFilterTriggered() {
// Hide the search bar
if (mSearchBar != null) {
+ int filterDuration = getResources().getInteger(
+ R.integer.recents_filter_animate_current_views_duration);
mSearchBar.animate()
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.filteringCurrentViewsAnimDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(filterDuration)
.withLayer()
.start();
}
@@ -713,11 +713,13 @@
public void onTaskStackUnfilterTriggered() {
// Show the search bar
if (mSearchBar != null) {
+ int filterDuration = getResources().getInteger(
+ R.integer.recents_filter_animate_new_views_duration);
mSearchBar.animate()
.alpha(1f)
.setStartDelay(0)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.filteringNewViewsAnimDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(filterDuration)
.withLayer()
.start();
}
@@ -729,14 +731,4 @@
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/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index 0428b48..e04699c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -22,13 +22,15 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
+import android.content.Context;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
-import com.android.systemui.recents.RecentsConfiguration;
/**
* This class facilitates swipe to dismiss. It defines an interface to be implemented by the
@@ -46,6 +48,7 @@
public static final int Y = 1;
private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
+ private Interpolator mLinearOutSlowInInterpolator;
private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
private int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms
@@ -74,13 +77,15 @@
public boolean mAllowSwipeTowardsEnd = true;
private boolean mRtl;
- public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
+ public SwipeHelper(Context context, int swipeDirection, Callback callback, float densityScale,
float pagingTouchSlop) {
mCallback = callback;
mSwipeDirection = swipeDirection;
mVelocityTracker = VelocityTracker.obtain();
mDensityScale = densityScale;
mPagingTouchSlop = pagingTouchSlop;
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
}
public void setDensityScale(float densityScale) {
@@ -265,7 +270,7 @@
ValueAnimator anim = createTranslationAnimation(view, 0);
int duration = SNAP_ANIM_LEN;
anim.setDuration(duration);
- anim.setInterpolator(RecentsConfiguration.getInstance().linearOutSlowInInterpolator);
+ anim.setInterpolator(mLinearOutSlowInInterpolator);
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 1086160..7ce50d8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -17,13 +17,19 @@
package com.android.systemui.recents.views;
import android.app.Activity;
+import android.content.Context;
import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.systemui.R;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
/** Manages the scrims for the various system bars. */
public class SystemBarScrimViews {
+ Context mContext;
RecentsConfiguration mConfig;
View mStatusBarScrimView;
@@ -34,10 +40,22 @@
boolean mHasStatusBarScrim;
boolean mShouldAnimateNavBarScrim;
- public SystemBarScrimViews(Activity activity, RecentsConfiguration config) {
- mConfig = config;
+ int mNavBarScrimEnterDuration;
+
+ Interpolator mFastOutSlowInInterpolator;
+ Interpolator mQuintOutInterpolator;
+
+ public SystemBarScrimViews(Activity activity) {
+ mContext = activity;
+ mConfig = RecentsConfiguration.getInstance();
mStatusBarScrimView = activity.findViewById(R.id.status_bar_scrim);
mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
+ mNavBarScrimEnterDuration = activity.getResources().getInteger(
+ R.integer.recents_nav_bar_scrim_enter_duration);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(activity,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ mQuintOutInterpolator = AnimationUtils.loadInterpolator(activity,
+ com.android.internal.R.interpolator.decelerate_quint);
}
/**
@@ -45,10 +63,11 @@
* the first draw.
*/
public void prepareEnterRecentsAnimation() {
- mHasNavBarScrim = mConfig.hasNavBarScrim();
- mShouldAnimateNavBarScrim = mConfig.shouldAnimateNavBarScrim();
- mHasStatusBarScrim = mConfig.hasStatusBarScrim();
- mShouldAnimateStatusBarScrim = mConfig.shouldAnimateStatusBarScrim();
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ mHasNavBarScrim = launchState.hasNavBarScrim();
+ mShouldAnimateNavBarScrim = launchState.shouldAnimateNavBarScrim();
+ mHasStatusBarScrim = launchState.hasStatusBarScrim();
+ mShouldAnimateStatusBarScrim = launchState.shouldAnimateStatusBarScrim();
mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
View.VISIBLE : View.INVISIBLE);
@@ -60,15 +79,21 @@
* Starts animating the scrim views when entering Recents.
*/
public void startEnterRecentsAnimation() {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ int transitionEnterFromAppDelay = mContext.getResources().getInteger(
+ R.integer.recents_enter_from_app_transition_duration);
+ int transitionEnterFromHomeDelay = mContext.getResources().getInteger(
+ R.integer.recents_enter_from_home_transition_duration);
+
if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
mStatusBarScrimView.animate()
.translationY(0)
- .setStartDelay(mConfig.launchedFromHome ?
- mConfig.transitionEnterFromHomeDelay :
- mConfig.transitionEnterFromAppDelay)
- .setDuration(mConfig.navBarScrimEnterDuration)
- .setInterpolator(mConfig.quintOutInterpolator)
+ .setStartDelay(launchState.launchedFromHome ?
+ transitionEnterFromHomeDelay :
+ transitionEnterFromAppDelay)
+ .setDuration(mNavBarScrimEnterDuration)
+ .setInterpolator(mQuintOutInterpolator)
.withStartAction(new Runnable() {
@Override
public void run() {
@@ -81,11 +106,11 @@
mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
mNavBarScrimView.animate()
.translationY(0)
- .setStartDelay(mConfig.launchedFromHome ?
- mConfig.transitionEnterFromHomeDelay :
- mConfig.transitionEnterFromAppDelay)
- .setDuration(mConfig.navBarScrimEnterDuration)
- .setInterpolator(mConfig.quintOutInterpolator)
+ .setStartDelay(launchState.launchedFromHome ?
+ transitionEnterFromHomeDelay :
+ transitionEnterFromAppDelay)
+ .setDuration(mNavBarScrimEnterDuration)
+ .setInterpolator(mQuintOutInterpolator)
.withStartAction(new Runnable() {
@Override
public void run() {
@@ -101,20 +126,22 @@
* going home).
*/
public void startExitRecentsAnimation() {
+ int taskViewExitToAppDuration = mContext.getResources().getInteger(
+ R.integer.recents_task_exit_to_app_duration);
if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
mStatusBarScrimView.animate()
.translationY(-mStatusBarScrimView.getMeasuredHeight())
.setStartDelay(0)
- .setDuration(mConfig.taskViewExitToAppDuration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
.start();
}
if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
mNavBarScrimView.animate()
.translationY(mNavBarScrimView.getMeasuredHeight())
.setStartDelay(0)
- .setDuration(mConfig.taskViewExitToAppDuration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
.start();
}
}
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 4db8b37..76b091c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -29,15 +29,17 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
-
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 @@
/* 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 @@
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 @@
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;
@@ -117,14 +119,17 @@
// Set the stack first
setStack(stack);
mConfig = RecentsConfiguration.getInstance();
- mViewPool = new ViewPool<TaskView, Task>(context, this);
+ mViewPool = new ViewPool<>(context, this);
mInflater = LayoutInflater.from(context);
- mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
- mFilterAlgorithm = new TaskStackViewFilterAlgorithm(mConfig, this, mViewPool);
- mStackScroller = new TaskStackViewScroller(context, mConfig, mLayoutAlgorithm);
+ mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(context, mConfig);
+ mFilterAlgorithm = new TaskStackViewFilterAlgorithm(this, mViewPool);
+ mStackScroller = new TaskStackViewScroller(context, mLayoutAlgorithm);
mStackScroller.setCallbacks(this);
- mTouchHandler = new TaskStackViewTouchHandler(context, this, mConfig, mStackScroller);
- mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
+ mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
+
+ int taskBarDismissDozeDelaySeconds = getResources().getInteger(
+ R.integer.recents_task_bar_dismiss_delay_seconds);
+ mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
@Override
public void run() {
// Show the task bar dismiss buttons
@@ -144,6 +149,18 @@
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
@@ -731,8 +748,9 @@
int height = MeasureSpec.getSize(heightMeasureSpec);
// Compute our stack/task rects
- computeRects(width, height, mTaskStackBounds, mConfig.launchedWithAltTab,
- mConfig.launchedFromHome);
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ computeRects(width, height, mTaskStackBounds, launchState.launchedWithAltTab,
+ launchState.launchedFromHome);
// If this is the first layout, then scroll to the front of the stack and synchronize the
// stack views immediately to load all the views
@@ -764,9 +782,11 @@
// Measure the dismiss button
if (mDismissAllButton != null) {
int taskRectWidth = mLayoutAlgorithm.mTaskRect.width();
+ int dismissAllButtonHeight = getResources().getDimensionPixelSize(
+ R.dimen.recents_dismiss_all_button_size);
mDismissAllButton.measure(
MeasureSpec.makeMeasureSpec(taskRectWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mConfig.dismissAllButtonSizePx, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(dismissAllButtonHeight, MeasureSpec.EXACTLY));
}
setMeasuredDimension(width, height);
@@ -847,20 +867,19 @@
// When Alt-Tabbing, focus the previous task (but leave the animation until we finish the
// enter animation).
- if (mConfig.launchedWithAltTab) {
- if (mConfig.launchedFromAppWithThumbnail) {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ if (launchState.launchedWithAltTab) {
+ if (launchState.launchedFromAppWithThumbnail) {
focusTask(Math.max(0, mStack.getTaskCount() - 2), false,
- mConfig.launchedHasConfigurationChanged);
+ launchState.launchedHasConfigurationChanged);
} else {
focusTask(Math.max(0, mStack.getTaskCount() - 1), false,
- mConfig.launchedHasConfigurationChanged);
+ launchState.launchedHasConfigurationChanged);
}
}
// Start dozing
- if (!mConfig.multiWindowEnabled) {
- mUIDozeTrigger.startDozing();
- }
+ mUIDozeTrigger.startDozing();
}
/** Requests this task stacks to start it's enter-recents animation */
@@ -924,7 +943,9 @@
// Start the focus animation when alt-tabbing
ArrayList<Task> tasks = mStack.getTasks();
- if (mConfig.launchedWithAltTab && !mConfig.launchedHasConfigurationChanged &&
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ if (launchState.launchedWithAltTab &&
+ !launchState.launchedHasConfigurationChanged &&
0 <= mFocusedTaskIndex && mFocusedTaskIndex < tasks.size()) {
TaskView tv = getChildViewForTask(tasks.get(mFocusedTaskIndex));
if (tv != null) {
@@ -1115,7 +1136,8 @@
}
// Update the min/max scroll and animate other task views into their new positions
- updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome);
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ updateMinMaxScroll(true, launchState.launchedWithAltTab, launchState.launchedFromHome);
// Offset the stack by as much as the anchor task would otherwise move back
if (pullStackForward) {
@@ -1133,7 +1155,8 @@
TaskView frontTv = getChildViewForTask(newFrontMostTask);
if (frontTv != null) {
frontTv.onTaskBound(newFrontMostTask);
- frontTv.fadeInActionButton(0, mConfig.taskViewEnterFromAppDuration);
+ frontTv.fadeInActionButton(0, getResources().getInteger(
+ R.integer.recents_task_enter_from_app_duration));
}
}
@@ -1282,7 +1305,7 @@
RecentsTaskLoader.getInstance().loadTaskData(task);
// If the doze trigger has already fired, then update the state for this task view
- if (mConfig.multiWindowEnabled || mUIDozeTrigger.hasTriggered()) {
+ if (mUIDozeTrigger.hasTriggered()) {
tv.setNoUserInteractionState();
}
@@ -1384,7 +1407,8 @@
if (nextTv != null) {
// Focus the next task, and only animate the visible state if we are launched
// from Alt-Tab
- nextTv.setFocusedTask(mConfig.launchedWithAltTab);
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ nextTv.setFocusedTask(launchState.launchedWithAltTab);
}
}
}
@@ -1420,13 +1444,12 @@
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/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
index 614ca53..e9f6a46 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
@@ -17,8 +17,8 @@
package com.android.systemui.recents.views;
import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
+import com.android.systemui.R;
import java.util.ArrayList;
import java.util.HashMap;
@@ -27,13 +27,10 @@
/* The layout logic for a TaskStackView */
public class TaskStackViewFilterAlgorithm {
- RecentsConfiguration mConfig;
TaskStackView mStackView;
ViewPool<TaskView, Task> mViewPool;
- public TaskStackViewFilterAlgorithm(RecentsConfiguration config, TaskStackView stackView,
- ViewPool<TaskView, Task> viewPool) {
- mConfig = config;
+ public TaskStackViewFilterAlgorithm(TaskStackView stackView, ViewPool<TaskView, Task> viewPool) {
mStackView = stackView;
mViewPool = viewPool;
}
@@ -126,7 +123,8 @@
}
}
}
- return mConfig.filteringNewViewsAnimDuration;
+ return mStackView.getResources().getInteger(
+ R.integer.recents_filter_animate_new_views_duration);
}
/**
@@ -172,7 +170,8 @@
childViewTransformsOut.put(tv, toTransform);
offset++;
}
- return mConfig.filteringCurrentViewsAnimDuration;
+ return mStackView.getResources().getInteger(
+ R.integer.recents_filter_animate_current_views_duration);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index f6df881..7f4c0a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -16,11 +16,13 @@
package com.android.systemui.recents.views;
+import android.content.Context;
import android.graphics.Rect;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
+import com.android.systemui.R;
import java.util.ArrayList;
import java.util.HashMap;
@@ -48,6 +50,7 @@
}
}
+ Context mContext;
RecentsConfiguration mConfig;
// The various rects that define the stack view
@@ -71,7 +74,8 @@
static float[] xp;
static float[] px;
- public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) {
+ public TaskStackViewLayoutAlgorithm(Context context, RecentsConfiguration config) {
+ mContext = context;
mConfig = config;
// Precompute the path
@@ -87,7 +91,8 @@
mStackVisibleRect.bottom = mViewRect.bottom;
int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
- int heightPadding = mConfig.taskStackTopPaddingPx;
+ int heightPadding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_stack_top_padding);
mStackRect.inset(widthPadding, heightPadding);
// Compute the task rect
@@ -98,7 +103,8 @@
// Update the affiliation offsets
float visibleTaskPct = 0.5f;
- mWithinAffiliationOffset = mConfig.taskBarHeight;
+ mWithinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_bar_height);
mBetweenAffiliationOffset = (int) (visibleTaskPct * mTaskRect.height());
}
@@ -134,8 +140,10 @@
mStackRect.bottom));
float pDismissAllButtonOffset = 0f;
if (Constants.DebugFlags.App.EnableDismissAll) {
+ int dismissAllButtonHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_dismiss_all_button_size);
pDismissAllButtonOffset = pAtBottomOfStackRect -
- screenYToCurveProgress(mStackVisibleRect.bottom - mConfig.dismissAllButtonSizePx);
+ screenYToCurveProgress(mStackVisibleRect.bottom - dismissAllButtonHeight);
}
// Update the task offsets
@@ -177,6 +185,8 @@
// Walk backwards in the task stack and count the number of tasks and visible thumbnails
int taskHeight = mTaskRect.height();
+ int taskBarHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_bar_height);
int numVisibleTasks = 1;
int numVisibleThumbnails = 1;
float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP;
@@ -192,7 +202,7 @@
float scaleAtP = curveProgressToScale(progress);
int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
int screenY = curveProgressToScreenY(progress) + scaleYOffsetAtP;
- boolean hasVisibleThumbnail = (prevScreenY - screenY) > mConfig.taskBarHeight;
+ boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
if (hasVisibleThumbnail) {
numVisibleThumbnails++;
numVisibleTasks++;
@@ -251,8 +261,8 @@
}
float scale = curveProgressToScale(pBounded);
int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
- int minZ = mConfig.taskViewTranslationZMinPx;
- int maxZ = mConfig.taskViewTranslationZMaxPx;
+ int minZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_min);
+ int maxZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_max);
transformOut.scale = scale;
transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
scaleYOffset;
@@ -265,11 +275,9 @@
return transformOut;
}
- /** Returns the untransformed task view size. */
- public Rect getUntransformedTaskViewSize() {
- Rect tvSize = new Rect(mTaskRect);
- tvSize.offsetTo(0, 0);
- return tvSize;
+ /** Returns the untransformed task view bounds. */
+ public Rect getUntransformedTaskViewBounds() {
+ return new Rect(mTaskRect);
}
/** Returns the scroll to such task top = 1f; */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index fabc86d..f0ae87f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -21,9 +21,11 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.OverScroller;
-import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.R;
/* The scrolling logic for a TaskStackView */
public class TaskStackViewScroller {
@@ -31,7 +33,7 @@
public void onScrollChanged(float p);
}
- RecentsConfiguration mConfig;
+ Context mContext;
TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScrollerCallbacks mCb;
@@ -41,10 +43,14 @@
ObjectAnimator mScrollAnimator;
float mFinalAnimatedScroll;
- public TaskStackViewScroller(Context context, RecentsConfiguration config, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
- mConfig = config;
+ Interpolator mLinearOutSlowInInterpolator;
+
+ public TaskStackViewScroller(Context context, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
+ mContext = context;
mScroller = new OverScroller(context);
mLayoutAlgorithm = layoutAlgorithm;
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
setStackScroll(getStackScroll());
}
@@ -140,8 +146,9 @@
mFinalAnimatedScroll = newScroll;
mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
- mScrollAnimator.setDuration(mConfig.taskStackScrollDuration);
- mScrollAnimator.setInterpolator(mConfig.linearOutSlowInInterpolator);
+ mScrollAnimator.setDuration(mContext.getResources().getInteger(
+ R.integer.recents_animate_task_stack_scroll_duration));
+ mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 7d079d9..86eced8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -26,7 +26,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.R;
import java.util.List;
@@ -34,7 +34,7 @@
class TaskStackViewTouchHandler implements SwipeHelper.Callback {
static int INACTIVE_POINTER_ID = -1;
- RecentsConfiguration mConfig;
+ Context mContext;
TaskStackView mSv;
TaskStackViewScroller mScroller;
VelocityTracker mVelocityTracker;
@@ -62,7 +62,8 @@
boolean mInterceptedBySwipeHelper;
public TaskStackViewTouchHandler(Context context, TaskStackView sv,
- RecentsConfiguration config, TaskStackViewScroller scroller) {
+ TaskStackViewScroller scroller) {
+ mContext = context;
ViewConfiguration configuration = ViewConfiguration.get(context);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
@@ -71,10 +72,9 @@
mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
mSv = sv;
mScroller = scroller;
- mConfig = config;
float densityScale = context.getResources().getDisplayMetrics().density;
- mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
+ mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale, mPagingTouchSlop);
mSwipeHelper.setMinAlpha(1f);
}
@@ -268,7 +268,8 @@
if (Float.compare(overScrollAmount, 0f) != 0) {
// Bound the overscroll to a fixed amount, and inversely scale the y-movement
// relative to how close we are to the max overscroll
- float maxOverScroll = mConfig.taskStackOverscrollPct;
+ float maxOverScroll = mContext.getResources().getFloat(
+ R.dimen.recents_stack_overscroll_percentage);
deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
/ maxOverScroll));
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 373fe7b..bbbaccf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -20,16 +20,25 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.*;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityManager;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
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.RecentsConfiguration;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
@@ -75,6 +84,10 @@
View mActionButtonView;
TaskViewCallbacks mCb;
+ Interpolator mFastOutSlowInInterpolator;
+ Interpolator mFastOutLinearInInterpolator;
+ Interpolator mQuintOutInterpolator;
+
// Optimizations
ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -99,14 +112,22 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ Resources res = context.getResources();
mConfig = RecentsConfiguration.getInstance();
- mMaxDimScale = mConfig.taskStackMaxDim / 255f;
+ mMaxDimScale = res.getInteger(R.integer.recents_max_task_stack_view_dim) / 255f;
mClipViewInStack = true;
- mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
+ mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius));
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
+ mQuintOutInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.decelerate_quint);
setTaskProgress(getTaskProgress());
setDim(getDim());
if (mConfig.fakeShadows) {
- setBackground(new FakeShadowDrawable(context.getResources(), mConfig));
+ setBackground(new FakeShadowDrawable(res, mConfig));
}
setOutlineProvider(mViewBounds);
}
@@ -159,6 +180,7 @@
int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
+ int taskBarHeight = getResources().getDimensionPixelSize(R.dimen.recents_task_bar_height);
// Measure the content
mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
@@ -166,7 +188,7 @@
// Measure the bar view, and action button
mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(taskBarHeight, MeasureSpec.EXACTLY));
mActionButtonView.measure(
MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST));
@@ -186,7 +208,7 @@
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration,
ValueAnimator.AnimatorUpdateListener updateCallback) {
// Apply the transform
- toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
+ toTransform.applyToTaskView(this, duration, mFastOutSlowInInterpolator, false,
!mConfig.fakeShadows, updateCallback);
// Update the task progress
@@ -238,10 +260,11 @@
* first layout because the actual animation into recents may take a long time. */
void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
boolean occludesLaunchTarget, int offscreenY) {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
int initialDim = getDim();
- if (mConfig.launchedHasConfigurationChanged) {
+ if (launchState.launchedHasConfigurationChanged) {
// Just load the views as-is
- } else if (mConfig.launchedFromAppWithThumbnail) {
+ } else if (launchState.launchedFromAppWithThumbnail) {
if (isTaskViewLaunchTargetTask) {
// Set the dim to 0 so we can animate it in
initialDim = 0;
@@ -252,7 +275,7 @@
setTranslationY(offscreenY);
}
- } else if (mConfig.launchedFromHome) {
+ } else if (launchState.launchedFromHome) {
// Move the task view off screen (below) so we can animate it in
setTranslationY(offscreenY);
setTranslationZ(0);
@@ -267,45 +290,59 @@
/** Animates this task view as it enters recents */
void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ Resources res = mContext.getResources();
final TaskViewTransform transform = ctx.currentTaskTransform;
+ final int transitionEnterFromAppDelay = res.getInteger(
+ R.integer.recents_enter_from_app_transition_duration);
+ final int transitionEnterFromHomeDelay = res.getInteger(
+ R.integer.recents_enter_from_home_transition_duration);
+ final int taskViewEnterFromAppDuration = res.getInteger(
+ R.integer.recents_task_enter_from_app_duration);
+ final int taskViewEnterFromHomeDuration = res.getInteger(
+ R.integer.recents_task_enter_from_home_duration);
+ final int taskViewEnterFromHomeStaggerDelay = res.getInteger(
+ R.integer.recents_task_enter_from_home_stagger_delay);
+ final int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
+ R.dimen.recents_task_view_affiliate_group_enter_offset);
int startDelay = 0;
- if (mConfig.launchedFromAppWithThumbnail) {
+ if (launchState.launchedFromAppWithThumbnail) {
if (mTask.isLaunchTarget) {
// Animate the dim/overlay
if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) {
// Animate the thumbnail alpha before the dim animation (to prevent updating the
// hardware layer)
- mThumbnailView.startEnterRecentsAnimation(mConfig.transitionEnterFromAppDelay,
+ mThumbnailView.startEnterRecentsAnimation(transitionEnterFromAppDelay,
new Runnable() {
@Override
public void run() {
- animateDimToProgress(0, mConfig.taskViewEnterFromAppDuration,
+ animateDimToProgress(0, taskViewEnterFromAppDuration,
ctx.postAnimationTrigger.decrementOnAnimationEnd());
}
});
} else {
// Immediately start the dim animation
- animateDimToProgress(mConfig.transitionEnterFromAppDelay,
- mConfig.taskViewEnterFromAppDuration,
+ animateDimToProgress(transitionEnterFromAppDelay,
+ taskViewEnterFromAppDuration,
ctx.postAnimationTrigger.decrementOnAnimationEnd());
}
ctx.postAnimationTrigger.increment();
// Animate the action button in
- fadeInActionButton(mConfig.transitionEnterFromAppDelay,
- mConfig.taskViewEnterFromAppDuration);
+ fadeInActionButton(transitionEnterFromAppDelay,
+ taskViewEnterFromAppDuration);
} else {
// Animate the task up if it was occluding the launch target
if (ctx.currentTaskOccludesLaunchTarget) {
- setTranslationY(transform.translationY + mConfig.taskViewAffiliateGroupEnterOffsetPx);
+ setTranslationY(transform.translationY + taskViewAffiliateGroupEnterOffset);
setAlpha(0f);
animate().alpha(1f)
.translationY(transform.translationY)
- .setStartDelay(mConfig.transitionEnterFromAppDelay)
+ .setStartDelay(transitionEnterFromAppDelay)
.setUpdateListener(null)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.taskViewEnterFromHomeDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(taskViewEnterFromHomeDuration)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -317,13 +354,13 @@
ctx.postAnimationTrigger.increment();
}
}
- startDelay = mConfig.transitionEnterFromAppDelay;
+ startDelay = transitionEnterFromAppDelay;
- } else if (mConfig.launchedFromHome) {
+ } else if (launchState.launchedFromHome) {
// Animate the tasks up
int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
- int delay = mConfig.transitionEnterFromHomeDelay +
- frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay;
+ int delay = transitionEnterFromHomeDelay +
+ frontIndex * taskViewEnterFromHomeStaggerDelay;
setScaleX(transform.scale);
setScaleY(transform.scale);
@@ -334,9 +371,9 @@
.translationY(transform.translationY)
.setStartDelay(delay)
.setUpdateListener(ctx.updateListener)
- .setInterpolator(mConfig.quintOutInterpolator)
- .setDuration(mConfig.taskViewEnterFromHomeDuration +
- frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay)
+ .setInterpolator(mQuintOutInterpolator)
+ .setDuration(taskViewEnterFromHomeDuration +
+ frontIndex * taskViewEnterFromHomeStaggerDelay)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -373,12 +410,14 @@
/** Animates this task view as it leaves recents by pressing home. */
void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ int taskViewExitToHomeDuration = getResources().getInteger(
+ R.integer.recents_task_exit_to_home_duration);
animate()
.translationY(ctx.offscreenTranslationY)
.setStartDelay(0)
.setUpdateListener(null)
- .setInterpolator(mConfig.fastOutLinearInInterpolator)
- .setDuration(mConfig.taskViewExitToHomeDuration)
+ .setInterpolator(mFastOutLinearInInterpolator)
+ .setDuration(taskViewExitToHomeDuration)
.withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
.start();
ctx.postAnimationTrigger.increment();
@@ -392,6 +431,11 @@
/** Animates this task view as it exits recents */
void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean isLaunchingTask,
boolean occludesLaunchTarget, boolean lockToTask) {
+ final int taskViewExitToAppDuration = mContext.getResources().getInteger(
+ R.integer.recents_task_exit_to_app_duration);
+ final int taskViewAffiliateGroupEnterOffset = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_affiliate_group_enter_offset);
+
if (isLaunchingTask) {
// Animate the thumbnail alpha back into full opacity for the window animation out
mThumbnailView.startLaunchTaskAnimation(postAnimRunnable);
@@ -399,8 +443,8 @@
// Animate the dim
if (mDimAlpha > 0) {
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
- anim.setDuration(mConfig.taskViewExitToAppDuration);
- anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+ anim.setDuration(taskViewExitToAppDuration);
+ anim.setInterpolator(mFastOutLinearInInterpolator);
anim.start();
}
@@ -414,8 +458,8 @@
mActionButtonView.animate()
.alpha(0f)
.setStartDelay(0)
- .setDuration(mConfig.taskViewExitToAppDuration)
- .setInterpolator(mConfig.fastOutLinearInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
+ .setInterpolator(mFastOutLinearInInterpolator)
.start();
} else {
// Hide the dismiss button
@@ -424,11 +468,11 @@
// animate it away first
if (occludesLaunchTarget) {
animate().alpha(0f)
- .translationY(getTranslationY() + mConfig.taskViewAffiliateGroupEnterOffsetPx)
+ .translationY(getTranslationY() + taskViewAffiliateGroupEnterOffset)
.setStartDelay(0)
.setUpdateListener(null)
- .setInterpolator(mConfig.fastOutLinearInInterpolator)
- .setDuration(mConfig.taskViewExitToAppDuration)
+ .setInterpolator(mFastOutLinearInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
.start();
}
}
@@ -436,15 +480,20 @@
/** Animates the deletion of this task view */
void startDeleteTaskAnimation(final Runnable r, int delay) {
+ int taskViewRemoveAnimDuration = getResources().getInteger(
+ R.integer.recents_animate_task_view_remove_duration);
+ int taskViewRemoveAnimTranslationXPx = getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_remove_anim_translation_x);
+
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
- animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
+ animate().translationX(taskViewRemoveAnimTranslationXPx)
.alpha(0f)
.setStartDelay(delay)
.setUpdateListener(null)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.taskViewRemoveAnimDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(taskViewRemoveAnimDuration)
.withEndAction(new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 3e9410e..f68dd64 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -26,19 +26,21 @@
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -67,6 +69,8 @@
boolean mCurrentPrimaryColorIsDark;
int mCurrentPrimaryColor;
int mBackgroundColor;
+ int mCornerRadius;
+ int mHighlightHeight;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
RippleDrawable mBackground;
@@ -81,6 +85,9 @@
Paint mDimLayerPaint = new Paint();
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
+ Interpolator mFastOutSlowInInterpolator;
+ Interpolator mFastOutLinearInInterpolator;
+
boolean mLayersDisabled;
public TaskViewHeader(Context context) {
@@ -113,13 +120,21 @@
mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
mDismissContentDescription =
context.getString(R.string.accessibility_recents_item_will_be_dismissed);
+ mCornerRadius = getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius);
+ mHighlightHeight = getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_highlight);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
// Configure the highlight paint
if (sHighlightPaint == null) {
sHighlightPaint = new Paint();
sHighlightPaint.setStyle(Paint.Style.STROKE);
- sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx);
- sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor);
+ sHighlightPaint.setStrokeWidth(mHighlightHeight);
+ sHighlightPaint.setColor(context.getColor(R.color.recents_task_bar_highlight_color));
sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
sHighlightPaint.setAntiAlias(true);
}
@@ -154,8 +169,8 @@
@Override
protected void onDraw(Canvas canvas) {
// Draw the highlight at the top edge (but put the bottom edge just out of view)
- float offset = (float) Math.ceil(mConfig.taskViewHighlightPx / 2f);
- float radius = mConfig.taskViewRoundedCornerRadiusPx;
+ float offset = (float) Math.ceil(mHighlightHeight / 2f);
+ float radius = mCornerRadius;
int count = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
@@ -207,10 +222,15 @@
mBackgroundColorDrawable.setColor(t.colorPrimary);
mBackgroundColor = t.colorPrimary;
}
+
+ int taskBarViewLightTextColor = getResources().getColor(
+ R.color.recents_task_bar_light_text_color);
+ int taskBarViewDarkTextColor = getResources().getColor(
+ R.color.recents_task_bar_dark_text_color);
mCurrentPrimaryColor = t.colorPrimary;
mCurrentPrimaryColorIsDark = t.useLightOnPrimaryColor;
mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
- mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor);
+ taskBarViewLightTextColor : taskBarViewDarkTextColor);
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
mLightDismissDrawable : mDarkDismissDrawable);
mDismissButton.setContentDescription(String.format(mDismissContentDescription,
@@ -262,12 +282,14 @@
/** Animates this task bar dismiss button when launching a task. */
void startLaunchTaskDismissAnimation() {
if (mDismissButton.getVisibility() == View.VISIBLE) {
+ int taskViewExitToAppDuration = mContext.getResources().getInteger(
+ R.integer.recents_task_exit_to_app_duration);
mDismissButton.animate().cancel();
mDismissButton.animate()
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.taskViewExitToAppDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
.start();
}
}
@@ -280,8 +302,9 @@
mDismissButton.animate()
.alpha(1f)
.setStartDelay(0)
- .setInterpolator(mConfig.fastOutLinearInInterpolator)
- .setDuration(mConfig.taskViewEnterFromAppDuration)
+ .setInterpolator(mFastOutLinearInInterpolator)
+ .setDuration(getResources().getInteger(
+ R.integer.recents_task_enter_from_app_duration))
.start();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 117a7d3..6c83bee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -32,9 +32,11 @@
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
-import com.android.systemui.recents.RecentsConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
+import com.android.systemui.R;
/**
@@ -43,9 +45,8 @@
*/
public class TaskViewThumbnail extends View {
- RecentsConfiguration mConfig;
-
// Drawing
+ int mCornerRadius;
float mDimAlpha;
Matrix mScaleMatrix = new Matrix();
Paint mDrawPaint = new Paint();
@@ -54,6 +55,8 @@
BitmapShader mBitmapShader;
LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
+ Interpolator mFastOutSlowInInterpolator;
+
// Thumbnail alpha
float mThumbnailAlpha;
ValueAnimator mThumbnailAlphaAnimator;
@@ -89,15 +92,18 @@
public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mConfig = RecentsConfiguration.getInstance();
mDrawPaint.setColorFilter(mLightingColorFilter);
mDrawPaint.setFilterBitmap(true);
mDrawPaint.setAntiAlias(true);
+ mCornerRadius = getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
}
@Override
protected void onFinishInflate() {
- mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
+ mThumbnailAlpha = getResources().getFloat(R.dimen.recents_task_view_thumbnail_alpha);
updateThumbnailPaintFilter();
}
@@ -117,8 +123,8 @@
}
// Draw the thumbnail with the rounded corners
canvas.drawRoundRect(0, 0, getWidth(), getHeight(),
- mConfig.taskViewRoundedCornerRadiusPx,
- mConfig.taskViewRoundedCornerRadiusPx, mDrawPaint);
+ mCornerRadius,
+ mCornerRadius, mDrawPaint);
}
/** Sets the thumbnail to a given bitmap. */
@@ -215,8 +221,10 @@
startFadeAnimation(1f, 0, 150, null);
}
} else {
- if (Float.compare(getAlpha(), mConfig.taskViewThumbnailAlpha) != 0) {
- startFadeAnimation(mConfig.taskViewThumbnailAlpha, 0, 150, null);
+ float taskViewThumbnailAlpha = getResources().getFloat(
+ R.dimen.recents_task_view_thumbnail_alpha);
+ if (Float.compare(getAlpha(), taskViewThumbnailAlpha) != 0) {
+ startFadeAnimation(taskViewThumbnailAlpha, 0, 150, null);
}
}
}
@@ -229,20 +237,26 @@
if (isTaskViewLaunchTargetTask) {
mThumbnailAlpha = 1f;
} else {
- mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
+ mThumbnailAlpha = getResources().getFloat(
+ R.dimen.recents_task_view_thumbnail_alpha);
}
updateThumbnailPaintFilter();
}
/** Animates this task thumbnail as it enters Recents. */
void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
- startFadeAnimation(mConfig.taskViewThumbnailAlpha, delay,
- mConfig.taskViewEnterFromAppDuration, postAnimRunnable);
+ float taskViewThumbnailAlpha = getResources().getFloat(
+ R.dimen.recents_task_view_thumbnail_alpha);
+ startFadeAnimation(taskViewThumbnailAlpha, delay,
+ getResources().getInteger(R.integer.recents_task_enter_from_app_duration),
+ postAnimRunnable);
}
/** Animates this task thumbnail as it exits Recents. */
void startLaunchTaskAnimation(Runnable postAnimRunnable) {
- startFadeAnimation(1f, 0, mConfig.taskViewExitToAppDuration, postAnimRunnable);
+ int taskViewExitToAppDuration = mContext.getResources().getInteger(
+ R.integer.recents_task_exit_to_app_duration);
+ startFadeAnimation(1f, 0, taskViewExitToAppDuration, postAnimRunnable);
}
/** Starts a new thumbnail alpha animation. */
@@ -251,7 +265,7 @@
mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha);
mThumbnailAlphaAnimator.setStartDelay(delay);
mThumbnailAlphaAnimator.setDuration(duration);
- mThumbnailAlphaAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mThumbnailAlphaAnimator.setInterpolator(mFastOutSlowInInterpolator);
mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener);
if (postAnimRunnable != null) {
mThumbnailAlphaAnimator.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index b01a2a8..10d4a96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -112,7 +112,7 @@
public void appTransitionStarting(long startTime, long duration);
public void showAssistDisclosure();
public void startAssist(Bundle args);
- public void onCameraLaunchGestureDetected();
+ public void onCameraLaunchGestureDetected(int source);
}
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -306,10 +306,10 @@
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
synchronized (mList) {
mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
- mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE).sendToTarget();
+ mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE, source, 0).sendToTarget();
}
}
@@ -415,7 +415,7 @@
mCallbacks.startAssist((Bundle) msg.obj);
break;
case MSG_CAMERA_LAUNCH_GESTURE:
- mCallbacks.onCameraLaunchGestureDetected();
+ mCallbacks.onCameraLaunchGestureDetected(msg.arg1);
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index d912795..687f6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -130,7 +130,7 @@
}
return true;
case MotionEvent.ACTION_UP:
- if (mDraggedFarEnough && mDragDownCallback.onDraggedDown(mStartingChild,
+ if (!isFalseTouch() && mDragDownCallback.onDraggedDown(mStartingChild,
(int) (y - mInitialTouchY))) {
if (mStartingChild == null) {
mDragDownCallback.setEmptyDragAmount(0f);
@@ -148,6 +148,13 @@
return false;
}
+ private boolean isFalseTouch() {
+ if (mFalsingManager.isClassiferEnabled()) {
+ return mFalsingManager.isFalseTouch();
+ }
+ return !mDraggedFarEnough;
+ }
+
private void captureStartingChild(float x, float y) {
if (mStartingChild == null) {
mStartingChild = findView(x, y);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index ac4dee2..5e6fdd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -187,32 +187,41 @@
}
// Try fetching charging time from battery stats.
+ long chargingTimeRemaining = 0;
try {
- long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
- if (chargingTimeRemaining > 0) {
- String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
- mContext, chargingTimeRemaining);
- return mContext.getResources().getString(
- R.string.keyguard_indication_charging_time, chargingTimeFormatted);
- }
+ chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
+
} catch (RemoteException e) {
Log.e(TAG, "Error calling IBatteryStats: ", e);
}
+ final boolean hasChargingTime = chargingTimeRemaining > 0;
- // Fall back to simple charging label.
int chargingId;
switch (mChargingSpeed) {
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
- chargingId = R.string.keyguard_plugged_in_charging_fast;
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_fast_if_translated
+ : R.string.keyguard_plugged_in_charging_fast;
break;
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
- chargingId = R.string.keyguard_plugged_in_charging_slowly;
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_slowly_if_translated
+ : R.string.keyguard_plugged_in_charging_slowly;
break;
default:
- chargingId = R.string.keyguard_plugged_in;
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time
+ : R.string.keyguard_plugged_in;
break;
}
- return mContext.getResources().getString(chargingId);
+
+ if (hasChargingTime) {
+ String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ mContext, chargingTimeRemaining);
+ return mContext.getResources().getString(chargingId, chargingTimeFormatted);
+ } else {
+ return mContext.getResources().getString(chargingId);
+ }
}
KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 60ebfdf..41adeb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -28,6 +28,7 @@
import android.view.animation.Interpolator;
import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -62,6 +63,7 @@
private Interpolator mAppearInterpolator;
private Interpolator mDisappearInterpolator;
private Animator mSwipeAnimator;
+ private FalsingManager mFalsingManager;
private int mMinBackgroundRadius;
private boolean mMotionCancelled;
private int mTouchTargetSize;
@@ -109,6 +111,7 @@
android.R.interpolator.linear_out_slow_in);
mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.fast_out_linear_in);
+ mFalsingManager = FalsingManager.getInstance(mContext);
}
private void initIcons() {
@@ -322,7 +325,12 @@
float vel = getCurrentVelocity(lastX, lastY);
// We snap back if the current translation is not far enough
- boolean snapBack = isBelowFalsingThreshold();
+ boolean snapBack;
+ if (mFalsingManager.isFalseTouch()) {
+ snapBack = mFalsingManager.isFalseTouch();
+ } else {
+ snapBack = isBelowFalsingThreshold();
+ }
// or if the velocity is in the opposite direction.
boolean velIsInWrongDirection = vel * mTranslation < 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 012dc9c..14176a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -77,6 +77,13 @@
final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
+ public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance";
+ public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture";
+ public static final String CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = "power_double_tap";
+
+ public static final String EXTRA_CAMERA_LAUNCH_SOURCE
+ = "com.android.systemui.camera_launch_source";
+
private static final Intent SECURE_CAMERA_INTENT =
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
@@ -170,7 +177,7 @@
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
return true;
} else if (host == mCameraImageView) {
- launchCamera();
+ launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
return true;
} else if (host == mLeftAffordanceView) {
launchLeftAffordance();
@@ -349,7 +356,7 @@
@Override
public void onClick(View v) {
if (v == mCameraImageView) {
- launchCamera();
+ launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
} else if (v == mLeftAffordanceView) {
launchLeftAffordance();
} if (v == mLockIcon) {
@@ -417,8 +424,9 @@
}
}
- public void launchCamera() {
+ public void launchCamera(String source) {
final Intent intent = getCameraIntent();
+ intent.putExtra(EXTRA_CAMERA_LAUNCH_SOURCE, source);
boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
mContext, intent, KeyguardUpdateMonitor.getCurrentUser());
if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) {
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 e70d146..71267cd 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 @@
}
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 @@
@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 @@
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/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 980527b..08353cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -22,6 +22,7 @@
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
+import android.app.StatusBarManager;
import android.content.Context;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -204,6 +205,7 @@
private boolean mHeadsUpAnimatingAway;
private boolean mLaunchingAffordance;
private FalsingManager mFalsingManager;
+ private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@Override
@@ -480,6 +482,7 @@
mUnlockIconActive = false;
if (!mLaunchingAffordance) {
mAfforanceHelper.reset(false);
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
closeQs();
mStatusBar.dismissPopups();
@@ -692,7 +695,7 @@
}
private boolean flingExpandsQs(float vel) {
- if (isBelowFalsingThreshold()) {
+ if (isFalseTouch()) {
return false;
}
if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -702,8 +705,14 @@
}
}
- private boolean isBelowFalsingThreshold() {
- return !mQsTouchAboveFalsingThreshold && mStatusBarState == StatusBarState.KEYGUARD;
+ private boolean isFalseTouch() {
+ if (mStatusBarState != StatusBarState.KEYGUARD) {
+ return false;
+ }
+ if (mFalsingManager.isClassiferEnabled()) {
+ return mFalsingManager.isFalseTouch();
+ }
+ return !mQsTouchAboveFalsingThreshold;
}
private float getQsExpansionFraction() {
@@ -1428,7 +1437,7 @@
}
return;
}
- boolean belowFalsingThreshold = isBelowFalsingThreshold();
+ boolean belowFalsingThreshold = isFalseTouch();
if (belowFalsingThreshold) {
vel = 0;
}
@@ -1965,20 +1974,23 @@
mKeyguardBottomArea.launchLeftAffordance();
}
} else {
- EventLogTags.writeSysuiLockscreenGesture(
- EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp);
-
+ if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
+ mLastCameraLaunchSource)) {
+ EventLogTags.writeSysuiLockscreenGesture(
+ EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA,
+ lengthDp, velocityDp);
+ }
mFalsingManager.onCameraOn();
if (mFalsingManager.shouldEnforceBouncer()) {
mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
@Override
public void run() {
- mKeyguardBottomArea.launchCamera();
+ mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
}
}, null, true /* dismissShade */, false /* afterKeyguardGone */);
}
else {
- mKeyguardBottomArea.launchCamera();
+ mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
}
}
mStatusBar.startLaunchTransitionTimeout();
@@ -2419,7 +2431,17 @@
return !mDozing;
}
- public void launchCamera(boolean animate) {
+ public void launchCamera(boolean animate, int source) {
+ if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
+ } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
+ } else {
+
+ // Default.
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+ }
+
// If we are launching it when we are occluded already we don't want it to animate,
// nor setting these flags, since the occluded state doesn't change anymore, hence it's
// never reset.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 3feead8..bd16257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -610,8 +610,8 @@
if (!mStatusBar.isFalsingThresholdNeeded()) {
return false;
}
- if (mFalsingManager.isFalseTouch()) {
- return true;
+ if (mFalsingManager.isClassiferEnabled()) {
+ return mFalsingManager.isFalseTouch();
}
if (!mTouchAboveFalsingThreshold) {
return true;
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 151fa3c..37edc28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -492,6 +492,7 @@
private ExpandableNotificationRow mDraggedDownRow;
private boolean mLaunchCameraOnScreenTurningOn;
private boolean mLaunchCameraOnFinishedGoingToSleep;
+ private int mLastCameraLaunchSource;
private PowerManager.WakeLock mGestureWakeLock;
private Vibrator mVibrator;
@@ -881,7 +882,7 @@
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()) {
@@ -903,8 +904,8 @@
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);
@@ -3000,6 +3001,10 @@
dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone);
}
+ public void dismissKeyguard() {
+ mStatusBarKeyguardViewManager.dismiss();
+ }
+
private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
boolean afterKeyguardGone) {
if (mStatusBarKeyguardViewManager.isShowing()) {
@@ -3992,7 +3997,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- onCameraLaunchGestureDetected();
+ onCameraLaunchGestureDetected(mLastCameraLaunchSource);
}
});
}
@@ -4010,7 +4015,7 @@
mFalsingManager.onScreenTurningOn();
mNotificationPanel.onScreenTurningOn();
if (mLaunchCameraOnScreenTurningOn) {
- mNotificationPanel.launchCamera(false);
+ mNotificationPanel.launchCamera(false, mLastCameraLaunchSource);
mLaunchCameraOnScreenTurningOn = false;
}
}
@@ -4175,7 +4180,8 @@
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
+ mLastCameraLaunchSource = source;
if (mStartedGoingToSleep) {
mLaunchCameraOnFinishedGoingToSleep = true;
return;
@@ -4201,7 +4207,7 @@
mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
- mNotificationPanel.launchCamera(mDeviceInteractive /* animate */);
+ mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
// we will dismiss us too early since we are waiting on an activity to be drawn and
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 385c5d5..6ec9494 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 d1b69ab..5071df0 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.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 @@
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 @@
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 @@
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 d907b00..cec0c0a 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 @@
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 @@
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/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 0f9dd5c..e0823b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -182,10 +182,10 @@
@Override
public void onUserSwitched(int newUserId) {
mCurrentUserId = newUserId;
- if (mUserManager.getUserInfo(newUserId).isRestricted()) {
+ final UserInfo newUserInfo = mUserManager.getUserInfo(newUserId);
+ if (newUserInfo.isRestricted()) {
// VPN for a restricted profile is routed through its owner user
- // TODO: http://b/22950929
- mVpnUserId = UserHandle.USER_SYSTEM;
+ mVpnUserId = newUserInfo.restrictedProfileParentId;
} else {
mVpnUserId = mCurrentUserId;
}
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 a8d4f13..6931d1e 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 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 8165894..e00b890 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 static boolean isUserSwitcherAvailable(UserManager um) {
- return UserManager.supportsMultipleUsers() && um.isUserSwitcherEnabled();
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 2587b9f..bbe5dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,7 +171,7 @@
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
index 4387b33..04a51f0 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 @@
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 772f866..703ee661 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 @@
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 920f875..9a78b6c 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.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 50234b2..71559389 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.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 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 8c830e8..3759c91 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -611,7 +611,7 @@
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 @@
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 @@
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 @@
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 87a0b80..b52687a 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 @@
/**
* 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 @@
private boolean mInstalled;
+ private int mUserId;
+
private int mEnabledFeatures;
private TouchExplorer mTouchExplorer;
@@ -327,13 +329,14 @@
/* 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 @@
resetStreamState();
if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
- mAutoclickController = new AutoclickController(mContext);
+ mAutoclickController = new AutoclickController(mContext, mUserId);
addFirstEventHandler(mAutoclickController);
}
@@ -360,7 +363,7 @@
}
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 @@
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 ff2a2ee..749a080 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 @@
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 8989625..3283378 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -51,6 +51,8 @@
*
* 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 {
@@ -60,13 +62,15 @@
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
@@ -75,7 +79,7 @@
if (mClickScheduler == null) {
Handler handler = new Handler(mContext.getMainLooper());
mClickScheduler = new ClickScheduler(handler, DEFAULT_CLICK_DELAY_MS);
- mClickDelayObserver = new ClickDelayObserver(handler);
+ mClickDelayObserver = new ClickDelayObserver(mUserId, handler);
mClickDelayObserver.start(mContext.getContentResolver(), mClickScheduler);
}
@@ -168,9 +172,11 @@
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 @@
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 @@
@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);
+ DEFAULT_CLICK_DELAY_MS, 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 37276bd..8845bc0 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.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 @@
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 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 @@
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 @@
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 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 @@
mEvent = MotionEvent.obtain(event);
mRawEvent = MotionEvent.obtain(rawEvent);
mPolicyFlags = policyFlags;
- mCachedTimeMillis = SystemClock.uptimeMillis();
}
public void recycle() {
@@ -964,7 +944,6 @@
mRawEvent.recycle();
mRawEvent = null;
mPolicyFlags = 0;
- mCachedTimeMillis = 0;
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6190a5a..a7e6f11 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -754,6 +754,8 @@
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTING);
intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+ intentFilter.addAction(Intent.ACTION_USER_ADDED);
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
@@ -3525,6 +3527,26 @@
}
}
+ private void onUserAdded(int userId) {
+ synchronized(mVpns) {
+ final int vpnsSize = mVpns.size();
+ for (int i = 0; i < vpnsSize; i++) {
+ Vpn vpn = mVpns.valueAt(i);
+ vpn.onUserAdded(userId);
+ }
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ synchronized(mVpns) {
+ final int vpnsSize = mVpns.size();
+ for (int i = 0; i < vpnsSize; i++) {
+ Vpn vpn = mVpns.valueAt(i);
+ vpn.onUserRemoved(userId);
+ }
+ }
+ }
+
private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -3536,6 +3558,10 @@
onUserStart(userId);
} else if (Intent.ACTION_USER_STOPPING.equals(action)) {
onUserStop(userId);
+ } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+ onUserAdded(userId);
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ onUserRemoved(userId);
}
}
};
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 0c6eb40..ebcdf7c 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -31,6 +31,8 @@
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.hardware.display.DisplayManager;
@@ -111,7 +113,7 @@
private INetworkPolicyManager mNetworkPolicyManager;
private DisplayManager mDisplayManager;
private SensorManager mSensorManager;
- private Sensor mSigMotionSensor;
+ private Sensor mMotionSensor;
private LocationManager mLocationManager;
private LocationRequest mLocationRequest;
private PendingIntent mSensingAlarmIntent;
@@ -123,7 +125,6 @@
private boolean mForceIdle;
private boolean mScreenOn;
private boolean mCharging;
- private boolean mSigMotionActive;
private boolean mSensing;
private boolean mNotMoving;
private boolean mLocating;
@@ -268,13 +269,57 @@
}
};
- private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
- @Override public void onTrigger(TriggerEvent event) {
+ private final class MotionListener extends TriggerEventListener
+ implements SensorEventListener {
+
+ boolean active = false;
+
+ @Override
+ public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
- significantMotionLocked();
+ active = false;
+ motionLocked();
}
}
- };
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (DeviceIdleController.this) {
+ mSensorManager.unregisterListener(this, mMotionSensor);
+ active = false;
+ motionLocked();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ public boolean registerLocked() {
+ boolean success = false;
+ if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ success = mSensorManager.requestTriggerSensor(mMotionListener, mMotionSensor);
+ } else {
+ success = mSensorManager.registerListener(
+ mMotionListener, mMotionSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if (success) {
+ active = true;
+ } else {
+ Slog.e(TAG, "Unable to register for " + mMotionSensor);
+ }
+ return success;
+ }
+
+ public void unregisterLocked() {
+ if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ mSensorManager.cancelTriggerSensor(mMotionListener, mMotionSensor);
+ } else {
+ mSensorManager.unregisterListener(mMotionListener);
+ }
+ active = false;
+ }
+ }
+ private final MotionListener mMotionListener = new MotionListener();
private final LocationListener mGenericLocationListener = new LocationListener() {
@Override
@@ -349,7 +394,7 @@
* This is the time, after becoming inactive, at which we start looking at the
* motion sensor to determine if the device is being left alone. We don't do this
* immediately after going inactive just because we don't want to be continually running
- * the significant motion sensor whenever the screen is off.
+ * the motion sensor whenever the screen is off.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_INACTIVE_TIMEOUT
*/
@@ -392,7 +437,7 @@
/**
* This is the time, after the inactive timeout elapses, that we will wait looking
- * for significant motion until we truly consider the device to be idle.
+ * for motion until we truly consider the device to be idle.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
*/
@@ -886,18 +931,19 @@
int sigMotionSensorId = getContext().getResources().getInteger(
com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
if (sigMotionSensorId > 0) {
- mSigMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
+ mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
}
- if (mSigMotionSensor == null && getContext().getResources().getBoolean(
+ if (mMotionSensor == null && getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
- mSigMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_WRIST_TILT_GESTURE);
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_WRIST_TILT_GESTURE, true);
}
- if (mSigMotionSensor == null) {
+ if (mMotionSensor == null) {
// As a last ditch, fall back to SMD.
- mSigMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_SIGNIFICANT_MOTION);
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_SIGNIFICANT_MOTION, true);
}
+
if (getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
mLocationManager = (LocationManager) getContext().getSystemService(
@@ -1242,7 +1288,7 @@
cancelAlarmLocked();
cancelSensingAlarmLocked();
cancelLocatingLocked();
- stopMonitoringSignificantMotion();
+ stopMonitoringMotionLocked();
mAnyMotionDetector.stop();
}
@@ -1271,8 +1317,8 @@
switch (mState) {
case STATE_INACTIVE:
// We have now been inactive long enough, it is time to start looking
- // for significant motion and sleep some more while doing so.
- startMonitoringSignificantMotion();
+ // for motion and sleep some more while doing so.
+ startMonitoringMotionLocked();
scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
// Reset the upcoming idle delays.
mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
@@ -1353,17 +1399,16 @@
}
}
- void significantMotionLocked() {
- if (DEBUG) Slog.d(TAG, "significantMotionLocked()");
- // When the sensor goes off, its trigger is automatically removed.
- mSigMotionActive = false;
+ void motionLocked() {
+ if (DEBUG) Slog.d(TAG, "motionLocked()");
+ // The motion sensor will have been disabled at this point
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
}
void handleMotionDetectedLocked(long timeout, String type) {
// The device is not yet active, so we want to go back to the pending idle
- // state to wait again for no motion. Note that we only monitor for significant
- // motion after moving out of the inactive state, so no need to worry about that.
+ // state to wait again for no motion. Note that we only monitor for motion
+ // after moving out of the inactive state, so no need to worry about that.
if (mState != STATE_ACTIVE) {
scheduleReportActiveLocked(type, Process.myUid());
mState = STATE_ACTIVE;
@@ -1405,19 +1450,17 @@
}
}
- void startMonitoringSignificantMotion() {
- if (DEBUG) Slog.d(TAG, "startMonitoringSignificantMotion()");
- if (mSigMotionSensor != null && !mSigMotionActive) {
- mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
- mSigMotionActive = true;
+ void startMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "startMonitoringMotionLocked()");
+ if (mMotionSensor != null && !mMotionListener.active) {
+ mMotionListener.registerLocked();
}
}
- void stopMonitoringSignificantMotion() {
- if (DEBUG) Slog.d(TAG, "stopMonitoringSignificantMotion()");
- if (mSigMotionActive) {
- mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
- mSigMotionActive = false;
+ void stopMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
+ if (mMotionSensor != null && mMotionListener.active) {
+ mMotionListener.unregisterLocked();
}
}
@@ -1446,7 +1489,7 @@
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
- if (mSigMotionSensor == null) {
+ if (mMotionSensor == null) {
// If there is no motion sensor on this device, then we won't schedule
// alarms, because we can't determine if the device is not moving. This effectively
// turns off normal execution of device idling, although it is still possible to
@@ -1929,11 +1972,11 @@
pw.print(" mEnabled="); pw.println(mEnabled);
pw.print(" mForceIdle="); pw.println(mForceIdle);
- pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor);
+ pw.print(" mMotionSensor="); pw.println(mMotionSensor);
pw.print(" mCurDisplay="); pw.println(mCurDisplay);
pw.print(" mScreenOn="); pw.println(mScreenOn);
pw.print(" mCharging="); pw.println(mCharging);
- pw.print(" mSigMotionActive="); pw.println(mSigMotionActive);
+ pw.print(" mMotionActive="); pw.println(mMotionListener.active);
pw.print(" mSensing="); pw.print(mSensing); pw.print(" mNotMoving=");
pw.println(mNotMoving);
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 7c85001..f245985 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.ActivityManager;
+import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -263,7 +264,8 @@
}
if (launched) {
Slog.i(TAG, "Power button double tap gesture detected, launching camera.");
- launched = handleCameraLaunchGesture(false /* useWakelock */);
+ launched = handleCameraLaunchGesture(false /* useWakelock */,
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
if (launched) {
MetricsLogger.action(mContext, MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
(int) doubleTapInterval);
@@ -276,7 +278,7 @@
/**
* @return true if camera was launched, false otherwise.
*/
- private boolean handleCameraLaunchGesture(boolean useWakelock) {
+ private boolean handleCameraLaunchGesture(boolean useWakelock, int source) {
boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
if (!userSetupComplete) {
@@ -295,7 +297,7 @@
}
StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
- service.onCameraLaunchGestureDetected();
+ service.onCameraLaunchGestureDetected(source);
return true;
}
@@ -334,7 +336,8 @@
Slog.d(TAG, String.format("Received a camera launch event: " +
"values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
}
- if (handleCameraLaunchGesture(true /* useWakelock */)) {
+ if (handleCameraLaunchGesture(true /* useWakelock */,
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)) {
MetricsLogger.action(mContext, MetricsLogger.ACTION_WIGGLE_CAMERA_GESTURE);
trackCameraLaunchEvent(event);
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 9dad7a1..ab1d775 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.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 @@
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 @@
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 @@
|| (newLocale != null && !newLocale.equals(mLastSystemLocale))) {
if (!updateOnlyWhenLocaleChanged) {
hideCurrentInputLocked(0, null);
- mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
}
if (DEBUG) {
@@ -1474,7 +1490,11 @@
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 @@
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 @@
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/ThermalObserver.java b/services/core/java/com/android/server/ThermalObserver.java
new file mode 100644
index 0000000..aee28fb
--- /dev/null
+++ b/services/core/java/com/android/server/ThermalObserver.java
@@ -0,0 +1,146 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UEventObserver;
+import android.os.UserHandle;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * ThermalObserver for monitoring temperature changes.
+ */
+public class ThermalObserver extends SystemService {
+ private static final String TAG = "ThermalObserver";
+
+ private static final String CALLSTATE_UEVENT_MATCH =
+ "DEVPATH=/devices/virtual/switch/thermalstate";
+
+ private static final int MSG_THERMAL_STATE_CHANGED = 0;
+
+ private static final int SWITCH_STATE_NORMAL = 0;
+ private static final int SWITCH_STATE_WARNING = 1;
+ private static final int SWITCH_STATE_EXCEEDED = 2;
+
+ private final PowerManager mPowerManager;
+ private final PowerManager.WakeLock mWakeLock;
+
+ private final Object mLock = new Object();
+ private Integer mLastState;
+
+ private final UEventObserver mThermalWarningObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ updateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+ }
+ };
+
+ private final Handler mHandler = new Handler(true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_THERMAL_STATE_CHANGED:
+ handleThermalStateChange(msg.arg1);
+ mWakeLock.release();
+ break;
+ }
+ }
+ };
+
+ public ThermalObserver(Context context) {
+ super(context);
+ mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ mThermalWarningObserver.startObserving(CALLSTATE_UEVENT_MATCH);
+ }
+
+ private void updateLocked(int state) {
+ Message message = new Message();
+ message.what = MSG_THERMAL_STATE_CHANGED;
+ message.arg1 = state;
+
+ mWakeLock.acquire();
+ mHandler.sendMessage(message);
+ }
+
+ private void handleThermalStateChange(int state) {
+ synchronized (mLock) {
+ mLastState = state;
+ Intent intent = new Intent(Intent.ACTION_THERMAL_EVENT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+ final int thermalState;
+
+ switch (state) {
+ case SWITCH_STATE_WARNING:
+ thermalState = Intent.EXTRA_THERMAL_STATE_WARNING;
+ break;
+ case SWITCH_STATE_EXCEEDED:
+ thermalState = Intent.EXTRA_THERMAL_STATE_EXCEEDED;
+ break;
+ case SWITCH_STATE_NORMAL:
+ default:
+ thermalState = Intent.EXTRA_THERMAL_STATE_NORMAL;
+ break;
+ }
+
+ intent.putExtra(Intent.EXTRA_THERMAL_STATE, thermalState);
+
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(TAG, new BinderService());
+ }
+
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump thermal observer service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (args == null || args.length == 0 || "-a".equals(args[0])) {
+ pw.println("Current Thermal Observer Service state:");
+ pw.println(" last state change: "
+ + (mLastState != null ? mLastState : "none"));
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index dbf1288..6b34612 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -29,6 +29,7 @@
import android.accounts.IAccountAuthenticatorResponse;
import android.accounts.IAccountManager;
import android.accounts.IAccountManagerResponse;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -2526,6 +2527,7 @@
* Returns the accounts visible to the client within the context of a specific user
* @hide
*/
+ @NonNull
public Account[] getAccounts(int userId, String opPackageName) {
int callingUid = Binder.getCallingUid();
List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
@@ -2551,6 +2553,7 @@
*
* @hide
*/
+ @NonNull
public AccountAndUser[] getRunningAccounts() {
final int[] runningUserIds;
try {
@@ -2563,6 +2566,7 @@
}
/** {@hide} */
+ @NonNull
public AccountAndUser[] getAllAccounts() {
final List<UserInfo> users = getUserManager().getUsers();
final int[] userIds = new int[users.size()];
@@ -2572,6 +2576,7 @@
return getAccounts(userIds);
}
+ @NonNull
private AccountAndUser[] getAccounts(int[] userIds) {
final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
for (int userId : userIds) {
@@ -2591,10 +2596,12 @@
}
@Override
+ @NonNull
public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
return getAccountsAsUser(type, userId, null, -1, opPackageName);
}
+ @NonNull
private Account[] getAccountsAsUser(
String type,
int userId,
@@ -2649,6 +2656,7 @@
}
}
+ @NonNull
private Account[] getAccountsInternal(
UserAccounts userAccounts,
int callingUid,
@@ -2672,7 +2680,15 @@
}
@Override
- public boolean addSharedAccountAsUser(Account account, int userId) {
+ public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
+ checkManageUsersPermission("addSharedAccountsFromParentUser");
+ Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
+ for (Account account : accounts) {
+ addSharedAccountAsUser(account, userId);
+ }
+ }
+
+ private boolean addSharedAccountAsUser(Account account, int userId) {
userId = handleIncomingUser(userId);
UserAccounts accounts = getUserAccounts(userId);
SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
@@ -2764,11 +2780,13 @@
}
@Override
+ @NonNull
public Account[] getAccounts(String type, String opPackageName) {
return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
}
@Override
+ @NonNull
public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
int callingUid = Binder.getCallingUid();
if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
@@ -2780,6 +2798,7 @@
}
@Override
+ @NonNull
public Account[] getAccountsByTypeForPackage(String type, String packageName,
String opPackageName) {
int packageUid = -1;
@@ -3844,6 +3863,14 @@
return false;
}
+ private static void checkManageUsersPermission(String message) {
+ if (ActivityManager.checkComponentPermission(
+ android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need MANAGE_USERS permission to: " + message);
+ }
+ }
+
private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
int callerUid) {
if (callerUid == Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bd10c63..617264c 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_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 @@
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 @@
updateCurrentProfileIdsLocked();
mRecentTasks.clear();
- mRecentTasks.addAll(mTaskPersister.restoreTasksLocked());
+ mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(
+ getUserManagerLocked().getUserIds()));
mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
mTaskPersister.startPersisting();
@@ -20644,9 +20673,6 @@
// 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 @@
}
}
+ void onUserRemovedLocked(int userId) {
+ mRecentTasks.removeTasksForUserLocked(userId);
+ }
+
@Override
public UserInfo getCurrentUser() {
if ((checkCallingPermission(INTERACT_ACROSS_USERS)
@@ -20949,6 +20979,13 @@
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/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5d106dc..a796ea7 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1957,7 +1957,6 @@
? AppTransition.TRANSIT_ACTIVITY_CLOSE
: AppTransition.TRANSIT_TASK_CLOSE, false);
}
- mWindowManager.setAppWillBeHidden(prev.appToken);
mWindowManager.setAppVisibility(prev.appToken, false);
} else {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
@@ -1973,10 +1972,6 @@
: AppTransition.TRANSIT_TASK_OPEN, false);
}
}
- if (false) {
- mWindowManager.setAppWillBeHidden(prev.appToken);
- mWindowManager.setAppVisibility(prev.appToken, false);
- }
} else {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
if (mNoAnimActivities.contains(next)) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 17a4472..98b6ee6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -24,6 +24,7 @@
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -87,6 +88,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -1348,8 +1350,7 @@
r.launchFailed = false;
if (stack.updateLRUListLocked(r)) {
- Slog.w(TAG, "Activity " + r
- + " being launched, but already in LRU list");
+ Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
}
if (andResume) {
@@ -2968,6 +2969,8 @@
return;
}
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
+
ActivityRecord r = stack.topRunningActivityLocked(null);
mTmpBounds.clear();
@@ -3051,9 +3054,11 @@
resumeTopActivitiesLocked(stack, null, null);
}
}
+
+ 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;
@@ -3061,7 +3066,7 @@
// 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;
@@ -3079,20 +3084,11 @@
return;
}
- // 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");
- }
+ // 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!
+
+ 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
@@ -3103,27 +3099,17 @@
ActivityRecord r = task.topRunningActivityLocked(null);
if (r != null) {
final ActivityStack stack = task.stack;
- final boolean resizedByUser = resizeMode == RESIZE_MODE_USER;
- final boolean preserveWindow = resizedByUser && !changedStacks;
kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
// All other activities must be made visible with their correct configuration.
ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
if (!kept) {
resumeTopActivitiesLocked(stack, null, null);
- if (changedStacks && stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
- // We are about to relaunch the activity because its configuration changed
- // due to being maximized, i.e. size change. The activity will first
- // remove the old window and then add a new one. This call will tell window
- // manager about this, so it can preserve the old window until the new
- // one is drawn. This prevents having a gap between the removal and
- // addition, in which no window is visible. We also want the entrace of the
- // new window to be properly animated.
- mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
- }
}
}
}
mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, kept, forced);
+
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
@@ -3204,7 +3190,7 @@
* @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);
@@ -3246,17 +3232,30 @@
return;
}
final String reason = "moveTaskToStack";
+ if (stackId == DOCKED_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ // We are about to relaunch the activity because its configuration changed due to
+ // being maximized, i.e. size change. The activity will first remove the old window
+ // and then add a new one. This call will tell window manager about this, so it can
+ // preserve the old window until the new one is drawn. This prevents having a gap
+ // between the removal and addition, in which no window is visible. We also want the
+ // entrace of the new window to be properly animated.
+ ActivityRecord r = task.getTopActivity();
+ mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
+ }
final ActivityStack stack =
moveTaskToStackUncheckedLocked(task, stackId, toTop, forceFocus, reason);
// 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
@@ -4617,16 +4616,6 @@
return mActivityDisplay != null;
}
- void getBounds(Point outBounds) {
- synchronized (mService) {
- if (mActivityDisplay != null) {
- mActivityDisplay.getBounds(outBounds);
- } else {
- outBounds.set(0, 0);
- }
- }
- }
-
// TODO: Make sure every change to ActivityRecord.visible results in a call to this.
void setVisible(boolean visible) {
if (mVisible != visible) {
@@ -4798,12 +4787,6 @@
mStacks.remove(stack);
}
- void getBounds(Point bounds) {
- mDisplay.getDisplayInfo(mDisplayInfo);
- bounds.x = mDisplayInfo.appWidth;
- bounds.y = mDisplayInfo.appHeight;
- }
-
void setVisibleBehindActivity(ActivityRecord r) {
mVisibleBehindActivity = r;
}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index aa154a7..871331b 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.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 @@
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 @@
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 43f5baa..7e14b2b 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 @@
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 99ca050..e49a7e4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2524,11 +2524,14 @@
}
/** @see AudioManager#setBluetoothScoOn(boolean) */
- public void setBluetoothScoOn(boolean on){
+ public void setBluetoothScoOn(boolean on) {
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
return;
}
+ setBluetoothScoOnInt(on);
+ }
+ public void setBluetoothScoOnInt(boolean on) {
if (on) {
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
@@ -2889,6 +2892,8 @@
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
+ AudioSystem.setParameters("A2dpSuspended=false");
+ setBluetoothScoOnInt(false);
}
private void broadcastScoConnectionState(int state) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 3df3cd0..2bea278 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -20,9 +20,6 @@
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
-import static android.os.UserHandle.PER_USER_RANGE;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
import android.Manifest;
import android.app.AppGlobals;
@@ -34,13 +31,11 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -126,7 +121,6 @@
/* list of users using this VPN. */
@GuardedBy("this")
private List<UidRange> mVpnUsers = null;
- private BroadcastReceiver mUserIntentReceiver = null;
// Handle of user initiating VPN.
private final int mUserHandle;
@@ -146,31 +140,6 @@
} catch (RemoteException e) {
Log.wtf(TAG, "Problem registering observer", e);
}
- // TODO: http://b/22950929
- if (userHandle == UserHandle.USER_SYSTEM) {
- // Owner's VPN also needs to handle restricted users
- mUserIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL);
- if (userHandle == UserHandle.USER_NULL) return;
-
- if (Intent.ACTION_USER_ADDED.equals(action)) {
- onUserAdded(userHandle);
- } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- onUserRemoved(userHandle);
- }
- }
- };
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_ADDED);
- intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiverAsUser(
- mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
- }
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
// TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
@@ -439,8 +408,8 @@
}
addVpnUserLocked(mUserHandle);
- // If we are owner assign all Restricted Users to this VPN
- if (mUserHandle == UserHandle.USER_OWNER) {
+ // If the user can have restricted profiles, assign all its restricted profiles to this VPN
+ if (canHaveRestrictedProfile(mUserHandle)) {
token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
@@ -449,7 +418,7 @@
Binder.restoreCallingIdentity(token);
}
for (UserInfo user : users) {
- if (user.isRestricted()) {
+ if (user.isRestricted() && (user.restrictedProfileParentId == mUserHandle)) {
addVpnUserLocked(user.id);
}
}
@@ -457,6 +426,15 @@
mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
}
+ private boolean canHaveRestrictedProfile(int userId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return UserManager.get(mContext).canHaveRestrictedProfile(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
networkInfo.setIsAvailable(false);
networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
@@ -681,12 +659,11 @@
mStatusIntent = null;
}
- private void onUserAdded(int userHandle) {
- // If the user is restricted tie them to the owner's VPN
- synchronized(Vpn.this) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(userHandle);
- if (user.isRestricted()) {
+ public void onUserAdded(int userHandle) {
+ // If the user is restricted tie them to the parent user's VPN
+ UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ synchronized(Vpn.this) {
try {
addVpnUserLocked(userHandle);
if (mNetworkAgent != null) {
@@ -700,12 +677,11 @@
}
}
- private void onUserRemoved(int userHandle) {
+ public void onUserRemoved(int userHandle) {
// clean up if restricted
- synchronized(Vpn.this) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(userHandle);
- if (user.isRestricted()) {
+ UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ synchronized(Vpn.this) {
try {
removeVpnUserLocked(userHandle);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 533f425..452378f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -837,7 +837,6 @@
if (mPendingScreenOff && target != Display.STATE_OFF) {
setScreenState(Display.STATE_OFF);
mPendingScreenOff = false;
- mPowerState.dismissColorFade();
}
if (target == Display.STATE_ON) {
@@ -911,7 +910,6 @@
// A black surface is already hiding the contents of the screen.
setScreenState(Display.STATE_OFF);
mPendingScreenOff = false;
- mPowerState.dismissColorFade();
} else if (performScreenOffTransition
&& mPowerState.prepareColorFade(mContext,
mColorFadeFadesConfig ?
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index be37f52..088d96e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -544,7 +544,7 @@
physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
}
}
- if (physIndex > 0 && mActivePhysIndex == physIndex) {
+ if (mActivePhysIndex == physIndex) {
return;
}
SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0c884f15..7b15aad 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.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 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 @@
// 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 @@
}
@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,7 +1687,30 @@
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)) {
+ return;
+ }
if (!checkPolicyAccess(pkg)) {
Slog.w(TAG, "Notification policy access denied calling " + method);
throw new SecurityException("Notification policy access denied");
@@ -1684,7 +1753,7 @@
public boolean matchesCallFilter(Bundle extras) {
enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
return mZenModeHelper.matchesCallFilter(
- UserHandle.getCallingUserHandle(),
+ Binder.getCallingUserHandle(),
extras,
mRankingHelper.findExtractor(ValidateNotificationPeople.class),
MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index cbe61c3..4d41e3a 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_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.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 java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -196,6 +200,121 @@
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 @@
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));
@@ -248,8 +376,9 @@
}
pw.printf("allow(calls=%s,callsFrom=%s,repeatCallers=%s,messages=%s,messagesFrom=%s,"
+ "events=%s,reminders=%s)\n",
- config.allowCalls, config.allowCallsFrom, config.allowRepeatCallers,
- config.allowMessages, config.allowMessagesFrom,
+ config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
+ config.allowRepeatCallers, config.allowMessages,
+ ZenModeConfig.sourceToString(config.allowMessagesFrom),
config.allowEvents, config.allowReminders);
pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule);
if (config.automaticRules.isEmpty()) return;
@@ -271,7 +400,7 @@
}
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;
@@ -318,36 +447,41 @@
}
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() {
@@ -505,6 +639,7 @@
.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();
@@ -518,6 +653,7 @@
.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);
}
@@ -532,6 +668,7 @@
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);
}
@@ -572,6 +709,7 @@
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/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f292c9c..d867616 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -31,6 +31,21 @@
public final class Installer extends SystemService {
private static final String TAG = "Installer";
+ /* ***************************************************************************
+ * IMPORTANT: These values are passed to native code. Keep them in sync with
+ * frameworks/native/cmds/installd/installd.h
+ * **************************************************************************/
+ /** Application should be visible to everyone */
+ public static final int DEXOPT_PUBLIC = 1 << 1;
+ /** Application wants to run in VM safe mode */
+ public static final int DEXOPT_SAFEMODE = 1 << 2;
+ /** Application wants to allow debugging of its code */
+ public static final int DEXOPT_DEBUGGABLE = 1 << 3;
+ /** The system boot has finished */
+ public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
+ /** Run the application with the JIT compiler */
+ public static final int DEXOPT_USEJIT = 1 << 5;
+
private final InstallerConnection mInstaller;
public Installer(Context context) {
@@ -75,39 +90,24 @@
return mInstaller.execute(builder.toString());
}
- public int dexopt(String apkPath, int uid, boolean isPublic,
- String instructionSet, int dexoptNeeded) {
- return dexopt(apkPath, uid, isPublic, instructionSet, dexoptNeeded, true);
- }
-
- public int dexopt(String apkPath, int uid, boolean isPublic,
- String instructionSet, int dexoptNeeded, boolean bootComplete) {
+ public int dexopt(String apkPath, int uid, String instructionSet,
+ int dexoptNeeded, int dexFlags) {
if (!isValidInstructionSet(instructionSet)) {
Slog.e(TAG, "Invalid instruction set: " + instructionSet);
return -1;
}
- return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet, dexoptNeeded,
- bootComplete);
+ return mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
}
- public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
- String instructionSet, int dexoptNeeded, boolean vmSafeMode,
- boolean debuggable, @Nullable String outputPath) {
- return dexopt(apkPath, uid, isPublic, pkgName, instructionSet, dexoptNeeded, vmSafeMode,
- debuggable, outputPath, true);
- }
-
- public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
- String instructionSet, int dexoptNeeded, boolean vmSafeMode,
- boolean debuggable, @Nullable String outputPath, boolean bootComplete) {
+ public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+ int dexoptNeeded, @Nullable String outputPath, int dexFlags) {
if (!isValidInstructionSet(instructionSet)) {
Slog.e(TAG, "Invalid instruction set: " + instructionSet);
return -1;
}
- return mInstaller.dexopt(apkPath, uid, isPublic, pkgName,
- instructionSet, dexoptNeeded, vmSafeMode,
- debuggable, outputPath, bootComplete);
+ return mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
+ outputPath, dexFlags);
}
public int idmap(String targetApkPath, String overlayApkPath, int uid) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b692def..6c6871f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -35,6 +35,11 @@
import dalvik.system.DexFile;
+import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
+import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
+import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
+import static com.android.server.pm.Installer.DEXOPT_USEJIT;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -71,7 +76,8 @@
* {@link PackageManagerService#mInstallLock}.
*/
int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
- boolean forceDex, boolean defer, boolean inclDependencies, boolean bootComplete) {
+ boolean forceDex, boolean defer, boolean inclDependencies,
+ boolean bootComplete, boolean useJit) {
ArraySet<String> done;
if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
done = new ArraySet<String>();
@@ -86,7 +92,8 @@
mDexoptWakeLock.acquire();
}
try {
- return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete, done);
+ return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete,
+ useJit, done);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -96,7 +103,8 @@
}
private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
- boolean forceDex, boolean defer, boolean bootComplete, ArraySet<String> done) {
+ boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
+ ArraySet<String> done) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
@@ -104,11 +112,11 @@
done.add(pkg.packageName);
if (pkg.usesLibraries != null) {
performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer,
- bootComplete, done);
+ bootComplete, useJit, done);
}
if (pkg.usesOptionalLibraries != null) {
performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,
- bootComplete, done);
+ bootComplete, useJit, done);
}
}
@@ -175,11 +183,17 @@
Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
- + " oatDir = " + oatDir + " bootComplete=" + bootComplete);
+ + " oatDir = " + oatDir + " bootComplete=" + bootComplete
+ + " useJit=" + useJit);
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ final int dexFlags =
+ (!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0)
+ | (vmSafeMode ? DEXOPT_SAFEMODE : 0)
+ | (debuggable ? DEXOPT_DEBUGGABLE : 0)
+ | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0)
+ | (useJit ? DEXOPT_USEJIT : 0);
final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
- !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,
- dexoptNeeded, vmSafeMode, debuggable, oatDir, bootComplete);
+ pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
// Dex2oat might fail due to compiler / verifier errors. We soldier on
// regardless, and attempt to interpret the app as a safety net.
@@ -236,12 +250,13 @@
}
private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
- boolean forceDex, boolean defer, boolean bootComplete, ArraySet<String> done) {
+ boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
+ ArraySet<String> done) {
for (String libName : libs) {
PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary(
libName);
if (libPkg != null && !done.contains(libName)) {
- performDexOptLI(libPkg, instructionSets, forceDex, defer, bootComplete, done);
+ performDexOptLI(libPkg, instructionSets, forceDex, defer, bootComplete, useJit, done);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0366fff..cf09b84 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 @@
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 @@
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 @@
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 4a473fd..a441cb2 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 @@
// 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 @@
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 17a3f56..2009ccf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -75,6 +75,7 @@
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.ArrayUtils.appendInt;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -2015,7 +2016,8 @@
int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
alreadyDexOpted.add(lib);
- mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);
+ mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
+ dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
@@ -2063,7 +2065,8 @@
try {
int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);
+ mInstaller.dexopt(path, Process.SYSTEM_UID, dexCodeInstructionSet,
+ dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Jar not found: " + path);
@@ -6248,7 +6251,7 @@
synchronized (mInstallLock) {
mPackageDexOptimizer.performDexOpt(p, null /* instruction sets */,
false /* force dex */, false /* defer */, true /* include dependencies */,
- false /* boot complete */);
+ false /* boot complete */, false /*useJit*/);
}
}
@@ -6308,7 +6311,7 @@
final String[] instructionSets = new String[] { targetInstructionSet };
int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
false /* forceDex */, false /* defer */, true /* inclDependencies */,
- true /* boot complete */);
+ true /* boot complete */, false /*useJit*/);
return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
}
} finally {
@@ -6359,7 +6362,7 @@
final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
true /*forceDex*/, false /* defer */, true /* inclDependencies */,
- true /* boot complete */);
+ true /* boot complete */, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -7176,7 +7179,7 @@
int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */,
- (scanFlags & SCAN_BOOTING) == 0);
+ (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
@@ -7257,7 +7260,7 @@
int result = mPackageDexOptimizer.performDexOpt(clientPkg,
null /* instruction sets */, forceDex,
(scanFlags & SCAN_DEFER_DEX) != 0, false,
- (scanFlags & SCAN_BOOTING) == 0);
+ (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
"scanPackageLI failed to dexopt clientLibPkgs");
@@ -7881,7 +7884,7 @@
int result = mPackageDexOptimizer.performDexOpt(ps.pkg,
null /* instruction sets */, forceDexOpt, deferDexOpt, true,
- bootComplete);
+ bootComplete, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
@@ -9683,7 +9686,8 @@
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;
@@ -12572,8 +12576,7 @@
int result = mPackageDexOptimizer
.performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
false /* defer */, false /* inclDependencies */,
- true /* boot complete */);
-
+ true /*bootComplete*/, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1924bab..6386a91 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -16,9 +16,11 @@
package com.android.server.pm;
+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;
@@ -63,6 +65,7 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -129,7 +132,7 @@
private static final int MIN_USER_ID = 10;
- private static final int USER_VERSION = 5;
+ private static final int USER_VERSION = 6;
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
@@ -240,13 +243,15 @@
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 + ")");
@@ -270,7 +275,8 @@
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;
@@ -285,7 +291,8 @@
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;
@@ -321,7 +328,8 @@
// 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;
@@ -406,6 +414,24 @@
}
}
+ @Override
+ public boolean canHaveRestrictedProfile(int userId) {
+ checkManageUsersPermission("canHaveRestrictedProfile");
+ synchronized (mPackagesLock) {
+ final UserInfo userInfo = getUserInfoLocked(userId);
+ if (userInfo == null || !userInfo.canHaveProfile()) {
+ return false;
+ }
+ if (!userInfo.isAdmin()) {
+ return false;
+ }
+ }
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ // restricted profile can be created if there is no DO set and the admin user has no PO
+ return dpm.getDeviceOwner() == null && dpm.getProfileOwnerAsUser(userId) == null;
+ }
+
/*
* Should be locked on mUsers before calling this.
*/
@@ -846,6 +872,20 @@
userVersion = 5;
}
+ if (userVersion < 6) {
+ final boolean splitSystemUser = UserManager.isSplitSystemUser();
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i);
+ // In non-split mode, only user 0 can have restricted profiles
+ if (!splitSystemUser && user.isRestricted()
+ && (user.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID)) {
+ user.restrictedProfileParentId = UserHandle.USER_SYSTEM;
+ scheduleWriteUserLocked(user);
+ }
+ }
+ userVersion = 6;
+ }
+
if (userVersion < USER_VERSION) {
Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
+ USER_VERSION);
@@ -976,7 +1016,8 @@
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));
@@ -1386,6 +1427,25 @@
}
/**
+ * @hide
+ */
+ public UserInfo createRestrictedProfile(String name, int parentUserId) {
+ checkManageUsersPermission("setupRestrictedProfile");
+ final UserInfo user = createProfileForUser(name, UserInfo.FLAG_RESTRICTED, parentUserId);
+ if (user == null) {
+ return null;
+ }
+ setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
+ // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
+ // the putIntForUser() will fail.
+ android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ android.provider.Settings.Secure.LOCATION_MODE,
+ android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
+ setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+ return user;
+ }
+
+ /**
* Find the current guest user. If the Guest user is partial,
* then do not include it in the results as it is about to die.
*/
@@ -1534,6 +1594,9 @@
}
new Thread() {
public void run() {
+ // Clean up any ActivityManager state
+ LocalServices.getService(ActivityManagerInternal.class)
+ .onUserRemoved(userHandle);
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
removeUserStateLocked(userHandle);
@@ -1898,14 +1961,15 @@
*/
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 dbc3970..c265000 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 @@
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 @@
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/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 5d01931..25d646d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -28,5 +28,5 @@
void showScreenPinningRequest();
void showAssistDisclosure();
void startAssist(Bundle args);
- void onCameraLaunchGestureDetected();
+ void onCameraLaunchGestureDetected(int source);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 11a1639..19b03d5 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -178,10 +178,10 @@
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
if (mBar != null) {
try {
- mBar.onCameraLaunchGestureDetected();
+ mBar.onCameraLaunchGestureDetected(source);
} catch (RemoteException e) {
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 192b168..cc51d20 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1003,19 +1003,36 @@
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
}
- private Animation createRelaunchAnimation(int appWidth, int appHeight) {
+ private Animation createRelaunchAnimation(int appWidth, int appHeight,
+ Rect containingFrame) {
getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
final int left = mTmpFromClipRect.left;
final int top = mTmpFromClipRect.top;
mTmpFromClipRect.offset(-left, -top);
mTmpToClipRect.set(0, 0, appWidth, appHeight);
AnimationSet set = new AnimationSet(true);
- ClipRectAnimation clip = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- TranslateAnimation translate = new TranslateAnimation(left, 0, top, 0);
- clip.setInterpolator(mDecelerateInterpolator);
- set.addAnimation(clip);
+ float fromWidth = mTmpFromClipRect.width();
+ float toWidth = mTmpToClipRect.width();
+ float fromHeight = mTmpFromClipRect.height();
+ float toHeight = mTmpToClipRect.height();
+ if (fromWidth <= toWidth && fromHeight <= toHeight) {
+ // The final window is larger in both dimensions than current window (e.g. we are
+ // maximizing), so we can simply unclip the new window and there will be no disappearing
+ // frame.
+ set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect));
+ } else {
+ // The disappearing window has one larger dimension. We need to apply scaling, so the
+ // first frame of the entry animation matches the old window.
+ set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
+ }
+
+ // We might not be going exactly full screen, but instead be aligned under the status bar.
+ // We need to take this into account when creating the translate animation.
+ TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
+ 0, top - containingFrame.top, 0);
set.addAnimation(translate);
set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+ set.setZAdjustment(Animation.ZORDER_TOP);
return set;
}
@@ -1056,7 +1073,12 @@
+ " anim=" + a + " transit=" + appTransitionToString(transit)
+ " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
} else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
- a = createRelaunchAnimation(appWidth, appHeight);
+ a = createRelaunchAnimation(appWidth, appHeight, containingFrame);
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+ "applyAnimation:"
+ + " anim=" + a + " nextAppTransition=" + mNextAppTransition
+ + " transit=" + appTransitionToString(transit)
+ + " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimationRes(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
@@ -1077,6 +1099,7 @@
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
+ + " transit=" + appTransitionToString(transit)
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ff216c5..5a1ed58 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -74,10 +74,6 @@
// case do not clear allDrawn until the animation completes.
boolean deferClearAllDrawn;
- // Is this token going to be hidden in a little while? If so, it
- // won't be taken into account for setting the screen orientation.
- boolean willBeHidden;
-
// Is this window's surface needed? This is almost like hidden, except
// it will sometimes be true a little earlier: when the token has
// been shown, but is still waiting for its app transition to execute
@@ -321,7 +317,6 @@
pw.print(" requestedOrientation="); pw.println(requestedOrientation);
pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
pw.print(" clientHidden="); pw.print(clientHidden);
- pw.print(" willBeHidden="); pw.print(willBeHidden);
pw.print(" reportedDrawn="); pw.print(reportedDrawn);
pw.print(" reportedVisible="); pw.println(reportedVisible);
if (paused) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ede377d..6b5ecdc 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 @@
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
mService = service;
+ initializeDisplayBaseInfo();
mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
}
@@ -192,6 +193,21 @@
}
}
+ 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;
@@ -247,7 +263,7 @@
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = tasks.get(taskNdx);
task.getBounds(mTmpRect);
- if (task.inFreeformWorkspace() && mTmpRect.contains(x, y)) {
+ if (mTmpRect.contains(x, y)) {
return task.mTaskId;
}
}
@@ -567,13 +583,8 @@
return "Display " + mDisplayId + " info=" + mDisplayInfo + " stacks=" + mStacks;
}
- TaskStack getDockedStack() {
- for (int i = mStacks.size() - 1; i >= 0; i--) {
- TaskStack stack = mStacks.get(i);
- if (stack.mStackId == DOCKED_STACK_ID) {
- return stack;
- }
- }
- return null;
+ TaskStack getDockedStackLocked() {
+ 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/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ad207d4..8c5d319 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.DOCKED_STACK_ID;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -23,11 +24,19 @@
import static android.view.WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING;
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static com.android.server.wm.TaskStack.DOCKED_BOTTOM;
+import static com.android.server.wm.TaskStack.DOCKED_INVALID;
+import static com.android.server.wm.TaskStack.DOCKED_LEFT;
+import static com.android.server.wm.TaskStack.DOCKED_RIGHT;
+import static com.android.server.wm.TaskStack.DOCKED_TOP;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.Slog;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -35,13 +44,20 @@
/**
* Controls showing and hiding of a docked stack divider on the display.
*/
-public class DockedStackDividerController {
+public class DockedStackDividerController implements View.OnTouchListener {
private static final String TAG = "DockedStackDivider";
private final Context mContext;
private final int mDividerWidth;
private final DisplayContent mDisplayContent;
private View mView;
private Rect mTmpRect = new Rect();
+ private Rect mLastResizeRect = new Rect();
+ private int mStartX;
+ private int mStartY;
+ private TaskStack mTaskStack;
+ private Rect mOriginalRect = new Rect();
+ private int mDockSide;
+
DockedStackDividerController(Context context, DisplayContent displayContent) {
mContext = context;
@@ -53,6 +69,7 @@
private void addDivider() {
View view = LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.docked_stack_divider, null);
+ view.setOnTouchListener(this);
WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
mDividerWidth, MATCH_PARENT, TYPE_DOCK_DIVIDER,
@@ -65,6 +82,7 @@
}
private void removeDivider() {
+ mView.setOnTouchListener(null);
WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
manager.removeView(mView, true /* immediate */);
mView = null;
@@ -75,7 +93,7 @@
}
void update() {
- TaskStack stack = mDisplayContent.getDockedStack();
+ TaskStack stack = mDisplayContent.getDockedStackLocked();
if (stack != null && mView == null) {
addDivider();
} else if (stack == null && mView != null) {
@@ -87,9 +105,8 @@
return mDividerWidth;
}
-
void positionDockedStackedDivider(Rect frame) {
- TaskStack stack = mDisplayContent.getDockedStack();
+ TaskStack stack = mDisplayContent.getDockedStackLocked();
if (stack == null) {
// Unfortunately we might end up with still having a divider, even though the underlying
// stack was already removed. This is because we are on AM thread and the removal of the
@@ -99,19 +116,84 @@
final @TaskStack.DockSide int side = stack.getDockSide();
stack.getBounds(mTmpRect);
switch (side) {
- case TaskStack.DOCKED_LEFT:
+ case DOCKED_LEFT:
frame.set(mTmpRect.right, frame.top, mTmpRect.right + frame.width(), frame.bottom);
break;
- case TaskStack.DOCKED_TOP:
+ case DOCKED_TOP:
frame.set(frame.left, mTmpRect.bottom, mTmpRect.right,
mTmpRect.bottom + frame.height());
break;
- case TaskStack.DOCKED_RIGHT:
+ case DOCKED_RIGHT:
frame.set(mTmpRect.left - frame.width(), frame.top, mTmpRect.left, frame.bottom);
break;
- case TaskStack.DOCKED_BOTTOM:
+ case DOCKED_BOTTOM:
frame.set(frame.left, mTmpRect.top - frame.height(), frame.right, mTmpRect.top);
break;
}
}
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ // We use raw values, because getX/Y() would give us results relative to the
+ // dock divider bounds.
+ mStartX = (int) event.getRawX();
+ mStartY = (int) event.getRawY();
+ synchronized (mDisplayContent.mService.mWindowMap) {
+ mTaskStack = mDisplayContent.getDockedStackLocked();
+ mTaskStack.getBounds(mOriginalRect);
+ mDockSide = mTaskStack.getDockSide();
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mTaskStack != null) {
+ resizeStack(event);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mTaskStack = null;
+ mDockSide = TaskStack.DOCKED_INVALID;
+ break;
+ }
+ return true;
+ }
+
+ private void resizeStack(MotionEvent event) {
+ mTmpRect.set(mOriginalRect);
+ final int deltaX = (int) event.getRawX() - mStartX;
+ final int deltaY = (int) event.getRawY() - mStartY;
+ switch (mDockSide) {
+ case DOCKED_LEFT:
+ mTmpRect.right += deltaX;
+ break;
+ case DOCKED_TOP:
+ mTmpRect.bottom += deltaY;
+ break;
+ case DOCKED_RIGHT:
+ mTmpRect.left += deltaX;
+ break;
+ case DOCKED_BOTTOM:
+ mTmpRect.top += deltaY;
+ break;
+ }
+ if (mTmpRect.equals(mLastResizeRect)) {
+ return;
+ }
+ mLastResizeRect.set(mTmpRect);
+ try {
+ mDisplayContent.mService.mActivityManager.resizeStack(DOCKED_STACK_ID, mTmpRect);
+ } catch (RemoteException e) {
+ }
+ }
+
+ boolean isResizing() {
+ return mTaskStack != null;
+ }
+
+ int getWidthAdjustment() {
+ return getWidth() / 2;
+ }
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 4244205..6c391ad 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
import android.app.ActivityManagerNative;
import android.graphics.Rect;
@@ -170,7 +171,7 @@
private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,
final WindowState child, int flags, final int type, final boolean isVisible,
- final boolean hasFocus, final boolean hasWallpaper) {
+ final boolean hasFocus, final boolean hasWallpaper, DisplayContent displayContent) {
// Add a window to our list of input windows.
inputWindowHandle.name = child.toString();
final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -202,6 +203,20 @@
inputWindowHandle.frameTop = frame.top;
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
+ if (child.mAttrs.type == TYPE_DOCK_DIVIDER) {
+ // We need to determine if the divider is horizontal or vertical and adjust its handle
+ // frame accordingly.
+ int adjustment = displayContent.mDividerControllerLocked.getWidthAdjustment();
+ if (inputWindowHandle.frameRight - inputWindowHandle.frameLeft >
+ inputWindowHandle.frameTop - inputWindowHandle.frameBottom) {
+ // Horizontal divider.
+ inputWindowHandle.frameTop -= adjustment;
+ inputWindowHandle.frameBottom += adjustment;
+ } else {
+ inputWindowHandle.frameLeft -= adjustment;
+ inputWindowHandle.frameRight += adjustment;
+ }
+ }
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
@@ -277,7 +292,8 @@
final int numDisplays = mService.mDisplayContents.size();
final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
+ final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
+ final WindowList windows = displayContent.getWindowList();
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
final WindowState child = windows.get(winNdx);
final InputChannel inputChannel = child.mInputChannel;
@@ -315,7 +331,7 @@
}
addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
- hasWallpaper);
+ hasWallpaper, displayContent);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d1111f7..1f986dd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,9 +17,11 @@
package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import android.content.res.Configuration;
@@ -245,8 +247,32 @@
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) {
@@ -274,7 +300,13 @@
// this happens, so update the task bounds so it stays in the same place.
mTmpRect2.set(mBounds);
displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
- setBounds(mTmpRect2, mOverrideConfig);
+ if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
+ // 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.
+ mService.mH.sendMessage(mService.mH.obtainMessage(
+ RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
+ }
}
/** Updates the dim layer bounds, recreating it if needed. */
@@ -433,10 +465,6 @@
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;
@@ -444,7 +472,13 @@
@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 d1904d8..9da7406 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -19,9 +19,10 @@
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;
import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
@@ -33,6 +34,7 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.Choreographer;
@@ -145,10 +147,12 @@
synchronized (mService.mWindowMap) {
mDragEnded = notifyMoveLocked(newX, newY);
}
+ 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: {
@@ -175,7 +179,7 @@
// 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 96fcf93..f030b9a 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -18,13 +18,13 @@
import static android.app.ActivityManager.*;
import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
import static com.android.server.wm.WindowManagerService.TAG;
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;
@@ -58,7 +58,7 @@
/** 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();
@@ -86,6 +86,7 @@
static final int DOCKED_TOP = 2;
static final int DOCKED_RIGHT = 3;
static final int DOCKED_BOTTOM = 4;
+
@IntDef({
DOCKED_INVALID,
DOCKED_LEFT,
@@ -180,24 +181,60 @@
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) {
+ mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
+ }
if (bounds != null) {
setBounds(bounds);
} else if (mFullscreen) {
setBounds(null);
} else {
- TmpRect2.set(mBounds);
+ mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(
- mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2);
- setBounds(TmpRect2);
- }
- for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
- mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
+ 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.
+ mService.mH.sendMessage(
+ mService.mH.obtainMessage(RESIZE_STACK, mStackId, -1, mBounds));
+ }
}
}
}
@@ -345,15 +382,19 @@
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,
- mDisplayContent.mDividerControllerLocked.getWidth() / 2);
+ mTmpRect2.setEmpty();
+ if (dockedStack != null) {
+ dockedStack.getRawBounds(mTmpRect2);
+ }
+ getInitialDockedStackBounds(mTmpRect, bounds, mStackId, mTmpRect2,
+ mDisplayContent.mDividerControllerLocked.getWidthAdjustment());
}
updateDisplayInfo(bounds);
@@ -362,7 +403,7 @@
// 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);
}
}
@@ -372,29 +413,49 @@
* @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 (splitHorizontally) {
- outBounds.right = displayRect.centerX() - adjustment;
+ 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 {
- outBounds.bottom = displayRect.centerY() - adjustment;
+ 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 = dockedBounds.left - adjustment;
+ } else {
+ 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;
}
}
}
@@ -403,11 +464,14 @@
* 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());
}
@@ -418,11 +482,8 @@
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));
}
}
}
@@ -438,8 +499,7 @@
for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
// We are in the middle of changing the state of displays/stacks/tasks. We need
// to finish that, before we let layout interfere with it.
- mService.removeWindowInnerLocked(appWindows.get(winNdx),
- false /* performLayout */);
+ mService.removeWindowLocked(appWindows.get(winNdx));
doAnotherLayoutPass = true;
}
}
@@ -451,7 +511,7 @@
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();
@@ -520,9 +580,23 @@
}
}
+ /** 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
@@ -566,4 +640,16 @@
return DOCKED_INVALID;
}
}
+
+ boolean isVisibleLocked() {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ Task task = mTasks.get(i);
+ for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
+ if (!task.mAppTokens.get(j).hidden) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 22f9f50..74572cf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,6 +24,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -221,6 +222,7 @@
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;
@@ -1894,13 +1896,13 @@
return res;
}
- if (outInputChannel != null && (attrs.inputFeatures
- & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
+ final boolean openInputChannels = (outInputChannel != null
+ && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ if (openInputChannels) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
-
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
@@ -2174,10 +2176,7 @@
// The exit animation is running... wait for it!
win.mExiting = true;
win.mRemoveOnExit = true;
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
final boolean focusChanged = updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
mWindowPlacerLocked.performSurfacePlacement();
@@ -2206,7 +2205,7 @@
removeWindowInnerLocked(win, true);
}
- void removeWindowInnerLocked(WindowState win, boolean performLayout) {
+ private void removeWindowInnerLocked(WindowState win, boolean performLayout) {
if (win.mRemoved) {
// Nothing to do.
return;
@@ -2300,10 +2299,7 @@
windows.remove(win);
if (!mWindowPlacerLocked.isInLayout()) {
assignLayersLocked(windows);
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
if (performLayout) {
mWindowPlacerLocked.performSurfacePlacement();
}
@@ -2385,10 +2381,7 @@
w.mGivenVisibleInsets.scale(w.mGlobalScale);
w.mGivenTouchableRegion.scale(w.mGlobalScale);
}
- final DisplayContent displayContent = w.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ w.setDisplayLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -2730,10 +2723,7 @@
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
configChanged = updateOrientationFromAppTokensLocked(false);
mWindowPlacerLocked.performSurfacePlacement();
@@ -2826,10 +2816,7 @@
getDefaultDisplayContentLocked().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
mWindowPlacerLocked.requestTraversal();
}
}
@@ -3268,8 +3255,7 @@
// if we're about to tear down this window and not seek for
// the behind activity, don't use it for orientation
- if (!findingBehind
- && (!atoken.hidden && atoken.hiddenRequested)) {
+ if (!findingBehind && !atoken.hidden && atoken.hiddenRequested) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
+ " -- going to hide");
continue;
@@ -3290,7 +3276,7 @@
}
// We ignore any hidden applications on the top.
- if (atoken.hiddenRequested || atoken.willBeHidden) {
+ if (atoken.hiddenRequested) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
+ " -- hidden on top");
continue;
@@ -3786,7 +3772,6 @@
if (!ttoken.hidden) {
wtoken.hidden = false;
wtoken.hiddenRequested = false;
- wtoken.willBeHidden = false;
}
if (wtoken.clientHidden != ttoken.clientHidden) {
wtoken.clientHidden = ttoken.clientHidden;
@@ -3842,25 +3827,6 @@
}
}
- @Override
- public void setAppWillBeHidden(IBinder token) {
- if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
- "setAppWillBeHidden()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
-
- AppWindowToken wtoken;
-
- synchronized(mWindowMap) {
- wtoken = findAppWindowToken(token);
- if (wtoken == null) {
- Slog.w(TAG, "Attempted to set will be hidden of non-existing app token: " + token);
- return;
- }
- wtoken.willBeHidden = true;
- }
- }
-
public void setAppFullscreen(IBinder token, boolean toOpaque) {
synchronized (mWindowMap) {
AppWindowToken atoken = findAppWindowToken(token);
@@ -3897,7 +3863,6 @@
wtoken.sendAppVisibilityToClients();
}
- wtoken.willBeHidden = false;
// Allow for state changes and animation to be applied if:
// * token is transitioning visibility state
// * or the token was marked as hidden and is exiting before we had a chance to play the
@@ -3950,10 +3915,7 @@
}
}
changed = true;
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
}
} else if (win.isVisibleNow()) {
if (!runningAppAnimation) {
@@ -3967,10 +3929,7 @@
}
}
changed = true;
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
}
}
@@ -4138,10 +4097,7 @@
}
w.mLastFreezeDuration = 0;
unfrozeWindows = true;
- final DisplayContent displayContent = w.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ w.setDisplayLayoutNeeded();
}
}
if (force || unfrozeWindows) {
@@ -4444,7 +4400,6 @@
mInputMonitor.setUpdateInputWindowsNeededLw();
mWindowPlacerLocked.performSurfacePlacement();
mInputMonitor.updateInputWindowsLw(false /*force*/);
-
//dump();
}
@@ -4529,20 +4484,16 @@
}
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;
}
}
@@ -4555,11 +4506,6 @@
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) {
@@ -4665,7 +4611,7 @@
stack.getDisplayContent().layoutNeeded = true;
mWindowPlacerLocked.performSurfacePlacement();
}
- return stack.isFullscreen();
+ return stack.getRawFullscreen();
}
}
@@ -7152,22 +7098,7 @@
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();
}
}
}
@@ -7230,6 +7161,9 @@
public static final int UPDATE_DOCKED_STACK_DIVIDER = 42;
+ public static final int RESIZE_STACK = 43;
+ public static final int RESIZE_TASK = 44;
+
@Override
public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) {
@@ -7776,6 +7710,22 @@
}
}
break;
+ case RESIZE_TASK: {
+ try {
+ mActivityManager.resizeTask(msg.arg1, (Rect) msg.obj, msg.arg2);
+ } catch (RemoteException e) {
+ // This will not happen since we are in the same process.
+ }
+ }
+ break;
+ case RESIZE_STACK: {
+ try {
+ mActivityManager.resizeStack(msg.arg1, (Rect) msg.obj);
+ } catch (RemoteException e) {
+ // This will not happen since we are in the same process.
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -8317,7 +8267,7 @@
final int numTokens = tokens.size();
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
- if (wtoken.mIsExiting) {
+ if (wtoken.mIsExiting && !wtoken.mReplacingWindow) {
continue;
}
i = reAddAppWindowsLocked(displayContent, i, wtoken);
@@ -8387,6 +8337,13 @@
} 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;
}
@@ -9909,6 +9866,12 @@
return mWindowMap;
}
+ /**
+ * Hint to a token that its activity will relaunch, which will trigger removal and addition of
+ * a window.
+ * @param token Application token for which the activity will be relaunched.
+ * @param animate Whether to animate the addition of the new window.
+ */
public void setReplacingWindow(IBinder token, boolean animate) {
synchronized (mWindowMap) {
AppWindowToken appWindowToken = findAppWindowToken(token);
@@ -10098,8 +10061,6 @@
@Override
public void saveLastInputMethodWindowForTransition() {
synchronized (mWindowMap) {
- // TODO(multidisplay): Pass in the displayID.
- DisplayContent displayContent = getDefaultDisplayContentLocked();
if (mInputMethodWindow != null) {
mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c73dbaf..55ddbc0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -34,10 +35,12 @@
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.WorkSource;
import android.util.DisplayMetrics;
import android.util.TimeUtils;
@@ -129,7 +132,6 @@
boolean mAttachedHidden; // is our parent window hidden?
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
boolean mDragResizing;
- boolean mDragResizeChanging;
RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -1321,6 +1323,12 @@
}
}
+ void setDisplayLayoutNeeded() {
+ if (mDisplayContent != null) {
+ mDisplayContent.layoutNeeded = true;
+ }
+ }
+
private class DeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
@@ -1465,10 +1473,7 @@
// We want the tag name to be somewhat stable so that it is easier to correlate
// in wake lock statistics. So in particular, we don't want to include the
// window's hash code as in toString().
- CharSequence tag = mAttrs.getTitle();
- if (tag == null) {
- tag = mAttrs.packageName;
- }
+ final CharSequence tag = getWindowTag();
mDrawLock = mService.mPowerManager.newWakeLock(
PowerManager.DRAW_WAKE_LOCK, "Window:" + tag);
mDrawLock.setReferenceCounted(false);
@@ -1605,6 +1610,7 @@
}
void reportResized() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mCompatFrame);
@@ -1670,6 +1676,7 @@
mService.mPendingRemove.add(this);
mService.mWindowPlacerLocked.requestTraversal();
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
public void registerFocusObserver(IWindowFocusObserver observer) {
@@ -1701,13 +1708,23 @@
}
boolean isDragResizeChanged() {
+ return mDragResizing != computeDragResizing();
+ }
+
+ private boolean computeDragResizing() {
final Task task = getTask();
- return task != null && mDragResizing != task.isDragResizing();
+ if (task == null) {
+ return false;
+ }
+ if (task.isDragResizing()) {
+ return true;
+ }
+ return mDisplayContent.mDividerControllerLocked.isResizing() &&
+ !task.inFreeformWorkspace() && !task.isFullscreen();
}
void setDragResizing() {
- final Task task = getTask();
- mDragResizing = task != null && task.isDragResizing();
+ mDragResizing = computeDragResizing();
}
boolean isDragResizing() {
@@ -1901,15 +1918,20 @@
String makeInputChannelName() {
return Integer.toHexString(System.identityHashCode(this))
- + " " + mAttrs.getTitle();
+ + " " + getWindowTag();
+ }
+
+ private CharSequence getWindowTag() {
+ CharSequence tag = mAttrs.getTitle();
+ if (tag == null || tag.length() <= 0) {
+ tag = mAttrs.packageName;
+ }
+ return tag;
}
@Override
public String toString() {
- CharSequence title = mAttrs.getTitle();
- if (title == null || title.length() <= 0) {
- title = mAttrs.packageName;
- }
+ final CharSequence title = getWindowTag();
if (mStringNameCache == null || mLastTitle != title || mWasExiting != mExiting) {
mLastTitle = title;
mWasExiting = mExiting;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 07e1fce..60bf571 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 @@
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;
}
@@ -1087,6 +1092,8 @@
mAnimator.getScreenRotationAnimationLocked(displayId);
final boolean screenAnimation =
screenRotationAnimation != null && screenRotationAnimation.isAnimating();
+
+ mHasClipRect = false;
if (selfTransformation || attachedTransformation != null
|| appTransformation != null || screenAnimation) {
// cache often used attributes locally
@@ -1165,7 +1172,6 @@
// transforming since it is more important to have that
// animation be smooth.
mShownAlpha = mAlpha;
- mHasClipRect = false;
if (!mService.mLimitedAlphaCompositing
|| (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
|| (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
@@ -1368,7 +1374,12 @@
// so we need to translate to match the actual surface coordinates.
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
- adjustCropToStackBounds(w, clipRect);
+ // We don't want to clip to stack bounds windows that are currently doing entrance
+ // animation. This is necessary for docking operation, otherwise the window will be
+ // suddenly cut off.
+ if (!mAnimator.mAnimating) {
+ adjustCropToStackBounds(w, clipRect);
+ }
if (!clipRect.equals(mLastClipRect)) {
mLastClipRect.set(clipRect);
@@ -1394,7 +1405,11 @@
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;
@@ -1707,8 +1722,7 @@
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 e77ebe7..df0a1c9 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -47,6 +47,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
@@ -966,6 +967,7 @@
}
mService.mPolicy.finishLayoutLw();
+ mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget();
}
/**
@@ -1228,11 +1230,15 @@
final WindowState oldWallpaper =
mWallpaperControllerLocked.isWallpaperTargetAnimating()
? null : wallpaperTarget;
+ final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps;
+ final ArraySet<AppWindowToken> closingApps = mService.mClosingApps;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New wallpaper target=" + wallpaperTarget
+ ", oldWallpaper=" + oldWallpaper
+ ", lower target=" + lowerWallpaperTarget
- + ", upper target=" + upperWallpaperTarget);
+ + ", upper target=" + upperWallpaperTarget
+ + ", openingApps=" + openingApps
+ + ", closingApps=" + closingApps);
mService.mAnimateWallpaperWithTarget = false;
if (closingAppHasWallpaper && openingAppHasWallpaper) {
if (DEBUG_APP_TRANSITIONS)
@@ -1251,15 +1257,16 @@
}
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New transit: " + AppTransition.appTransitionToString(transit));
- } else if ((oldWallpaper != null) && !mService.mOpeningApps.isEmpty()
- && !mService.mOpeningApps.contains(oldWallpaper.mAppToken)) {
- // We are transitioning from an activity with
- // a wallpaper to one without.
+ } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
+ && !openingApps.contains(oldWallpaper.mAppToken)
+ && closingApps.contains(oldWallpaper.mAppToken)) {
+ // We are transitioning from an activity with a wallpaper to one without.
transit = AppTransition.TRANSIT_WALLPAPER_CLOSE;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New transit away from wallpaper: "
+ AppTransition.appTransitionToString(transit));
- } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()) {
+ } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
+ openingApps.contains(wallpaperTarget.mAppToken)) {
// We are transitioning from an activity without
// a wallpaper to now showing the wallpaper
transit = AppTransition.TRANSIT_WALLPAPER_OPEN;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3f70e0c..2dd7cde 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -29,6 +29,7 @@
import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accounts.AccountManager;
+import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
@@ -54,6 +55,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -76,7 +78,6 @@
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RecoverySystem;
@@ -269,16 +270,12 @@
| DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
final Context mContext;
- final PackageManager mPackageManager;
+ final Injector mInjector;
+ final IPackageManager mIPackageManager;
final UserManager mUserManager;
final LocalService mLocalService;
- final PowerManagerInternal mPowerManagerInternal;
-
- final IWindowManager mIWindowManager;
- final NotificationManager mNotificationManager;
-
// Stores and loads state on device and profile owners.
private final Owners mOwners;
@@ -994,7 +991,6 @@
boolean removed = false;
if (DBG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
DevicePolicyData policy = getUserData(userHandle);
- IPackageManager pm = getIPackageManager();
synchronized (this) {
for (int i = policy.mAdminList.size() - 1; i >= 0; i--) {
ActiveAdmin aa = policy.mAdminList.get(i);
@@ -1003,9 +999,9 @@
// then check if the package and receiver still exist.
final String adminPackage = aa.info.getPackageName();
if (packageName == null || packageName.equals(adminPackage)) {
- if (pm.getPackageInfo(adminPackage, 0, userHandle) == null
- || pm.getReceiverInfo(aa.info.getComponent(), 0, userHandle)
- == null) {
+ if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null
+ || mIPackageManager.getReceiverInfo(
+ aa.info.getComponent(), 0, userHandle) == null) {
removed = true;
policy.mAdminList.remove(i);
policy.mAdminMap.remove(aa.info.getComponent());
@@ -1026,7 +1022,7 @@
|| packageName.equals(policy.mDelegatedCertInstallerPackage))) {
try {
// Check if delegated cert installer package is removed.
- if (pm.getPackageInfo(
+ if (mIPackageManager.getPackageInfo(
policy.mDelegatedCertInstallerPackage, 0, userHandle) == null) {
policy.mDelegatedCertInstallerPackage = null;
saveSettingsLocked(policy.mUserHandle);
@@ -1038,148 +1034,146 @@
}
}
- /** Unit test will override it to inject a mock. */
+ /**
+ * Unit test will subclass it to inject mocks.
+ */
@VisibleForTesting
- Owners newOwners() {
- return new Owners(mContext);
- }
+ static class Injector {
- /** Unit test will override it to inject a mock. */
- @VisibleForTesting
- UserManager getUserManager() {
- return UserManager.get(mContext);
- }
+ private final Context mContext;
- /** Unit test will override it to inject a mock. */
- @VisibleForTesting
- PackageManager getPackageManager() {
- return mContext.getPackageManager();
- }
+ Injector(Context context) {
+ mContext = context;
+ }
- /** Unit test will override it to inject a mock. */
- @VisibleForTesting
- NotificationManager getNotificationManager() {
- return mContext.getSystemService(NotificationManager.class);
- }
+ Owners newOwners() {
+ return new Owners(mContext);
+ }
- /** Unit test will override it to inject a mock. */
- @VisibleForTesting
- PowerManagerInternal getPowerManagerInternal() {
- return LocalServices.getService(PowerManagerInternal.class);
- }
+ UserManager getUserManager() {
+ return UserManager.get(mContext);
+ }
- /** Unit test will override it to inject a mock. */
- @VisibleForTesting
- IWindowManager getIWindowManager() {
- return IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
- }
+ NotificationManager getNotificationManager() {
+ return mContext.getSystemService(NotificationManager.class);
+ }
- /** Unit test will override it to inject a mock. */
- @VisibleForTesting
- IActivityManager getIActivityManager() {
- return ActivityManagerNative.getDefault();
- }
+ PowerManagerInternal getPowerManagerInternal() {
+ return LocalServices.getService(PowerManagerInternal.class);
+ }
- /** Unit test will override it to inject a mock. */
- @VisibleForTesting
- IPackageManager getIPackageManager() {
- return AppGlobals.getPackageManager();
- }
+ IWindowManager getIWindowManager() {
+ return IWindowManager.Stub
+ .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
+ }
- /** Unit test will override it to inject a mock. */
- @VisibleForTesting
- LockPatternUtils newLockPatternUtils(Context context) {
- return new LockPatternUtils(context);
- }
+ IActivityManager getIActivityManager() {
+ return ActivityManagerNative.getDefault();
+ }
- /** Unit test will override it to inject. */
- @VisibleForTesting
- Looper getMyLooper() {
- return Looper.myLooper();
- }
+ IPackageManager getIPackageManager() {
+ return AppGlobals.getPackageManager();
+ }
- @VisibleForTesting
- long binderClearCallingIdentity() {
- return Binder.clearCallingIdentity();
- }
+ IBackupManager getIBackupManager() {
+ return IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ }
- @VisibleForTesting
- void binderRestoreCallingIdentity(long token) {
- Binder.restoreCallingIdentity(token);
- }
+ IAudioService getIAudioService() {
+ return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
+ }
- @VisibleForTesting
- int binderGetCallingUid() {
- return Binder.getCallingUid();
- }
+ LockPatternUtils newLockPatternUtils() {
+ return new LockPatternUtils(mContext);
+ }
- @VisibleForTesting
- int binderGetCallingPid() {
- return Binder.getCallingPid();
- }
+ Looper getMyLooper() {
+ return Looper.myLooper();
+ }
- @VisibleForTesting
- UserHandle binderGetCallingUserHandle() {
- return Binder.getCallingUserHandle();
- }
+ long binderClearCallingIdentity() {
+ return Binder.clearCallingIdentity();
+ }
- @VisibleForTesting
- boolean binderIsCallingUidMyUid() {
- return getCallingUid() == Process.myUid();
- }
+ void binderRestoreCallingIdentity(long token) {
+ Binder.restoreCallingIdentity(token);
+ }
- @VisibleForTesting
- File environmentGetUserSystemDirectory(int userId) {
- return Environment.getUserSystemDirectory(userId);
- }
+ int binderGetCallingUid() {
+ return Binder.getCallingUid();
+ }
- @VisibleForTesting
- void powerManagerGoToSleep(long time, int reason, int flags) {
- mContext.getSystemService(PowerManager.class).goToSleep(time, reason, flags);
- }
+ int binderGetCallingPid() {
+ return Binder.getCallingPid();
+ }
- @VisibleForTesting
- boolean systemPropertiesGetBoolean(String key, boolean def) {
- return SystemProperties.getBoolean(key, def);
- }
+ UserHandle binderGetCallingUserHandle() {
+ return Binder.getCallingUserHandle();
+ }
- @VisibleForTesting
- long systemPropertiesGetLong(String key, long def) {
- return SystemProperties.getLong(key, def);
- }
+ boolean binderIsCallingUidMyUid() {
+ return getCallingUid() == Process.myUid();
+ }
- @VisibleForTesting
- String systemPropertiesGet(String key, String def) {
- return SystemProperties.get(key, def);
- }
+ File environmentGetUserSystemDirectory(int userId) {
+ return Environment.getUserSystemDirectory(userId);
+ }
- @VisibleForTesting
- String systemPropertiesGet(String key) {
- return SystemProperties.get(key);
- }
+ void powerManagerGoToSleep(long time, int reason, int flags) {
+ mContext.getSystemService(PowerManager.class).goToSleep(time, reason, flags);
+ }
- @VisibleForTesting
- void systemPropertiesSet(String key, String value) {
- SystemProperties.set(key, value);
+ boolean systemPropertiesGetBoolean(String key, boolean def) {
+ return SystemProperties.getBoolean(key, def);
+ }
+
+ long systemPropertiesGetLong(String key, long def) {
+ return SystemProperties.getLong(key, def);
+ }
+
+ String systemPropertiesGet(String key, String def) {
+ return SystemProperties.get(key, def);
+ }
+
+ String systemPropertiesGet(String key) {
+ return SystemProperties.get(key);
+ }
+
+ void systemPropertiesSet(String key, String value) {
+ SystemProperties.set(key, value);
+ }
+
+ boolean userManagerIsSplitSystemUser() {
+ return UserManager.isSplitSystemUser();
+ }
+
+ String getDevicePolicyFilePathForSystemUser() {
+ return "/data/system/";
+ }
}
/**
* Instantiates the service.
*/
public DevicePolicyManagerService(Context context) {
- mContext = context;
- mHandler = new Handler(getMyLooper());
- mOwners = newOwners();
+ this(new Injector(context));
+ }
- mUserManager = Preconditions.checkNotNull(getUserManager());
- mPackageManager = Preconditions.checkNotNull(getPackageManager());
- mPowerManagerInternal = Preconditions.checkNotNull(getPowerManagerInternal());
- mIWindowManager = Preconditions.checkNotNull(getIWindowManager());
- mNotificationManager = Preconditions.checkNotNull(getNotificationManager());
+ @VisibleForTesting
+ DevicePolicyManagerService(Injector injector) {
+ mInjector = injector;
+ mContext = Preconditions.checkNotNull(injector.mContext);
+ mHandler = new Handler(Preconditions.checkNotNull(injector.getMyLooper()));
+ mOwners = Preconditions.checkNotNull(injector.newOwners());
+
+ mUserManager = Preconditions.checkNotNull(injector.getUserManager());
+ mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
mLocalService = new LocalService();
- mHasFeature = mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
+ mHasFeature = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
if (!mHasFeature) {
// Skip the rest of the initialization
return;
@@ -1191,35 +1185,27 @@
filter.addAction(Intent.ACTION_USER_STARTED);
filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addDataScheme("package");
- context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
LocalServices.addService(DevicePolicyManagerInternal.class, mLocalService);
}
/**
- * We need it for testing to allow accessing context from the test-only subclass while this
- * class's constructor is still running.
- */
- @VisibleForTesting
- Context getContext() {
- return mContext;
- }
-
- /**
* Creates and loads the policy data from xml.
* @param userHandle the user for whom to load the policy data
* @return
*/
+ @NonNull
DevicePolicyData getUserData(int userHandle) {
synchronized (this) {
DevicePolicyData policy = mUserData.get(userHandle);
@@ -1243,11 +1229,11 @@
* @return
*/
DevicePolicyData getUserDataUnchecked(int userHandle) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
return getUserData(userHandle);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -1264,7 +1250,7 @@
if (policy != null) {
mUserData.remove(userHandle);
}
- File policyFile = new File(environmentGetUserSystemDirectory(userHandle),
+ File policyFile = new File(mInjector.environmentGetUserSystemDirectory(userHandle),
DEVICE_POLICIES_XML);
policyFile.delete();
Slog.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
@@ -1304,7 +1290,7 @@
alarmTime = now + alarmInterval;
}
- long token = binderClearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD,
@@ -1316,7 +1302,7 @@
am.set(AlarmManager.RTC, alarmTime, pi);
}
} finally {
- binderRestoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
}
@@ -1332,7 +1318,7 @@
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
throws SecurityException {
- final int callingUid = binderGetCallingUid();
+ final int callingUid = mInjector.binderGetCallingUid();
ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
if (result != null) {
@@ -1356,7 +1342,7 @@
+ admin.info.getTagForPolicy(reqPolicy));
} else {
throw new SecurityException("No active admin owned by uid "
- + binderGetCallingUid() + " for policy #" + reqPolicy);
+ + mInjector.binderGetCallingUid() + " for policy #" + reqPolicy);
}
}
@@ -1372,7 +1358,7 @@
}
if (admin.getUid() != uid) {
throw new SecurityException("Admin " + who + " is not owned by uid "
- + binderGetCallingUid());
+ + mInjector.binderGetCallingUid());
}
if (isActiveAdminWithPolicyForUserLocked(admin, reqPolicy, userId)) {
return admin;
@@ -1536,17 +1522,12 @@
private JournaledFile makeJournaledFile(int userHandle) {
final String base = userHandle == UserHandle.USER_SYSTEM
- ? getDevicePolicyFilePathForSystemUser() + DEVICE_POLICIES_XML
- : new File(environmentGetUserSystemDirectory(userHandle), DEVICE_POLICIES_XML)
- .getAbsolutePath();
+ ? mInjector.getDevicePolicyFilePathForSystemUser() + DEVICE_POLICIES_XML
+ : new File(mInjector.environmentGetUserSystemDirectory(userHandle),
+ DEVICE_POLICIES_XML).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
- @VisibleForTesting
- String getDevicePolicyFilePathForSystemUser() {
- return "/data/system/";
- }
-
private void saveSettingsLocked(int userHandle) {
DevicePolicyData policy = getUserData(userHandle);
JournaledFile journal = makeJournaledFile(userHandle);
@@ -1657,11 +1638,11 @@
private void sendChangedNotification(int userHandle) {
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -1793,9 +1774,9 @@
// sufficiently what is currently set. Note that this is only
// a sanity check in case the two get out of sync; this should
// never normally happen.
- final long identity = binderClearCallingIdentity();
+ final long identity = mInjector.binderClearCallingIdentity();
try {
- LockPatternUtils utils = newLockPatternUtils(mContext);
+ LockPatternUtils utils = mInjector.newLockPatternUtils();
if (utils.getActivePasswordQuality(userHandle) < policy.mActivePasswordQuality) {
Slog.w(LOG_TAG, "Active password quality 0x"
+ Integer.toHexString(policy.mActivePasswordQuality)
@@ -1811,7 +1792,7 @@
policy.mActivePasswordNonLetter = 0;
}
} finally {
- binderRestoreCallingIdentity(identity);
+ mInjector.binderRestoreCallingIdentity(identity);
}
validatePasswordOwnerLocked(policy);
@@ -1825,26 +1806,26 @@
}
private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
- IActivityManager am = getIActivityManager();
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- am.updateLockTaskPackages(userId, packages.toArray(new String[packages.size()]));
+ mInjector.getIActivityManager()
+ .updateLockTaskPackages(userId, packages.toArray(new String[packages.size()]));
} catch (RemoteException e) {
// Not gonna happen.
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
private void updateDeviceOwnerLocked() {
- IActivityManager am = getIActivityManager();
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- am.updateDeviceOwner(getDeviceOwner());
+ mInjector.getIActivityManager()
+ .updateDeviceOwner(getDeviceOwner());
} catch (RemoteException e) {
// Not gonna happen.
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -1889,17 +1870,17 @@
// Ensure the status of the camera is synced down to the system. Interested native services
// should monitor this value and act accordingly.
String cameraPropertyForUser = SYSTEM_PROP_DISABLE_CAMERA_PREFIX + policy.mUserHandle;
- boolean systemState = systemPropertiesGetBoolean(cameraPropertyForUser, false);
+ boolean systemState = mInjector.systemPropertiesGetBoolean(cameraPropertyForUser, false);
boolean cameraDisabled = getCameraDisabled(null, policy.mUserHandle);
if (cameraDisabled != systemState) {
- long token = binderClearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
String value = cameraDisabled ? "1" : "0";
if (DBG) Slog.v(LOG_TAG, "Change in camera state ["
+ cameraPropertyForUser + "] = " + value);
- systemPropertiesSet(cameraPropertyForUser, value);
+ mInjector.systemPropertiesSet(cameraPropertyForUser, value);
} finally {
- binderRestoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
}
}
@@ -1939,14 +1920,13 @@
private void ensureDeviceOwnerUserStarted() {
if (mOwners.hasDeviceOwner()) {
- final IActivityManager am = getIActivityManager();
final int userId = mOwners.getDeviceOwnerUserId();
if (VERBOSE_LOG) {
Log.v(LOG_TAG, "Starting non-system DO user: " + userId);
}
if (userId != UserHandle.USER_SYSTEM) {
try {
- am.startUserInBackground(userId);
+ mInjector.getIActivityManager().startUserInBackground(userId);
// STOPSHIP Prevent the DO user from being killed.
@@ -2047,7 +2027,7 @@
Log.e(LOG_TAG, "Could not connect to KeyChain service", e);
}
if (!hasCert) {
- mNotificationManager.cancelAsUser(
+ mInjector.getNotificationManager().cancelAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, userHandle);
return;
}
@@ -2092,7 +2072,7 @@
com.android.internal.R.color.system_notification_accent_color))
.build();
- mNotificationManager.notifyAsUser(
+ mInjector.getNotificationManager().notifyAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
}
}
@@ -2121,7 +2101,7 @@
throw new IllegalArgumentException("Bad admin: " + adminReceiver);
}
synchronized (this) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
if (!refreshing
&& getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
@@ -2148,7 +2128,7 @@
sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
onEnableData, null);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -2242,7 +2222,7 @@
if (admin == null) {
return;
}
- if (admin.getUid() != binderGetCallingUid()) {
+ if (admin.getUid() != mInjector.binderGetCallingUid()) {
// Active device owners must remain active admins.
if (isDeviceOwner(adminReceiver.getPackageName())) {
return;
@@ -2250,11 +2230,11 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
}
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
removeActiveAdminLocked(adminReceiver, userHandle);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -2524,7 +2504,7 @@
|| activeAdmin.crossProfileWidgetProviders.isEmpty()) {
return null;
}
- if (binderIsCallingUidMyUid()) {
+ if (mInjector.binderIsCallingUidMyUid()) {
return new ArrayList<>(activeAdmin.crossProfileWidgetProviders);
} else {
return activeAdmin.crossProfileWidgetProviders;
@@ -3089,7 +3069,7 @@
}
}
- int callingUid = binderGetCallingUid();
+ int callingUid = mInjector.binderGetCallingUid();
DevicePolicyData policy = getUserData(userHandle);
if (policy.mPasswordOwner >= 0 && policy.mPasswordOwner != callingUid) {
Slog.w(LOG_TAG, "resetPassword: already set by another uid and not entered by user");
@@ -3105,7 +3085,7 @@
// Don't do this with the lock held, because it is going to call
// back in to the service.
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
LockPatternUtils utils = new LockPatternUtils(mContext);
if (!TextUtils.isEmpty(password)) {
@@ -3126,7 +3106,7 @@
}
}
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
return true;
@@ -3176,7 +3156,7 @@
return;
}
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
if (timeMs <= 0) {
timeMs = Integer.MAX_VALUE;
@@ -3188,9 +3168,11 @@
}
policy.mLastMaximumTimeToLock = timeMs;
- mPowerManagerInternal.setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs);
+ // TODO It can overflow. Cap it.
+ mInjector.getPowerManagerInternal()
+ .setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -3242,26 +3224,21 @@
}
private void lockNowUnchecked() {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
// Power off the display
- powerManagerGoToSleep(SystemClock.uptimeMillis(),
+ mInjector.powerManagerGoToSleep(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN, 0);
// Ensure the device is locked
new LockPatternUtils(mContext).requireStrongAuth(
STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, UserHandle.USER_ALL);
- mIWindowManager.lockNow(null);
+ mInjector.getIWindowManager().lockNow(null);
} catch (RemoteException e) {
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
- private boolean isExtStorageEncrypted() {
- String state = systemPropertiesGet("vold.decrypt");
- return !"".equals(state);
- }
-
@Override
public void enforceCanManageCaCerts(ComponentName who) {
if (who == null) {
@@ -3276,7 +3253,7 @@
}
private boolean isCallerDelegatedCertInstaller() {
- final int callingUid = binderGetCallingUid();
+ final int callingUid = mInjector.binderGetCallingUid();
final int userHandle = UserHandle.getUserId(callingUid);
synchronized (this) {
final DevicePolicyData policy = getUserData(userHandle);
@@ -3311,7 +3288,7 @@
}
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
- final long id = binderClearCallingIdentity();
+ final long id = mInjector.binderClearCallingIdentity();
try {
final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
try {
@@ -3326,7 +3303,7 @@
Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
Thread.currentThread().interrupt();
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return false;
}
@@ -3342,7 +3319,7 @@
enforceCanManageCaCerts(admin);
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
- final long id = binderClearCallingIdentity();
+ final long id = mInjector.binderClearCallingIdentity();
try {
final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
try {
@@ -3358,7 +3335,7 @@
Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
Thread.currentThread().interrupt();
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -3374,7 +3351,7 @@
}
}
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
- final long id = binderClearCallingIdentity();
+ final long id = mInjector.binderClearCallingIdentity();
try {
final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
try {
@@ -3389,7 +3366,7 @@
Log.w(LOG_TAG, "Interrupted while installing certificate", e);
Thread.currentThread().interrupt();
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return false;
}
@@ -3398,11 +3375,11 @@
public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
final IBinder response) {
// Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
- if (UserHandle.getAppId(binderGetCallingUid()) != Process.SYSTEM_UID) {
+ if (UserHandle.getAppId(mInjector.binderGetCallingUid()) != Process.SYSTEM_UID) {
return;
}
- final UserHandle caller = binderGetCallingUserHandle();
+ final UserHandle caller = mInjector.binderGetCallingUserHandle();
// If there is a profile owner, redirect to that; otherwise query the device owner.
ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
if (aliasChooser == null && caller.isOwner()) {
@@ -3423,7 +3400,7 @@
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS, alias);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE, response);
- final long id = binderClearCallingIdentity();
+ final long id = mInjector.binderClearCallingIdentity();
try {
mContext.sendOrderedBroadcastAsUser(intent, caller, null, new BroadcastReceiver() {
@Override
@@ -3433,7 +3410,7 @@
}
}, null, Activity.RESULT_OK, null, null);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -3504,7 +3481,7 @@
final String source = admin.info.getComponent().flattenToShortString();
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
@@ -3525,7 +3502,7 @@
wipeDeviceOrUserLocked(wipeExtRequested, userHandle,
"DevicePolicyManager.wipeData() from " + source);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -3538,7 +3515,7 @@
@Override
public void run() {
try {
- IActivityManager am = getIActivityManager();
+ IActivityManager am = mInjector.getIActivityManager();
if (am.getCurrentUser().id == userHandle) {
am.switchUser(UserHandle.USER_SYSTEM);
}
@@ -3566,11 +3543,11 @@
.setColor(mContext.getColor(R.color.system_notification_accent_color))
.setStyle(new Notification.BigTextStyle().bigText(contentText))
.build();
- mNotificationManager.notify(PROFILE_WIPED_NOTIFICATION_ID, notification);
+ mInjector.getNotificationManager().notify(PROFILE_WIPED_NOTIFICATION_ID, notification);
}
private void clearWipeProfileNotification() {
- mNotificationManager.cancel(PROFILE_WIPED_NOTIFICATION_ID);
+ mInjector.getNotificationManager().cancel(PROFILE_WIPED_NOTIFICATION_ID);
}
@Override
@@ -3630,7 +3607,7 @@
|| p.mActivePasswordNumeric != numbers
|| p.mActivePasswordSymbols != symbols
|| p.mActivePasswordNonLetter != nonletter) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
p.mActivePasswordQuality = quality;
p.mActivePasswordLength = length;
@@ -3648,7 +3625,7 @@
DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -3684,7 +3661,7 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
boolean wipeData = false;
int identifier = 0;
@@ -3715,7 +3692,7 @@
"reportFailedPasswordAttempt()");
}
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -3728,7 +3705,7 @@
synchronized (this) {
DevicePolicyData policy = getUserData(userHandle);
if (policy.mFailedPasswordAttempts != 0 || policy.mPasswordOwner >= 0) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
policy.mFailedPasswordAttempts = 0;
policy.mPasswordOwner = -1;
@@ -3739,7 +3716,7 @@
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
}
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -3790,11 +3767,11 @@
// Reset the global proxy accordingly
// Do this using system permissions, as apps cannot write to secure settings
- long origId = binderClearCallingIdentity();
+ long origId = mInjector.binderClearCallingIdentity();
try {
resetGlobalProxyLocked(policy);
} finally {
- binderRestoreCallingIdentity(origId);
+ mInjector.binderRestoreCallingIdentity(origId);
}
return null;
}
@@ -3829,13 +3806,13 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
}
- long token = binderClearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
ConnectivityManager connectivityManager = (ConnectivityManager)
mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.setGlobalProxy(proxyInfo);
} finally {
- binderRestoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
}
@@ -3996,15 +3973,15 @@
* {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}.
*/
private int getEncryptionStatus() {
- String status = systemPropertiesGet("ro.crypto.state", "unsupported");
+ String status = mInjector.systemPropertiesGet("ro.crypto.state", "unsupported");
if ("encrypted".equalsIgnoreCase(status)) {
- final long token = binderClearCallingIdentity();
+ final long token = mInjector.binderClearCallingIdentity();
try {
return LockPatternUtils.isDeviceEncrypted()
? DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
: DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY;
} finally {
- binderRestoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
} else if ("unencrypted".equalsIgnoreCase(status)) {
return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
@@ -4069,13 +4046,13 @@
}
private void updateScreenCaptureDisabledInWindowManager(int userHandle, boolean disabled) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- mIWindowManager.setScreenCaptureDisabled(userHandle, disabled);
+ mInjector.getIWindowManager().setScreenCaptureDisabled(userHandle, disabled);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -4100,12 +4077,12 @@
// Turn AUTO_TIME on in settings if it is required
if (required) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -4214,7 +4191,7 @@
return 0;
}
enforceCrossUserPermission(userHandle);
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (this) {
if (who != null) {
@@ -4257,7 +4234,7 @@
return which;
}
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -4267,7 +4244,7 @@
return false;
}
if (packageName == null
- || !Owners.isInstalledForUser(packageName, userId)) {
+ || !isPackageInstalledForUser(packageName, userId)) {
throw new IllegalArgumentException("Invalid package name " + packageName
+ " for device owner");
}
@@ -4275,15 +4252,13 @@
enforceCanSetDeviceOwner(userId);
// Shutting down backup manager service permanently.
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- IBackupManager ibm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, false);
} catch (RemoteException e) {
throw new IllegalStateException("Failed deactivating backup service.", e);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
mOwners.setDeviceOwner(packageName, ownerName, userId);
@@ -4291,12 +4266,12 @@
updateDeviceOwnerLocked();
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
- ident = binderClearCallingIdentity();
+ ident = mInjector.binderClearCallingIdentity();
try {
// TODO Send to system too?
mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
return true;
}
@@ -4328,11 +4303,14 @@
if (!mHasFeature) {
return null;
}
+ // TODO: Do we really need it? getDeviceOwner() doesn't require it.
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
synchronized (this) {
if (!mOwners.hasDeviceOwner()) {
return null;
}
+ // TODO This totally ignores the name passed to setDeviceOwner (change for b/20679292)
+ // Should setDeviceOwner/ProfileOwner still take a name?
String deviceOwnerPackage = mOwners.getDeviceOwnerPackageName();
return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_SYSTEM);
}
@@ -4361,7 +4339,7 @@
Preconditions.checkNotNull(packageName, "packageName is null");
try {
int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
- if (uid != binderGetCallingUid()) {
+ if (uid != mInjector.binderGetCallingUid()) {
throw new SecurityException("Invalid packageName");
}
} catch (NameNotFoundException e) {
@@ -4377,15 +4355,13 @@
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
// Reactivate backup service.
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- IBackupManager ibm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, true);
} catch (RemoteException e) {
throw new IllegalStateException("Failed reactivating backup service.", e);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -4397,15 +4373,16 @@
}
if (initializer == null ||
!mOwners.hasDeviceOwner() ||
- !Owners.isInstalledForUser(initializer.getPackageName(),
+ !isPackageInstalledForUser(initializer.getPackageName(),
mOwners.getDeviceOwnerUserId())) {
throw new IllegalArgumentException("Invalid component name " + initializer
+ " for device initializer or no device owner set");
}
boolean isInitializerSystemApp;
try {
- isInitializerSystemApp = isSystemApp(getIPackageManager(),
- initializer.getPackageName(), binderGetCallingUserHandle().getIdentifier());
+ isInitializerSystemApp = isSystemApp(mIPackageManager,
+ initializer.getPackageName(),
+ mInjector.binderGetCallingUserHandle().getIdentifier());
} catch (RemoteException | IllegalArgumentException e) {
isInitializerSystemApp = false;
Slog.e(LOG_TAG, "Fail to check if device initialzer is system app.", e);
@@ -4488,9 +4465,9 @@
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, UserHandle.getCallingUserId());
- if (admin.getUid() != binderGetCallingUid()) {
+ if (admin.getUid() != mInjector.binderGetCallingUid()) {
throw new SecurityException("Admin " + who + " is not owned by uid "
- + binderGetCallingUid());
+ + mInjector.binderGetCallingUid());
}
if (!isDeviceInitializer(admin.info.getPackageName())
@@ -4499,12 +4476,12 @@
"clearDeviceInitializer can only be called by the device initializer/owner");
}
synchronized (this) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
mOwners.clearDeviceInitializer();
mOwners.writeDeviceOwner();
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -4515,7 +4492,7 @@
return false;
}
if (who == null
- || !Owners.isInstalledForUser(who.getPackageName(), userHandle)) {
+ || !isPackageInstalledForUser(who.getPackageName(), userHandle)) {
throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
@@ -4532,7 +4509,7 @@
if (!mHasFeature) {
return;
}
- UserHandle callingUser = binderGetCallingUserHandle();
+ UserHandle callingUser = mInjector.binderGetCallingUserHandle();
// Check if this is the profile owner who is calling
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
synchronized (this) {
@@ -4552,15 +4529,15 @@
policy.mStatusBarDisabled = false;
saveSettingsLocked(userId);
- final long ident = binderClearCallingIdentity();
+ final long ident = mInjector.binderClearCallingIdentity();
try {
clearUserRestrictions(userHandle);
- getIPackageManager().updatePermissionFlagsForAllApps(
+ mIPackageManager.updatePermissionFlagsForAllApps(
PackageManager.FLAG_PERMISSION_POLICY_FIXED,
0 /* flagValues */, userHandle.getIdentifier());
} catch (RemoteException re) {
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -4568,11 +4545,9 @@
private void clearUserRestrictions(UserHandle userHandle) {
Bundle userRestrictions = mUserManager.getUserRestrictions();
mUserManager.setUserRestrictions(new Bundle(), userHandle);
- IAudioService iAudioService = IAudioService.Stub.asInterface(
- ServiceManager.getService(Context.AUDIO_SERVICE));
if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
try {
- iAudioService.setMasterMute(true, 0, mContext.getPackageName(),
+ mInjector.getIAudioService().setMasterMute(true, 0, mContext.getPackageName(),
userHandle.getIdentifier());
} catch (RemoteException e) {
// Not much we can do here.
@@ -4580,7 +4555,7 @@
}
if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
try {
- iAudioService.setMicrophoneMute(true, mContext.getPackageName(),
+ mInjector.getIAudioService().setMicrophoneMute(true, mContext.getPackageName(),
userHandle.getIdentifier());
} catch (RemoteException e) {
// Not much we can do here.
@@ -4597,9 +4572,7 @@
if (!mHasFeature) {
return true;
}
- DevicePolicyData policy = getUserData(userHandle);
- // If policy is null, return true, else check if the setup has completed.
- return policy == null || policy.mUserSetupComplete;
+ return getUserData(userHandle).mUserSetupComplete;
}
@Override
@@ -4620,11 +4593,10 @@
"This method can only be called by device initializers");
}
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
if (!isDeviceOwner(activeAdmin.info.getPackageName())) {
- IPackageManager ipm = getIPackageManager();
- ipm.setComponentEnabledSetting(who,
+ mIPackageManager.setComponentEnabledSetting(who,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP, userId);
@@ -4641,7 +4613,7 @@
Log.i(LOG_TAG, "Can't talk to package manager", e);
return false;
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return true;
}
@@ -4659,7 +4631,7 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setUserEnabled(userId);
UserInfo parent = mUserManager.getProfileParent(userId);
@@ -4669,7 +4641,7 @@
Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, new UserHandle(parent.id));
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -4681,11 +4653,11 @@
// Check if this is the profile owner (includes device owner).
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setUserName(userId, profileName);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -4735,7 +4707,7 @@
* Canonical name for a given package.
*/
private String getApplicationLabel(String packageName, int userHandle) {
- long token = binderClearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
final Context userContext;
try {
@@ -4753,7 +4725,7 @@
}
return result != null ? result.toString() : null;
} finally {
- binderRestoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
}
@@ -4779,7 +4751,7 @@
throw new IllegalStateException("Trying to set the profile owner, but profile owner "
+ "is already set.");
}
- int callingUid = binderGetCallingUid();
+ int callingUid = mInjector.binderGetCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
if (hasUserSetupCompleted(userHandle) &&
AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) {
@@ -4812,13 +4784,13 @@
throw new IllegalStateException("User not running: " + userId);
}
- int callingUid = binderGetCallingUid();
+ int callingUid = mInjector.binderGetCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
if (!hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
return;
}
// STOPSHIP Do proper check in split user mode
- if (!UserManager.isSplitSystemUser()) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
if (mUserManager.getUserCount() > 1) {
throw new IllegalStateException(
"Not allowed to set the device owner because there "
@@ -4837,7 +4809,7 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
// STOPSHIP Do proper check in split user mode
- if (!UserManager.isSplitSystemUser()) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
throw new IllegalStateException("Cannot set the device owner if the device is "
+ "already set-up");
@@ -4849,7 +4821,7 @@
if (userHandle < 0) {
throw new IllegalArgumentException("Invalid userId " + userHandle);
}
- final int callingUid = binderGetCallingUid();
+ final int callingUid = mInjector.binderGetCallingUid();
if (userHandle == UserHandle.getUserId(callingUid)) return;
if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
mContext.enforceCallingOrSelfPermission(
@@ -4865,32 +4837,31 @@
}
private UserInfo getProfileParent(int userHandle) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
return mUserManager.getProfileParent(userHandle);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
private boolean isManagedProfile(int userHandle) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
return mUserManager.getUserInfo(userHandle).isManagedProfile();
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
private void enableIfNecessary(String packageName, int userId) {
try {
- IPackageManager ipm = getIPackageManager();
- ApplicationInfo ai = ipm.getApplicationInfo(packageName,
+ ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
userId);
if (ai.enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
- ipm.setApplicationEnabledSetting(packageName,
+ mIPackageManager.setApplicationEnabledSetting(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.DONT_KILL_APP, userId, "DevicePolicyManager");
}
@@ -4904,8 +4875,8 @@
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump DevicePolicyManagerService from from pid="
- + binderGetCallingPid()
- + ", uid=" + binderGetCallingUid());
+ + mInjector.binderGetCallingPid()
+ + ", uid=" + mInjector.binderGetCallingUid());
return;
}
@@ -4946,14 +4917,13 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- IPackageManager pm = getIPackageManager();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- pm.addPersistentPreferredActivity(filter, activity, userHandle);
+ mIPackageManager.addPersistentPreferredActivity(filter, activity, userHandle);
} catch (RemoteException re) {
// Shouldn't happen
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -4965,14 +4935,13 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- IPackageManager pm = getIPackageManager();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- pm.clearPackagePersistentPreferredActivities(packageName, userHandle);
+ mIPackageManager.clearPackagePersistentPreferredActivities(packageName, userHandle);
} catch (RemoteException re) {
// Shouldn't happen
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -4984,11 +4953,11 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5086,7 +5055,7 @@
@Override
public ComponentName getRestrictionsProvider(int userHandle) {
synchronized (this) {
- if (binderGetCallingUid() != Process.SYSTEM_UID) {
+ if (mInjector.binderGetCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system can query the permission provider");
}
DevicePolicyData userData = getUserData(userHandle);
@@ -5101,8 +5070,7 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- IPackageManager pm = getIPackageManager();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo parent = mUserManager.getProfileParent(callingUserId);
if (parent == null) {
@@ -5111,17 +5079,17 @@
return;
}
if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) {
- pm.addCrossProfileIntentFilter(filter, who.getPackageName(), callingUserId,
- parent.id, 0);
+ mIPackageManager.addCrossProfileIntentFilter(
+ filter, who.getPackageName(), callingUserId, parent.id, 0);
}
if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) {
- pm.addCrossProfileIntentFilter(filter, who.getPackageName(),
+ mIPackageManager.addCrossProfileIntentFilter(filter, who.getPackageName(),
parent.id, callingUserId, 0);
}
} catch (RemoteException re) {
// Shouldn't happen
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5132,8 +5100,7 @@
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- IPackageManager pm = getIPackageManager();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo parent = mUserManager.getProfileParent(callingUserId);
if (parent == null) {
@@ -5142,15 +5109,16 @@
return;
}
// Removing those that go from the managed profile to the parent.
- pm.clearCrossProfileIntentFilters(callingUserId, who.getPackageName());
+ mIPackageManager.clearCrossProfileIntentFilters(
+ callingUserId, who.getPackageName());
// And those that go from the parent to the managed profile.
// If we want to support multiple managed profiles, we will have to only remove
// those that have callingUserId as their target.
- pm.clearCrossProfileIntentFilters(parent.id, who.getPackageName());
+ mIPackageManager.clearCrossProfileIntentFilters(parent.id, who.getPackageName());
} catch (RemoteException re) {
// Shouldn't happen
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5162,7 +5130,7 @@
private boolean checkPackagesInPermittedListOrSystem(List<String> enabledPackages,
List<String> permittedList) {
int userIdToCheck = UserHandle.getCallingUserId();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
// If we have an enabled packages list for a managed profile the packages
// we should check are installed for the parent user.
@@ -5171,12 +5139,11 @@
userIdToCheck = user.profileGroupId;
}
- IPackageManager pm = getIPackageManager();
for (String enabledPackage : enabledPackages) {
boolean systemService = false;
try {
- ApplicationInfo applicationInfo = pm.getApplicationInfo(enabledPackage,
- PackageManager.GET_UNINSTALLED_PACKAGES, userIdToCheck);
+ ApplicationInfo applicationInfo = mIPackageManager.getApplicationInfo(
+ enabledPackage, PackageManager.GET_UNINSTALLED_PACKAGES, userIdToCheck);
systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
} catch (RemoteException e) {
Log.i(LOG_TAG, "Can't talk to package managed", e);
@@ -5186,7 +5153,7 @@
}
}
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return true;
}
@@ -5211,7 +5178,7 @@
if (packageList != null) {
int userId = UserHandle.getCallingUserId();
List<AccessibilityServiceInfo> enabledServices = null;
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo user = mUserManager.getUserInfo(userId);
if (user.isManagedProfile()) {
@@ -5221,7 +5188,7 @@
enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
if (enabledServices != null) {
@@ -5292,7 +5259,7 @@
// If we have a permitted list add all system accessibility services.
if (result != null) {
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo user = mUserManager.getUserInfo(userId);
if (user.isManagedProfile()) {
@@ -5303,7 +5270,6 @@
List<AccessibilityServiceInfo> installedServices =
accessibilityManager.getInstalledAccessibilityServiceList();
- IPackageManager pm = getIPackageManager();
if (installedServices != null) {
for (AccessibilityServiceInfo service : installedServices) {
ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
@@ -5314,7 +5280,7 @@
}
}
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -5324,12 +5290,12 @@
private boolean checkCallerIsCurrentUserOrProfile() {
int callingUserId = UserHandle.getCallingUserId();
- long token = binderClearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
UserInfo currentUser;
UserInfo callingUser = mUserManager.getUserInfo(callingUserId);
try {
- currentUser = getIActivityManager().getCurrentUser();
+ currentUser = mInjector.getIActivityManager().getCurrentUser();
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to talk to activity managed.", e);
return false;
@@ -5346,7 +5312,7 @@
return false;
}
} finally {
- binderRestoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
return true;
}
@@ -5412,7 +5378,7 @@
public List getPermittedInputMethodsForCurrentUser() {
UserInfo currentUser;
try {
- currentUser = getIActivityManager().getCurrentUser();
+ currentUser = mInjector.getIActivityManager().getCurrentUser();
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to make remote calls to get current user", e);
// Activity managed is dead, just allow all IMEs
@@ -5450,9 +5416,8 @@
InputMethodManager inputMethodManager = (InputMethodManager) mContext
.getSystemService(Context.INPUT_METHOD_SERVICE);
List<InputMethodInfo> imes = inputMethodManager.getInputMethodList();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = getIPackageManager();
if (imes != null) {
for (InputMethodInfo ime : imes) {
ServiceInfo serviceInfo = ime.getServiceInfo();
@@ -5463,7 +5428,7 @@
}
}
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
return result;
@@ -5476,7 +5441,7 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo userInfo = mUserManager.createUser(name, 0 /* flags */);
if (userInfo != null) {
@@ -5484,7 +5449,7 @@
}
return null;
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5496,21 +5461,19 @@
if (user == null) {
return null;
}
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
String profileOwnerPkg = profileOwnerComponent.getPackageName();
- final IPackageManager ipm = getIPackageManager();
- IActivityManager activityManager = getIActivityManager();
final int userHandle = user.getIdentifier();
try {
// Install the profile owner if not present.
- if (!ipm.isPackageAvailable(profileOwnerPkg, userHandle)) {
- ipm.installExistingPackageAsUser(profileOwnerPkg, userHandle);
+ if (!mIPackageManager.isPackageAvailable(profileOwnerPkg, userHandle)) {
+ mIPackageManager.installExistingPackageAsUser(profileOwnerPkg, userHandle);
}
// Start user in background.
- activityManager.startUserInBackground(userHandle);
+ mInjector.getIActivityManager().startUserInBackground(userHandle);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to make remote calls for configureUser", e);
}
@@ -5519,7 +5482,7 @@
setProfileOwner(profileOwnerComponent, ownerName, userHandle);
return user;
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -5529,11 +5492,11 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
return mUserManager.removeUser(userHandle.getIdentifier());
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5544,18 +5507,18 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
int userId = UserHandle.USER_SYSTEM;
if (userHandle != null) {
userId = userHandle.getIdentifier();
}
- return getIActivityManager().switchUser(userId);
+ return mInjector.getIActivityManager().switchUser(userId);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Couldn't switch user", e);
return false;
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5568,14 +5531,14 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
// if no restrictions were saved, mUserManager.getApplicationRestrictions
// returns null, but DPM method should return an empty Bundle as per JavaDoc
return bundle != null ? bundle : Bundle.EMPTY;
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5598,22 +5561,15 @@
}
boolean alreadyRestricted = mUserManager.hasUserRestriction(key, user);
- IAudioService iAudioService = null;
- if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)
- || UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService = IAudioService.Stub.asInterface(
- ServiceManager.getService(Context.AUDIO_SERVICE));
- }
-
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
if (enabled && !alreadyRestricted) {
if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- iAudioService.setMicrophoneMute(true, mContext.getPackageName(),
- userHandle);
+ mInjector.getIAudioService()
+ .setMicrophoneMute(true, mContext.getPackageName(), userHandle);
} else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService.setMasterMute(true, 0, mContext.getPackageName(),
- userHandle);
+ mInjector.getIAudioService()
+ .setMasterMute(true, 0, mContext.getPackageName(), userHandle);
} else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
@@ -5651,8 +5607,8 @@
// Send out notifications however as some clients may want to reread the
// value which actually changed due to a restriction having been applied.
final String property = Settings.Secure.SYS_PROP_SETTING_VERSION;
- long version = systemPropertiesGetLong(property, 0) + 1;
- systemPropertiesSet(property, Long.toString(version));
+ long version = mInjector.systemPropertiesGetLong(property, 0) + 1;
+ mInjector.systemPropertiesSet(property, Long.toString(version));
final String name = Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
Uri url = Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
@@ -5661,17 +5617,17 @@
}
if (!enabled && alreadyRestricted) {
if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- iAudioService.setMicrophoneMute(false, mContext.getPackageName(),
- userHandle);
+ mInjector.getIAudioService()
+ .setMicrophoneMute(false, mContext.getPackageName(), userHandle);
} else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService.setMasterMute(false, 0, mContext.getPackageName(),
- userHandle);
+ mInjector.getIAudioService()
+ .setMasterMute(false, 0, mContext.getPackageName(), userHandle);
}
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
sendChangedNotification(userHandle);
}
@@ -5685,15 +5641,15 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = getIPackageManager();
- return pm.setApplicationHiddenSettingAsUser(packageName, hidden, callingUserId);
+ return mIPackageManager.setApplicationHiddenSettingAsUser(
+ packageName, hidden, callingUserId);
} catch (RemoteException re) {
// shouldn't happen
Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return false;
}
@@ -5706,15 +5662,15 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = getIPackageManager();
- return pm.getApplicationHiddenSettingAsUser(packageName, callingUserId);
+ return mIPackageManager.getApplicationHiddenSettingAsUser(
+ packageName, callingUserId);
} catch (RemoteException re) {
// shouldn't happen
Slog.e(LOG_TAG, "Failed to getApplicationHiddenSettingAsUser", re);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return false;
}
@@ -5729,7 +5685,7 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
if (DBG) {
@@ -5745,19 +5701,18 @@
primaryUser = um.getUserInfo(userId);
}
- IPackageManager pm = getIPackageManager();
- if (!isSystemApp(pm, packageName, primaryUser.id)) {
+ if (!isSystemApp(mIPackageManager, packageName, primaryUser.id)) {
throw new IllegalArgumentException("Only system apps can be enabled this way.");
}
// Install the app.
- pm.installExistingPackageAsUser(packageName, userId);
+ mIPackageManager.installExistingPackageAsUser(packageName, userId);
} catch (RemoteException re) {
// shouldn't happen
Slog.wtf(LOG_TAG, "Failed to install " + packageName, re);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5771,7 +5726,7 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserManager um = UserManager.get(mContext);
@@ -5782,8 +5737,8 @@
primaryUser = um.getUserInfo(userId);
}
- IPackageManager pm = getIPackageManager();
- List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent,
+ List<ResolveInfo> activitiesToEnable = mIPackageManager.queryIntentActivities(
+ intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
0, // no flags
primaryUser.id);
@@ -5794,9 +5749,9 @@
for (ResolveInfo info : activitiesToEnable) {
if (info.activityInfo != null) {
String packageName = info.activityInfo.packageName;
- if (isSystemApp(pm, packageName, primaryUser.id)) {
+ if (isSystemApp(mIPackageManager, packageName, primaryUser.id)) {
numberOfAppsInstalled++;
- pm.installExistingPackageAsUser(packageName, userId);
+ mIPackageManager.installExistingPackageAsUser(packageName, userId);
} else {
Slog.d(LOG_TAG, "Not enabling " + packageName + " since is not a"
+ " system app");
@@ -5810,7 +5765,7 @@
Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
return 0;
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5876,15 +5831,14 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = getIPackageManager();
- pm.setBlockUninstallForUser(packageName, uninstallBlocked, userId);
+ mIPackageManager.setBlockUninstallForUser(packageName, uninstallBlocked, userId);
} catch (RemoteException re) {
// Shouldn't happen.
Slog.e(LOG_TAG, "Failed to setBlockUninstallForUser", re);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5901,15 +5855,14 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
}
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = getIPackageManager();
- return pm.getBlockUninstallForUser(packageName, userId);
+ return mIPackageManager.getBlockUninstallForUser(packageName, userId);
} catch (RemoteException re) {
// Shouldn't happen.
Slog.e(LOG_TAG, "Failed to getBlockUninstallForUser", re);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
return false;
@@ -5961,7 +5914,7 @@
actualLookupKey, actualContactId, originalIntent);
final int callingUserId = UserHandle.getCallingUserId();
- final long ident = binderClearCallingIdentity();
+ final long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (this) {
final int managedUserId = getManagedUserId(callingUserId);
@@ -5979,7 +5932,7 @@
mContext, intent, new UserHandle(managedUserId));
}
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -6060,7 +6013,7 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- int userHandle = binderGetCallingUserHandle().getIdentifier();
+ int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
}
}
@@ -6082,7 +6035,7 @@
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- int userHandle = binderGetCallingUserHandle().getIdentifier();
+ int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
final List<String> packages = getLockTaskPackagesLocked(userHandle);
return packages.toArray(new String[packages.size()]);
}
@@ -6101,7 +6054,7 @@
@Override
public boolean isLockTaskPermitted(String pkg) {
// Get current user's devicepolicy
- int uid = binderGetCallingUid();
+ int uid = mInjector.binderGetCallingUid();
int userHandle = UserHandle.getUserId(uid);
DevicePolicyData policy = getUserData(userHandle);
synchronized (this) {
@@ -6120,7 +6073,7 @@
@Override
public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
- if (binderGetCallingUid() != Process.SYSTEM_UID) {
+ if (mInjector.binderGetCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("notifyLockTaskModeChanged can only be called by system");
}
synchronized (this) {
@@ -6171,11 +6124,11 @@
}
}
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
Settings.Global.putString(contentResolver, setting, value);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -6200,11 +6153,11 @@
"Permission denial: Profile owners cannot update %1$s", setting));
}
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
Settings.Secure.putStringForUser(contentResolver, setting, value, callingUserId);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -6215,7 +6168,7 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long identity = binderClearCallingIdentity();
+ long identity = mInjector.binderClearCallingIdentity();
try {
IAudioService iAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
@@ -6223,7 +6176,7 @@
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to setMasterMute", re);
} finally {
- binderRestoreCallingIdentity(identity);
+ mInjector.binderRestoreCallingIdentity(identity);
}
}
}
@@ -6247,11 +6200,11 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long id = binderClearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setUserIcon(userId, icon);
} finally {
- binderRestoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -6265,7 +6218,7 @@
final int userId = UserHandle.getCallingUserId();
LockPatternUtils utils = new LockPatternUtils(mContext);
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
// disallow disabling the keyguard if a password is currently set
if (disabled && utils.isSecure(userId)) {
@@ -6273,7 +6226,7 @@
}
utils.setLockScreenDisabled(disabled, userId);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
return true;
}
@@ -6296,7 +6249,7 @@
}
private boolean setStatusBarDisabledInternal(boolean disabled, int userId) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(
ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
@@ -6310,7 +6263,7 @@
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to disable the status bar", e);
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
return false;
}
@@ -6541,7 +6494,7 @@
Log.e(LOG_TAG, "Cannot find device owner package", e);
}
if (receivers != null) {
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
for (int i = 0; i < receivers.length; i++) {
if (permission.BIND_DEVICE_ADMIN.equals(receivers[i].permission)) {
@@ -6551,7 +6504,7 @@
}
}
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -6582,12 +6535,12 @@
@Override
public boolean setPermissionGrantState(ComponentName admin, String packageName,
String permission, int grantState) throws RemoteException {
- UserHandle user = binderGetCallingUserHandle();
+ UserHandle user = mInjector.binderGetCallingUserHandle();
synchronized (this) {
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- final ApplicationInfo ai = getIPackageManager()
+ final ApplicationInfo ai = mIPackageManager
.getApplicationInfo(packageName, 0, user.getIdentifier());
final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
if (targetSdkVersion < android.os.Build.VERSION_CODES.M) {
@@ -6619,7 +6572,7 @@
} catch (SecurityException se) {
return false;
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -6629,12 +6582,12 @@
String permission) throws RemoteException {
PackageManager packageManager = mContext.getPackageManager();
- UserHandle user = binderGetCallingUserHandle();
+ UserHandle user = mInjector.binderGetCallingUserHandle();
synchronized (this) {
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long ident = binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- int granted = getIPackageManager().checkPermission(permission,
+ int granted = mIPackageManager.checkPermission(permission,
packageName, user.getIdentifier());
int permFlags = packageManager.getPermissionFlags(permission, packageName, user);
if ((permFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED)
@@ -6648,8 +6601,17 @@
: DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
}
} finally {
- binderRestoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
+
+ boolean isPackageInstalledForUser(String packageName, int userHandle) {
+ try {
+ PackageInfo pi = mIPackageManager.getPackageInfo(packageName, 0, userHandle);
+ return (pi != null) && (pi.applicationInfo.flags != 0);
+ } catch (RemoteException re) {
+ throw new RuntimeException("Package manager has died", re);
+ }
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 87cf28f..370cf48 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -101,7 +101,7 @@
public Owners(Context context) {
mContext = context;
- mUserManager = UserManager.get(mContext);
+ mUserManager = context.getSystemService(UserManager.class);
}
/**
@@ -230,20 +230,6 @@
return mDeviceOwner != null;
}
- static boolean isInstalledForUser(String packageName, int userHandle) {
- try {
- PackageInfo pi = (AppGlobals.getPackageManager())
- .getPackageInfo(packageName, 0, userHandle);
- if (pi != null && pi.applicationInfo.flags != 0) {
- return true;
- }
- } catch (RemoteException re) {
- throw new RuntimeException("Package manager has died", re);
- }
-
- return false;
- }
-
private boolean readLegacyOwnerFile(File file) {
if (!file.exists()) {
// Already migrated or the device has no owners.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 93dc6cb..1ec1a46 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -867,6 +867,11 @@
if (!disableNonCoreServices) {
mSystemServiceManager.startService(DockObserver.class);
+
+ if (context.getPackageManager().hasSystemFeature
+ (PackageManager.FEATURE_WATCH)) {
+ mSystemServiceManager.startService(ThermalObserver.class);
+ }
}
traceBeginAndSlog("StartWiredAccessoryManager");
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 9ee9cf4..e0d2ac1 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -244,8 +244,9 @@
private PendingIntent createStateMachineCommandIntent(final String cmdName, final int cmd) {
String action = DhcpClient.class.getName() + "." + mIfaceName + "." + cmdName;
- Intent intent = new Intent(action, null)
- .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ Intent intent = new Intent(action, null).addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
// TODO: The intent's package covers the whole of the system server, so it's pretty generic.
// Consider adding some sort of token as well.
intent.setPackage(mContext.getPackageName());
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 3acd565..53f55cd 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -60,6 +60,74 @@
* Monitors on-link IP reachability and notifies callers whenever any on-link
* addresses of interest appear to have become unresponsive.
*
+ * This code does not concern itself with "why" a neighbour might have become
+ * unreachable. Instead, it primarily reacts to the kernel's notion of IP
+ * reachability for each of the neighbours we know to be critically important
+ * to normal network connectivity. As such, it is often "just the messenger":
+ * the neighbours about which it warns are already deemed by the kernel to have
+ * become unreachable.
+ *
+ *
+ * How it works:
+ *
+ * 1. The "on-link neighbours of interest" found in a given LinkProperties
+ * instance are added to a "watch list" via #updateLinkProperties().
+ * This usually means all default gateways and any on-link DNS servers.
+ *
+ * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH,
+ * RTM_DELNEIGH), watching only for neighbours in the watch list.
+ *
+ * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and
+ * even NUD_PROBE is perfectly normal; we merely record the new state.
+ *
+ * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due
+ * to garbage collection. This is not necessarily of immediate
+ * concern; we record the neighbour as moving to NUD_NONE.
+ *
+ * - A neighbour transitioning to NUD_FAILED (for any reason) is
+ * critically important and is handled as described below in #4.
+ *
+ * 3. All on-link neighbours in the watch list can be forcibly "probed" by
+ * calling #probeAll(). This should be called whenever it is important to
+ * verify that critical neighbours on the link are still reachable, e.g.
+ * when roaming between BSSIDs.
+ *
+ * - The kernel will send unicast ARP requests for IPv4 neighbours and
+ * unicast NS packets for IPv6 neighbours. The expected replies will
+ * likely be unicast.
+ *
+ * - The forced probing is done holding a wakelock. The kernel may,
+ * however, initiate probing of a neighbor on its own, i.e. whenever
+ * a neighbour has expired from NUD_DELAY.
+ *
+ * - The kernel sends:
+ *
+ * /proc/sys/net/ipv{4,6}/neigh/<ifname>/ucast_solicit
+ *
+ * number of probes (usually 3) every:
+ *
+ * /proc/sys/net/ipv{4,6}/neigh/<ifname>/retrans_time_ms
+ *
+ * number of milliseconds (usually 1000ms). This normally results in
+ * 3 unicast packets, 1 per second.
+ *
+ * - If no response is received to any of the probe packets, the kernel
+ * marks the neighbour as being in state NUD_FAILED, and the listening
+ * process in #2 will learn of it.
+ *
+ * 4. We call the supplied Callback#notifyLost() function if the loss of a
+ * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to
+ * become incomplete (a loss of provisioning).
+ *
+ * - For example, losing all our IPv4 on-link DNS servers (or losing
+ * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6)
+ * provisioning; Callback#notifyLost() would be called.
+ *
+ * - Since it can be non-trivial to reacquire certain IP provisioning
+ * state it may be best for the link to disconnect completely and
+ * reconnect afresh.
+ *
+ *
* @hide
*/
public class IpReachabilityMonitor {
@@ -355,7 +423,7 @@
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/ApplicationRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
deleted file mode 100644
index ca270e7..0000000
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-import android.app.admin.DeviceAdminReceiver;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
-/**
- * Tests for application restrictions persisting via profile owner:
- * make -j FrameworksServicesTests
- * runtest --path frameworks/base/services/tests/servicestests/ \
- * src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
- */
-public class ApplicationRestrictionsTest extends AndroidTestCase {
-
- static DevicePolicyManager sDpm;
- static ComponentName sAdminReceiver;
- private static final String RESTRICTED_APP = "com.example.restrictedApp";
- static boolean sAddBack = false;
-
- public static class AdminReceiver extends DeviceAdminReceiver {
-
- @Override
- public void onDisabled(Context context, Intent intent) {
- if (sAddBack) {
- sDpm.setActiveAdmin(sAdminReceiver, false);
- sAddBack = false;
- }
-
- super.onDisabled(context, intent);
- }
- }
-
- @Override
- public void setUp() {
- final Context context = getContext();
- sAdminReceiver = new ComponentName(mContext.getPackageName(),
- AdminReceiver.class.getName());
- sDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
- Settings.Secure.putInt(context.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0);
- sDpm.setProfileOwner(sAdminReceiver, "Test", UserHandle.myUserId());
- Settings.Secure.putInt(context.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 1);
- // Remove the admin if already registered. It's async, so add it back
- // when the admin gets a broadcast. Otherwise add it back right away.
- if (sDpm.isAdminActive(sAdminReceiver)) {
- sAddBack = true;
- sDpm.removeActiveAdmin(sAdminReceiver);
- } else {
- sDpm.setActiveAdmin(sAdminReceiver, false);
- }
- }
-
- @Override
- public void tearDown() {
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0);
- sDpm.removeActiveAdmin(sAdminReceiver);
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 1);
- }
-
- public void testSettingRestrictions() {
- Bundle restrictions = new Bundle();
- restrictions.putString("KEY_STRING", "Foo");
- assertNotNull(sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP));
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
- Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
- assertNotNull(returned);
- assertEquals(returned.size(), 1);
- assertEquals(returned.get("KEY_STRING"), "Foo");
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
- returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
- assertEquals(returned.size(), 0);
- }
-
- public void testRestrictionTypes() {
- Bundle restrictions = new Bundle();
- restrictions.putString("KEY_STRING", "Foo");
- restrictions.putInt("KEY_INT", 7);
- restrictions.putBoolean("KEY_BOOLEAN", true);
- restrictions.putBoolean("KEY_BOOLEAN_2", false);
- restrictions.putString("KEY_STRING_2", "Bar");
- restrictions.putStringArray("KEY_STR_ARRAY", new String[] { "Foo", "Bar" });
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
- Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
- assertTrue(returned.getBoolean("KEY_BOOLEAN"));
- assertFalse(returned.getBoolean("KEY_BOOLEAN_2"));
- assertFalse(returned.getBoolean("KEY_BOOLEAN_3"));
- assertEquals(returned.getInt("KEY_INT"), 7);
- assertTrue(returned.get("KEY_BOOLEAN") instanceof Boolean);
- assertTrue(returned.get("KEY_INT") instanceof Integer);
- assertEquals(returned.get("KEY_STRING"), "Foo");
- assertEquals(returned.get("KEY_STRING_2"), "Bar");
- assertTrue(returned.getStringArray("KEY_STR_ARRAY") instanceof String[]);
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
- }
-
- public void testTextEscaping() {
- String fancyText = "<This contains XML/> <JSON> "
- + "{ \"One\": { \"OneOne\": \"11\", \"OneTwo\": \"12\" }, \"Two\": \"2\" } <JSON/>";
- Bundle restrictions = new Bundle();
- restrictions.putString("KEY_FANCY_TEXT", fancyText);
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
- Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
- assertEquals(returned.getString("KEY_FANCY_TEXT"), fancyText);
- }
-}
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 7e730f6..f4ffe2e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -19,11 +19,11 @@
import android.app.IActivityManager;
import android.app.NotificationManager;
+import android.app.backup.IBackupManager;
import android.content.Context;
import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
+import android.media.IAudioService;
import android.os.Looper;
-import android.os.PowerManager.WakeLock;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
@@ -50,11 +50,11 @@
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
@@ -73,146 +73,157 @@
}
}
- public final File dataDir;
- public final File systemUserDataDir;
- public final File secondUserDataDir;
+ public final DpmMockContext context;
public DevicePolicyManagerServiceTestable(DpmMockContext context, File dataDir) {
- super(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(getContext().environment.getUserSystemDirectory(
- eq(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(secondUserDataDir);
+ this(new MockInjector(context, dataDir));
}
- @Override
- DpmMockContext getContext() {
- return (DpmMockContext) super.getContext();
+ private DevicePolicyManagerServiceTestable(MockInjector injector) {
+ super(injector);
+ this.context = injector.context;
}
- @Override
- Owners newOwners() {
- return new OwnersTestable(getContext(), dataDir);
- }
+ private static class MockInjector extends Injector {
- @Override
- UserManager getUserManager() {
- return getContext().userManager;
- }
+ public final DpmMockContext context;
- @Override
- PackageManager getPackageManager() {
- return getContext().packageManager;
- }
+ public final File dataDir;
- @Override
- PowerManagerInternal getPowerManagerInternal() {
- return getContext().powerManagerInternal;
- }
+ private MockInjector(DpmMockContext context, File dataDir) {
+ super(context);
+ this.context = context;
+ this.dataDir = dataDir;
+ }
- @Override
- NotificationManager getNotificationManager() {
- return getContext().notificationManager;
- }
+ @Override
+ Owners newOwners() {
+ return new OwnersTestable(context);
+ }
- @Override
- IWindowManager getIWindowManager() {
- return getContext().iwindowManager;
- }
+ @Override
+ UserManager getUserManager() {
+ return context.userManager;
+ }
- @Override
- IActivityManager getIActivityManager() {
- return getContext().iactivityManager;
- }
+ @Override
+ PowerManagerInternal getPowerManagerInternal() {
+ return context.powerManagerInternal;
+ }
- @Override
- IPackageManager getIPackageManager() {
- return getContext().ipackageManager;
- }
+ @Override
+ NotificationManager getNotificationManager() {
+ return context.notificationManager;
+ }
- @Override
- LockPatternUtils newLockPatternUtils(Context context) {
- return getContext().lockPatternUtils;
- }
+ @Override
+ IWindowManager getIWindowManager() {
+ return context.iwindowManager;
+ }
- @Override
- Looper getMyLooper() {
- return Looper.getMainLooper();
- }
+ @Override
+ IActivityManager getIActivityManager() {
+ return context.iactivityManager;
+ }
- @Override
- String getDevicePolicyFilePathForSystemUser() {
- return systemUserDataDir.getAbsolutePath();
- }
+ @Override
+ IPackageManager getIPackageManager() {
+ return context.ipackageManager;
+ }
- @Override
- long binderClearCallingIdentity() {
- return getContext().binder.clearCallingIdentity();
- }
+ @Override
+ IBackupManager getIBackupManager() {
+ return context.ibackupManager;
+ }
- @Override
- void binderRestoreCallingIdentity(long token) {
- getContext().binder.restoreCallingIdentity(token);
- }
+ @Override
+ IAudioService getIAudioService() {
+ return context.iaudioService;
+ }
- @Override
- int binderGetCallingUid() {
- return getContext().binder.getCallingUid();
- }
+ @Override
+ Looper getMyLooper() {
+ return Looper.getMainLooper();
+ }
- @Override
- int binderGetCallingPid() {
- return getContext().binder.getCallingPid();
- }
+ @Override
+ LockPatternUtils newLockPatternUtils() {
+ return context.lockPatternUtils;
+ }
- @Override
- UserHandle binderGetCallingUserHandle() {
- return getContext().binder.getCallingUserHandle();
- }
+ @Override
+ String getDevicePolicyFilePathForSystemUser() {
+ return context.systemUserDataDir.getAbsolutePath();
+ }
- @Override
- boolean binderIsCallingUidMyUid() {
- return getContext().binder.isCallerUidMyUid();
- }
+ @Override
+ long binderClearCallingIdentity() {
+ return context.binder.clearCallingIdentity();
+ }
- @Override
- File environmentGetUserSystemDirectory(int userId) {
- return getContext().environment.getUserSystemDirectory(userId);
- }
+ @Override
+ void binderRestoreCallingIdentity(long token) {
+ context.binder.restoreCallingIdentity(token);
+ }
- @Override
- void powerManagerGoToSleep(long time, int reason, int flags) {
- getContext().powerManager.goToSleep(time, reason, flags);
- }
+ @Override
+ int binderGetCallingUid() {
+ return context.binder.getCallingUid();
+ }
- @Override
- boolean systemPropertiesGetBoolean(String key, boolean def) {
- return getContext().systemProperties.getBoolean(key, def);
- }
+ @Override
+ int binderGetCallingPid() {
+ return context.binder.getCallingPid();
+ }
- @Override
- long systemPropertiesGetLong(String key, long def) {
- return getContext().systemProperties.getLong(key, def);
- }
+ @Override
+ UserHandle binderGetCallingUserHandle() {
+ return context.binder.getCallingUserHandle();
+ }
- @Override
- String systemPropertiesGet(String key, String def) {
- return getContext().systemProperties.get(key, def);
- }
+ @Override
+ boolean binderIsCallingUidMyUid() {
+ return context.binder.isCallerUidMyUid();
+ }
- @Override
- String systemPropertiesGet(String key) {
- return getContext().systemProperties.get(key);
- }
+ @Override
+ File environmentGetUserSystemDirectory(int userId) {
+ return context.environment.getUserSystemDirectory(userId);
+ }
- @Override
- void systemPropertiesSet(String key, String value) {
- getContext().systemProperties.set(key, value);
+ @Override
+ void powerManagerGoToSleep(long time, int reason, int flags) {
+ context.powerManager.goToSleep(time, reason, flags);
+ }
+
+ @Override
+ boolean systemPropertiesGetBoolean(String key, boolean def) {
+ return context.systemProperties.getBoolean(key, def);
+ }
+
+ @Override
+ long systemPropertiesGetLong(String key, long def) {
+ return context.systemProperties.getLong(key, def);
+ }
+
+ @Override
+ String systemPropertiesGet(String key, String def) {
+ return context.systemProperties.get(key, def);
+ }
+
+ @Override
+ String systemPropertiesGet(String key) {
+ return context.systemProperties.get(key);
+ }
+
+ @Override
+ void systemPropertiesSet(String key, String value) {
+ context.systemProperties.set(key, value);
+ }
+
+ @Override
+ boolean userManagerIsSplitSystemUser() {
+ return context.userManagerForMock.isSplitSystemUser();
+ }
}
}
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 0da459d..5b23798 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -30,15 +30,24 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
+import android.content.pm.PackageInfo;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.util.Pair;
import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -82,11 +91,15 @@
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);
+ setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
+ DpmMockContext.CALLER_UID);
+
+ setUpPackageInfo();
+ setUpUserManager();
}
/**
@@ -94,7 +107,7 @@
* 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 =
@@ -104,35 +117,109 @@
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)));
}
- public void testHasNoFeature() {
+ /**
+ * Set up a mock result for {@link IPackageManager#getPackageInfo(String, int, int)} for user
+ * {@link DpmMockContext#CALLER_USER_HANDLE} as well as the system user.
+ */
+ private void setUpPackageInfo() throws Exception {
+ final PackageInfo pi = mRealTestContext.getPackageManager().getPackageInfo(
+ admin1.getPackageName(), 0);
+ assertTrue(pi.applicationInfo.flags != 0);
+
+ doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+ eq(admin1.getPackageName()),
+ eq(0),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+ doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+ eq(admin1.getPackageName()),
+ eq(0),
+ eq(UserHandle.USER_SYSTEM));
+ }
+
+ private void setUpUserManager() {
+ // Emulate UserManager.set/getApplicationRestriction().
+ final Map<Pair<String, UserHandle>, Bundle> appRestrictions = new HashMap<>();
+
+ // UM.setApplicationRestrictions() will save to appRestrictions.
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ String pkg = (String) invocation.getArguments()[0];
+ Bundle bundle = (Bundle) invocation.getArguments()[1];
+ UserHandle user = (UserHandle) invocation.getArguments()[2];
+
+ appRestrictions.put(Pair.create(pkg, user), bundle);
+
+ return null;
+ }
+ }).when(mContext.userManager).setApplicationRestrictions(
+ anyString(), any(Bundle.class), any(UserHandle.class));
+
+ // UM.getApplicationRestrictions() will read from appRestrictions.
+ doAnswer(new Answer<Bundle>() {
+ @Override
+ public Bundle answer(InvocationOnMock invocation) throws Throwable {
+ String pkg = (String) invocation.getArguments()[0];
+ UserHandle user = (UserHandle) invocation.getArguments()[1];
+
+ return appRestrictions.get(Pair.create(pkg, user));
+ }
+ }).when(mContext.userManager).getApplicationRestrictions(
+ anyString(), any(UserHandle.class));
+
+ // Add the first secondary user.
+ mContext.addUser(DpmMockContext.CALLER_USER_HANDLE, 0);
+ }
+
+ private void setAsProfileOwner(ComponentName admin) {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE, "user", 0);
+
+ // DO needs to be an DA.
+ dpm.setActiveAdmin(admin, /* replace =*/ false);
+
+ // Fire!
+ assertTrue(dpm.setProfileOwner(admin, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
+
+ // Check
+ assertEquals(admin1, dpm.getProfileOwnerAsUser(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ public void testHasNoFeature() throws Exception {
when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
.thenReturn(false);
@@ -220,7 +307,8 @@
// 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);
@@ -265,6 +353,35 @@
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}
@@ -311,9 +428,11 @@
// 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) {
}
@@ -323,7 +442,7 @@
* 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.
@@ -335,8 +454,11 @@
// 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));
@@ -399,6 +521,108 @@
// TODO Check other internal calls.
}
+
+ /**
+ * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs
+ * successfully.
+ */
+ public void testSetDeviceOwner() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // 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);
+
+ // Fire!
+ assertTrue(dpm.setDeviceOwner(admin1.getPackageName(), "owner-name"));
+
+ // Verify internal calls.
+ verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+ eq(admin1.getPackageName()));
+
+ // TODO We should check if the caller has called clearCallerIdentity().
+ verify(mContext.ibackupManager, times(1)).setBackupServiceActive(
+ eq(UserHandle.USER_SYSTEM), eq(false));
+
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
+ MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+
+ assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+
+ // TODO Test getDeviceOwnerName() too. To do so, we need to change
+ // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
+ }
+
+ /**
+ * Test for: {@link DevicePolicyManager#setDeviceOwner} Package doesn't exist.
+ */
+ public void testSetDeviceOwner_noSuchPackage() {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ 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.
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ try {
+ dpm.setDeviceOwner("a.b.c");
+ fail("Didn't throw IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testSetDeviceOwner_failures() throws Exception {
+ // TODO Test more failure cases. Basically test all chacks in enforceCanSetDeviceOwner().
+ }
+
+ public void testSetProfileOwner() throws Exception {
+ setAsProfileOwner(admin1);
+ }
+
+ public void testSetProfileOwner_failures() throws Exception {
+ // TODO Test more failure cases. Basically test all chacks in enforceCanSetProfileOwner().
+ }
+
+ public void testSetGetApplicationRestriction() {
+ setAsProfileOwner(admin1);
+
+ {
+ Bundle rest = new Bundle();
+ rest.putString("KEY_STRING", "Foo1");
+ dpm.setApplicationRestrictions(admin1, "pkg1", rest);
+ }
+
+ {
+ Bundle rest = new Bundle();
+ rest.putString("KEY_STRING", "Foo2");
+ dpm.setApplicationRestrictions(admin1, "pkg2", rest);
+ }
+
+ {
+ Bundle returned = dpm.getApplicationRestrictions(admin1, "pkg1");
+ assertNotNull(returned);
+ assertEquals(returned.size(), 1);
+ assertEquals(returned.get("KEY_STRING"), "Foo1");
+ }
+
+ {
+ Bundle returned = dpm.getApplicationRestrictions(admin1, "pkg2");
+ assertNotNull(returned);
+ assertEquals(returned.size(), 1);
+ assertEquals(returned.get("KEY_STRING"), "Foo2");
+ }
+
+ dpm.setApplicationRestrictions(admin1, "pkg2", new Bundle());
+ assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
+ }
}
-
-
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 325bf9f..b80f3bf 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 @@
@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 6bb9833..7b36e88 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -16,33 +16,38 @@
package com.android.server.devicepolicy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import android.app.IActivityManager;
import android.app.NotificationManager;
+import android.app.backup.IBackupManager;
import android.content.BroadcastReceiver;
import android.content.Context;
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;
import android.os.PowerManager.WakeLock;
import android.os.PowerManagerInternal;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.test.mock.MockContext;
import android.view.IWindowManager;
+import org.junit.Assert;
+
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.
@@ -54,9 +59,14 @@
public static final int CALLER_USER_HANDLE = 20;
/**
- * UID of the caller.
+ * 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 = 20321;
/**
* PID of the caller.
@@ -142,6 +152,12 @@
}
}
+ public static class UserManagerForMock {
+ public boolean isSplitSystemUser() {
+ return false;
+ }
+ }
+
public final Context realTestContext;
/**
@@ -151,16 +167,22 @@
*/
public final Context spiedContext;
+ public final File dataDir;
+ public final File systemUserDataDir;
+
public final MockBinder binder;
public final EnvironmentForMock environment;
public final SystemPropertiesForMock systemProperties;
public final UserManager userManager;
+ public final UserManagerForMock userManagerForMock;
public final PowerManagerForMock powerManager;
public final PowerManagerInternal powerManagerInternal;
public final NotificationManager notificationManager;
public final IWindowManager iwindowManager;
public final IActivityManager iactivityManager;
public final IPackageManager ipackageManager;
+ public final IBackupManager ibackupManager;
+ public final IAudioService iaudioService;
public final LockPatternUtils lockPatternUtils;
/** Note this is a partial mock, not a real mock. */
@@ -168,24 +190,66 @@
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);
userManager = mock(UserManager.class);
+ userManagerForMock = mock(UserManagerForMock.class);
powerManager = mock(PowerManagerForMock.class);
powerManagerInternal = mock(PowerManagerInternal.class);
notificationManager = mock(NotificationManager.class);
iwindowManager = mock(IWindowManager.class);
iactivityManager = mock(IActivityManager.class);
ipackageManager = mock(IPackageManager.class);
+ ibackupManager = mock(IBackupManager.class);
+ iaudioService = mock(IAudioService.class);
lockPatternUtils = mock(LockPatternUtils.class);
// Package manager is huge, so we use a partial mock instead.
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
@@ -200,6 +264,11 @@
}
@Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ return realTestContext.getSystemServiceName(serviceClass);
+ }
+
+ @Override
public PackageManager getPackageManager() {
return packageManager;
}
@@ -272,6 +341,13 @@
@Override
public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+ if (binder.callingPid != SYSTEM_PID) {
+ // Unless called as the system process, can only call if the target user is the
+ // calling user.
+ // (The actual check is more complex; we may need to change it later.)
+ Assert.assertEquals(UserHandle.getUserId(binder.getCallingUid()), user.getIdentifier());
+ }
+
spiedContext.sendBroadcastAsUser(intent, user);
}
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 77270c8..63bf125 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -21,7 +21,7 @@
import java.io.File;
-public class DpmTestBase extends AndroidTestCase {
+public abstract class DpmTestBase extends AndroidTestCase {
public static final String TAG = "DpmTest";
protected Context mRealTestContext;
@@ -34,10 +34,9 @@
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 44a851a..7506273 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 @@
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/DummyDeviceAdmins.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
index 5cd1555..08293a2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
@@ -24,4 +24,4 @@
}
public static class Admin3 extends DeviceAdminReceiver {
}
-}
\ No newline at end of file
+}
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 a07d615..4a39614 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 @@
}
}
- 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 @@
// 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 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 @@
// 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 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 @@
// 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 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 @@
// 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 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 @@
// 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 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 @@
// 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 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/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8779462..a8f2aca 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -14,6 +14,7 @@
package android.telecom;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
@@ -116,6 +117,15 @@
"android.telecom.action.PHONE_ACCOUNT_REGISTERED";
/**
+ * The {@link android.content.Intent} action used indicate that a phone account was
+ * just unregistered.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_PHONE_ACCOUNT_UNREGISTERED =
+ "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
+
+ /**
* Activity action: Shows a dialog asking the user whether or not they want to replace the
* current default Dialer with the one specified in
* {@link #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME}.
@@ -472,9 +482,12 @@
* <p>
* If no {@link PhoneAccount} fits the criteria above, this method will return {@code null}.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @param uriScheme The URI scheme.
* @return The {@link PhoneAccountHandle} corresponding to the account to be used.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
try {
if (isServiceConnected()) {
@@ -606,9 +619,12 @@
* calls. The returned list includes only those accounts which have been explicitly enabled
* by the user.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @see #EXTRA_PHONE_ACCOUNT_HANDLE
* @return A list of {@code PhoneAccountHandle} objects.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
return getCallCapablePhoneAccounts(false);
}
@@ -881,9 +897,12 @@
* Return whether a given phone number is the configured voicemail number for a
* particular phone account.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @param accountHandle The handle for the account to check the voicemail number against
* @param number The number to look up.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
try {
if (isServiceConnected()) {
@@ -899,10 +918,13 @@
/**
* Return the voicemail number for a given phone account.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @param accountHandle The handle for the phone account.
* @return The voicemail number for the phone account, and {@code null} if one has not been
* configured.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getVoiceMailNumber(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
@@ -918,9 +940,12 @@
/**
* Return the line 1 phone number for given phone account.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @param accountHandle The handle for the account retrieve a number for.
* @return A string representation of the line 1 phone number.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getLine1Number(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
@@ -940,6 +965,7 @@
* Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
* </p>
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public boolean isInCall() {
try {
if (isServiceConnected()) {
@@ -1031,7 +1057,10 @@
/**
* Silences the ringer if a ringing call exists.
+ *
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void silenceRinger() {
try {
if (isServiceConnected()) {
@@ -1136,9 +1165,12 @@
* Requires that the method-caller be set as the system dialer app.
* </p>
*
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ *
* @param dialString The digits to dial.
* @return True if the digits were processed as an MMI code, false otherwise.
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean handleMmi(String dialString) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1159,10 +1191,13 @@
* Requires that the method-caller be set as the system dialer app.
* </p>
*
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ *
* @param accountHandle The handle for the account the MMI code should apply to.
* @param dialString The digits to dial.
* @return True if the digits were processed as an MMI code, false otherwise.
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean handleMmi(String dialString, PhoneAccountHandle accountHandle) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1177,11 +1212,14 @@
}
/**
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ *
* @param accountHandle The handle for the account to derive an adn query URI for or
* {@code null} to return a URI which will use the default account.
* @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount}
* for the the content retrieve.
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle) {
ITelecomService service = getTelecomService();
if (service != null && accountHandle != null) {
@@ -1199,7 +1237,10 @@
* <p>
* Requires that the method-caller be set as the system dialer app.
* </p>
+ *
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void cancelMissedCallsNotification() {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1221,6 +1262,7 @@
*
* @param showDialpad Brings up the in-call dialpad as part of showing the in-call screen.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public void showInCallScreen(boolean showDialpad) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1262,6 +1304,7 @@
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
+ @RequiresPermission(android.Manifest.permission.CALL_PHONE)
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 1ff621a..17c24af 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -63,8 +63,14 @@
public class MockPackageManager extends PackageManager {
@Override
- public PackageInfo getPackageInfo(String packageName, int flags)
- throws NameNotFoundException {
+ public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ @Override
+ public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+ throws NameNotFoundException {
throw new UnsupportedOperationException();
}
@@ -524,6 +530,14 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer,
+ int flags, String installerPackageName, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+
@Override
public void setInstallerPackageName(String targetPackage,
String installerPackageName) {
@@ -629,6 +643,15 @@
throw new UnsupportedOperationException();
}
+ /**
+ * @hide - to match hiding in superclass
+ */
+ @Override
+ public void deletePackageAsUser(
+ String packageName, IPackageDeleteObserver observer, int flags, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public void addPackageToPreferred(String packageName) {
throw new UnsupportedOperationException();
@@ -792,7 +815,15 @@
* @hide
*/
@Override
- public int installExistingPackage(String packageName)
+ public int installExistingPackage(String packageName) throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
throw new UnsupportedOperationException();
}
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
index 0b43666..8085db7 100644
--- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
@@ -31,5 +31,7 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.camera_activity);
Log.i(TAG, "Activity created");
+ Log.i(TAG, "Source: "
+ + getIntent().getStringExtra("com.android.systemui.camera_launch_source"));
}
}
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
index 530fe00..242d3b2 100644
--- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
@@ -17,6 +17,7 @@
package com.google.android.test.cameraprewarm;
import android.app.Activity;
+import android.graphics.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
@@ -31,5 +32,7 @@
setContentView(R.layout.camera_activity);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
Log.i(CameraActivity.TAG, "Activity created");
+ Log.i(CameraActivity.TAG, "Source: "
+ + getIntent().getStringExtra("com.android.systemui.camera_launch_source"));
}
}
diff --git a/tests/WindowAnimationJank/Android.mk b/tests/WindowAnimationJank/Android.mk
new file mode 100644
index 0000000..888ae64
--- /dev/null
+++ b/tests/WindowAnimationJank/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WindowAnimationJank
+
+LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator ub-janktesthelper
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tests/WindowAnimationJank/AndroidManifest.xml b/tests/WindowAnimationJank/AndroidManifest.xml
new file mode 100644
index 0000000..d7aef33
--- /dev/null
+++ b/tests/WindowAnimationJank/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.windowanimationjank">
+
+ <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="ElementLayoutActivity"
+ android:label="ElementLayoutActivity"
+ android:taskAffinity="android.windowanimationjank.ElementLayoutActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="android.windowanimationjank">
+ </instrumentation>
+
+</manifest>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/tests/WindowAnimationJank/res/layout/flowlayout.xml
similarity index 60%
copy from packages/SystemUI/res/values-sw600dp-port/dimens.xml
copy to tests/WindowAnimationJank/res/layout/flowlayout.xml
index 7dc91d1..f2b559b 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/tests/WindowAnimationJank/res/layout/flowlayout.xml
@@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- * Copyright (c) 2012, The Android Open Source Project
+ * 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
+ * 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.
-*/
--->
-<resources>
- <!-- Recent Applications parameters -->
- <dimen name="status_bar_recents_app_label_width">140dip</dimen>
-</resources>
+ -->
+<android.windowanimationjank.FlowLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/root_flow_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" />
\ No newline at end of file
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java b/tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java
new file mode 100644
index 0000000..3b1fabc
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java
@@ -0,0 +1,159 @@
+/*
+ * 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.windowanimationjank;
+
+import java.util.Random;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+import android.widget.Chronometer;
+import android.widget.RadioButton;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+/*
+ * Activity with arbitrary number of random UI elements, refresh itself constantly.
+ */
+public class ElementLayoutActivity extends Activity implements OnPreDrawListener {
+ public final static String NUM_ELEMENTS_KEY = "num_elements";
+
+ private final static int DEFAULT_NUM_ELEMENTS = 100;
+ private final static int BACKGROUND_COLOR = 0xfffff000;
+ private final static int INDICATOR_COLOR = 0xffff0000;
+
+ private FlowLayout mLayout;
+ // Use the constant seed in order to get predefined order of elements.
+ private Random mRandom = new Random(0);
+ // Blinker indicator for visual feedback that Activity is currently updating.
+ private TextView mIndicator;
+ private static float mIndicatorState;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.flowlayout);
+
+ mLayout = (FlowLayout)findViewById(R.id.root_flow_layout);
+ mLayout.setBackgroundColor(BACKGROUND_COLOR);
+
+ mIndicator = new TextView(this);
+ mLayout.addView(mIndicator);
+ mIndicator.setText("***\n***");
+ mIndicator.setBackgroundColor(BACKGROUND_COLOR);
+ mIndicatorState = 0.0f;
+
+ // Need constantly invalidate view in order to get max redraw rate.
+ mLayout.getViewTreeObserver().addOnPreDrawListener(this);
+
+ // Read requested number of elements in layout.
+ int numElements = getIntent().getIntExtra(NUM_ELEMENTS_KEY, DEFAULT_NUM_ELEMENTS);
+
+ for (int i = 0; i < numElements; ++i) {
+ switch (mRandom.nextInt(5)) {
+ case 0:
+ createRadioButton();
+ break;
+ case 1:
+ createToggleButton();
+ break;
+ case 2:
+ createSwitch();
+ break;
+ case 3:
+ createTextView();
+ break;
+ case 4:
+ createChronometer();
+ break;
+ }
+ }
+
+ setContentView(mLayout);
+ }
+
+ private void createTextView() {
+ TextView textView = new TextView(this);
+ int lineCnt = mRandom.nextInt(4);
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < lineCnt; ++i) {
+ if (i != 0) {
+ buffer.append("\n");
+ }
+ buffer.append("Line:" + mRandom.nextInt());
+ }
+ textView.setText(buffer);
+ mLayout.addView(textView);
+ }
+
+ private void createRadioButton() {
+ RadioButton button = new RadioButton(this);
+ button.setText("RadioButton:" + mRandom.nextInt());
+ mLayout.addView(button);
+ }
+
+ private void createToggleButton() {
+ ToggleButton button = new ToggleButton(this);
+ button.setChecked(mRandom.nextBoolean());
+ mLayout.addView(button);
+ }
+
+ private void createSwitch() {
+ Switch button = new Switch(this);
+ button.setChecked(mRandom.nextBoolean());
+ mLayout.addView(button);
+ }
+
+ private void createChronometer() {
+ Chronometer chronometer = new Chronometer(this);
+ chronometer.setBase(mRandom.nextLong());
+ mLayout.addView(chronometer);
+ chronometer.start();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ // Interpolate indicator color
+ int background = 0xff000000;
+ for (int i = 0; i < 3; ++i) {
+ int shift = 8 * i;
+ int colorB = (BACKGROUND_COLOR >> shift) & 0xff;
+ int colorI = (INDICATOR_COLOR >> shift) & 0xff;
+ int color = (int)((float)colorB * (1.0f - mIndicatorState) +
+ (float)colorI * mIndicatorState);
+ if (color > 255) {
+ color = 255;
+ }
+ background |= (color << shift);
+ }
+
+ mIndicator.setBackgroundColor(background);
+ mIndicatorState += (3 / 60.0f); // around 3 times per second
+ mIndicatorState = mIndicatorState - (int)mIndicatorState;
+
+ mLayout.postInvalidate();
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java b/tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java
new file mode 100644
index 0000000..9a2b9cc
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java
@@ -0,0 +1,111 @@
+/*
+ * 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.windowanimationjank;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Custom layout that place all elements in flows with and automatically wraps them.
+ */
+public class FlowLayout extends ViewGroup {
+ private int mLineHeight;
+
+ public FlowLayout(Context context) {
+ super(context);
+ }
+
+ public FlowLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int width =
+ MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() -getPaddingRight();
+ int height =
+ MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
+ final int count = getChildCount();
+
+ int x = getPaddingLeft();
+ int y = getPaddingTop();
+ int lineHeight = 0;
+
+ int childHeightMeasureSpec;
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+ } else {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ }
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+ childHeightMeasureSpec);
+ final int childWidth = child.getMeasuredWidth();
+ lineHeight = Math.max(lineHeight, child.getMeasuredHeight());
+
+ if (x + childWidth > width) {
+ x = getPaddingLeft();
+ y += lineHeight;
+ }
+
+ x += childWidth;
+ }
+ }
+ mLineHeight = lineHeight;
+
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
+ height = y + lineHeight;
+ } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+ if (y + lineHeight < height) {
+ height = y + lineHeight;
+ }
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ if (p instanceof LayoutParams) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int count = getChildCount();
+ final int width = r - l;
+ int x = getPaddingLeft();
+ int y = getPaddingTop();
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+ if (x + childWidth > width) {
+ x = getPaddingLeft();
+ y += mLineHeight;
+ }
+ child.layout(x, y, x + childWidth, y + childHeight);
+ x += childWidth;
+ }
+ }
+ }
+}
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java b/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java
new file mode 100644
index 0000000..1fb502a
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.windowanimationjank;
+
+import android.os.Bundle;
+import android.support.test.jank.JankTest;
+import android.support.test.jank.GfxMonitor;
+
+/**
+ * Detect janks during screen rotation for full-screen activity. Periodically change
+ * orientation from left to right and track ElementLayoutActivity rendering performance
+ * via GfxMonitor.
+ */
+public class FullscreenRotationTest extends WindowAnimationJankTestBase {
+ private final static int STEP_CNT = 3;
+
+ @Override
+ public void beforeTest() throws Exception {
+ getUiDevice().setOrientationLeft();
+ Utils.startElementLayout(getInstrumentation(), 100);
+ super.beforeTest();
+ }
+
+ @Override
+ public void afterTest(Bundle metrics) {
+ Utils.rotateDevice(getInstrumentation(), Utils.ROTATION_MODE_NATURAL);
+ super.afterTest(metrics);
+ }
+
+ @JankTest(expectedFrames=100, defaultIterationCount=2)
+ @GfxMonitor(processName=Utils.PACKAGE)
+ public void testRotation() throws Exception {
+ for (int i = 0; i < STEP_CNT; ++i) {
+ Utils.rotateDevice(getInstrumentation(),
+ Utils.getDeviceRotation(getInstrumentation()) == Utils.ROTATION_MODE_LEFT ?
+ Utils.ROTATION_MODE_RIGHT : Utils.ROTATION_MODE_LEFT);
+ }
+ }
+}
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
new file mode 100644
index 0000000..2531464
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
@@ -0,0 +1,118 @@
+/*
+ * 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.windowanimationjank;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+/**
+ * Set of helpers to manipulate test activities.
+ */
+public class Utils {
+ protected final static String PACKAGE = "android.windowanimationjank";
+ protected final static String ELEMENT_LAYOUT_ACTIVITY = "ElementLayoutActivity";
+ protected final static String ELEMENT_LAYOUT_CLASS = PACKAGE + "." + ELEMENT_LAYOUT_ACTIVITY;
+ protected final static long WAIT_FOR_ACTIVITY_TIMEOUT = 10000;
+ private static final BySelector ROOT_ELEMENT_LAYOUT = By.res(PACKAGE, "root_flow_layout");
+
+ private final static long ROTATION_ANIMATION_TIME_FULL_SCREEN_MS = 1000;
+
+ protected final static int ROTATION_MODE_NATURAL = 0;
+ protected final static int ROTATION_MODE_LEFT = 1;
+ protected final static int ROTATION_MODE_RIGHT = 2;
+
+ private static UiObject2 waitForActivity(Instrumentation instrumentation, BySelector selector) {
+ UiDevice device = UiDevice.getInstance(instrumentation);
+ UiObject2 window = device.wait(Until.findObject(selector), WAIT_FOR_ACTIVITY_TIMEOUT);
+ if (window == null) {
+ throw new RuntimeException(selector.toString() + " has not been started.");
+ }
+
+ // Get root object.
+ while (window.getParent() != null) {
+ window = window.getParent();
+ }
+ return window;
+ }
+
+ public static UiObject2 waitForElementLayout(Instrumentation instrumentation) {
+ return waitForActivity(instrumentation, ROOT_ELEMENT_LAYOUT);
+ }
+
+ /**
+ * Start and return activity with requested number of random elements.
+ */
+ public static UiObject2 startElementLayout(Instrumentation instrumentation, int numElements) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName(PACKAGE, ELEMENT_LAYOUT_CLASS));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(ElementLayoutActivity.NUM_ELEMENTS_KEY, numElements);
+ instrumentation.getTargetContext().startActivity(intent);
+ return waitForElementLayout(instrumentation);
+ }
+
+ public static int getDeviceRotation(Instrumentation instrumentation) {
+ try {
+ UiDevice device = UiDevice.getInstance(instrumentation);
+ switch (device.getDisplayRotation()) {
+ case UiAutomation.ROTATION_FREEZE_90:
+ return ROTATION_MODE_LEFT;
+ case UiAutomation.ROTATION_FREEZE_270:
+ return ROTATION_MODE_RIGHT;
+ case UiAutomation.ROTATION_FREEZE_0:
+ case UiAutomation.ROTATION_FREEZE_180:
+ return ROTATION_MODE_NATURAL;
+ }
+ } catch(Exception e) {
+ throw new RuntimeException();
+ }
+ throw new RuntimeException("Unsupported device rotation.");
+ }
+
+ public static void rotateDevice(Instrumentation instrumentation, int rotationMode) {
+ try {
+ UiDevice device = UiDevice.getInstance(instrumentation);
+ long startTime = System.currentTimeMillis();
+ switch (rotationMode) {
+ case ROTATION_MODE_NATURAL:
+ device.setOrientationNatural();
+ break;
+ case ROTATION_MODE_LEFT:
+ device.setOrientationLeft();
+ break;
+ case ROTATION_MODE_RIGHT:
+ device.setOrientationRight();
+ break;
+ default:
+ throw new RuntimeException("Unsupported rotation mode: " + rotationMode);
+ }
+
+ long toSleep = ROTATION_ANIMATION_TIME_FULL_SCREEN_MS -
+ (System.currentTimeMillis() - startTime);
+ if (toSleep > 0) {
+ SystemClock.sleep(toSleep);
+ }
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
new file mode 100644
index 0000000..bf739fa
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
@@ -0,0 +1,56 @@
+/*
+ * 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.windowanimationjank;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+import android.support.test.jank.JankTestBase;
+import android.support.test.uiautomator.UiDevice;
+
+/**
+ * This adds additional system level jank monitor and its result is merged with primary monitor
+ * used in test.
+ */
+public abstract class WindowAnimationJankTestBase extends JankTestBase {
+ private static final String TAG = "WindowAnimationJankTestBase";
+
+ protected WindowAnimationJankTestBase() {
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // fix device orientation
+ getUiDevice().setOrientationNatural();
+
+ // Start from the home screen
+ getUiDevice().pressHome();
+ getUiDevice().waitForIdle();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ getUiDevice().unfreezeRotation();
+ super.tearDown();
+ }
+
+ protected UiDevice getUiDevice() {
+ return UiDevice.getInstance(getInstrumentation());
+ }
+}
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index b9e7500..a390b0c 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -175,16 +175,6 @@
}
try {
- mWm.setAppWillBeHidden(null);
- fail("IWindowManager.setAppWillBeHidden did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
mWm.setAppVisibility(null, false);
fail("IWindowManager.setAppVisibility did not throw SecurityException as"
+ " expected");
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index 3b7bf85..f4b1f2c 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 @@
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 857e6d0..c7b24bc 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 @@
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 @@
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 65b65ec..a545283 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 @@
return;
}
- delegate.mTextSize = textSize;
- delegate.updateFontObject();
+ if (delegate.mTextSize != textSize) {
+ delegate.mTextSize = textSize;
+ delegate.updateFontObject();
+ }
}
@LayoutlibDelegate
@@ -503,8 +505,10 @@
return;
}
- delegate.mTextScaleX = scaleX;
- delegate.updateFontObject();
+ if (delegate.mTextScaleX != scaleX) {
+ delegate.mTextScaleX = scaleX;
+ delegate.updateFontObject();
+ }
}
@LayoutlibDelegate
@@ -526,8 +530,10 @@
return;
}
- delegate.mTextSkewX = skewX;
- delegate.updateFontObject();
+ if (delegate.mTextSkewX != skewX) {
+ delegate.mTextSkewX = skewX;
+ delegate.updateFontObject();
+ }
}
@LayoutlibDelegate
@@ -897,9 +903,12 @@
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 @@
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 @@
mMaskFilter = paint.mMaskFilter;
mRasterizer = paint.mRasterizer;
mHintingMode = paint.mHintingMode;
- updateFontObject();
+
+ if (needsFontUpdate) {
+ updateFontObject();
+ }
}
private void reset() {
@@ -1264,10 +1294,18 @@
// 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 f1726eb..5db1bde 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -206,7 +206,7 @@
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/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 778d1a5..0da6bb6 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -338,11 +338,6 @@
}
@Override
- public void setAppWillBeHidden(IBinder arg0) throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
public void setEventDispatching(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}
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 689e359..b2dc29a 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 @@
// 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/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index f04654e..e3a19e7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -65,6 +65,12 @@
}
@Override
+ public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+ throws NameNotFoundException {
+ return null;
+ }
+
+ @Override
public String[] currentToCanonicalPackageNames(String[] names) {
return new String[0];
}
@@ -499,6 +505,11 @@
}
@Override
+ public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer,int flags,
+ String installerPackageName, int userId) {
+ }
+
+ @Override
public void installPackageWithVerification(Uri packageURI, PackageInstallObserver observer,
int flags, String installerPackageName, Uri verificationURI,
ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
@@ -516,6 +527,12 @@
}
@Override
+ public int installExistingPackageAsUser(String packageName, int userId)
+ throws NameNotFoundException {
+ return 0;
+ }
+
+ @Override
public void verifyPendingInstall(int id, int verificationCode) {
}
@@ -568,6 +585,11 @@
}
@Override
+ public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
+ int userId) {
+ }
+
+ @Override
public String getInstallerPackageName(String packageName) {
return null;
}
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 0000000..71e7fd2
--- /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 6e67f59..e273b2c 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 @@
@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 0000000..2c33862
--- /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>";
+}