diff options
233 files changed, 4248 insertions, 2469 deletions
diff --git a/api/current.txt b/api/current.txt index c3a4a0c8919d..2331170c6748 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4021,6 +4021,8 @@ package android.app { ctor public AutomaticZenRule(android.os.Parcel); method public int describeContents(); method public android.net.Uri getConditionId(); + method public long getCreationTime(); + method public java.lang.String getId(); method public int getInterruptionFilter(); method public java.lang.String getName(); method public android.content.ComponentName getOwner(); @@ -5629,7 +5631,7 @@ package android.app.admin { method public void onPasswordFailed(android.content.Context, android.content.Intent); method public void onPasswordSucceeded(android.content.Context, android.content.Intent); method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent); - method public void onReadyForUserInitialization(android.content.Context, android.content.Intent); + method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent); method public void onReceive(android.content.Context, android.content.Intent); method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long); field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED"; @@ -22848,6 +22850,7 @@ package android.os { field public static final int LOLLIPOP = 21; // 0x15 field public static final int LOLLIPOP_MR1 = 22; // 0x16 field public static final int M = 23; // 0x17 + field public static final int N = 10000; // 0x2710 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/api/removed.txt b/api/removed.txt index 642d2a8ac19c..f12e61eea332 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -6,6 +6,15 @@ package android.app { } +package android.app.admin { + + public class DevicePolicyManager { + method public deprecated java.lang.String getDeviceInitializerApp(); + method public deprecated android.content.ComponentName getDeviceInitializerComponent(); + } + +} + package android.content.pm { public class PackageInfo implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 325aa28bef05..dacc338b0ce3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4132,6 +4132,8 @@ package android.app { ctor public AutomaticZenRule(android.os.Parcel); method public int describeContents(); method public android.net.Uri getConditionId(); + method public long getCreationTime(); + method public java.lang.String getId(); method public int getInterruptionFilter(); method public java.lang.String getName(); method public android.content.ComponentName getOwner(); @@ -5750,7 +5752,7 @@ package android.app.admin { method public void onPasswordFailed(android.content.Context, android.content.Intent); method public void onPasswordSucceeded(android.content.Context, android.content.Intent); method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent); - method public void onReadyForUserInitialization(android.content.Context, android.content.Intent); + method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent); method public void onReceive(android.content.Context, android.content.Intent); method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long); field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED"; @@ -5792,8 +5794,8 @@ package android.app.admin { method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName); method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName); method public int getCurrentFailedPasswordAttempts(); - method public java.lang.String getDeviceInitializerApp(); - method public android.content.ComponentName getDeviceInitializerComponent(); + method public deprecated java.lang.String getDeviceInitializerApp(); + method public deprecated android.content.ComponentName getDeviceInitializerComponent(); method public java.lang.String getDeviceOwner(); method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName); method public int getKeyguardDisabledFeatures(android.content.ComponentName); @@ -24792,6 +24794,7 @@ package android.os { field public static final int LOLLIPOP = 21; // 0x15 field public static final int LOLLIPOP_MR1 = 22; // 0x16 field public static final int M = 23; // 0x17 + field public static final int N = 10000; // 0x2710 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index cb1a89fd6601..faa3a43f6c2e 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -330,9 +330,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case START_ACTIVITY_FROM_RECENTS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - int taskId = data.readInt(); - Bundle options = data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data); - int result = startActivityFromRecents(taskId, options); + final int taskId = data.readInt(); + final int launchStackId = data.readInt(); + final Bundle options = + data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data); + final int result = startActivityFromRecents(taskId, launchStackId, options); reply.writeNoException(); reply.writeInt(result); return true; @@ -2689,6 +2691,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeNoException(); return true; } + case REMOVE_STACK: { + data.enforceInterface(IActivityManager.descriptor); + final int stackId = data.readInt(); + removeStack(stackId); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -2984,11 +2993,13 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); return result != 0; } - public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException { + public int startActivityFromRecents(int taskId, int launchStackId, Bundle options) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(taskId); + data.writeInt(launchStackId); if (options == null) { data.writeInt(0); } else { @@ -6235,5 +6246,17 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + @Override + public void removeStack(int stackId) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(stackId); + mRemote.transact(REMOVE_STACK, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 4b8efab3e59e..9f24de8dfe65 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3956,13 +3956,12 @@ public final class ActivityThread { } IBinder wtoken = v.getWindowToken(); if (r.activity.mWindowAdded) { - boolean reuseForResize = r.window.hasNonClientDecorView() && r.mPreserveWindow; - if (r.onlyLocalRequest || reuseForResize) { + if (r.onlyLocalRequest || r.mPreserveWindow) { // Hold off on removing this until the new activity's // window is being added. r.mPendingRemoveWindow = r.window; r.mPendingRemoveWindowManager = wm; - if (reuseForResize) { + if (r.mPreserveWindow) { // We can only keep the part of the view hierarchy that we control, // everything else must be removed, because it might not be able to // behave properly when activity is relaunching. diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index fea5624411ad..e5fa02b248f4 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -33,6 +33,8 @@ public class AutomaticZenRule implements Parcelable { private int interruptionFilter; private Uri conditionId; private ComponentName owner; + private String id; + private long creationTime; /** * Creates an automatic zen rule. @@ -55,6 +57,17 @@ public class AutomaticZenRule implements Parcelable { this.enabled = enabled; } + /** + * @SystemApi + * @hide + */ + public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, + int interruptionFilter, boolean enabled, String id, long creationTime) { + this(name, owner, conditionId, interruptionFilter, enabled); + this.id = id; + this.creationTime = creationTime; + } + public AutomaticZenRule(Parcel source) { enabled = source.readInt() == 1; if (source.readInt() == 1) { @@ -63,6 +76,10 @@ public class AutomaticZenRule implements Parcelable { interruptionFilter = source.readInt(); conditionId = source.readParcelable(null); owner = source.readParcelable(null); + if (source.readInt() == 1) { + id = source.readString(); + } + creationTime = source.readLong(); } /** @@ -101,6 +118,20 @@ public class AutomaticZenRule implements Parcelable { } /** + * Returns the wall time in milliseconds when this rule was created, if known. + */ + public long getCreationTime() { + return creationTime; + } + + /** + * Returns the unique identifier for this rule. + */ + public String getId() { + return id; + } + + /** * Sets the representation of the state that causes this rule to become active. */ public void setConditionId(Uri conditionId) { @@ -146,6 +177,13 @@ public class AutomaticZenRule implements Parcelable { dest.writeInt(interruptionFilter); dest.writeParcelable(conditionId, 0); dest.writeParcelable(owner, 0); + if (id != null) { + dest.writeInt(1); + dest.writeString(id); + } else { + dest.writeInt(0); + } + dest.writeLong(creationTime); } @Override @@ -156,6 +194,8 @@ public class AutomaticZenRule implements Parcelable { .append(",interruptionFilter=").append(interruptionFilter) .append(",conditionId=").append(conditionId) .append(",owner=").append(owner) + .append(",id=").append(id) + .append(",creationTime=").append(creationTime) .append(']').toString(); } @@ -168,12 +208,14 @@ public class AutomaticZenRule implements Parcelable { && Objects.equals(other.name, name) && other.interruptionFilter == interruptionFilter && Objects.equals(other.conditionId, conditionId) - && Objects.equals(other.owner, owner); + && Objects.equals(other.owner, owner) + && Objects.equals(other.id, id) + && other.creationTime == creationTime; } @Override public int hashCode() { - return Objects.hash(enabled, name, interruptionFilter, conditionId, owner); + return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, id, creationTime); } public static final Parcelable.Creator<AutomaticZenRule> CREATOR @@ -187,4 +229,4 @@ public class AutomaticZenRule implements Parcelable { return new AutomaticZenRule[size]; } }; -}
\ No newline at end of file +} diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 478fdd14e093..e23620d9b0ea 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -16,8 +16,8 @@ package android.app; -import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningServiceInfo; +import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; @@ -92,7 +92,8 @@ public interface IActivityManager extends IInterface { int userId) throws RemoteException; public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent, Bundle options) throws RemoteException; - public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException; + public int startActivityFromRecents(int taskId, int launchStackId, Bundle options) + throws RemoteException; public boolean finishActivity(IBinder token, int code, Intent data, int finishTask) throws RemoteException; public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException; @@ -537,6 +538,8 @@ public interface IActivityManager extends IInterface { public void suppressResizeConfigChanges(boolean suppress) throws RemoteException; + public void removeStack(int stackId) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -894,4 +897,5 @@ public interface IActivityManager extends IInterface { int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345; int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346; int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347; + int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 348; } diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index a1bb40c9298d..84b6d392b83e 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -227,24 +227,6 @@ public class DeviceAdminReceiver extends BroadcastReceiver { public static final String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE"; - /** - * @hide - * Broadcast Action: This broadcast is sent to indicate that the system is ready for the device - * initializer to perform user setup tasks. This is only applicable to devices managed by a - * device owner app. - * - * <p>The broadcast will be limited to the {@link DeviceAdminReceiver} component specified in - * the device initializer field of the original intent or NFC bump that started the provisioning - * process. You will generally handle this in - * {@link DeviceAdminReceiver#onReadyForUserInitialization}. - * - * <p>Input: Nothing.</p> - * <p>Output: Nothing</p> - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_READY_FOR_USER_INITIALIZATION = - "android.app.action.READY_FOR_USER_INITIALIZATION"; - /** @hide */ public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS"; @@ -435,23 +417,13 @@ public class DeviceAdminReceiver extends BroadcastReceiver { /** * Called during provisioning of a managed device to allow the device initializer to perform - * user setup steps. Only device initializers should override this method. - * - * <p> Called when the DeviceAdminReceiver receives an - * android.app.action.ACTION_READY_FOR_USER_INITIALIZATION broadcast. As a prerequisite for the - * execution of this callback the {@link DeviceAdminReceiver} has - * to declare an intent filter for android.app.action.ACTION_READY_FOR_USER_INITIALIZATION. Only - * the component specified in the device initializer component name field of the - * original intent or NFC bump that started the provisioning process will receive this callback. - * - * <p>It is not assumed that the device initializer is finished when it returns from - * this call, as it may do additional setup asynchronously. The device initializer must enable - * the current user when it has finished any additional setup (such as adding an account by - * using the {@link AccountManager}) in order for the user to be functional. + * user setup steps. * * @param context The running context as per {@link #onReceive}. * @param intent The received intent as per {@link #onReceive}. + * @deprecated Do not use */ + @Deprecated @SystemApi public void onReadyForUserInitialization(Context context, Intent intent) { } @@ -549,8 +521,6 @@ public class DeviceAdminReceiver extends BroadcastReceiver { onLockTaskModeEntering(context, intent, pkg); } else if (ACTION_LOCK_TASK_EXITING.equals(action)) { onLockTaskModeExiting(context, intent); - } else if (ACTION_READY_FOR_USER_INITIALIZATION.equals(action)) { - onReadyForUserInitialization(context, intent); } else if (ACTION_NOTIFY_PENDING_SYSTEM_UPDATE.equals(action)) { long receivedTime = intent.getLongExtra(EXTRA_SYSTEM_UPDATE_RECEIVED_TIME, -1); onSystemUpdatePending(context, intent, receivedTime); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a118f167ae5c..50764f740bc6 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -497,96 +497,6 @@ public class DevicePolicyManager { "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; /** - * @hide - * On devices managed by a device owner app, a {@link ComponentName} extra indicating the - * component of the application that is temporarily granted device owner privileges during - * device initialization and profile owner privileges during secondary user initialization. - * - * <p> - * It can also be used in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts - * device owner provisioning via an NFC bump. For the NFC record, it should be flattened to a - * string first. - * - * @see ComponentName#flattenToShortString() - */ - public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME - = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME"; - - /** - * @hide - * A String extra holding an http url that specifies the download location of the device - * initializer package. When not provided it is assumed that the device initializer package is - * already installed. - * - * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner - * provisioning via an NFC bump. - */ - public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION - = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION"; - - /** - * @hide - * An int extra holding a minimum required version code for the device initializer package. - * If the initializer is already installed on the device, it will only be re-downloaded from - * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION} if the version of - * the installed package is less than this version code. - * - * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner - * provisioning via an NFC bump. - */ - public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE - = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE"; - - /** - * @hide - * A String extra holding a http cookie header which should be used in the http request to the - * url specified in {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. - * - * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner - * provisioning via an NFC bump. - */ - public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER - = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER"; - - /** - * @hide - * A String extra holding the URL-safe base64 encoded SHA-256 checksum of the file at download - * location specified in - * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. - * - * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM} - * should be present. The provided checksum should match the checksum of the file at the - * download location. If the checksum doesn't match an error will be shown to the user and the - * user will be asked to factory reset the device. - * - * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner - * provisioning via an NFC bump. - */ - public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM - = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM"; - - /** - * @hide - * A String extra holding the URL-safe base64 encoded SHA-256 checksum of any signature of the - * android package archive at the download location specified in {@link - * #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. - * - * <p>The signatures of an android package archive can be obtained using - * {@link android.content.pm.PackageManager#getPackageArchiveInfo} with flag - * {@link android.content.pm.PackageManager#GET_SIGNATURES}. - * - * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM} - * should be present. The provided checksum should match the checksum of any signature of the - * file at the download location. If the checksum doesn't match an error will be shown to the - * user and the user will be asked to factory reset the device. - * - * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner - * provisioning via an NFC bump. - */ - public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM - = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM"; - - /** * This MIME type is used for starting the Device Owner provisioning. * * <p>During device owner provisioning a device admin app is set as the owner of the device. @@ -629,44 +539,6 @@ public class DevicePolicyManager { public static final String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning"; - - /** - * @hide - * This MIME type is used for starting the Device Owner provisioning that requires - * new provisioning features introduced in API version - * {@link android.os.Build.VERSION_CODES#M} in addition to those supported in earlier - * versions. - * - * <p>During device owner provisioning a device admin app is set as the owner of the device. - * A device owner has full control over the device. The device owner can not be modified by the - * user. - * - * <p> A typical use case would be a device that is owned by a company, but used by either an - * employee or client. - * - * <p> The NFC message should be sent to an unprovisioned device. - * - * <p>The NFC record must contain a serialized {@link java.util.Properties} object which - * contains the following properties in addition to properties listed at - * {@link #MIME_TYPE_PROVISIONING_NFC}: - * <ul> - * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li> - * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}. - * Replaces {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}. The value of the property - * should be converted to a String via - * {@link android.content.ComponentName#flattenToString()}</li> - * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE}, optional</li></ul> - * - * <p> When device owner provisioning has completed, an intent of the type - * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the - * device owner. - * - * <p> - * If provisioning fails, the device is factory reset. - */ - public static final String MIME_TYPE_PROVISIONING_NFC_V2 - = "application/com.android.managedprovisioning.v2"; - /** * Activity action: ask the user to add a new device administrator to the system. * The desired policy is the ComponentName of the policy in the @@ -2830,134 +2702,26 @@ public class DevicePolicyManager { /** * @hide - * Sets the given component as the device initializer. The package must already be installed and - * set as an active device administrator, and there must not be an existing device initializer, - * for this call to succeed. This method can only be called by an app holding the - * MANAGE_DEVICE_ADMINS permission before the device is provisioned or by a device owner app. A - * device initializer app is granted device owner privileges during device initialization and - * profile owner privileges during secondary user initialization. - * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or - * {@code null} if not called by the device owner. - * @param initializer Which {@link DeviceAdminReceiver} to make device initializer. - * @return whether the component was successfully registered as the device initializer. - * @throws IllegalArgumentException if the componentname is null or invalid - * @throws IllegalStateException if the caller is not device owner or the device has - * already been provisioned or a device initializer already exists. - */ - public boolean setDeviceInitializer(@Nullable ComponentName admin, - @NonNull ComponentName initializer) - throws IllegalArgumentException, IllegalStateException { - if (mService != null) { - try { - return mService.setDeviceInitializer(admin, initializer); - } catch (RemoteException re) { - Log.w(TAG, "Failed to set device initializer"); - } - } - return false; - } - - /** - * @hide - * Used to determine if a particular package has been registered as the device initializer. - * - * @param packageName the package name of the app, to compare with the registered device - * initializer app, if any. - * @return whether or not the caller is registered as the device initializer app. - */ - public boolean isDeviceInitializerApp(String packageName) { - if (mService != null) { - try { - return mService.isDeviceInitializer(packageName); - } catch (RemoteException re) { - Log.w(TAG, "Failed to check device initializer"); - } - } - return false; - } - - /** - * @hide - * Removes the device initializer, so that it will not be invoked on user initialization for any - * subsequently created users. This method can be called by either the device owner or device - * initializer itself. The caller must be an active administrator. - * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - */ - public void clearDeviceInitializerApp(@NonNull ComponentName admin) { - if (mService != null) { - try { - mService.clearDeviceInitializer(admin); - } catch (RemoteException re) { - Log.w(TAG, "Failed to clear device initializer"); - } - } - } - - /** - * @hide - * Gets the device initializer of the system. - * - * @return the package name of the device initializer. + * @deprecated Do not use + * @removed */ + @Deprecated @SystemApi public String getDeviceInitializerApp() { - if (mService != null) { - try { - return mService.getDeviceInitializer(); - } catch (RemoteException re) { - Log.w(TAG, "Failed to get device initializer"); - } - } return null; } /** * @hide - * Gets the device initializer component of the system. - * - * @return the component name of the device initializer. + * @deprecated Do not use + * @removed */ + @Deprecated @SystemApi public ComponentName getDeviceInitializerComponent() { - if (mService != null) { - try { - return mService.getDeviceInitializerComponent(); - } catch (RemoteException re) { - Log.w(TAG, "Failed to get device initializer"); - } - } return null; } - - /** - * @hide - * Sets the enabled state of the user. A user should be enabled only once it is ready to - * be used. - * - * <p>Device initializer must call this method to mark the user as functional. - * Only the device initializer agent can call this. - * - * <p>When the user is enabled, if the device initializer is not also the device owner, the - * device initializer will no longer have elevated permissions to call methods in this class. - * Additionally, it will be removed as an active administrator and its - * {@link DeviceAdminReceiver} will be disabled. - * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @return whether the user is now enabled. - */ - public boolean setUserEnabled(@NonNull ComponentName admin) { - if (mService != null) { - try { - return mService.setUserEnabled(admin); - } catch (RemoteException e) { - Log.w(TAG, "Failed talking with device policy service", e); - } - } - return false; - } - /** * @hide * @deprecated Use #ACTION_SET_PROFILE_OWNER diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 55a21c61f53a..74fb11dbc60c 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -211,13 +211,6 @@ interface IDevicePolicyManager { boolean isRemovingAdmin(in ComponentName adminReceiver, int userHandle); - boolean setUserEnabled(in ComponentName who); - boolean isDeviceInitializer(String packageName); - void clearDeviceInitializer(in ComponentName who); - boolean setDeviceInitializer(in ComponentName who, in ComponentName initializer); - String getDeviceInitializer(); - ComponentName getDeviceInitializerComponent(); - void setUserIcon(in ComponentName admin, in Bitmap icon); void setSystemUpdatePolicy(in ComponentName who, in SystemUpdatePolicy policy); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 04b1a3bd2369..1a8602b19c06 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3201,15 +3201,12 @@ public class PackageParser { } a.info.resizeable = sa.getBoolean( - R.styleable.AndroidManifestActivity_resizeableActivity, false); - if (a.info.resizeable) { - // Fixed screen orientation isn't supported with resizeable activities. - a.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - } else { - a.info.screenOrientation = sa.getInt( - R.styleable.AndroidManifestActivity_screenOrientation, - ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - } + R.styleable.AndroidManifestActivity_resizeableActivity, + owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N); + + a.info.screenOrientation = sa.getInt( + R.styleable.AndroidManifestActivity_screenOrientation, + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); a.info.lockTaskLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0); diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 20b0be11e23c..8f45f72de6e2 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -46,7 +46,7 @@ import java.lang.annotation.RetentionPolicy; * provide substantially improved capabilities over the older camera * API. Applications that target the limited level devices will run unchanged on * the full-level devices; if your application requires a full-level device for - * proper operation, declare the "android.hardware.camera2.full" feature in your + * proper operation, declare the "android.hardware.camera.level.full" feature in your * manifest.</p> * * @see CameraManager#openCamera diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 23748993f217..85041adbe975 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -634,6 +634,11 @@ public class Build { * M comes after L. */ public static final int M = 23; + + /** + * N is for ¯\_(ツ)_/¯. + */ + public static final int N = CUR_DEVELOPMENT; } /** The type of build, like "user" or "eng". */ @@ -691,6 +696,9 @@ public class Build { * @hide */ public static boolean isBuildConsistent() { + // Don't care on eng builds. Incremental build may trigger false negative. + if ("eng".equals(TYPE)) return true; + final String system = SystemProperties.get("ro.build.fingerprint"); final String vendor = SystemProperties.get("ro.vendor.build.fingerprint"); final String bootimage = SystemProperties.get("ro.bootimage.build.fingerprint"); diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java index 66642debc032..db04c7125adc 100644 --- a/core/java/android/preference/PreferenceFragment.java +++ b/core/java/android/preference/PreferenceFragment.java @@ -214,6 +214,9 @@ public abstract class PreferenceFragment extends Fragment implements @Override public void onDestroyView() { + if (mList != null) { + mList.setOnKeyListener(null); + } mList = null; mHandler.removeCallbacks(mRequestFocus); mHandler.removeMessages(MSG_BIND_PREFERENCES); diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java index d6e9e61f358b..13c36614729e 100644 --- a/core/java/android/preference/PreferenceGroup.java +++ b/core/java/android/preference/PreferenceGroup.java @@ -148,16 +148,15 @@ public abstract class PreferenceGroup extends Preference implements GenericInfla } } - int insertionIndex = Collections.binarySearch(mPreferenceList, preference); - if (insertionIndex < 0) { - insertionIndex = insertionIndex * -1 - 1; - } - if (!onPrepareAddPreference(preference)) { return false; } synchronized(this) { + int insertionIndex = Collections.binarySearch(mPreferenceList, preference); + if (insertionIndex < 0) { + insertionIndex = insertionIndex * -1 - 1; + } mPreferenceList.add(insertionIndex, preference); } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 4de903efff18..9a0f7fc811f2 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -110,6 +110,7 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_COMPONENT = "component"; private static final String RULE_ATT_ZEN = "zen"; private static final String RULE_ATT_CONDITION_ID = "conditionId"; + private static final String RULE_ATT_CREATION_TIME = "creationTime"; public boolean allowCalls = DEFAULT_ALLOW_CALLS; public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS; @@ -415,6 +416,7 @@ public class ZenModeConfig implements Parcelable { final String id = parser.getAttributeValue(null, RULE_ATT_ID); final ZenRule automaticRule = readRuleXml(parser); if (id != null && automaticRule != null) { + automaticRule.id = id; rt.automaticRules.put(id, automaticRule); } } @@ -468,6 +470,7 @@ public class ZenModeConfig implements Parcelable { } rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); + rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0); rt.condition = readConditionXml(parser); return rt; } @@ -485,6 +488,7 @@ public class ZenModeConfig implements Parcelable { if (rule.conditionId != null) { out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString()); } + out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime)); if (rule.condition != null) { writeConditionXml(rule.condition, out); } @@ -552,6 +556,11 @@ public class ZenModeConfig implements Parcelable { return Uri.parse(val); } + private static long safeLong(XmlPullParser parser, String att, long defValue) { + final String val = parser.getAttributeValue(null, att); + return tryParseLong(val, defValue); + } + public ArraySet<String> getAutomaticRuleNames() { final ArraySet<String> rt = new ArraySet<String>(); for (int i = 0; i < automaticRules.size(); i++) { @@ -943,6 +952,8 @@ public class ZenModeConfig implements Parcelable { public Uri conditionId; // required for automatic public Condition condition; // optional public ComponentName component; // optional + public String id; // required for automatic (unique) + public long creationTime; // required for automatic public ZenRule() { } @@ -956,6 +967,10 @@ public class ZenModeConfig implements Parcelable { conditionId = source.readParcelable(null); condition = source.readParcelable(null); component = source.readParcelable(null); + if (source.readInt() == 1) { + id = source.readString(); + } + creationTime = source.readLong(); } @Override @@ -977,6 +992,13 @@ public class ZenModeConfig implements Parcelable { dest.writeParcelable(conditionId, 0); dest.writeParcelable(condition, 0); dest.writeParcelable(component, 0); + if (id != null) { + dest.writeInt(1); + dest.writeString(id); + } else { + dest.writeInt(0); + } + dest.writeLong(creationTime); } @Override @@ -989,6 +1011,8 @@ public class ZenModeConfig implements Parcelable { .append(",conditionId=").append(conditionId) .append(",condition=").append(condition) .append(",component=").append(component) + .append(",id=").append(id) + .append(",creationTime=").append(creationTime) .append(']').toString(); } @@ -1029,6 +1053,12 @@ public class ZenModeConfig implements Parcelable { if (!Objects.equals(component, to.component)) { d.addLine(item, "component", component, to.component); } + if (!Objects.equals(id, to.id)) { + d.addLine(item, "id", id, to.id); + } + if (creationTime == to.creationTime) { + d.addLine(item, "creationTime", creationTime, to.creationTime); + } } @Override @@ -1042,13 +1072,15 @@ public class ZenModeConfig implements Parcelable { && other.zenMode == zenMode && Objects.equals(other.conditionId, conditionId) && Objects.equals(other.condition, condition) - && Objects.equals(other.component, component); + && Objects.equals(other.component, component) + && Objects.equals(other.id, id) + && other.creationTime == creationTime; } @Override public int hashCode() { return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, - component); + component, id, creationTime); } public boolean isAutomaticActive() { diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index acad49673694..cc4bcb6e2d77 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -80,20 +80,6 @@ oneway interface IWindow { int localValue, int localChanges); /** - * The window is beginning to animate. The application should stop drawing frames until the - * window is not animating anymore, indicated by being called {@link #windowEndAnimating}. - * - * @param remainingFrameCount how many frames the app might still draw before stopping drawing; - * pass -1 to let it continue drawing - */ - void onAnimationStarted(int remainingFrameCount); - - /** - * The window has ended animating. See {@link #onAnimationStarted}. - */ - void onAnimationStopped(); - - /** * Called for non-application windows when the enter animation has completed. */ void dispatchWindowShown(); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 7d48a9a11eda..db68c29d025f 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -447,10 +447,11 @@ public class SurfaceView extends View { final boolean formatChanged = mFormat != mRequestedFormat; final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; final boolean visibleChanged = mVisible != mRequestedVisible; + final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height; if (force || creating || formatChanged || sizeChanged || visibleChanged || mLeft != mLocation[0] || mTop != mLocation[1] - || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) { + || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) { if (DEBUG) Log.i(TAG, "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7cf23e717f6e..14e7d6c4e81e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -141,10 +141,10 @@ public final class ViewRootImpl implements ViewParent, static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList(); static boolean sFirstDrawComplete = false; - static final ArrayList<WindowCallbacks> sWindowCallbacks = new ArrayList(); static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList(); + final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList(); final Context mContext; final IWindowSession mWindowSession; final Display mDisplay; @@ -238,11 +238,7 @@ public final class ViewRootImpl implements ViewParent, boolean mNewSurfaceNeeded; boolean mHasHadWindowFocus; boolean mLastWasImTarget; - boolean mWindowsAnimating; - boolean mDrawDuringWindowsAnimating; - /** How many frames the app is still allowed to draw when a window animation is happening. */ - private int mRemainingFrameCount; boolean mIsDrawing; int mLastSystemUiVisibility; int mClientWindowLayoutFlags; @@ -424,18 +420,18 @@ public final class ViewRootImpl implements ViewParent, } } - public static void addWindowCallbacks(WindowCallbacks callback) { + public void addWindowCallbacks(WindowCallbacks callback) { if (USE_MT_RENDERER) { - synchronized (sWindowCallbacks) { - sWindowCallbacks.add(callback); + synchronized (mWindowCallbacks) { + mWindowCallbacks.add(callback); } } } - public static void removeWindowCallbacks(WindowCallbacks callback) { + public void removeWindowCallbacks(WindowCallbacks callback) { if (USE_MT_RENDERER) { - synchronized (sWindowCallbacks) { - sWindowCallbacks.remove(callback); + synchronized (mWindowCallbacks) { + mWindowCallbacks.remove(callback); } } } @@ -1978,8 +1974,6 @@ public final class ViewRootImpl implements ViewParent, } } - boolean skipDraw = false; - if (mFirst) { // handle first focus request if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()=" @@ -1994,11 +1988,6 @@ public final class ViewRootImpl implements ViewParent, + mView.findFocus()); } } - } else if (mWindowsAnimating) { - if (mRemainingFrameCount <= 0) { - skipDraw = true; - } - mRemainingFrameCount--; } final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible; @@ -2043,16 +2032,14 @@ public final class ViewRootImpl implements ViewParent, boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible; if (!cancelDraw && !newSurface) { - if (!skipDraw || mReportNextDraw) { - if (mPendingTransitions != null && mPendingTransitions.size() > 0) { - for (int i = 0; i < mPendingTransitions.size(); ++i) { - mPendingTransitions.get(i).startChangingAnimations(); - } - mPendingTransitions.clear(); + if (mPendingTransitions != null && mPendingTransitions.size() > 0) { + for (int i = 0; i < mPendingTransitions.size(); ++i) { + mPendingTransitions.get(i).startChangingAnimations(); } - - performDraw(); + mPendingTransitions.clear(); } + + performDraw(); } else { if (isViewVisible) { // Try again @@ -2579,6 +2566,14 @@ public final class ViewRootImpl implements ViewParent, dirty.setEmpty(); + // Stage the content drawn size now. It will be transferred to the renderer + // shortly before the draw commands get send to the renderer. + synchronized (mWindowCallbacks) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onContentDraw(mWindowAttributes.surfaceInsets.left, + mWindowAttributes.surfaceInsets.top, mWidth, mHeight); + } + } mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this); } else { // If we get here with a disabled & requested hardware renderer, something went @@ -2787,16 +2782,6 @@ public final class ViewRootImpl implements ViewParent, return mAttachInfo.mAccessibilityFocusDrawable; } - /** - * @hide - */ - public void setDrawDuringWindowsAnimating(boolean value) { - mDrawDuringWindowsAnimating = value; - if (value) { - handleDispatchWindowAnimationStopped(); - } - } - boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) { final Rect ci = mAttachInfo.mContentInsets; final Rect vi = mAttachInfo.mVisibleInsets; @@ -3160,8 +3145,6 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_WINDOW_MOVED = 23; private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24; private final static int MSG_DISPATCH_WINDOW_SHOWN = 25; - private final static int MSG_DISPATCH_WINDOW_ANIMATION_STOPPED = 26; - private final static int MSG_DISPATCH_WINDOW_ANIMATION_STARTED = 27; final class ViewRootHandler extends Handler { @Override @@ -3205,10 +3188,6 @@ public final class ViewRootImpl implements ViewParent, return "MSG_PROCESS_INPUT_EVENTS"; case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST"; - case MSG_DISPATCH_WINDOW_ANIMATION_STARTED: - return "MSG_DISPATCH_WINDOW_ANIMATION_STARTED"; - case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED: - return "MSG_DISPATCH_WINDOW_ANIMATION_STOPPED"; case MSG_WINDOW_MOVED: return "MSG_WINDOW_MOVED"; case MSG_SYNTHESIZE_INPUT_EVENT: @@ -3429,13 +3408,6 @@ public final class ViewRootImpl implements ViewParent, case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { setAccessibilityFocus(null, null); } break; - case MSG_DISPATCH_WINDOW_ANIMATION_STARTED: { - int remainingFrameCount = msg.arg1; - handleDispatchWindowAnimationStarted(remainingFrameCount); - } break; - case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED: { - handleDispatchWindowAnimationStopped(); - } break; case MSG_INVALIDATE_WORLD: { if (mView != null) { invalidateWorld(mView); @@ -4048,9 +4020,6 @@ public final class ViewRootImpl implements ViewParent, if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); } else { - // If delivering a new non-key event, make sure the window is - // now allowed to start updating. - handleDispatchWindowAnimationStopped(); final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { return processPointerEvent(q); @@ -4077,12 +4046,6 @@ public final class ViewRootImpl implements ViewParent, private int processKeyEvent(QueuedInputEvent q) { final KeyEvent event = (KeyEvent)q.mEvent; - if (event.getAction() != KeyEvent.ACTION_UP) { - // If delivering a new key event, make sure the window is - // now allowed to start updating. - handleDispatchWindowAnimationStopped(); - } - // Deliver the key to the view hierarchy. if (mView.dispatchKeyEvent(event)) { return FINISH_HANDLED; @@ -5297,22 +5260,6 @@ public final class ViewRootImpl implements ViewParent, } } - public void handleDispatchWindowAnimationStarted(int remainingFrameCount) { - if (!mDrawDuringWindowsAnimating && remainingFrameCount != -1) { - mRemainingFrameCount = remainingFrameCount; - mWindowsAnimating = true; - } - } - - public void handleDispatchWindowAnimationStopped() { - if (mWindowsAnimating) { - mWindowsAnimating = false; - if (!mDirty.isEmpty() || mIsAnimating || mFullRedrawNeeded) { - scheduleTraversals(); - } - } - } - public void handleDispatchWindowShown() { mAttachInfo.mTreeObserver.dispatchOnWindowShown(); } @@ -5653,6 +5600,17 @@ public final class ViewRootImpl implements ViewParent, + " contentInsets=" + contentInsets.toShortString() + " visibleInsets=" + visibleInsets.toShortString() + " reportDraw=" + reportDraw); + + // Tell all listeners that we are resizing the window so that the chrome can get + // updated as fast as possible on a separate thread, + if (mDragResizing) { + synchronized (mWindowCallbacks) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onWindowSizeIsChanging(frame); + } + } + } + Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED); if (mTranslator != null) { mTranslator.translateRectInScreenToAppWindow(frame); @@ -6224,15 +6182,6 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args)); } - public void dispatchWindowAnimationStarted(int remainingFrameCount) { - mHandler.obtainMessage(MSG_DISPATCH_WINDOW_ANIMATION_STARTED, - remainingFrameCount, 0 /* unused */).sendToTarget(); - } - - public void dispatchWindowAnimationStopped() { - mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_ANIMATION_STOPPED); - } - public void dispatchCheckFocus() { if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) { // This will result in a call to checkFocus() below. @@ -6665,15 +6614,6 @@ public final class ViewRootImpl implements ViewParent, Configuration newConfig) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - // Tell all listeners that we are resizing the window so that the chrome can get - // updated as fast as possible on a separate thread, - if (mViewAncestor.get().mDragResizing) { - synchronized (sWindowCallbacks) { - for (int i = sWindowCallbacks.size() - 1; i >= 0; i--) { - sWindowCallbacks.get(i).onWindowSizeIsChanging(frame); - } - } - } viewAncestor.dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets, reportDraw, newConfig); } @@ -6802,22 +6742,6 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void onAnimationStarted(int remainingFrameCount) { - final ViewRootImpl viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchWindowAnimationStarted(remainingFrameCount); - } - } - - @Override - public void onAnimationStopped() { - final ViewRootImpl viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchWindowAnimationStopped(); - } - } - - @Override public void dispatchWindowShown() { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { @@ -6848,9 +6772,9 @@ public final class ViewRootImpl implements ViewParent, private void startDragResizing(Rect initialBounds) { if (!mDragResizing) { mDragResizing = true; - synchronized (sWindowCallbacks) { - for (int i = sWindowCallbacks.size() - 1; i >= 0; i--) { - sWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds); + synchronized (mWindowCallbacks) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds); } } mFullRedrawNeeded = true; @@ -6863,9 +6787,9 @@ public final class ViewRootImpl implements ViewParent, private void endDragResizing() { if (mDragResizing) { mDragResizing = false; - synchronized (sWindowCallbacks) { - for (int i = sWindowCallbacks.size() - 1; i >= 0; i--) { - sWindowCallbacks.get(i).onWindowDragResizeEnd(); + synchronized (mWindowCallbacks) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onWindowDragResizeEnd(); } } mFullRedrawNeeded = true; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 5f4e7af9390e..135448511db3 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -28,9 +28,8 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; import android.graphics.PixelFormat; -import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.media.session.MediaController; import android.net.Uri; import android.os.Bundle; @@ -2007,16 +2006,6 @@ public abstract class Window { */ public abstract void setNavigationBarColor(@ColorInt int color); - /** - * Get information whether the activity has non client decoration view. These views are used in - * the multi window environment, to provide dragging handle and maximize/close buttons. - * - * @hide - */ - public boolean hasNonClientDecorView() { - return false; - } - /** @hide */ public void setTheme(int resId) { } diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java index cb6e98353a59..1aeaec8c6d15 100644 --- a/core/java/android/view/WindowCallbacks.java +++ b/core/java/android/view/WindowCallbacks.java @@ -21,6 +21,8 @@ import android.graphics.Rect; /** * These callbacks are used to communicate window configuration changes while the user is performing * window changes. + * Note: Note that at the time of onWindowDragResizeStart the content size isn't known. A consumer + * should therfore not draw anything before the additional onContentDraw call has arrived. * @hide */ public interface WindowCallbacks { @@ -45,4 +47,9 @@ public interface WindowCallbacks { * Called when a drag resize ends. */ void onWindowDragResizeEnd(); + + /** + * The content will now be drawn to these bounds. + */ + void onContentDraw(int offsetX, int offsetY, int sizeX, int sizeY); } diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java index 932b3540925b..0e3a69f5f0cd 100644 --- a/core/java/android/widget/AdapterViewAnimator.java +++ b/core/java/android/widget/AdapterViewAnimator.java @@ -401,12 +401,11 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> } LayoutParams createOrReuseLayoutParams(View v) { - final ViewGroup.LayoutParams currentLp = v.getLayoutParams(); - if (currentLp instanceof ViewGroup.LayoutParams) { - LayoutParams lp = (LayoutParams) currentLp; - return lp; + final LayoutParams currentLp = v.getLayoutParams(); + if (currentLp != null) { + return currentLp; } - return new ViewGroup.LayoutParams(0, 0); + return new LayoutParams(0, 0); } void refreshChildren() { diff --git a/core/java/android/widget/DropDownListView.java b/core/java/android/widget/DropDownListView.java index c869ccbdef58..69649ac71868 100644 --- a/core/java/android/widget/DropDownListView.java +++ b/core/java/android/widget/DropDownListView.java @@ -159,23 +159,6 @@ public class DropDownListView extends ListView { return super.onHoverEvent(ev); } - @Override - public boolean onTouchEvent(MotionEvent event) { - final int x = (int) event.getX(); - final int y = (int) event.getY(); - final int position = pointToPosition(x, y); - if (position == INVALID_POSITION) { - return super.onTouchEvent(event); - } - - if (position != mSelectedPosition) { - setSelectedPositionInt(position); - setNextSelectedPositionInt(position); - } - - return super.onTouchEvent(event); - } - /** * Handles forwarded events. * diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index c40289e44bcf..559181bdb823 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -383,6 +383,7 @@ class FastScroller { break; } } + ta.recycle(); updateAppearance(); } diff --git a/core/java/android/widget/MenuItemHoverListener.java b/core/java/android/widget/MenuItemHoverListener.java index 87c5c852973e..77d6674ead21 100644 --- a/core/java/android/widget/MenuItemHoverListener.java +++ b/core/java/android/widget/MenuItemHoverListener.java @@ -2,6 +2,8 @@ package android.widget; import com.android.internal.view.menu.MenuBuilder; +import android.annotation.NonNull; + /** * An interface notified when a menu item is hovered. Useful for cases when hover should trigger * some behavior at a higher level, like managing the opening and closing of submenus. @@ -9,5 +11,22 @@ import com.android.internal.view.menu.MenuBuilder; * @hide */ public interface MenuItemHoverListener { - public void onItemHovered(MenuBuilder menu, int position); + /** + * Called when hover exits a menu item. + * <p> + * If hover is moving to another item, this method will be called before + * {@link #onItemHoverEnter(MenuBuilder, int)} for the newly-hovered item. + * + * @param menu the item's parent menu + * @param position the position of the item within the menu + */ + void onItemHoverExit(@NonNull MenuBuilder menu, int position); + + /** + * Called when hover enters a menu item. + * + * @param menu the item's parent menu + * @param position the position of the item within the menu + */ + void onItemHoverEnter(@NonNull MenuBuilder menu, int position); } diff --git a/core/java/android/widget/MenuPopupWindow.java b/core/java/android/widget/MenuPopupWindow.java index 1fb62d0fa6c9..6b740d9268f9 100644 --- a/core/java/android/widget/MenuPopupWindow.java +++ b/core/java/android/widget/MenuPopupWindow.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -72,10 +73,18 @@ public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverLis } @Override - public void onItemHovered(MenuBuilder menu, int position) { + public void onItemHoverEnter(@NonNull MenuBuilder menu, int position) { // Forward up the chain if (mHoverListener != null) { - mHoverListener.onItemHovered(menu, position); + mHoverListener.onItemHoverEnter(menu, position); + } + } + + @Override + public void onItemHoverExit(@NonNull MenuBuilder menu, int position) { + // Forward up the chain + if (mHoverListener != null) { + mHoverListener.onItemHoverExit(menu, position); } } @@ -88,6 +97,8 @@ public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverLis private MenuItemHoverListener mHoverListener; + private int mPreviouslyHoveredPosition = INVALID_POSITION; + public MenuDropDownListView(Context context, boolean hijackFocus) { super(context, hijackFocus); @@ -135,25 +146,17 @@ public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverLis @Override public boolean onHoverEvent(MotionEvent ev) { - boolean dispatchHover = false; - final int position = pointToPosition((int) ev.getX(), (int) ev.getY()); - - final int action = ev.getActionMasked(); - if (action == MotionEvent.ACTION_HOVER_ENTER - || action == MotionEvent.ACTION_HOVER_MOVE) { - if (position != INVALID_POSITION && position != mSelectedPosition) { - final View hoveredItem = getChildAt(position - getFirstVisiblePosition()); - if (hoveredItem.isEnabled()) { - dispatchHover = true; - } - } + final int position; + if (ev.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + position = INVALID_POSITION; + } else { + position = pointToPosition((int) ev.getX(), (int) ev.getY()); } - boolean superVal = super.onHoverEvent(ev); - - if (dispatchHover && mHoverListener != null) { - ListAdapter adapter = getAdapter(); - MenuAdapter menuAdapter; + // Dispatch any changes in hovered position to the listener. + if (mHoverListener != null && mPreviouslyHoveredPosition != position) { + final ListAdapter adapter = getAdapter(); + final MenuAdapter menuAdapter; if (adapter instanceof HeaderViewListAdapter) { menuAdapter = (MenuAdapter) ((HeaderViewListAdapter) adapter) .getWrappedAdapter(); @@ -161,10 +164,18 @@ public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverLis menuAdapter = (MenuAdapter) adapter; } - mHoverListener.onItemHovered(menuAdapter.getAdapterMenu(), position); + final MenuBuilder menu = menuAdapter.getAdapterMenu(); + if (mPreviouslyHoveredPosition != INVALID_POSITION) { + mHoverListener.onItemHoverExit(menu, mPreviouslyHoveredPosition); + } + if (position != INVALID_POSITION) { + mHoverListener.onItemHoverEnter(menu, position); + } } - return superVal; + mPreviouslyHoveredPosition = position; + + return super.onHoverEvent(ev); } } }
\ No newline at end of file diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index f0e216fc7873..255e23a56a30 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import android.animation.ObjectAnimator; +import android.annotation.NonNull; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -29,6 +31,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.database.DataSetObserver; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; @@ -46,13 +49,18 @@ import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; import android.service.chooser.IChooserTargetService; import android.text.TextUtils; +import android.util.FloatProperty; import android.util.Log; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; +import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ListView; @@ -80,6 +88,7 @@ public class ChooserActivity extends ResolverActivity { private Intent mReferrerFillInIntent; private ChooserListAdapter mChooserListAdapter; + private ChooserRowAdapter mChooserRowAdapter; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); @@ -253,7 +262,9 @@ public class ChooserActivity extends ResolverActivity { boolean alwaysUseOption) { final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; mChooserListAdapter = (ChooserListAdapter) adapter; - adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter)); + mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); + mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView)); + adapterView.setAdapter(mChooserRowAdapter); if (listView != null) { listView.setItemsCanFocus(true); } @@ -913,19 +924,103 @@ public class ChooserActivity extends ResolverActivity { } } + static class RowScale { + private static final int DURATION = 400; + + float mScale; + ChooserRowAdapter mAdapter; + private final ObjectAnimator mAnimator; + + public static final FloatProperty<RowScale> PROPERTY = + new FloatProperty<RowScale>("scale") { + @Override + public void setValue(RowScale object, float value) { + object.mScale = value; + object.mAdapter.notifyDataSetChanged(); + } + + @Override + public Float get(RowScale object) { + return object.mScale; + } + }; + + public RowScale(@NonNull ChooserRowAdapter adapter, float from, float to) { + mAdapter = adapter; + mScale = from; + if (from == to) { + mAnimator = null; + return; + } + + mAnimator = ObjectAnimator.ofFloat(this, PROPERTY, from, to).setDuration(DURATION); + } + + public RowScale setInterpolator(Interpolator interpolator) { + if (mAnimator != null) { + mAnimator.setInterpolator(interpolator); + } + return this; + } + + public float get() { + return mScale; + } + + public void startAnimation() { + if (mAnimator != null) { + mAnimator.start(); + } + } + + public void cancelAnimation() { + if (mAnimator != null) { + mAnimator.cancel(); + } + } + } + class ChooserRowAdapter extends BaseAdapter { private ChooserListAdapter mChooserListAdapter; private final LayoutInflater mLayoutInflater; private final int mColumnCount = 4; + private RowScale[] mServiceTargetScale; + private final Interpolator mInterpolator; public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) { mChooserListAdapter = wrappedAdapter; mLayoutInflater = LayoutInflater.from(ChooserActivity.this); + mInterpolator = AnimationUtils.loadInterpolator(ChooserActivity.this, + android.R.interpolator.decelerate_quint); + wrappedAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { super.onChanged(); + final int rcount = getServiceTargetRowCount(); + if (mServiceTargetScale == null + || mServiceTargetScale.length != rcount) { + RowScale[] old = mServiceTargetScale; + int oldRCount = old != null ? old.length : 0; + mServiceTargetScale = new RowScale[rcount]; + if (old != null && rcount > 0) { + System.arraycopy(old, 0, mServiceTargetScale, 0, + Math.min(old.length, rcount)); + } + + for (int i = rcount; i < oldRCount; i++) { + old[i].cancelAnimation(); + } + + for (int i = oldRCount; i < rcount; i++) { + final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) + .setInterpolator(mInterpolator); + mServiceTargetScale[i] = rs; + rs.startAnimation(); + } + } + notifyDataSetChanged(); } @@ -933,19 +1028,43 @@ public class ChooserActivity extends ResolverActivity { public void onInvalidated() { super.onInvalidated(); notifyDataSetInvalidated(); + if (mServiceTargetScale != null) { + for (RowScale rs : mServiceTargetScale) { + rs.cancelAnimation(); + } + } } }); } + private float getRowScale(int rowPosition) { + final int start = getCallerTargetRowCount(); + final int end = start + getServiceTargetRowCount(); + if (rowPosition >= start && rowPosition < end) { + return mServiceTargetScale[rowPosition - start].get(); + } + return 1.f; + } + @Override public int getCount() { return (int) ( - Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount) - + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount) + getCallerTargetRowCount() + + getServiceTargetRowCount() + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount) ); } + public int getCallerTargetRowCount() { + return (int) Math.ceil( + (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount); + } + + public int getServiceTargetRowCount() { + return (int) Math.ceil( + (float) mChooserListAdapter.getServiceTargetCount() / mColumnCount); + } + @Override public Object getItem(int position) { // We have nothing useful to return here. @@ -959,33 +1078,67 @@ public class ChooserActivity extends ResolverActivity { @Override public View getView(int position, View convertView, ViewGroup parent) { - final View[] holder; + final RowViewHolder holder; if (convertView == null) { holder = createViewHolder(parent); } else { - holder = (View[]) convertView.getTag(); + holder = (RowViewHolder) convertView.getTag(); } bindViewHolder(position, holder); - // We keep the actual list item view as the last item in the holder array - return holder[mColumnCount]; + return holder.row; } - View[] createViewHolder(ViewGroup parent) { - final View[] holder = new View[mColumnCount + 1]; - + RowViewHolder createViewHolder(ViewGroup parent) { final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent, false); + final RowViewHolder holder = new RowViewHolder(row, mColumnCount); + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + for (int i = 0; i < mColumnCount; i++) { - holder[i] = mChooserListAdapter.createView(row); - row.addView(holder[i]); + final View v = mChooserListAdapter.createView(row); + v.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startSelected(holder.itemIndex, false, true); + } + }); + v.setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + showAppDetails( + mChooserListAdapter.resolveInfoForPosition(holder.itemIndex, true)); + return true; + } + }); + row.addView(v); + holder.cells[i] = v; + + // Force height to be a given so we don't have visual disruption during scaling. + LayoutParams lp = v.getLayoutParams(); + v.measure(spec, spec); + if (lp == null) { + lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight()); + row.setLayoutParams(lp); + } else { + lp.height = v.getMeasuredHeight(); + } + } + + // Pre-measure so we can scale later. + holder.measure(); + LayoutParams lp = row.getLayoutParams(); + if (lp == null) { + lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight); + row.setLayoutParams(lp); + } else { + lp.height = holder.measuredRowHeight; } row.setTag(holder); - holder[mColumnCount] = row; return holder; } - void bindViewHolder(int rowPosition, View[] holder) { + void bindViewHolder(int rowPosition, RowViewHolder holder) { final int start = getFirstRowPosition(rowPosition); final int startType = mChooserListAdapter.getPositionTargetType(start); @@ -994,34 +1147,26 @@ public class ChooserActivity extends ResolverActivity { end--; } - final ViewGroup row = (ViewGroup) holder[mColumnCount]; - if (startType == ChooserListAdapter.TARGET_SERVICE) { - row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color)); + holder.row.setBackgroundColor( + getColor(R.color.chooser_service_row_background_color)); } else { - row.setBackground(null); + holder.row.setBackgroundColor(Color.TRANSPARENT); + } + + final int oldHeight = holder.row.getLayoutParams().height; + holder.row.getLayoutParams().height = Math.max(1, + (int) (holder.measuredRowHeight * getRowScale(rowPosition))); + if (holder.row.getLayoutParams().height != oldHeight) { + holder.row.requestLayout(); } for (int i = 0; i < mColumnCount; i++) { - final View v = holder[i]; + final View v = holder.cells[i]; if (start + i <= end) { v.setVisibility(View.VISIBLE); - final int itemIndex = start + i; - mChooserListAdapter.bindView(itemIndex, v); - v.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startSelected(itemIndex, false, true); - } - }); - v.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - showAppDetails( - mChooserListAdapter.resolveInfoForPosition(itemIndex, true)); - return true; - } - }); + holder.itemIndex = start + i; + mChooserListAdapter.bindView(holder.itemIndex, v); } else { v.setVisibility(View.GONE); } @@ -1048,6 +1193,24 @@ public class ChooserActivity extends ResolverActivity { } } + static class RowViewHolder { + final View[] cells; + final ViewGroup row; + int measuredRowHeight; + int itemIndex; + + public RowViewHolder(ViewGroup row, int cellCount) { + this.row = row; + this.cells = new View[cellCount]; + } + + public void measure() { + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + row.measure(spec, spec); + measuredRowHeight = row.getMeasuredHeight(); + } + } + static class ChooserTargetServiceConnection implements ServiceConnection { private final DisplayResolveInfo mOriginalTarget; private ComponentName mConnectedComponent; @@ -1199,4 +1362,44 @@ public class ChooserActivity extends ResolverActivity { mSelectedTarget = null; } } + + class OffsetDataSetObserver extends DataSetObserver { + private final AbsListView mListView; + private int mCachedViewType = -1; + private View mCachedView; + + public OffsetDataSetObserver(AbsListView listView) { + mListView = listView; + } + + @Override + public void onChanged() { + if (mResolverDrawerLayout == null) { + return; + } + + final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount(); + int offset = 0; + for (int i = 0; i < chooserTargetRows; i++) { + final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i; + final int vt = mChooserRowAdapter.getItemViewType(pos); + if (vt != mCachedViewType) { + mCachedView = null; + } + final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView); + int height = ((RowViewHolder) (v.getTag())).measuredRowHeight; + + offset += (int) (height * mChooserRowAdapter.getRowScale(pos) * chooserTargetRows); + + if (vt >= 0) { + mCachedViewType = vt; + mCachedView = v; + } else { + mCachedViewType = -1; + } + } + + mResolverDrawerLayout.setCollapsibleHeightReserved(offset); + } + } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 17104894310e..ba0912a5536a 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -103,6 +103,8 @@ public class ResolverActivity extends Activity { private ResolverComparator mResolverComparator; private PickTargetOptionRequest mPickOptionRequest; + protected ResolverDrawerLayout mResolverDrawerLayout; + private boolean mRegistered; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { @@ -253,6 +255,7 @@ public class ResolverActivity extends Activity { if (isVoiceInteraction()) { rdl.setCollapsed(false); } + mResolverDrawerLayout = rdl; } if (title == null) { @@ -1570,7 +1573,10 @@ public class ResolverActivity extends Activity { private void onBindView(View view, TargetInfo info) { final ViewHolder holder = (ViewHolder) view.getTag(); - holder.text.setText(info.getDisplayLabel()); + final CharSequence label = info.getDisplayLabel(); + if (!TextUtils.equals(holder.text.getText(), label)) { + holder.text.setText(info.getDisplayLabel()); + } if (showsExtendedInfo(info)) { holder.text2.setVisibility(View.VISIBLE); holder.text2.setText(info.getExtendedInfo()); diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index c9b81190ce38..23c40473f621 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -488,7 +488,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } public void clearContentView() { - if (mNonClientDecorView.getChildCount() > 1) { + if (mNonClientDecorView != null && mNonClientDecorView.getChildCount() > 1) { mNonClientDecorView.removeViewAt(1); } } @@ -5413,16 +5413,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @Return Returns true if the window should show a shadow. **/ private boolean nonClientDecorHasShadow(int workspaceId) { - // TODO(skuhne): Add side by side mode here to add a decor. return workspaceId == FREEFORM_WORKSPACE_STACK_ID; } @Override - public boolean hasNonClientDecorView() { - return mNonClientDecorView != null; - } - - @Override public void setTheme(int resid) { mTheme = resid; if (mDecor != null) { diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 3eeabcddb3c7..07bfce727964 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -100,14 +100,6 @@ public class BaseIWindow extends IWindow.Stub { } @Override - public void onAnimationStarted(int remainingFrameCount) { - } - - @Override - public void onAnimationStopped() { - } - - @Override public void dispatchWindowShown() { } } diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java index 293e2ade7229..1a7d91ebc2e0 100644 --- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java +++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java @@ -5,28 +5,32 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import android.annotation.AttrRes; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StyleRes; import android.content.Context; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.os.Handler; import android.os.Parcelable; +import android.os.SystemClock; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewTreeObserver; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnKeyListener; import android.view.ViewTreeObserver.OnGlobalLayoutListener; -import android.widget.DropDownListView; import android.widget.FrameLayout; -import android.widget.MenuItemHoverListener; +import android.widget.HeaderViewListAdapter; import android.widget.ListAdapter; +import android.widget.MenuItemHoverListener; 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; @@ -47,6 +51,10 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey private static final int HORIZ_POSITION_LEFT = 0; private static final int HORIZ_POSITION_RIGHT = 1; + /** + * Delay between hovering over a menu item with a mouse and receiving + * side-effects (ex. opening a sub-menu or closing unrelated menus). + */ private static final int SUBMENU_TIMEOUT_MS = 200; private final Context mContext; @@ -54,9 +62,14 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey private final int mPopupStyleAttr; private final int mPopupStyleRes; private final boolean mOverflowOnly; - private final int mLayoutDirection; private final Handler mSubMenuHoverHandler; + /** + * List of open menus. The first item is the root menu and each + * subsequent item is a direct submenu of the previous item. + */ + private final List<CascadingMenuInfo> mAddedMenus = new ArrayList<>(); + private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { @@ -66,8 +79,8 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey dismiss(); } else if (isShowing()) { // Recompute window sizes and positions. - for (MenuPopupWindow popup : mPopupWindows) { - popup.show(); + for (CascadingMenuInfo info : mAddedMenus) { + info.window.show(); } } } @@ -94,13 +107,22 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey private final MenuItemHoverListener mMenuItemHoverListener = new MenuItemHoverListener() { @Override - public void onItemHovered(MenuBuilder menu, int position) { - int menuIndex = -1; - for (int i = 0; i < mListViews.size(); i++) { - final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(i); - final MenuAdapter adapter = toMenuAdapter(view.getAdapter()); + public void onItemHoverExit(@NonNull MenuBuilder menu, int position) { + // If the mouse moves between two windows, hover enter/exit pairs + // may be received out of order. So, instead of canceling all + // pending runnables, only cancel runnables for the host menu. + mSubMenuHoverHandler.removeCallbacksAndMessages(menu); + } + + @Override + public void onItemHoverEnter(@NonNull final MenuBuilder menu, final int position) { + // Something new was hovered, cancel all scheduled runnables. + mSubMenuHoverHandler.removeCallbacksAndMessages(null); - if (adapter.getAdapterMenu() == menu) { + // Find the position of the hovered menu within the added menus. + int menuIndex = -1; + for (int i = 0, count = mAddedMenus.size(); i < count; i++) { + if (menu == mAddedMenus.get(i).menu) { menuIndex = i; break; } @@ -110,82 +132,44 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey return; } - final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(menuIndex); - final ListMenuItemView selectedItemView = (ListMenuItemView) view.getSelectedView(); - - if (selectedItemView != null && selectedItemView.isEnabled() - && selectedItemView.getItemData().hasSubMenu()) { - // If the currently selected item corresponds to a submenu, schedule to open the - // submenu on a timeout. - - mSubMenuHoverHandler.removeCallbacksAndMessages(null); - mSubMenuHoverHandler.postDelayed(new Runnable() { - @Override - public void run() { - // Make sure the submenu item is still the one selected. - if (view.getSelectedView() == selectedItemView - && selectedItemView.isEnabled() - && selectedItemView.getItemData().hasSubMenu()) { - // Close any other submenus that might be open at the current or - // a deeper level. - int nextIndex = mListViews.indexOf(view) + 1; - if (nextIndex < mListViews.size()) { - MenuAdapter nextSubMenuAdapter = - toMenuAdapter(mListViews.get(nextIndex).getAdapter()); - // Disable exit animation, to prevent overlapping fading out - // submenus. - mPopupWindows.get(nextIndex).setExitTransition(null); - nextSubMenuAdapter.getAdapterMenu().close(); - } - - // Then open the selected submenu. - view.performItemClick( - selectedItemView, - view.getSelectedItemPosition(), - view.getSelectedItemId()); - } + final CascadingMenuInfo nextInfo; + final int nextIndex = menuIndex + 1; + if (nextIndex < mAddedMenus.size()) { + nextInfo = mAddedMenus.get(nextIndex); + } else { + nextInfo = null; + } + + final Runnable runnable = new Runnable() { + @Override + public void run() { + // Close any other submenus that might be open at the + // current or a deeper level. + if (nextInfo != null) { + // Disable exit animations to prevent overlapping + // fading out submenus. + nextInfo.window.setExitTransition(null); + nextInfo.window.setAnimationStyle(0); + nextInfo.menu.close(false); } - }, SUBMENU_TIMEOUT_MS); - } else if (menuIndex + 1 < mListViews.size()) { - // If the currently selected item does NOT corresponds to a submenu, check if there - // is a submenu already open that is one level deeper. If so, schedule to close it - // on a timeout. - - final MenuDropDownListView nextView = - (MenuDropDownListView) mListViews.get(menuIndex + 1); - final MenuAdapter nextAdapter = toMenuAdapter(nextView.getAdapter()); - - mSubMenuHoverHandler.removeCallbacksAndMessages(null); - mSubMenuHoverHandler.postDelayed(new Runnable() { - @Override - public void run() { - // Make sure the menu wasn't already closed by something else and that - // it wasn't re-hovered by the user since this was scheduled. - int nextMenuIndex = mListViews.indexOf(nextView); - - if (nextMenuIndex != -1 && nextView.getSelectedView() == null) { - // Disable exit animation, to prevent overlapping fading out submenus. - for (int i = nextMenuIndex; i < mPopupWindows.size(); i++) { - final MenuPopupWindow popupWindow = mPopupWindows.get(i); - popupWindow.setExitTransition(null); - popupWindow.setAnimationStyle(0); - } - nextAdapter.getAdapterMenu().close(); - } + + // Then open the selected submenu, if there is one. + final MenuItem menuItem = menu.getItem(position); + if (menuItem.isEnabled() && menuItem.hasSubMenu()) { + menu.performItemAction(menuItem, 0); } - }, SUBMENU_TIMEOUT_MS); - } + } + }; + final long uptimeMillis = SystemClock.uptimeMillis() + SUBMENU_TIMEOUT_MS; + mSubMenuHoverHandler.postAtTime(runnable, menu, uptimeMillis); } }; + private int mRawDropDownGravity = Gravity.NO_GRAVITY; private int mDropDownGravity = Gravity.NO_GRAVITY; private View mAnchorView; private View mShownAnchorView; - private List<DropDownListView> mListViews; - private List<MenuPopupWindow> mPopupWindows; private int mLastPosition; - private List<Integer> mPositions; - private List<int[]> mOffsets; private int mInitXOffset; private int mInitYOffset; private boolean mForceShowIcon; @@ -197,10 +181,10 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey /** * Initializes a new cascading-capable menu popup. * - * @param parent A parent view to get the {@link android.view.View#getWindowToken()} token from. + * @param anchor A parent view to get the {@link android.view.View#getWindowToken()} token from. */ - public CascadingMenuPopup(Context context, View anchor, int popupStyleAttr, - int popupStyleRes, boolean overflowOnly) { + public CascadingMenuPopup(@NonNull Context context, @NonNull View anchor, + @AttrRes int popupStyleAttr, @StyleRes int popupStyleRes, boolean overflowOnly) { mContext = Preconditions.checkNotNull(context); mAnchorView = Preconditions.checkNotNull(anchor); mPopupStyleAttr = popupStyleAttr; @@ -208,18 +192,12 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey mOverflowOnly = overflowOnly; mForceShowIcon = false; + mLastPosition = getInitialMenuPosition(); final Resources res = context.getResources(); - final Configuration config = res.getConfiguration(); - mLayoutDirection = config.getLayoutDirection(); - mLastPosition = getInitialMenuPosition(); mMenuMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2, res.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth)); - mPopupWindows = new ArrayList<MenuPopupWindow>(); - mListViews = new ArrayList<DropDownListView>(); - mOffsets = new ArrayList<int[]>(); - mPositions = new ArrayList<Integer>(); mSubMenuHoverHandler = new Handler(); } @@ -246,28 +224,28 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey return; } - // Show any menus that have been added via #addMenu(MenuBuilder) but which have not yet been - // shown. - // In a typical use case, #addMenu(MenuBuilder) would be called once, followed by a call to - // this #show() method -- which would actually show the popup on the screen. - for (int i = 0; i < mPopupWindows.size(); i++) { - MenuPopupWindow popupWindow = mPopupWindows.get(i); + // Show any menus that have been added via #addMenu(MenuBuilder) but + // which have not yet been shown. In a typical use case, + // #addMenu(MenuBuilder) would be called once, followed by a call to + // this #show() method -- which would actually show the popup on the + // screen. + for (int i = 0, count = mAddedMenus.size(); i < count; i++) { + final CascadingMenuInfo info = mAddedMenus.get(i); + final MenuPopupWindow popupWindow = info.window; popupWindow.show(); - DropDownListView listView = (DropDownListView) popupWindow.getListView(); - mListViews.add(listView); - MenuBuilder menu = toMenuAdapter(listView.getAdapter()).getAdapterMenu(); + final MenuBuilder menu = info.menu; 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, + info.getListView(), false); TextView titleView = (TextView) titleItemView.findViewById( com.android.internal.R.id.title); titleView.setText(menu.getHeaderTitle()); titleItemView.setEnabled(false); - listView.addHeaderView(titleItemView, null, false); + info.getListView().addHeaderView(titleItemView, null, false); // Update to show the title. popupWindow.show(); @@ -287,12 +265,13 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey @Override public void dismiss() { - // Need to make another list to avoid a concurrent modification exception, as #onDismiss - // may clear mPopupWindows while we are iterating. - List<MenuPopupWindow> popupWindows = new ArrayList<MenuPopupWindow>(mPopupWindows); - for (MenuPopupWindow popupWindow : popupWindows) { - if (popupWindow != null && popupWindow.isShowing()) { - popupWindow.dismiss(); + // Need to make another list to avoid a concurrent modification + // exception, as #onDismiss may clear mPopupWindows while we are + // iterating. + final List<CascadingMenuInfo> addedMenus = new ArrayList<>(mAddedMenus); + for (CascadingMenuInfo info : addedMenus) { + if (info.window.isShowing()) { + info.window.dismiss(); } } } @@ -312,7 +291,8 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey */ @HorizPosition private int getInitialMenuPosition() { - return mLayoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT : + final int layoutDirection = mAnchorView.getLayoutDirection(); + return layoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT : HORIZ_POSITION_RIGHT; } @@ -325,7 +305,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey */ @HorizPosition private int getNextMenuPosition(int nextMenuWidth) { - ListView lastListView = mListViews.get(mListViews.size() - 1); + ListView lastListView = mAddedMenus.get(mAddedMenus.size() - 1).getListView(); final int[] screenLocation = new int[2]; lastListView.getLocationOnScreen(screenLocation); @@ -350,76 +330,140 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey @Override public void addMenu(MenuBuilder menu) { - boolean addSubMenu = mListViews.size() > 0; - menu.addMenuPresenter(this, mContext); - MenuPopupWindow popupWindow = createPopupWindow(); - - MenuAdapter adapter = new MenuAdapter(menu, LayoutInflater.from(mContext), mOverflowOnly); + final LayoutInflater inflater = LayoutInflater.from(mContext); + final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly); adapter.setForceShowIcon(mForceShowIcon); + final int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth); + final MenuPopupWindow popupWindow = createPopupWindow(); popupWindow.setAdapter(adapter); + popupWindow.setWidth(menuWidth); + popupWindow.setDropDownGravity(mDropDownGravity); - int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth); - - int x = 0; - int y = 0; + final CascadingMenuInfo parentInfo; + final View parentView; + if (mAddedMenus.size() > 0) { + parentInfo = mAddedMenus.get(mAddedMenus.size() - 1); + parentView = findParentViewForSubmenu(parentInfo, menu); + } else { + parentInfo = null; + parentView = null; + } - if (addSubMenu) { + final int x; + final int y; + if (parentView != null) { + // This menu is a cascading submenu anchored to a parent view. popupWindow.setTouchModal(false); popupWindow.setEnterTransition(null); - ListView lastListView = mListViews.get(mListViews.size() - 1); - @HorizPosition int nextMenuPosition = getNextMenuPosition(menuWidth); - boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT; + final @HorizPosition int nextMenuPosition = getNextMenuPosition(menuWidth); + final boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT; mLastPosition = nextMenuPosition; - int[] lastLocation = new int[2]; - lastListView.getLocationOnScreen(lastLocation); + final int[] tempLocation = new int[2]; - int[] lastOffset = mOffsets.get(mOffsets.size() - 1); + // This popup menu will be positioned relative to the top-left edge + // of the view representing its parent menu. + parentView.getLocationInWindow(tempLocation); + final int parentOffsetLeft = parentInfo.window.getHorizontalOffset() + tempLocation[0]; + final int parentOffsetTop = parentInfo.window.getVerticalOffset() + tempLocation[1]; - // Note: By now, mDropDownGravity is the absolute gravity, so this should work in both - // LTR and RTL. + // By now, mDropDownGravity is the resolved absolute gravity, so + // this should work in both LTR and RTL. if ((mDropDownGravity & Gravity.RIGHT) == Gravity.RIGHT) { if (showOnRight) { - x = lastOffset[0] + menuWidth; + x = parentOffsetLeft + menuWidth; } else { - x = lastOffset[0] - lastListView.getWidth(); + x = parentOffsetLeft - parentView.getWidth(); } } else { if (showOnRight) { - x = lastOffset[0] + lastListView.getWidth(); + x = parentOffsetLeft + parentView.getWidth(); } else { - x = lastOffset[0] - menuWidth; + x = parentOffsetLeft - menuWidth; } } - y = lastOffset[1] + lastListView.getSelectedView().getTop() - - lastListView.getChildAt(0).getTop(); + y = parentOffsetTop; } else { x = mInitXOffset; y = mInitYOffset; } - popupWindow.setWidth(menuWidth); popupWindow.setHorizontalOffset(x); popupWindow.setVerticalOffset(y); - mPopupWindows.add(popupWindow); + + final CascadingMenuInfo menuInfo = new CascadingMenuInfo(popupWindow, menu, mLastPosition); + mAddedMenus.add(menuInfo); // NOTE: This case handles showing submenus once the CascadingMenuPopup has already // been shown via a call to its #show() method. If it hasn't yet been show()n, then // we deliberately do not yet show the popupWindow, as #show() will do that later. if (isShowing()) { popupWindow.show(); - DropDownListView listView = (DropDownListView) popupWindow.getListView(); - mListViews.add(listView); + } + } + + /** + * Returns the index within the specified parent menu of the menu item that + * owns specified submenu. + * + * @param parent the parent menu + * @param submenu the submenu for which the index should be returned + * @return the index or {@code -1} if not present + */ + private int findIndexOfSubmenuInMenu( + @NonNull MenuBuilder parent, @NonNull MenuBuilder submenu) { + for (int i = 0, count = parent.size(); i < count; i++) { + final MenuItem item = parent.getItem(i); + if (item.hasSubMenu() && submenu == item.getSubMenu()) { + return i; + } } - int[] offsets = {x, y}; - mOffsets.add(offsets); - mPositions.add(mLastPosition); + return -1; + } + + /** + * Attempts to find the view for the menu item that owns the specified + * submenu. + * + * @param parentInfo info for the parent menu + * @param submenu the submenu whose parent view should be obtained + * @return the parent view, or {@code null} if one could not be found + */ + @Nullable + private View findParentViewForSubmenu( + @NonNull CascadingMenuInfo parentInfo, @NonNull MenuBuilder submenu) { + final int parentIndex = findIndexOfSubmenuInMenu(parentInfo.menu, submenu); + if (parentIndex < 0) { + // Couldn't find the submenu. + return null; + } + + // The adapter may be wrapped. Adjust the index if necessary. + final ListView listView = parentInfo.getListView(); + final ListAdapter listAdapter = listView.getAdapter(); + final int adjParentIndex; + if (listAdapter instanceof HeaderViewListAdapter) { + final int numHeaders = ((HeaderViewListAdapter) listAdapter).getHeadersCount(); + adjParentIndex = parentIndex + numHeaders; + } else { + adjParentIndex = parentIndex; + } + + // Obtain the view at the menu item's adjusted index. + final int firstPosition = listView.getFirstVisiblePosition(); + final int parentPosition = adjParentIndex - firstPosition; + if (parentPosition < 0 || parentPosition >= listView.getChildCount()) { + // Not visible on screen. + return null; + } + + return listView.getChildAt(parentPosition); } /** @@ -427,7 +471,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey */ @Override public boolean isShowing() { - return mPopupWindows.size() > 0 && mPopupWindows.get(0).isShowing(); + return mAddedMenus.size() > 0 && mAddedMenus.get(0).window.isShowing(); } /** @@ -435,28 +479,28 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey */ @Override public void onDismiss() { - int dismissedIndex = -1; - for (int i = 0; i < mPopupWindows.size(); i++) { - if (!mPopupWindows.get(i).isShowing()) { - dismissedIndex = i; + // The dismiss listener doesn't pass the calling window, so walk + // through the stack to figure out which one was just dismissed. + CascadingMenuInfo dismissedInfo = null; + for (int i = 0, count = mAddedMenus.size(); i < count; i++) { + final CascadingMenuInfo info = mAddedMenus.get(i); + if (!info.window.isShowing()) { + dismissedInfo = info; break; } } - if (dismissedIndex != -1) { - for (int i = dismissedIndex; i < mListViews.size(); i++) { - ListView view = mListViews.get(i); - ListAdapter adapter = view.getAdapter(); - MenuAdapter menuAdapter = toMenuAdapter(adapter); - menuAdapter.mAdapterMenu.close(); - } + // Close all menus starting from the dismissed menu, passing false + // since we are manually closing only a subset of windows. + if (dismissedInfo != null) { + dismissedInfo.menu.close(false); } } @Override public void updateMenuView(boolean cleared) { - for (ListView view : mListViews) { - toMenuAdapter(view.getAdapter()).notifyDataSetChanged(); + for (CascadingMenuInfo info : mAddedMenus) { + toMenuAdapter(info.getListView().getAdapter()).notifyDataSetChanged(); } } @@ -468,16 +512,17 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey @Override public boolean onSubMenuSelected(SubMenuBuilder subMenu) { // Don't allow double-opening of the same submenu. - for (ListView view : mListViews) { - if (toMenuAdapter(view.getAdapter()).mAdapterMenu.equals(subMenu)) { + for (CascadingMenuInfo info : mAddedMenus) { + if (subMenu == info.menu) { // Just re-focus that one. - view.requestFocus(); + info.getListView().requestFocus(); return true; } } if (subMenu.hasVisibleItems()) { - this.addMenu(subMenu); + addMenu(subMenu); + if (mPresenterCallback != null) { mPresenterCallback.onOpenSubMenu(subMenu); } @@ -486,52 +531,61 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey return false; } + /** + * Finds the index of the specified menu within the list of added menus. + * + * @param menu the menu to find + * @return the index of the menu, or {@code -1} if not present + */ + private int findIndexOfAddedMenu(@NonNull MenuBuilder menu) { + for (int i = 0, count = mAddedMenus.size(); i < count; i++) { + final CascadingMenuInfo info = mAddedMenus.get(i); + if (menu == info.menu) { + return i; + } + } + + return -1; + } + @Override public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { - int menuIndex = -1; - boolean wasSelected = false; - - for (int i = 0; i < mListViews.size(); i++) { - ListView view = mListViews.get(i); - MenuAdapter adapter = toMenuAdapter(view.getAdapter()); + final int menuIndex = findIndexOfAddedMenu(menu); + if (menuIndex < 0) { + return; + } - if (menuIndex == -1 && menu == adapter.mAdapterMenu) { - menuIndex = i; - wasSelected = view.getSelectedView() != null; - } + // Close this and all descendant menus. + final int nextMenuIndex = menuIndex + 1; + if (nextMenuIndex < mAddedMenus.size()) { + final CascadingMenuInfo info = mAddedMenus.get(nextMenuIndex); - // Once the menu has been found, remove it and all submenus beneath it from the - // container view. Also remove the presenter. - if (menuIndex != -1) { - adapter.mAdapterMenu.removeMenuPresenter(this); - } + // Disable all exit animations. + info.window.setExitTransition(null); + info.window.setAnimationStyle(0); + info.menu.close(false); } - // Then, actually remove the views for these [sub]menu(s) from our list of views. - if (menuIndex != -1) { - for (int i = menuIndex; i < mPopupWindows.size(); i++) { - mPopupWindows.get(i).dismiss(); - } - mPopupWindows.subList(menuIndex, mPopupWindows.size()).clear(); - mListViews.subList(menuIndex, mListViews.size()).clear(); - mOffsets.subList(menuIndex, mOffsets.size()).clear(); + final CascadingMenuInfo info = mAddedMenus.remove(menuIndex); + info.menu.removeMenuPresenter(this); + info.window.dismiss(); - mPositions.subList(menuIndex, mPositions.size()).clear(); - if (mPositions.size() > 0) { - mLastPosition = mPositions.get(mPositions.size() - 1); - } else { - mLastPosition = getInitialMenuPosition(); - } + final int count = mAddedMenus.size(); + if (count > 0) { + mLastPosition = mAddedMenus.get(count - 1).position; + } else { + mLastPosition = getInitialMenuPosition(); } - if (mListViews.size() == 0 || wasSelected) { + // If this was the root menu or we're supposed to close all menus, + // dismiss the window and close all menus from the root. + if (count == 0 || allMenusAreClosing) { dismiss(); + if (mPresenterCallback != null) { - mPresenterCallback.onCloseMenu(menu, allMenusAreClosing); + mPresenterCallback.onCloseMenu(menu, true); } - } - if (mPopupWindows.size() == 0) { if (mTreeObserver != null) { if (mTreeObserver.isAlive()) { mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener); @@ -539,8 +593,9 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey mTreeObserver = null; } mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener); - // If every [sub]menu was dismissed, that means the whole thing was dismissed, so notify - // the owner. + + // If every [sub]menu was dismissed, that means the whole thing was + // dismissed, so notify the owner. mOnDismissListener.onDismiss(); } } @@ -561,12 +616,22 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey @Override public void setGravity(int dropDownGravity) { - mDropDownGravity = Gravity.getAbsoluteGravity(dropDownGravity, mLayoutDirection); + if (mRawDropDownGravity != dropDownGravity) { + mRawDropDownGravity = dropDownGravity; + mDropDownGravity = Gravity.getAbsoluteGravity( + dropDownGravity, mAnchorView.getLayoutDirection()); + } } @Override - public void setAnchorView(View anchor) { - mAnchorView = anchor; + public void setAnchorView(@NonNull View anchor) { + if (mAnchorView != anchor) { + mAnchorView = anchor; + + // Gravity resolution may have changed, update from raw gravity. + mDropDownGravity = Gravity.getAbsoluteGravity( + mRawDropDownGravity, mAnchorView.getLayoutDirection()); + } } @Override @@ -576,7 +641,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey @Override public ListView getListView() { - return mListViews.size() > 0 ? mListViews.get(mListViews.size() - 1) : null; + return mAddedMenus.isEmpty() ? null : mAddedMenus.get(mAddedMenus.size() - 1).getListView(); } @Override @@ -593,4 +658,21 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey public void setShowTitle(boolean showTitle) { mShowTitle = showTitle; } + + private static class CascadingMenuInfo { + public final MenuPopupWindow window; + public final MenuBuilder menu; + public final int position; + + public CascadingMenuInfo(@NonNull MenuPopupWindow window, @NonNull MenuBuilder menu, + int position) { + this.window = window; + this.menu = menu; + this.position = position; + } + + public ListView getListView() { + return window.getListView(); + } + } }
\ No newline at end of file diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index 167392874173..0cfb1b9c053f 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -936,15 +936,14 @@ public class MenuBuilder implements Menu { } /** - * Closes the visible menu. - * - * @param allMenusAreClosing Whether the menus are completely closing (true), - * or whether there is another menu coming in this menu's place - * (false). For example, if the menu is closing because a - * sub menu is about to be shown, <var>allMenusAreClosing</var> - * is false. + * Closes the menu. + * + * @param closeAllMenus {@code true} if all displayed menus and submenus + * should be completely closed (as when a menu item is + * selected) or {@code false} if only this menu should + * be closed */ - public final void close(boolean allMenusAreClosing) { + public final void close(boolean closeAllMenus) { if (mIsClosing) return; mIsClosing = true; @@ -953,7 +952,7 @@ public class MenuBuilder implements Menu { if (presenter == null) { mPresenters.remove(ref); } else { - presenter.onCloseMenu(this, allMenusAreClosing); + presenter.onCloseMenu(this, closeAllMenus); } } mIsClosing = false; diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java index f207b9834885..c847c15607e1 100644 --- a/core/java/com/android/internal/view/menu/MenuPresenter.java +++ b/core/java/com/android/internal/view/menu/MenuPresenter.java @@ -97,8 +97,10 @@ public interface MenuPresenter { * closing. Presenter implementations should close the representation * of the menu indicated as necessary and notify a registered callback. * - * @param menu Menu or submenu that is closing. - * @param allMenusAreClosing True if all associated menus are closing. + * @param menu the menu or submenu that is closing + * @param allMenusAreClosing {@code true} if all displayed menus and + * submenus are closing, {@code false} if only + * the specified menu is closing */ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing); diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java index 7b80ac261d73..591da1c76484 100644 --- a/core/java/com/android/internal/widget/NonClientDecorView.java +++ b/core/java/com/android/internal/widget/NonClientDecorView.java @@ -110,6 +110,30 @@ public class NonClientDecorView extends LinearLayout super(context, attrs, defStyle); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!mAttachedCallbacksToRootViewImpl) { + // If there is no window callback installed there was no window set before. Set it now. + // Note that our ViewRootImpl object will not change. + getViewRootImpl().addWindowCallbacks(this); + mAttachedCallbacksToRootViewImpl = true; + } else if (mFrameRendererThread != null) { + // We are resizing and this call happened due to a configuration change. Tell the + // renderer about it. + mFrameRendererThread.onConfigurationChange(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mAttachedCallbacksToRootViewImpl) { + getViewRootImpl().removeWindowCallbacks(this); + mAttachedCallbacksToRootViewImpl = false; + } + } + public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow) { mOwner = owner; mWindowHasShadow = windowHasShadow; @@ -122,17 +146,6 @@ public class NonClientDecorView extends LinearLayout // background without removing the shadow. mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS); - if (!mAttachedCallbacksToRootViewImpl) { - // If there is no window callback installed there was no window set before. Set it now. - // Note that our ViewRootImpl object will not change. - getViewRootImpl().addWindowCallbacks(this); - mAttachedCallbacksToRootViewImpl = true; - } else if (mFrameRendererThread != null) { - // We are resizing and this call happened due to a configuration change. Tell the - // renderer about it. - mFrameRendererThread.onConfigurationChange(); - } - findViewById(R.id.maximize_window).setOnClickListener(this); findViewById(R.id.close_window).setOnClickListener(this); } @@ -343,6 +356,13 @@ public class NonClientDecorView extends LinearLayout } @Override + public void onContentDraw(int xOffset, int yOffset, int xSize, int ySize) { + if (mFrameRendererThread != null) { + mFrameRendererThread.onContentDraw(xOffset, yOffset, xSize, ySize); + } + } + + @Override public void onWindowDragResizeEnd() { releaseThreadedRenderer(); } @@ -372,10 +392,6 @@ public class NonClientDecorView extends LinearLayout */ private void releaseResources() { releaseThreadedRenderer(); - if (mAttachedCallbacksToRootViewImpl) { - ViewRootImpl.removeWindowCallbacks(this); - mAttachedCallbacksToRootViewImpl = false; - } } /** @@ -509,6 +525,38 @@ public class NonClientDecorView extends LinearLayout } /** + * The content is about to be drawn and we got the location of where it will be shown. + * If a "changeWindowSize" call has already been processed, we will re-issue the call + * if the previous call was ignored since the size was unknown. + * @param xOffset The x offset where the content is drawn to. + * @param yOffset The y offset where the content is drawn to. + * @param xSize The width size of the content. This should not be 0. + * @param ySize The height of the content. + */ + public void onContentDraw(int xOffset, int yOffset, int xSize, int ySize) { + synchronized (this) { + final boolean firstCall = mLastContentWidth == 0; + // The current content buffer is drawn here. + mLastContentWidth = xSize; + mLastContentHeight = ySize - mLastCaptionHeight; + mLastXOffset = xOffset; + mLastYOffset = yOffset; + + mRenderer.setContentDrawBounds( + mLastXOffset, + mLastYOffset + mLastCaptionHeight, + mLastXOffset + mLastContentWidth, + mLastYOffset + mLastCaptionHeight + mLastContentHeight); + // If this was the first call and changeWindowSize got already called prior to us, + // We should re-issue a changeWindowSize now. + if (firstCall && (mLastCaptionHeight != 0 || !mShowDecor)) { + mOldTargetRect.set(0, 0, 0, 0); + pingRenderLocked(); + } + } + } + + /** * Resizing the frame to fit the new window size. * @param newBounds The window bounds which needs to be drawn. */ @@ -517,28 +565,23 @@ public class NonClientDecorView extends LinearLayout // While a configuration change is taking place the view hierarchy might become // inaccessible. For that case we remember the previous metrics to avoid flashes. + // Note that even when there is no visible caption, the caption child will exist. View caption = getChildAt(0); - View content = getChildAt(1); - if (caption != null && content != null) { - int captionHeight = caption.getHeight(); - int contentWidth = content.getWidth(); - int contentHeight = content.getHeight(); - // Get the draw position within our surface (shadow offsets). - int[] surfaceOrigin = new int[2]; - surfaceOrigin[0] = 0; - surfaceOrigin[1] = 0; - getLocationInSurface(surfaceOrigin); - // Only update if a layout has already be performed (which might not be after a - // relayout. Otherwise use the previous values for the content. - if (captionHeight != 0 && contentWidth != 0 && contentHeight != 0) { + if (caption != null) { + final int captionHeight = caption.getHeight(); + // The caption height will probably never dynamically change while we are resizing. + // Once set to something other then 0 it should be kept that way. + if (captionHeight != 0) { + // Remember the height of the caption. mLastCaptionHeight = captionHeight; - mLastXOffset = surfaceOrigin[0]; - mLastYOffset = surfaceOrigin[1]; - mLastContentWidth = contentWidth; - mLastContentHeight = contentHeight; } } - + // Make sure that the other thread has already prepared the render draw calls for the + // content. If any size is 0, we have to wait for it to be drawn first. + if ((mLastCaptionHeight == 0 && mShowDecor) || + mLastContentWidth == 0 || mLastContentHeight == 0) { + return; + } // Since the surface is spanning the entire screen, we have to add the start offset of // the bounds to get to the surface location. final int left = mLastXOffset + newBounds.left; @@ -567,13 +610,6 @@ public class NonClientDecorView extends LinearLayout canvas.drawColor(0xff808080); mBackdropNode.end(canvas); - // The current content buffer is drawn here. - mRenderer.setContentDrawBounds( - mLastXOffset, - mLastYOffset + mLastCaptionHeight, - mLastXOffset + mLastContentWidth, - mLastYOffset + mLastCaptionHeight + mLastContentHeight); - // We need to render both rendered nodes explicitly. mRenderer.drawRenderNode(mFrameNode); mRenderer.drawRenderNode(mBackdropNode); diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 76796243f3f5..c4347f832bd5 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -69,6 +69,12 @@ public class ResolverDrawerLayout extends ViewGroup { private int mCollapsibleHeight; private int mUncollapsibleHeight; + /** + * The height in pixels of reserved space added to the top of the collapsed UI; + * e.g. chooser targets + */ + private int mCollapsibleHeightReserved; + private int mTopOffset; private boolean mIsDragging; @@ -153,12 +159,62 @@ public class ResolverDrawerLayout extends ViewGroup { } } + public void setCollapsibleHeightReserved(int heightPixels) { + final int oldReserved = mCollapsibleHeightReserved; + mCollapsibleHeightReserved = heightPixels; + + final int dReserved = mCollapsibleHeightReserved - oldReserved; + if (dReserved != 0 && mIsDragging) { + mLastTouchY -= dReserved; + } + + final int oldCollapsibleHeight = mCollapsibleHeight; + mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight()); + + if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) { + return; + } + + invalidate(); + } + private boolean isMoving() { return mIsDragging || !mScroller.isFinished(); } + private boolean isDragging() { + return mIsDragging || getNestedScrollAxes() == SCROLL_AXIS_VERTICAL; + } + + private boolean updateCollapseOffset(int oldCollapsibleHeight, boolean remainClosed) { + if (oldCollapsibleHeight == mCollapsibleHeight) { + return false; + } + + if (isLaidOut()) { + final boolean isCollapsedOld = mCollapseOffset != 0; + if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight + && mCollapseOffset == oldCollapsibleHeight)) { + // Stay closed even at the new height. + mCollapseOffset = mCollapsibleHeight; + } else { + mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + } + final boolean isCollapsedNew = mCollapseOffset != 0; + if (isCollapsedOld != isCollapsedNew) { + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } + } else { + // Start out collapsed at first unless we restored state for otherwise + mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; + } + return true; + } + private int getMaxCollapsedHeight() { - return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight; + return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight) + + mCollapsibleHeightReserved; } public void setOnDismissedListener(OnDismissedListener listener) { @@ -676,7 +732,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); - heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; + heightUsed += getHeightUsed(child); } } @@ -688,7 +744,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); - heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; + heightUsed += getHeightUsed(child); } } @@ -697,30 +753,43 @@ public class ResolverDrawerLayout extends ViewGroup { heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); mUncollapsibleHeight = heightUsed - mCollapsibleHeight; - if (isLaidOut()) { - final boolean isCollapsedOld = mCollapseOffset != 0; - if (oldCollapsibleHeight < mCollapsibleHeight - && mCollapseOffset == oldCollapsibleHeight) { - // Stay closed even at the new height. - mCollapseOffset = mCollapsibleHeight; - } else { - mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); - } - final boolean isCollapsedNew = mCollapseOffset != 0; - if (isCollapsedOld != isCollapsedNew) { - notifyViewAccessibilityStateChangedIfNeeded( - AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); - } - } else { - // Start out collapsed at first unless we restored state for otherwise - mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; - } + updateCollapseOffset(oldCollapsibleHeight, !isDragging()); mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset; setMeasuredDimension(sourceWidth, heightSize); } + private int getHeightUsed(View child) { + // This method exists because we're taking a fast path at measuring ListViews that + // lets us get away with not doing the more expensive wrap_content measurement which + // imposes double child view measurement costs. If we're looking at a ListView, we can + // check against the lowest child view plus padding and margin instead of the actual + // measured height of the ListView. This lets the ListView hang off the edge when + // all of the content would fit on-screen. + + int heightUsed = child.getMeasuredHeight(); + if (child instanceof AbsListView) { + final AbsListView lv = (AbsListView) child; + final int lvPaddingBottom = lv.getPaddingBottom(); + + int lowest = 0; + for (int i = 0, N = lv.getChildCount(); i < N; i++) { + final int bottom = lv.getChildAt(i).getBottom() + lvPaddingBottom; + if (bottom > lowest) { + lowest = bottom; + } + } + + if (lowest < heightUsed) { + heightUsed = lowest; + } + } + + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + return lp.topMargin + heightUsed + lp.bottomMargin; + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int width = getWidth(); diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index 32a877ae3398..3d96fab9709d 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -749,57 +749,57 @@ static void freeTextLayoutCaches(JNIEnv* env, jobject) { static const JNINativeMethod gMethods[] = { {"finalizer", "(J)V", (void*) CanvasJNI::finalizer}, {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster}, - {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap}, - {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque}, - {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth}, - {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight}, - {"native_setHighContrastText","(JZ)V", (void*) CanvasJNI::setHighContrastText}, - {"native_save","(JI)I", (void*) CanvasJNI::save}, - {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer}, - {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha}, - {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount}, - {"native_restore","(JZ)V", (void*) CanvasJNI::restore}, - {"native_restoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount}, - {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM}, - {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix}, - {"native_concat","(JJ)V", (void*) CanvasJNI::concat}, - {"native_rotate","(JF)V", (void*) CanvasJNI::rotate}, - {"native_scale","(JFF)V", (void*) CanvasJNI::scale}, - {"native_skew","(JFF)V", (void*) CanvasJNI::skew}, - {"native_translate","(JFF)V", (void*) CanvasJNI::translate}, - {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds}, - {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath}, - {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect}, - {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect}, - {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath}, - {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion}, - {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor}, - {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint}, - {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint}, - {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints}, - {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine}, - {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines}, - {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect}, - {"native_drawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion }, - {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect}, - {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle}, - {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval}, - {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc}, - {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath}, - {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices}, - {"native_drawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch}, - {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap}, - {"nativeDrawBitmapMatrix", "(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix}, - {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, - {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray}, - {"nativeDrawBitmapMesh", "(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh}, - {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars}, - {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString}, - {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars}, - {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString}, - {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars}, - {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString}, - {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter}, + {"native_setBitmap", "!(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap}, + {"native_isOpaque","!(J)Z", (void*) CanvasJNI::isOpaque}, + {"native_getWidth","!(J)I", (void*) CanvasJNI::getWidth}, + {"native_getHeight","!(J)I", (void*) CanvasJNI::getHeight}, + {"native_setHighContrastText","!(JZ)V", (void*) CanvasJNI::setHighContrastText}, + {"native_save","!(JI)I", (void*) CanvasJNI::save}, + {"native_saveLayer","!(JFFFFJI)I", (void*) CanvasJNI::saveLayer}, + {"native_saveLayerAlpha","!(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha}, + {"native_getSaveCount","!(J)I", (void*) CanvasJNI::getSaveCount}, + {"native_restore","!(JZ)V", (void*) CanvasJNI::restore}, + {"native_restoreToCount","!(JIZ)V", (void*) CanvasJNI::restoreToCount}, + {"native_getCTM", "!(JJ)V", (void*)CanvasJNI::getCTM}, + {"native_setMatrix","!(JJ)V", (void*) CanvasJNI::setMatrix}, + {"native_concat","!(JJ)V", (void*) CanvasJNI::concat}, + {"native_rotate","!(JF)V", (void*) CanvasJNI::rotate}, + {"native_scale","!(JFF)V", (void*) CanvasJNI::scale}, + {"native_skew","!(JFF)V", (void*) CanvasJNI::skew}, + {"native_translate","!(JFF)V", (void*) CanvasJNI::translate}, + {"native_getClipBounds","!(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds}, + {"native_quickReject","!(JJ)Z", (void*) CanvasJNI::quickRejectPath}, + {"native_quickReject","!(JFFFF)Z", (void*)CanvasJNI::quickRejectRect}, + {"native_clipRect","!(JFFFFI)Z", (void*) CanvasJNI::clipRect}, + {"native_clipPath","!(JJI)Z", (void*) CanvasJNI::clipPath}, + {"native_clipRegion","!(JJI)Z", (void*) CanvasJNI::clipRegion}, + {"native_drawColor","!(JII)V", (void*) CanvasJNI::drawColor}, + {"native_drawPaint","!(JJ)V", (void*) CanvasJNI::drawPaint}, + {"native_drawPoint", "!(JFFJ)V", (void*) CanvasJNI::drawPoint}, + {"native_drawPoints", "!(J[FIIJ)V", (void*) CanvasJNI::drawPoints}, + {"native_drawLine", "!(JFFFFJ)V", (void*) CanvasJNI::drawLine}, + {"native_drawLines", "!(J[FIIJ)V", (void*) CanvasJNI::drawLines}, + {"native_drawRect","!(JFFFFJ)V", (void*) CanvasJNI::drawRect}, + {"native_drawRegion", "!(JJJ)V", (void*) CanvasJNI::drawRegion }, + {"native_drawRoundRect","!(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect}, + {"native_drawCircle","!(JFFFJ)V", (void*) CanvasJNI::drawCircle}, + {"native_drawOval","!(JFFFFJ)V", (void*) CanvasJNI::drawOval}, + {"native_drawArc","!(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc}, + {"native_drawPath","!(JJJ)V", (void*) CanvasJNI::drawPath}, + {"nativeDrawVertices", "!(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices}, + {"native_drawNinePatch", "!(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch}, + {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap}, + {"nativeDrawBitmapMatrix", "!(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix}, + {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, + {"native_drawBitmap", "!(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray}, + {"nativeDrawBitmapMesh", "!(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh}, + {"native_drawText","!(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars}, + {"native_drawText","!(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString}, + {"native_drawTextRun","!(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars}, + {"native_drawTextRun","!(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString}, + {"native_drawTextOnPath","!(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars}, + {"native_drawTextOnPath","!(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString}, + {"nativeSetDrawFilter", "!(JJ)V", (void*) CanvasJNI::setDrawFilter}, {"freeCaches", "()V", (void*) CanvasJNI::freeCaches}, {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches} }; diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index 437bd192b70a..24eb961bd431 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -267,7 +267,7 @@ static const JNINativeMethod gMethods[] = { { "nativeDispose", "(J)V", (void*)nativeDispose }, - { "nativeScheduleVsync", "(J)V", + { "nativeScheduleVsync", "!(J)V", (void*)nativeScheduleVsync } }; diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp index 460c1a1a2ab9..b64acc32e634 100644 --- a/core/jni/android_view_DisplayListCanvas.cpp +++ b/core/jni/android_view_DisplayListCanvas.cpp @@ -174,24 +174,24 @@ android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject java const char* const kClassPathName = "android/view/DisplayListCanvas"; static JNINativeMethod gMethods[] = { - { "nIsAvailable", "()Z", (void*) android_view_DisplayListCanvas_isAvailable }, - { "nInsertReorderBarrier","(JZ)V", (void*) android_view_DisplayListCanvas_insertReorderBarrier }, + { "nIsAvailable", "!()Z", (void*) android_view_DisplayListCanvas_isAvailable }, + { "nInsertReorderBarrier","!(JZ)V", (void*) android_view_DisplayListCanvas_insertReorderBarrier }, - { "nCallDrawGLFunction", "(JJ)V", (void*) android_view_DisplayListCanvas_callDrawGLFunction }, + { "nCallDrawGLFunction", "!(JJ)V", (void*) android_view_DisplayListCanvas_callDrawGLFunction }, - { "nDrawRoundRect", "(JJJJJJJJ)V", (void*) android_view_DisplayListCanvas_drawRoundRectProps }, - { "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps }, + { "nDrawRoundRect", "!(JJJJJJJJ)V", (void*) android_view_DisplayListCanvas_drawRoundRectProps }, + { "nDrawCircle", "!(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps }, - { "nFinishRecording", "(J)J", (void*) android_view_DisplayListCanvas_finishRecording }, - { "nDrawRenderNode", "(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode }, + { "nFinishRecording", "!(J)J", (void*) android_view_DisplayListCanvas_finishRecording }, + { "nDrawRenderNode", "!(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode }, - { "nCreateDisplayListCanvas", "(II)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas }, - { "nResetDisplayListCanvas", "(JII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas }, + { "nCreateDisplayListCanvas", "!(II)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas }, + { "nResetDisplayListCanvas", "!(JII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas }, - { "nDrawLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawLayer }, + { "nDrawLayer", "!(JJ)V", (void*) android_view_DisplayListCanvas_drawLayer }, - { "nGetMaximumTextureWidth", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureWidth }, - { "nGetMaximumTextureHeight", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureHeight }, + { "nGetMaximumTextureWidth", "!()I", (void*) android_view_DisplayListCanvas_getMaxTextureWidth }, + { "nGetMaximumTextureHeight", "!()I", (void*) android_view_DisplayListCanvas_getMaxTextureHeight }, }; static JNINativeMethod gActivityThreadMethods[] = { diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index dcdfb6c318d4..41726fb71f7f 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -85,7 +85,7 @@ <ListView android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:id="@+id/resolver_list" android:clipToPadding="false" android:scrollbarStyle="outsideOverlay" diff --git a/core/res/res/layout/list_content.xml b/core/res/res/layout/list_content.xml index 14140322b7cd..45ade4d0c6d7 100644 --- a/core/res/res/layout/list_content.xml +++ b/core/res/res/layout/list_content.xml @@ -44,8 +44,7 @@ <ListView android:id="@android:id/list" android:layout_width="match_parent" - android:layout_height="match_parent" - android:drawSelectorOnTop="false" /> + android:layout_height="match_parent" /> <TextView android:id="@+android:id/internalEmpty" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml index 4e895b0417ff..f073c3379727 100644 --- a/core/res/res/layout/preference_list_fragment.xml +++ b/core/res/res/layout/preference_list_fragment.xml @@ -41,6 +41,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/preference_fragment_padding_side" + android:textAppearance="?android:attr/textAppearanceMedium" android:gravity="center" android:visibility="gone" /> diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml index 1c496f6435d6..0a7ac776744a 100644 --- a/core/res/res/layout/resolve_grid_item.xml +++ b/core/res/res/layout/resolve_grid_item.xml @@ -70,6 +70,7 @@ android:minLines="2" android:maxLines="2" android:gravity="top|center_horizontal" - android:ellipsize="marquee" /> + android:ellipsize="marquee" + android:visibility="gone" /> </LinearLayout> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 45d9dc36adae..5032c2538c94 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -51,7 +51,7 @@ <string name="serviceDisabled" msgid="1937553226592516411">"سرویس غیرفعال شده است."</string> <string name="serviceRegistered" msgid="6275019082598102493">"ثبت با موفقیت انجام شد"</string> <string name="serviceErased" msgid="1288584695297200972">"پاک کردن با موفقیت انجام شد."</string> - <string name="passwordIncorrect" msgid="7612208839450128715">"رمز ورود اشتباه است."</string> + <string name="passwordIncorrect" msgid="7612208839450128715">"گذرواژه اشتباه است."</string> <string name="mmiComplete" msgid="8232527495411698359">"MMI کامل شد."</string> <string name="badPin" msgid="9015277645546710014">"پین قدیمی که نوشتهاید صحیح نیست."</string> <string name="badPuk" msgid="5487257647081132201">"PUK که نوشتهاید صحیح نیست."</string> @@ -74,7 +74,7 @@ <string name="CfMmi" msgid="5123218989141573515">"هدایت تماس"</string> <string name="CwMmi" msgid="9129678056795016867">"انتظار تماس"</string> <string name="BaMmi" msgid="455193067926770581">"محدودیت تماس"</string> - <string name="PwdMmi" msgid="7043715687905254199">"تغییر رمز ورود"</string> + <string name="PwdMmi" msgid="7043715687905254199">"تغییر گذرواژه"</string> <string name="PinMmi" msgid="3113117780361190304">"تغییر پین"</string> <string name="CnipMmi" msgid="3110534680557857162">"شماره تماس حاضر"</string> <string name="CnirMmi" msgid="3062102121430548731">"شماره تماس محدود شده"</string> @@ -495,7 +495,7 @@ <string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"به دارنده امکان میدهد به سرویسهای شرکت مخابراتی متصل شود. هرگز نباید برای برنامههای عادی مورد نیاز باشد."</string> <string name="permlab_access_notification_policy" msgid="4247510821662059671">"دسترسی به حالت «مزاحم نشوید»"</string> <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"به برنامه امکان میدهد پیکربندی «مزاحم نشوید» را بخواند و بنویسد."</string> - <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string> + <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین گذرواژه"</string> <string name="policydesc_limitPassword" msgid="2502021457917874968">"کنترل طول و نوع نویسههایی که در گذرواژه و پین قفل صفحه مجاز است."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاشهای قفل گشایی صفحه"</string> <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"تعداد گذرواژههای نادرست تایپ شده را هنگام بازکردن قفل صفحه کنترل میکند، و اگر دفعات زیادی گذرواژه نادرست وارد شود رایانهٔ لوحی را قفل میکند و همه دادههای رایانهٔ لوحی را پاک میکند."</string> @@ -532,7 +532,7 @@ <item msgid="7897544654242874543">"محل کار"</item> <item msgid="1103601433382158155">"نمابر محل کار"</item> <item msgid="1735177144948329370">"نمابر خانه"</item> - <item msgid="603878674477207394">"پیجر"</item> + <item msgid="603878674477207394">"پیجو"</item> <item msgid="1650824275177931637">"سایر موارد"</item> <item msgid="9192514806975898961">"سفارشی"</item> </string-array> @@ -575,7 +575,7 @@ <string name="phoneTypeWork" msgid="8863939667059911633">"محل کار"</string> <string name="phoneTypeFaxWork" msgid="3517792160008890912">"نمابر محل کار"</string> <string name="phoneTypeFaxHome" msgid="2067265972322971467">"نمابر خانه"</string> - <string name="phoneTypePager" msgid="7582359955394921732">"پیجر"</string> + <string name="phoneTypePager" msgid="7582359955394921732">"پیجو"</string> <string name="phoneTypeOther" msgid="1544425847868765990">"سایر موارد"</string> <string name="phoneTypeCallback" msgid="2712175203065678206">"برگرداندن تماس"</string> <string name="phoneTypeCar" msgid="8738360689616716982">"خودرو"</string> @@ -700,9 +700,9 @@ <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"تلاشهای زیادی برای کشیدن الگو صورت گرفته است"</string> <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"برای بازگشایی قفل، با حساب Google خود وارد سیستم شوید."</string> <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"نام کاربری (ایمیل)"</string> - <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"رمز ورود"</string> + <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"گذرواژه"</string> <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ورود به سیستم"</string> - <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا رمز ورود نامعتبر است."</string> + <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا گذرواژه نامعتبر است."</string> <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"نام کاربری یا گذرواژهٔ خود را فراموش کردید؟\nاز "<b>"google.com/accounts/recovery"</b>" بازدید کنید."</string> <string name="lockscreen_glogin_checking_password" msgid="7114627351286933867">"در حال بررسی..."</string> <string name="lockscreen_unlock_label" msgid="737440483220667054">"بازگشایی قفل"</string> @@ -786,7 +786,7 @@ <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_message" msgid="767344687139195790">"میخواهید مرورگر این گذرواژه را به خاطر داشته باشد؟"</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> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 72b1c54293d6..a034a256546c 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -290,7 +290,7 @@ <string name="permdesc_enableCarMode" msgid="4853187425751419467">"Consente all\'applicazione di abilitare la modalità automobile."</string> <string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"chiusura altre applicazioni"</string> <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Consente all\'applicazione di terminare i processi in background di altre applicazioni. Ciò potrebbe causare l\'interruzione di altre applicazioni."</string> - <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"spostamento sopra altre app"</string> + <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"posizionamento davanti ad altre app"</string> <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Consente all\'applicazione di spostarsi sopra ad altre applicazioni o parti dell\'interfaccia utente. Potrebbe interferire con il tuo utilizzo dell\'interfaccia in qualsiasi applicazione o cambiare ciò che credi di vedere in altre applicazioni."</string> <string name="permlab_persistentActivity" msgid="8841113627955563938">"esecuzione permanente delle applicazioni"</string> <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Consente all\'applicazione di rendere persistenti in memoria alcune sue parti. Ciò può limitare la memoria disponibile per altre applicazioni, rallentando il tablet."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 29fd6a1be775..e803cbb2dca0 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -163,7 +163,7 @@ <string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"För många <xliff:g id="CONTENT_TYPE">%s</xliff:g>-borttagningar."</string> <string name="low_memory" product="tablet" msgid="6494019234102154896">"Pekdatorns lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string> <string name="low_memory" product="watch" msgid="4415914910770005166">"Klockans lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string> - <string name="low_memory" product="tv" msgid="516619861191025923">"Lagringsutrymmet på tv:n är fullt. Ta bort några filer för att frigöra utrymme."</string> + <string name="low_memory" product="tv" msgid="516619861191025923">"Lagringsutrymmet på TV:n är fullt. Ta bort några filer för att frigöra utrymme."</string> <string name="low_memory" product="default" msgid="3475999286680000541">"Mobilens lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string> <string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Nätverket kan vara övervakat"</string> <string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Av en okänd tredje part"</string> @@ -177,7 +177,7 @@ <string name="factory_reset_message" msgid="4905025204141900666">"Administratörsappen saknar delar eller är skadad och kan inte användas. Enheten kommer nu att rensas. Kontakta administratören om du behöver hjälp."</string> <string name="me" msgid="6545696007631404292">"Jag"</string> <string name="power_dialog" product="tablet" msgid="8545351420865202853">"Alternativ för surfplattan"</string> - <string name="power_dialog" product="tv" msgid="6153888706430556356">"Tv-alternativ"</string> + <string name="power_dialog" product="tv" msgid="6153888706430556356">"TV-alternativ"</string> <string name="power_dialog" product="default" msgid="1319919075463988638">"Telefonalternativ"</string> <string name="silent_mode" msgid="7167703389802618663">"Tyst läge"</string> <string name="turn_on_radio" msgid="3912793092339962371">"Aktivera trådlöst"</string> @@ -195,7 +195,7 @@ <string name="reboot_to_reset_message" msgid="2432077491101416345">"Startar om …"</string> <string name="shutdown_progress" msgid="2281079257329981203">"Avslutar…"</string> <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Din surfplatta stängs av."</string> - <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"Tv:n stängs av."</string> + <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"TV:n stängs av."</string> <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Klockan stängs av."</string> <string name="shutdown_confirm" product="default" msgid="649792175242821353">"Din telefon stängs av."</string> <string name="shutdown_confirm_question" msgid="2906544768881136183">"Vill du stänga av?"</string> @@ -276,7 +276,7 @@ <string name="permdesc_sendSms" msgid="7094729298204937667">"Tillåter att appen skickar SMS. Detta kan leda till oväntade avgifter. Skadliga appar kan skicka meddelanden utan ditt godkännande vilket kan kosta pengar."</string> <string name="permlab_readSms" msgid="8745086572213270480">"läsa dina textmeddelanden (SMS eller MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Tillåter att appen läser SMS som sparats på surfplattan eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string> - <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Tillåter att appen läser sms som har sparats på tv:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess."</string> + <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Tillåter att appen läser sms som har sparats på TV:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Tillåter att appen läser SMS som sparats på mobilen eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string> <string name="permlab_receiveWapPush" msgid="5991398711936590410">"ta emot textmeddelanden (WAP)"</string> <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillåter att appen tar emot och hanterar WAP-meddelanden. Med den här behörigheten kan appen övervaka eller ta bort meddelanden som skickats till dig utan att visa dem för dig."</string> @@ -294,7 +294,7 @@ <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Tillåter att appen att dras ovanpå andra appar eller delar av användargränssnittet. De kan störa din användning av gränssnittet i olika appar eller ändra vad du tror visas i andra appar."</string> <string name="permlab_persistentActivity" msgid="8841113627955563938">"se till att appen alltid körs"</string> <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör surfplattan långsam."</string> - <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör tv:n långsammare."</string> + <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör TV:n långsammare."</string> <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör mobilen långsam."</string> <string name="permlab_getPackageSize" msgid="7472921768357981986">"mäta appens lagringsplats"</string> <string name="permdesc_getPackageSize" msgid="3921068154420738296">"Tillåter att appen hämtar kod, data och cachestorlekar"</string> @@ -306,33 +306,33 @@ <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"Tillåter att appen startar automatiskt när systemet har startats om. Detta kan innebära att det tar längre tid att starta mobilen och att mobilen blir långsammare i och med att appen hela tiden körs i bakgrunden."</string> <string name="permlab_broadcastSticky" msgid="7919126372606881614">"Skicka sticky broadcast"</string> <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Tillåter att appen skickar sticky broadcasts, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string> - <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra tv:n seg eller instabil eftersom den använder för mycket minne."</string> + <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra TV:n seg eller instabil eftersom den använder för mycket minne."</string> <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Tillåter att appen skickar sticky broadcast, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string> <string name="permlab_readContacts" msgid="8348481131899886131">"läsa dina kontakter"</string> <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Tillåter att appen läser kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string> - <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Tillåter att appen läser data om dina kontakter som sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det."</string> + <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Tillåter att appen läser data om dina kontakter som sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det."</string> <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Tillåter att appen läser kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string> <string name="permlab_writeContacts" msgid="5107492086416793544">"ändra kontakterna"</string> <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillåter att appen ändrar kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string> - <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter."</string> + <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter."</string> <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillåter att appen ändrar kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"läs samtalslogg"</string> <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Tillåter att appen läser pekdatorns samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string> - <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Tillåter att appen läser tv:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det."</string> + <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Tillåter att appen läser TV:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det."</string> <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Tillåter att appen läser mobilens samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"skriv samtalslogg"</string> <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Tillåter att appen gör ändringar i pekdatorns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string> - <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Tillåter att appen gör ändringar i tv:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista."</string> + <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Tillåter att appen gör ändringar i TV:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista."</string> <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Tillåter att appen gör ändringar i mobilens samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string> <string name="permlab_bodySensors" msgid="4871091374767171066">"kroppssens. (för hjärtat m.m.)"</string> <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ger appen åtkomst till information från sensorer om ditt fysiska tillstånd, till exempel din puls."</string> <string name="permlab_readCalendar" msgid="5972727560257612398">"läsa kalenderuppgifter plus konfidentiell information"</string> <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillåter att appen läser alla kalenderuppgifter som sparats på surfplattan, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string> - <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Tillåter att appen läser alla kalenderhändelser som sparats på tv:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet."</string> + <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Tillåter att appen läser alla kalenderhändelser som sparats på TV:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet."</string> <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Tillåter att appen läser alla kalenderuppgifter som sparats på mobilen, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string> <string name="permlab_writeCalendar" msgid="8438874755193825647">"lägga till eller ändra kalenderuppgifter och skicka e-post till gäster utan ägarens vetskap"</string> <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på surfplattan, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string> - <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på tv:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string> + <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på TV:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string> <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på mobilen, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string> <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få åtkomst till extra kommandon för platsleverantör"</string> <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillåter att appen får åtkomst till extra kommandon för platsleverantör. Detta kan innebära att appen tillåts störa funktionen för GPS eller andra platskällor."</string> @@ -359,14 +359,14 @@ <string name="permlab_readPhoneState" msgid="9178228524507610486">"läsa telefonens status och identitet"</string> <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till."</string> <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"förhindra att surfplattan går in i viloläge"</string> - <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"förhindra att tv:n försätts i viloläge"</string> + <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"förhindra att TV:n försätts i viloläge"</string> <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"förhindra att telefonen sätts i viloläge"</string> <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Tillåter att appen förhindrar att surfplattan går in i viloläge."</string> - <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Tillåter att appen förhindrar att tv:n försätts i viloläge."</string> + <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Tillåter att appen förhindrar att TV:n försätts i viloläge."</string> <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Tillåter att appen förhindrar att mobilen går in i viloläge."</string> <string name="permlab_transmitIr" msgid="7545858504238530105">"tillåt IR-sändning"</string> <string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"Tillåter att appen använder surfplattans IR-sändare."</string> - <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Tillåter att appen använder den infraröda sändaren på tv:n."</string> + <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Tillåter att appen använder den infraröda sändaren på TV:n."</string> <string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"Tillåter att appen använder mobilens IR-sändare."</string> <string name="permlab_setWallpaper" msgid="6627192333373465143">"ange bakgrund"</string> <string name="permdesc_setWallpaper" msgid="7373447920977624745">"Tillåter att appen anger systemets bakgrund."</string> @@ -374,11 +374,11 @@ <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"Tillåter att appen ger tips om systemets bakgrundsstorlek."</string> <string name="permlab_setTimeZone" msgid="2945079801013077340">"ange tidszon"</string> <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"Tillåter att appen ändrar pekdatorns tidszon."</string> - <string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Tillåter att appen ändrar tidszonen på tv:n."</string> + <string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Tillåter att appen ändrar tidszonen på TV:n."</string> <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"Tillåter att appen ändrar mobilens tidszon."</string> <string name="permlab_getAccounts" msgid="1086795467760122114">"hitta konton på enheten"</string> <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Tillåter att appen hämtar en lista över alla kända konton på surfplattan. Detta kan inkludera konton som har skapats av appar som du har installerat."</string> - <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Tillåter att appen hämtar listan med konton som tv:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat."</string> + <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Tillåter att appen hämtar listan med konton som TV:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat."</string> <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Tillåter att appen hämtar en lista över alla kända konton på mobilen. Detta kan inkludera konton som har skapats av appar som du har installerat."</string> <string name="permlab_accessNetworkState" msgid="4951027964348974773">"visa nätverksanslutningar"</string> <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Tillåter att appen kommer åt information om nätverksanslutningarna, till exempel vilka nätverk som finns och är anslutna."</string> @@ -394,21 +394,21 @@ <string name="permdesc_changeWifiState" msgid="7137950297386127533">"Tillåter att appen ansluter till och kopplar från Wi-Fi-åtkomstpunkter samt gör ändringar i enhetens konfiguration för Wi-Fi-nätverk."</string> <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"tillåt Wi-Fi multicast-mottagning"</string> <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här surfplattan. Detta drar mer batteri än när multicastläget inte används."</string> - <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till tv:n. Detta drar mer batteri än när multicastläget inte används."</string> + <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till TV:n. Detta drar mer batteri än när multicastläget inte används."</string> <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här mobilen. Detta drar mer batteri än när multicastläget inte används."</string> <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"få åtkomst till Bluetooth-inställningar"</string> <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Tillåter att appen konfigurerar den lokala Bluetooth-surfplattan samt upptäcker och parkopplar den med fjärranslutna enheter."</string> - <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Tillåter att appen konfigurerar den lokala Bluetooth-tv:n och identifierar och kopplar den till fjärrenheter."</string> + <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Tillåter att appen konfigurerar den lokala Bluetooth-TV:n och identifierar och kopplar den till fjärrenheter."</string> <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tillåter att appen konfigurerar den lokala Bluetooth-mobilen samt upptäcker och parkopplar den med fjärranslutna enheter."</string> <string name="permlab_accessWimaxState" msgid="4195907010610205703">"ansluta till och koppla från WiMAX"</string> <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Tillåter att appen avgör om WiMAX är aktiverat och kommer åt information om eventuella anslutna WiMAX-nätverk."</string> <string name="permlab_changeWimaxState" msgid="2405042267131496579">"ändra WiMAX-status"</string> <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillåter att appen ansluter surfplattan till eller kopplar från WiMAX-nätverk."</string> - <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Tillåter att appen ansluter tv:n till och kopplar från tv:n från WiMAX-nätverk."</string> + <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Tillåter att appen ansluter TV:n till och kopplar från TV:n från WiMAX-nätverk."</string> <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tillåter att appen ansluter mobilen till eller kopplar från WiMAX-nätverk."</string> <string name="permlab_bluetooth" msgid="6127769336339276828">"koppla till Bluetooth-enheter"</string> <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Tillåter att appen kommer åt pekdatorns Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string> - <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Tillåter att appen visar konfigurationen av Bluetooth på tv:n och godkänner alla anslutningar till kopplade enheter."</string> + <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Tillåter att appen visar konfigurationen av Bluetooth på TV:n och godkänner alla anslutningar till kopplade enheter."</string> <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Tillåter att appen kommer åt mobilens Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string> <string name="permlab_nfc" msgid="4423351274757876953">"kontrollera närfältskommunikationen"</string> <string name="permdesc_nfc" msgid="7120611819401789907">"Tillåter att appen kommunicerar med etiketter, kort och läsare för närfältskommunikation (NFC)."</string> @@ -499,10 +499,10 @@ <string name="policydesc_limitPassword" msgid="2502021457917874968">"Styr tillåten längd och tillåtna tecken i lösenord och pinkoder för skärmlåset."</string> <string name="policylab_watchLogin" msgid="914130646942199503">"Övervaka försök att låsa upp skärmen"</string> <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås surfplattan eller ta bort alla data från surfplattan om för många felaktiga försök görs."</string> - <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser tv:n eller rensar alla uppgifter på tv:n om för många felaktiga lösenord har skrivits in."</string> + <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser TV:n eller rensar alla uppgifter på TV:n om för många felaktiga lösenord har skrivits in."</string> <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås mobilen eller ta bort alla data från mobilen om för många felaktiga försök görs."</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås surfplattan eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string> - <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås tv:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string> + <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås TV:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string> <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås mobilen eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string> <string name="policylab_resetPassword" msgid="4934707632423915395">"Ändra skärmlåset"</string> <string name="policydesc_resetPassword" msgid="1278323891710619128">"Ändra skärmlåset."</string> @@ -510,11 +510,11 @@ <string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrollera hur och när skärmlåset aktiveras."</string> <string name="policylab_wipeData" msgid="3910545446758639713">"Radera all data"</string> <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ta bort data från surfplattan utan förvarning genom att återställa standardinställningarna."</string> - <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Rensar uppgifterna på tv:n utan föregående varning genom att återställa standardinställningarna."</string> + <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Rensar uppgifterna på TV:n utan föregående varning genom att återställa standardinställningarna."</string> <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Ta bort data från mobilen utan förvarning genom att återställa standardinställningarna."</string> <string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Radera användaruppgifter"</string> <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Rensa användarens uppgifter på den här surfplattan utan förvarning."</string> - <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Rensa användarens uppgifter på den här tv:n utan förvarning."</string> + <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Rensa användarens uppgifter på den här TV:n utan förvarning."</string> <string name="policydesc_wipeData_secondaryUser" product="default" msgid="6787904546711590238">"Rensa användarens data på den här mobilen utan förvarning."</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ange global proxyserver"</string> <string name="policydesc_setGlobalProxy" msgid="8459859731153370499">"Ange enhetens globala proxy som ska användas när policyn aktiveras. Det är bara enhetens ägare som kan ange global proxy."</string> @@ -663,7 +663,7 @@ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har försökt låsa upp med Ansiktslås för många gånger"</string> <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Inget SIM-kort"</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Inget SIM-kort i surfplattan."</string> - <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Det finns inget SIM-kort i tv:n."</string> + <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Det finns inget SIM-kort i TV:n."</string> <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Inget SIM-kort i telefonen."</string> <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Sätt i ett SIM-kort."</string> <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM-kort saknas eller kan inte läsas. Sätt i ett SIM-kort."</string> @@ -686,13 +686,13 @@ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp surfplattan med din Google-inloggning.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp tv:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp mobilen med uppgifterna som du använder när du loggar in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer surfplattan att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string> - <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer mobilen att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string> <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string> - <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER">%d</xliff:g> gånger. Tv:n kommer nu att återställas till standardinställningarna."</string> + <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string> <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string> <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string> <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Glömt ditt grafiska lösenord?"</string> @@ -778,7 +778,7 @@ <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Tillåter att appen läser historiken för besökta sidor och alla bokmärken i webbläsaren. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string> <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"skriva bokmärken och historik på webben"</string> <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på surfplattan. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string> - <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på tv:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner."</string> + <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på TV:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner."</string> <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på telefonen. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string> <string name="permlab_setAlarm" msgid="1379294556362091814">"ställa in ett alarm"</string> <string name="permdesc_setAlarm" msgid="316392039157473848">"Tillåter att appen ställer in ett alarm i en befintlig alarmapp. Vissa alarmappar har inte den här funktionen."</string> @@ -988,7 +988,7 @@ <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ange den obligatoriska PIN-koden:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kod:"</string> <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Surfplattans Wi-Fi-anslutning kommer tillfälligt att avbrytas när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> - <string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"Tv:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> + <string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"TV:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Infoga tecken"</string> <string name="sms_control_title" msgid="7296612781128917719">"Skickar SMS"</string> @@ -1289,13 +1289,13 @@ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs surfplattan till fabriksinställningarna. Du förlorar då alla användardata."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string> <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs mobilen till fabriksinställningarna. Du förlorar då alla användardata."</string> <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string> - <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER">%d</xliff:g> gånger. Tv:n kommer nu att återställas till standardinställningarna."</string> + <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string> <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string> <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp surfplattan med ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> - <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp tv:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> + <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ta bort"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index d367887275ac..af04a46a69a3 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -589,7 +589,7 @@ <string name="phoneTypeTelex" msgid="3367879952476250512">"Teleksi"</string> <string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string> <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Nambari ya Simu ya Mkononi ya Kazini"</string> - <string name="phoneTypeWorkPager" msgid="649938731231157056">"Kiunda ujumbe cha Kazini"</string> + <string name="phoneTypeWorkPager" msgid="649938731231157056">"Peja ya Kazini"</string> <string name="phoneTypeAssistant" msgid="5596772636128562884">"Msaidizi"</string> <string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string> <string name="eventTypeCustom" msgid="7837586198458073404">"Maalum"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 439b9f284e76..83b0d1813fde 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -530,8 +530,8 @@ <item msgid="8901098336658710359">"Nhà riêng"</item> <item msgid="869923650527136615">"Di Động"</item> <item msgid="7897544654242874543">"Cơ quan"</item> - <item msgid="1103601433382158155">"Số fax Cơ quan"</item> - <item msgid="1735177144948329370">"Số fax Nhà riêng"</item> + <item msgid="1103601433382158155">"Số fax cơ quan"</item> + <item msgid="1735177144948329370">"Số fax nhà riêng"</item> <item msgid="603878674477207394">"Số máy nhắn tin"</item> <item msgid="1650824275177931637">"Khác"</item> <item msgid="9192514806975898961">"Tùy chỉnh"</item> @@ -573,8 +573,8 @@ <string name="phoneTypeHome" msgid="2570923463033985887">"Nhà riêng"</string> <string name="phoneTypeMobile" msgid="6501463557754751037">"Di Động"</string> <string name="phoneTypeWork" msgid="8863939667059911633">"Cơ quan"</string> - <string name="phoneTypeFaxWork" msgid="3517792160008890912">"Số fax Cơ quan"</string> - <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Số fax Nhà riêng"</string> + <string name="phoneTypeFaxWork" msgid="3517792160008890912">"Số fax cơ quan"</string> + <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Số fax nhà riêng"</string> <string name="phoneTypePager" msgid="7582359955394921732">"Số máy nhắn tin"</string> <string name="phoneTypeOther" msgid="1544425847868765990">"Khác"</string> <string name="phoneTypeCallback" msgid="2712175203065678206">"Số gọi lại"</string> @@ -586,8 +586,8 @@ <string name="phoneTypeRadio" msgid="4093738079908667513">"Radio"</string> <string name="phoneTypeTelex" msgid="3367879952476250512">"Số telex"</string> <string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string> - <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Số điện thoại di động tại Cơ quan"</string> - <string name="phoneTypeWorkPager" msgid="649938731231157056">"Số Máy nhắn tin tại Cơ quan"</string> + <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Di động tại cơ quan"</string> + <string name="phoneTypeWorkPager" msgid="649938731231157056">"Số máy nhắn tin cơ quan"</string> <string name="phoneTypeAssistant" msgid="5596772636128562884">"Số điện thoại Hỗ trợ"</string> <string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string> <string name="eventTypeCustom" msgid="7837586198458073404">"Tùy chỉnh"</string> diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml index 9d1dda547586..917c781812ca 100644 --- a/core/res/res/values/colors_holo.xml +++ b/core/res/res/values/colors_holo.xml @@ -78,14 +78,14 @@ <!-- Forward compatibility for Material-style theme colors --> <eat-comment /> - <color name="holo_primary_dark">#ff000000</color> + <color name="holo_primary_dark">@color/black</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> <color name="holo_button_normal">#bd292f34</color> - <color name="holo_light_primary_dark">#ff000000</color> + <color name="holo_light_primary_dark">@color/black</color> <color name="holo_light_primary">#ffe6e6e6</color> <color name="holo_light_control_activated">@color/holo_blue_light</color> <color name="holo_light_control_normal">#dacccccc</color> diff --git a/core/res/res/values/colors_legacy.xml b/core/res/res/values/colors_legacy.xml index ad22845174e4..a3ce6524bdcb 100644 --- a/core/res/res/values/colors_legacy.xml +++ b/core/res/res/values/colors_legacy.xml @@ -17,6 +17,12 @@ <!-- Colors specific to pre-Holo themes. --> <resources> + <!-- A bright Android-y green --> + <color name="legacy_green">#ff90df25</color> + + <!-- A bright orange suitable for use in the early 2000s --> + <color name="legacy_orange">#fffea50b</color> + <!-- Highlight colors for the legacy themes --> <eat-comment /> @@ -27,19 +33,17 @@ <!-- Forward compatibility for Material-style theme colors --> <eat-comment /> - <color name="legacy_primary_dark">#ff000000</color> - <color name="legacy_primary">#ffe6e6e6</color> - <color name="legacy_primary_light">#ffffffff</color> - <color name="legacy_control_activated">#ff90df25</color> + <color name="legacy_primary_dark">@color/black</color> + <color name="legacy_primary">#ff222222</color> + <color name="legacy_control_activated">@color/legacy_green</color> <color name="legacy_control_normal">#99ffffff</color> - <color name="legacy_button_pressed">#fffea50b</color> + <color name="legacy_button_pressed">@color/legacy_orange</color> <color name="legacy_button_normal">#f3dbdbdb</color> - <color name="legacy_light_primary_dark">@color/legacy_primary_dark</color> - <color name="legacy_light_primary">@color/legacy_primary</color> - <color name="legacy_light_primary_light">@color/legacy_primary_light</color> - <color name="legacy_light_control_activated">@color/legacy_control_activated</color> + <color name="legacy_light_primary_dark">@color/black</color> + <color name="legacy_light_primary">#ffe6e6e6</color> + <color name="legacy_light_control_activated">@color/legacy_green</color> <color name="legacy_light_control_normal">#99000000</color> - <color name="legacy_light_button_pressed">@color/legacy_button_pressed</color> - <color name="legacy_light_button_normal">@color/legacy_button_normal</color> + <color name="legacy_light_button_pressed">@color/legacy_orange</color> + <color name="legacy_light_button_normal">#f3dbdbdb</color> </resources> diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd index 07ca45a957a4..c179a2e8cae4 100644 --- a/docs/html/guide/topics/resources/providing-resources.jd +++ b/docs/html/guide/topics/resources/providing-resources.jd @@ -646,7 +646,8 @@ application during runtime.</p> <code>xxhdpi</code><br/> <code>xxxhdpi</code><br/> <code>nodpi</code><br/> - <code>tvdpi</code> + <code>tvdpi</code><br/> + <code>anydpi</code> </td> <td> <ul class="nolist"> @@ -667,7 +668,11 @@ to match the device density.</li> <li>{@code tvdpi}: Screens somewhere between mdpi and hdpi; approximately 213dpi. This is not considered a "primary" density group. It is mostly intended for televisions and most apps shouldn't need it—providing mdpi and hdpi resources is sufficient for most apps and -the system will scale them as appropriate. This qualifier was introduced with API level 13.</li> +the system will scale them as appropriate. <em>Added in API Level 13</em></li> + <li>{@code anydpi}: This qualifier matches all screen densities and takes precedence over +other qualifiers. This is useful for +<a href="{@docRoot}training/material/drawables.html#VectorDrawables">vector drawables</a>. +<em>Added in API Level 21</em></li> </ul> <p>There is a 3:4:6:8:12:16 scaling ratio between the six primary densities (ignoring the tvdpi density). So, a 9x9 bitmap in ldpi is 12x12 in mdpi, 18x18 in hdpi, 24x24 in xhdpi and so on. diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index cc0943f52fdb..eaade34a1db6 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -209,6 +209,7 @@ endif LOCAL_SRC_FILES += \ tests/TestContext.cpp \ + tests/TreeContentAnimation.cpp \ tests/main.cpp include $(BUILD_EXECUTABLE) diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp index c128ca775155..eca71c6e0e8d 100644 --- a/libs/hwui/CanvasState.cpp +++ b/libs/hwui/CanvasState.cpp @@ -28,10 +28,21 @@ CanvasState::CanvasState(CanvasStateClient& renderer) , mWidth(-1) , mHeight(-1) , mSaveCount(1) - , mFirstSnapshot(new Snapshot) , mCanvas(renderer) - , mSnapshot(mFirstSnapshot) { + , mSnapshot(&mFirstSnapshot) { +} + +CanvasState::~CanvasState() { + // First call freeSnapshot on all but mFirstSnapshot + // to invoke all the dtors + freeAllSnapshots(); + // Now actually release the memory + while (mSnapshotPool) { + void* temp = mSnapshotPool; + mSnapshotPool = mSnapshotPool->previous; + free(temp); + } } void CanvasState::initializeSaveStack( @@ -41,11 +52,12 @@ void CanvasState::initializeSaveStack( if (mWidth != viewportWidth || mHeight != viewportHeight) { mWidth = viewportWidth; mHeight = viewportHeight; - mFirstSnapshot->initializeViewport(viewportWidth, viewportHeight); + mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight); mCanvas.onViewportInitialized(); } - mSnapshot = new Snapshot(mFirstSnapshot, + freeAllSnapshots(); + mSnapshot = allocSnapshot(&mFirstSnapshot, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); mSnapshot->fbo = mCanvas.getTargetFbo(); @@ -53,6 +65,38 @@ void CanvasState::initializeSaveStack( mSaveCount = 1; } +Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) { + void* memory; + if (mSnapshotPool) { + memory = mSnapshotPool; + mSnapshotPool = mSnapshotPool->previous; + mSnapshotPoolCount--; + } else { + memory = malloc(sizeof(Snapshot)); + } + return new (memory) Snapshot(previous, savecount); +} + +void CanvasState::freeSnapshot(Snapshot* snapshot) { + snapshot->~Snapshot(); + // Arbitrary number, just don't let this grown unbounded + if (mSnapshotPoolCount > 10) { + free((void*) snapshot); + } else { + snapshot->previous = mSnapshotPool; + mSnapshotPool = snapshot; + mSnapshotPoolCount++; + } +} + +void CanvasState::freeAllSnapshots() { + while (mSnapshot != &mFirstSnapshot) { + Snapshot* temp = mSnapshot; + mSnapshot = mSnapshot->previous; + freeSnapshot(temp); + } +} + /////////////////////////////////////////////////////////////////////////////// // Save (layer) /////////////////////////////////////////////////////////////////////////////// @@ -64,7 +108,7 @@ void CanvasState::initializeSaveStack( * stack, and ensures restoreToCount() doesn't call back into subclass overrides. */ int CanvasState::saveSnapshot(int flags) { - mSnapshot = new Snapshot(mSnapshot, flags); + mSnapshot = allocSnapshot(mSnapshot, flags); return mSaveCount++; } @@ -76,14 +120,16 @@ int CanvasState::save(int flags) { * Guaranteed to restore without side-effects. */ void CanvasState::restoreSnapshot() { - sp<Snapshot> toRemove = mSnapshot; - sp<Snapshot> toRestore = mSnapshot->previous; + Snapshot* toRemove = mSnapshot; + Snapshot* toRestore = mSnapshot->previous; mSaveCount--; mSnapshot = toRestore; // subclass handles restore implementation mCanvas.onSnapshotRestored(*toRemove, *toRestore); + + freeSnapshot(toRemove); } void CanvasState::restore() { diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h index f0fb9ba8b324..be57f44210ef 100644 --- a/libs/hwui/CanvasState.h +++ b/libs/hwui/CanvasState.h @@ -17,12 +17,12 @@ #ifndef ANDROID_HWUI_CANVAS_STATE_H #define ANDROID_HWUI_CANVAS_STATE_H +#include "Snapshot.h" + #include <SkMatrix.h> #include <SkPath.h> #include <SkRegion.h> -#include "Snapshot.h" - namespace android { namespace uirenderer { @@ -74,6 +74,7 @@ public: class CanvasState { public: CanvasState(CanvasStateClient& renderer); + ~CanvasState(); /** * Initializes the first snapshot, computing the projection matrix, @@ -157,11 +158,15 @@ public: int getHeight() const { return mHeight; } bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); } - inline const Snapshot* currentSnapshot() const { return mSnapshot.get(); } - inline Snapshot* writableSnapshot() { return mSnapshot.get(); } - inline const Snapshot* firstSnapshot() const { return mFirstSnapshot.get(); } + inline const Snapshot* currentSnapshot() const { return mSnapshot; } + inline Snapshot* writableSnapshot() { return mSnapshot; } + inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; } private: + Snapshot* allocSnapshot(Snapshot* previous, int savecount); + void freeSnapshot(Snapshot* snapshot); + void freeAllSnapshots(); + /// indicates that the clip has been changed since the last time it was consumed bool mDirtyClip; @@ -172,13 +177,18 @@ private: int mSaveCount; /// Base state - sp<Snapshot> mFirstSnapshot; + Snapshot mFirstSnapshot; /// Host providing callbacks CanvasStateClient& mCanvas; /// Current state - sp<Snapshot> mSnapshot; + Snapshot* mSnapshot; + + // Pool of allocated snapshots to re-use + // NOTE: The dtors have already been invoked! + Snapshot* mSnapshotPool = nullptr; + int mSnapshotPoolCount = 0; }; // class CanvasState diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp index 8e7efb4e35d6..a9d1e4284d2e 100644 --- a/libs/hwui/ClipArea.cpp +++ b/libs/hwui/ClipArea.cpp @@ -23,14 +23,6 @@ namespace android { namespace uirenderer { -static bool intersect(Rect& r, const Rect& r2) { - bool hasIntersection = r.intersect(r2); - if (!hasIntersection) { - r.setEmpty(); - } - return hasIntersection; -} - static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) { Vertex v = {x, y}; transform.mapPoint(v.x, v.y); @@ -67,9 +59,8 @@ bool TransformedRectangle::canSimplyIntersectWith( return mTransform == other.mTransform; } -bool TransformedRectangle::intersectWith(const TransformedRectangle& other) { - Rect translatedBounds(other.mBounds); - return intersect(mBounds, translatedBounds); +void TransformedRectangle::intersectWith(const TransformedRectangle& other) { + mBounds.doIntersect(other.mBounds); } bool TransformedRectangle::isEmpty() const { @@ -146,7 +137,7 @@ Rect RectangleList::calculateBounds() const { if (index == 0) { bounds = tr.transformedBounds(); } else { - bounds.intersect(tr.transformedBounds()); + bounds.doIntersect(tr.transformedBounds()); } } return bounds; @@ -275,10 +266,7 @@ void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, if (transform->rectToRect()) { Rect transformed(r); transform->mapRect(transformed); - bool hasIntersection = mClipRect.intersect(transformed); - if (!hasIntersection) { - mClipRect.setEmpty(); - } + mClipRect.doIntersect(transformed); return; } diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h index 38fefe5ab097..f88fd92e234d 100644 --- a/libs/hwui/ClipArea.h +++ b/libs/hwui/ClipArea.h @@ -33,7 +33,7 @@ public: TransformedRectangle(const Rect& bounds, const Matrix4& transform); bool canSimplyIntersectWith(const TransformedRectangle& other) const; - bool intersectWith(const TransformedRectangle& other); + void intersectWith(const TransformedRectangle& other); bool isEmpty() const; diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index c63b5597f284..227271d83cf8 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -58,7 +58,7 @@ void LayerRenderer::prepareDirty(int viewportWidth, int viewportHeight, mLayer->region.clear(); dirty.set(0.0f, 0.0f, width, height); } else { - dirty.intersect(0.0f, 0.0f, width, height); + dirty.doIntersect(0.0f, 0.0f, width, height); android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom); mLayer->region.subtractSelf(r); } diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 9f24e379e418..cd03ac407d81 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -488,7 +488,8 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool currentTransform()->mapRect(bounds); // Layers only make sense if they are in the framebuffer's bounds - if (bounds.intersect(mState.currentClipRect())) { + bounds.doIntersect(mState.currentClipRect()); + if (!bounds.isEmpty()) { // We cannot work with sub-pixels in this case bounds.snapToPixelBoundaries(); @@ -497,23 +498,20 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool // of the framebuffer const Snapshot& previous = *(currentSnapshot()->previous); Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight()); - if (!bounds.intersect(previousViewport)) { - bounds.setEmpty(); - } else if (fboLayer) { + + bounds.doIntersect(previousViewport); + if (!bounds.isEmpty() && fboLayer) { clip.set(bounds); mat4 inverse; inverse.loadInverse(*currentTransform()); inverse.mapRect(clip); clip.snapToPixelBoundaries(); - if (clip.intersect(untransformedBounds)) { + clip.doIntersect(untransformedBounds); + if (!clip.isEmpty()) { clip.translate(-untransformedBounds.left, -untransformedBounds.top); bounds.set(untransformedBounds); - } else { - clip.setEmpty(); } } - } else { - bounds.setEmpty(); } } @@ -1038,7 +1036,8 @@ void OpenGLRenderer::dirtyLayer(const float left, const float top, } void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { - if (CC_LIKELY(!bounds.isEmpty() && bounds.intersect(mState.currentClipRect()))) { + bounds.doIntersect(mState.currentClipRect()); + if (!bounds.isEmpty()) { bounds.snapToPixelBoundaries(); android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom); if (!dirty.isEmpty()) { @@ -1112,7 +1111,8 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef // is used, it should more closely duplicate the quickReject logic (in how it uses // snapToPixelBoundaries) - if (!clippedBounds.intersect(currentClip)) { + clippedBounds.doIntersect(currentClip); + if (clippedBounds.isEmpty()) { // quick rejected return true; } @@ -1242,9 +1242,8 @@ void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) { Rect bounds = tr.getBounds(); if (transform.rectToRect()) { transform.mapRect(bounds); - if (!bounds.intersect(scissorBox)) { - bounds.setEmpty(); - } else { + bounds.doIntersect(scissorBox); + if (!bounds.isEmpty()) { handlePointNoTransform(rectangleVertices, bounds.left, bounds.top); handlePointNoTransform(rectangleVertices, bounds.right, bounds.top); handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom); diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index 4c4cd3da3be4..50199db75640 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -125,25 +125,32 @@ public: } bool intersects(float l, float t, float r, float b) const { - return !intersectWith(l, t, r, b).isEmpty(); + float tempLeft = std::max(left, l); + float tempTop = std::max(top, t); + float tempRight = std::min(right, r); + float tempBottom = std::min(bottom, b); + + return ((tempLeft < tempRight) && (tempTop < tempBottom)); // !isEmpty } bool intersects(const Rect& r) const { return intersects(r.left, r.top, r.right, r.bottom); } - bool intersect(float l, float t, float r, float b) { - Rect tmp(l, t, r, b); - intersectWith(tmp); - if (!tmp.isEmpty()) { - set(tmp); - return true; - } - return false; + /** + * This method is named 'doIntersect' instead of 'intersect' so as not to be confused with + * SkRect::intersect / android.graphics.Rect#intersect behavior, which do not modify the object + * if the intersection of the rects would be empty. + */ + void doIntersect(float l, float t, float r, float b) { + left = std::max(left, l); + top = std::max(top, t); + right = std::min(right, r); + bottom = std::min(bottom, b); } - bool intersect(const Rect& r) { - return intersect(r.left, r.top, r.right, r.bottom); + void doIntersect(const Rect& r) { + doIntersect(r.left, r.top, r.right, r.bottom); } inline bool contains(float l, float t, float r, float b) const { @@ -271,24 +278,6 @@ public: void dump(const char* label = nullptr) const { ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom); } - -private: - void intersectWith(Rect& tmp) const { - tmp.left = std::max(left, tmp.left); - tmp.top = std::max(top, tmp.top); - tmp.right = std::min(right, tmp.right); - tmp.bottom = std::min(bottom, tmp.bottom); - } - - Rect intersectWith(float l, float t, float r, float b) const { - Rect tmp; - tmp.left = std::max(left, l); - tmp.top = std::max(top, t); - tmp.right = std::min(right, r); - tmp.bottom = std::min(bottom, b); - return tmp; - } - }; // class Rect }; // namespace uirenderer diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 71589c802749..f824cc020196 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -549,7 +549,7 @@ public: if (flags & CLIP_TO_BOUNDS) { outRect->set(0, 0, getWidth(), getHeight()); if (flags & CLIP_TO_CLIP_BOUNDS) { - outRect->intersect(mPrimitiveFields.mClipBounds); + outRect->doIntersect(mPrimitiveFields.mClipBounds); } } else { outRect->set(mPrimitiveFields.mClipBounds); diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index 4d60b8dd0e7c..0a58f4b42e4c 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -44,7 +44,7 @@ Snapshot::Snapshot() * Copies the specified snapshot/ The specified snapshot is stored as * the previous snapshot. */ -Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) +Snapshot::Snapshot(Snapshot* s, int saveFlags) : flags(0) , previous(s) , layer(s->layer) @@ -148,7 +148,7 @@ void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const { const Snapshot* current = this; do { snapshotList.push(current); - current = current->previous.get(); + current = current->previous; } while (current); // traverse the list, adding in each transform that contributes to the total transform @@ -240,7 +240,7 @@ bool Snapshot::isIgnored() const { void Snapshot::dump() const { ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d", - this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple()); + this, flags, previous, getViewportHeight(), isIgnored(), !mClipArea->isSimple()); const Rect& clipRect(mClipArea->getClipRect()); ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple()); diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index cf8f11c80058..aeeda965c48f 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -83,11 +83,11 @@ public: * Each snapshot has a link to a previous snapshot, indicating the previous * state of the renderer. */ -class Snapshot: public LightRefBase<Snapshot> { +class Snapshot { public: Snapshot(); - Snapshot(const sp<Snapshot>& s, int saveFlags); + Snapshot(Snapshot* s, int saveFlags); /** * Various flags set on ::flags. @@ -229,7 +229,7 @@ public: /** * Previous snapshot. */ - sp<Snapshot> previous; + Snapshot* previous; /** * A pointer to the currently active layer. diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index ddfd62141f5d..38f6e539693e 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -339,7 +339,7 @@ void CanvasContext::draw() { // Remember the intersection of the target bounds and the intersection bounds against // which we have to crop the content. backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight()); - backdropBounds.intersect(targetBounds); + backdropBounds.doIntersect(targetBounds); // Check if we have to draw something on the left side ... if (targetBounds.left < contentBounds.left) { mCanvas->save(SkCanvas::kClip_SaveFlag); diff --git a/libs/hwui/tests/Benchmark.h b/libs/hwui/tests/Benchmark.h new file mode 100644 index 000000000000..e16310e034be --- /dev/null +++ b/libs/hwui/tests/Benchmark.h @@ -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. + */ +#ifndef TESTS_BENCHMARK_H +#define TESTS_BENCHMARK_H + +#include <string> +#include <vector> + +namespace android { +namespace uirenderer { + +struct BenchmarkOptions { + int count; +}; + +typedef void (*BenchmarkFunctor)(const BenchmarkOptions&); + +struct BenchmarkInfo { + std::string name; + std::string description; + BenchmarkFunctor functor; +}; + +class Benchmark { +public: + Benchmark(const BenchmarkInfo& info) { + registerBenchmark(info); + } + +private: + Benchmark() = delete; + Benchmark(const Benchmark&) = delete; + Benchmark& operator=(const Benchmark&) = delete; + + static void registerBenchmark(const BenchmarkInfo& info); +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* TESTS_BENCHMARK_H */ diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp index cebe7650dea2..ba763a8def62 100644 --- a/libs/hwui/tests/TestContext.cpp +++ b/libs/hwui/tests/TestContext.cpp @@ -22,16 +22,35 @@ namespace test { static const int IDENT_DISPLAYEVENT = 1; -static DisplayInfo getBuiltInDisplay() { +static android::DisplayInfo DUMMY_DISPLAY { + 1080, //w + 1920, //h + 320.0, // xdpi + 320.0, // ydpi + 60.0, // fps + 2.0, // density + 0, // orientation + false, // secure? + 0, // appVsyncOffset + 0, // presentationDeadline + 0, // colorTransform +}; + +DisplayInfo getBuiltInDisplay() { +#if !HWUI_NULL_GPU DisplayInfo display; sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display); LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n"); return display; +#else + return DUMMY_DISPLAY; +#endif } -android::DisplayInfo gDisplay = getBuiltInDisplay(); +// Initialize to a dummy default +android::DisplayInfo gDisplay = DUMMY_DISPLAY; TestContext::TestContext() { mLooper = new Looper(true); diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h index 7b30fc1dc7ce..2bbe5dffd9b8 100644 --- a/libs/hwui/tests/TestContext.h +++ b/libs/hwui/tests/TestContext.h @@ -32,6 +32,8 @@ namespace test { extern DisplayInfo gDisplay; #define dp(x) ((x) * android::uirenderer::test::gDisplay.density) +DisplayInfo getBuiltInDisplay(); + class TestContext { public: TestContext(); diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp new file mode 100644 index 000000000000..a59261c14fc5 --- /dev/null +++ b/libs/hwui/tests/TreeContentAnimation.cpp @@ -0,0 +1,385 @@ +/* + * 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. + */ + +#include <cutils/log.h> +#include <gui/Surface.h> +#include <ui/PixelFormat.h> + +#include <AnimationContext.h> +#include <DisplayListCanvas.h> +#include <RenderNode.h> +#include <renderthread/RenderProxy.h> +#include <renderthread/RenderTask.h> + +#include "Benchmark.h" +#include "TestContext.h" + +#include "protos/hwui.pb.h" + +#include <stdio.h> +#include <unistd.h> +#include <getopt.h> +#include <vector> + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; +using namespace android::uirenderer::test; + +class ContextFactory : public IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { + return new AnimationContext(clock); + } +}; + +static DisplayListCanvas* startRecording(RenderNode* node) { + DisplayListCanvas* renderer = new DisplayListCanvas( + node->stagingProperties().getWidth(), node->stagingProperties().getHeight()); + return renderer; +} + +static void endRecording(DisplayListCanvas* renderer, RenderNode* node) { + node->setStagingDisplayList(renderer->finishRecording()); + delete renderer; +} + +class TreeContentAnimation { +public: + virtual ~TreeContentAnimation() {} + int frameCount = 150; + virtual int getFrameCount() { return frameCount; } + virtual void setFrameCount(int fc) { + if (fc > 0) { + frameCount = fc; + } + } + virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0; + virtual void doFrame(int frameNr) = 0; + + template <class T> + static void run(const BenchmarkOptions& opts) { + // Switch to the real display + gDisplay = getBuiltInDisplay(); + + T animation; + animation.setFrameCount(opts.count); + + TestContext testContext; + + // create the native surface + const int width = gDisplay.w; + const int height = gDisplay.h; + sp<Surface> surface = testContext.surface(); + + RenderNode* rootNode = new RenderNode(); + rootNode->incStrong(nullptr); + rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height); + rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + rootNode->mutateStagingProperties().setClipToBounds(false); + rootNode->setPropertyFieldsDirty(RenderNode::GENERIC); + + ContextFactory factory; + std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory)); + proxy->loadSystemProperties(); + proxy->initialize(surface); + float lightX = width / 2.0; + proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15); + proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)}); + + android::uirenderer::Rect DUMMY; + + DisplayListCanvas* renderer = startRecording(rootNode); + animation.createContent(width, height, renderer); + endRecording(renderer, rootNode); + + // Do a few cold runs then reset the stats so that the caches are all hot + for (int i = 0; i < 3; i++) { + testContext.waitForVsync(); + proxy->syncAndDrawFrame(); + } + proxy->resetProfileInfo(); + + for (int i = 0; i < animation.getFrameCount(); i++) { + testContext.waitForVsync(); + + ATRACE_NAME("UI-Draw Frame"); + nsecs_t vsync = systemTime(CLOCK_MONOTONIC); + UiFrameInfoBuilder(proxy->frameInfo()) + .setVsync(vsync, vsync); + animation.doFrame(i); + proxy->syncAndDrawFrame(); + } + + proxy->dumpProfileInfo(STDOUT_FILENO, 0); + rootNode->decStrong(nullptr); + } +}; + +class ShadowGridAnimation : public TreeContentAnimation { +public: + std::vector< sp<RenderNode> > cards; + void createContent(int width, int height, DisplayListCanvas* renderer) override { + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer->insertReorderBarrier(true); + + for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { + for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { + sp<RenderNode> card = createCard(x, y, dp(100), dp(100)); + renderer->drawRenderNode(card.get()); + cards.push_back(card); + } + } + + renderer->insertReorderBarrier(false); + } + void doFrame(int frameNr) override { + int curFrame = frameNr % 150; + for (size_t ci = 0; ci < cards.size(); ci++) { + cards[ci]->mutateStagingProperties().setTranslationX(curFrame); + cards[ci]->mutateStagingProperties().setTranslationY(curFrame); + cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } + } +private: + sp<RenderNode> createCard(int x, int y, int width, int height) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->mutateStagingProperties().setElevation(dp(16)); + node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1); + node->mutateStagingProperties().mutableOutline().setShouldClip(true); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z); + + DisplayListCanvas* renderer = startRecording(node.get()); + renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); + endRecording(renderer, node.get()); + return node; + } +}; +static Benchmark _ShadowGrid(BenchmarkInfo{ + "shadowgrid", + "A grid of rounded rects that cast a shadow. Simplified scenario of an " + "Android TV-style launcher interface. High CPU/GPU load.", + TreeContentAnimation::run<ShadowGridAnimation> +}); + +class ShadowGrid2Animation : public TreeContentAnimation { +public: + std::vector< sp<RenderNode> > cards; + void createContent(int width, int height, DisplayListCanvas* renderer) override { + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer->insertReorderBarrier(true); + + for (int x = dp(8); x < (width - dp(58)); x += dp(58)) { + for (int y = dp(8); y < (height - dp(58)); y += dp(58)) { + sp<RenderNode> card = createCard(x, y, dp(50), dp(50)); + renderer->drawRenderNode(card.get()); + cards.push_back(card); + } + } + + renderer->insertReorderBarrier(false); + } + void doFrame(int frameNr) override { + int curFrame = frameNr % 150; + for (size_t ci = 0; ci < cards.size(); ci++) { + cards[ci]->mutateStagingProperties().setTranslationX(curFrame); + cards[ci]->mutateStagingProperties().setTranslationY(curFrame); + cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } + } +private: + sp<RenderNode> createCard(int x, int y, int width, int height) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->mutateStagingProperties().setElevation(dp(16)); + node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1); + node->mutateStagingProperties().mutableOutline().setShouldClip(true); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z); + + DisplayListCanvas* renderer = startRecording(node.get()); + renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); + endRecording(renderer, node.get()); + return node; + } +}; +static Benchmark _ShadowGrid2(BenchmarkInfo{ + "shadowgrid2", + "A dense grid of rounded rects that cast a shadow. This is a higher CPU load " + "variant of shadowgrid. Very high CPU load, high GPU load.", + TreeContentAnimation::run<ShadowGrid2Animation> +}); + +class RectGridAnimation : public TreeContentAnimation { +public: + sp<RenderNode> card; + void createContent(int width, int height, DisplayListCanvas* renderer) override { + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer->insertReorderBarrier(true); + + card = createCard(40, 40, 200, 200); + renderer->drawRenderNode(card.get()); + + renderer->insertReorderBarrier(false); + } + void doFrame(int frameNr) override { + int curFrame = frameNr % 150; + card->mutateStagingProperties().setTranslationX(curFrame); + card->mutateStagingProperties().setTranslationY(curFrame); + card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } +private: + sp<RenderNode> createCard(int x, int y, int width, int height) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + DisplayListCanvas* renderer = startRecording(node.get()); + renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode); + + SkRegion region; + for (int xOffset = 0; xOffset < width; xOffset+=2) { + for (int yOffset = 0; yOffset < height; yOffset+=2) { + region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op); + } + } + + SkPaint paint; + paint.setColor(0xff00ffff); + renderer->drawRegion(region, paint); + + endRecording(renderer, node.get()); + return node; + } +}; +static Benchmark _RectGrid(BenchmarkInfo{ + "rectgrid", + "A dense grid of 1x1 rects that should visually look like a single rect. " + "Low CPU/GPU load.", + TreeContentAnimation::run<RectGridAnimation> +}); + +class OvalAnimation : public TreeContentAnimation { +public: + sp<RenderNode> card; + void createContent(int width, int height, DisplayListCanvas* renderer) override { + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer->insertReorderBarrier(true); + + card = createCard(40, 40, 400, 400); + renderer->drawRenderNode(card.get()); + + renderer->insertReorderBarrier(false); + } + + void doFrame(int frameNr) override { + int curFrame = frameNr % 150; + card->mutateStagingProperties().setTranslationX(curFrame); + card->mutateStagingProperties().setTranslationY(curFrame); + card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } +private: + sp<RenderNode> createCard(int x, int y, int width, int height) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + DisplayListCanvas* renderer = startRecording(node.get()); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(0xFF000000); + renderer->drawOval(0, 0, width, height, paint); + + endRecording(renderer, node.get()); + return node; + } +}; +static Benchmark _Oval(BenchmarkInfo{ + "oval", + "Draws 1 oval.", + TreeContentAnimation::run<OvalAnimation> +}); + +class PartialDamageTest : public TreeContentAnimation { +public: + std::vector< sp<RenderNode> > cards; + void createContent(int width, int height, DisplayListCanvas* renderer) override { + static SkColor COLORS[] = { + 0xFFF44336, + 0xFF9C27B0, + 0xFF2196F3, + 0xFF4CAF50, + }; + + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + + for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { + for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { + sp<RenderNode> card = createCard(x, y, dp(100), dp(100), + COLORS[static_cast<int>((y / dp(116))) % 4]); + renderer->drawRenderNode(card.get()); + cards.push_back(card); + } + } + } + void doFrame(int frameNr) override { + int curFrame = frameNr % 150; + cards[0]->mutateStagingProperties().setTranslationX(curFrame); + cards[0]->mutateStagingProperties().setTranslationY(curFrame); + cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + DisplayListCanvas* renderer = startRecording(cards[0].get()); + renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0), + SkXfermode::kSrcOver_Mode); + endRecording(renderer, cards[0].get()); + } + + static SkColor interpolateColor(float fraction, SkColor start, SkColor end) { + int startA = (start >> 24) & 0xff; + int startR = (start >> 16) & 0xff; + int startG = (start >> 8) & 0xff; + int startB = start & 0xff; + + int endA = (end >> 24) & 0xff; + int endR = (end >> 16) & 0xff; + int endG = (end >> 8) & 0xff; + int endB = end & 0xff; + + return (int)((startA + (int)(fraction * (endA - startA))) << 24) | + (int)((startR + (int)(fraction * (endR - startR))) << 16) | + (int)((startG + (int)(fraction * (endG - startG))) << 8) | + (int)((startB + (int)(fraction * (endB - startB)))); + } +private: + sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + DisplayListCanvas* renderer = startRecording(node.get()); + renderer->drawColor(color, SkXfermode::kSrcOver_Mode); + endRecording(renderer, node.get()); + return node; + } +}; +static Benchmark _PartialDamage(BenchmarkInfo{ + "partialdamage", + "Tests the partial invalidation path. Draws a grid of rects and animates 1 " + "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or " + "EGL_KHR_partial_update is supported by the device & are enabled in hwui.", + TreeContentAnimation::run<PartialDamageTest> +}); diff --git a/libs/hwui/tests/how_to_run.txt b/libs/hwui/tests/how_to_run.txt index 85900eff2f78..b051768f3262 100644 --- a/libs/hwui/tests/how_to_run.txt +++ b/libs/hwui/tests/how_to_run.txt @@ -2,16 +2,4 @@ mmm -j8 frameworks/base/libs/hwui/ && adb push $OUT/data/local/tmp/hwuitest /data/local/tmp/hwuitest && adb shell /data/local/tmp/hwuitest - -Command arguments: -hwuitest [testname] - -Default test is 'shadowgrid' - -List of tests: - -shadowgrid: creates a grid of rounded rects that cast shadows, high CPU & GPU load - -rectgrid: creates a grid of 1x1 rects - -oval: draws 1 oval +Pass --help to get help diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp index 0bbf08c76c90..aee84de3ae7b 100644 --- a/libs/hwui/tests/main.cpp +++ b/libs/hwui/tests/main.cpp @@ -14,385 +14,181 @@ * limitations under the License. */ -#include <cutils/log.h> -#include <gui/Surface.h> -#include <ui/PixelFormat.h> - -#include <AnimationContext.h> -#include <DisplayListCanvas.h> -#include <RenderNode.h> -#include <renderthread/RenderProxy.h> -#include <renderthread/RenderTask.h> - -#include "TestContext.h" +#include "Benchmark.h" #include "protos/hwui.pb.h" +#include <getopt.h> #include <stdio.h> +#include <string> #include <unistd.h> +#include <unordered_map> +#include <vector> using namespace android; using namespace android::uirenderer; -using namespace android::uirenderer::renderthread; -using namespace android::uirenderer::test; - -class ContextFactory : public IContextFactory { -public: - virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { - return new AnimationContext(clock); - } -}; -static DisplayListCanvas* startRecording(RenderNode* node) { - DisplayListCanvas* renderer = new DisplayListCanvas( - node->stagingProperties().getWidth(), node->stagingProperties().getHeight()); - return renderer; +// Not a static global because we need to force the map to be constructed +// before we try to add things to it. +std::unordered_map<std::string, BenchmarkInfo>& testMap() { + static std::unordered_map<std::string, BenchmarkInfo> testMap; + return testMap; } -static void endRecording(DisplayListCanvas* renderer, RenderNode* node) { - node->setStagingDisplayList(renderer->finishRecording()); - delete renderer; +void Benchmark::registerBenchmark(const BenchmarkInfo& info) { + testMap()[info.name] = info; } -class TreeContentAnimation { -public: - virtual ~TreeContentAnimation() {} - int frameCount = 150; - virtual int getFrameCount() { return frameCount; } - virtual void setFrameCount(int fc) { - if (fc > 0) { - frameCount = fc; - } - } - virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0; - virtual void doFrame(int frameNr) = 0; - - template <class T> - static void run(int frameCount) { - T animation; - animation.setFrameCount(frameCount); - - TestContext testContext; - - // create the native surface - const int width = gDisplay.w; - const int height = gDisplay.h; - sp<Surface> surface = testContext.surface(); - - RenderNode* rootNode = new RenderNode(); - rootNode->incStrong(nullptr); - rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height); - rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - rootNode->mutateStagingProperties().setClipToBounds(false); - rootNode->setPropertyFieldsDirty(RenderNode::GENERIC); - - ContextFactory factory; - std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory)); - proxy->loadSystemProperties(); - proxy->initialize(surface); - float lightX = width / 2.0; - proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15); - proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)}); - - android::uirenderer::Rect DUMMY; - - DisplayListCanvas* renderer = startRecording(rootNode); - animation.createContent(width, height, renderer); - endRecording(renderer, rootNode); - - // Do a few cold runs then reset the stats so that the caches are all hot - for (int i = 0; i < 3; i++) { - testContext.waitForVsync(); - proxy->syncAndDrawFrame(); - } - proxy->resetProfileInfo(); - - for (int i = 0; i < animation.getFrameCount(); i++) { - testContext.waitForVsync(); - - ATRACE_NAME("UI-Draw Frame"); - nsecs_t vsync = systemTime(CLOCK_MONOTONIC); - UiFrameInfoBuilder(proxy->frameInfo()) - .setVsync(vsync, vsync); - animation.doFrame(i); - proxy->syncAndDrawFrame(); - } - - proxy->dumpProfileInfo(STDOUT_FILENO, 0); - rootNode->decStrong(nullptr); - } -}; - -class ShadowGridAnimation : public TreeContentAnimation { -public: - std::vector< sp<RenderNode> > cards; - void createContent(int width, int height, DisplayListCanvas* renderer) override { - renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); - renderer->insertReorderBarrier(true); +static int gFrameCount = 150; +static int gRepeatCount = 1; +static std::vector<BenchmarkInfo> gRunTests; + +static void printHelp() { + printf("\ +USAGE: hwuitest [OPTIONS] <TESTNAME>\n\ +\n\ +OPTIONS:\n\ + -c, --count=NUM NUM loops a test should run (example, number of frames)\n\ + -r, --runs=NUM Repeat the test(s) NUM times\n\ + -h, --help Display this help\n\ + --list List all tests\n\ +\n"); +} - for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { - for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { - sp<RenderNode> card = createCard(x, y, dp(100), dp(100)); - renderer->drawRenderNode(card.get()); - cards.push_back(card); +static void listTests() { + printf("Tests: \n"); + for (auto&& test : testMap()) { + auto&& info = test.second; + const char* col1 = info.name.c_str(); + int dlen = info.description.length(); + const char* col2 = info.description.c_str(); + // World's best line breaking algorithm. + do { + int toPrint = dlen; + if (toPrint > 50) { + char* found = (char*) memrchr(col2, ' ', 50); + if (found) { + toPrint = found - col2; + } else { + toPrint = 50; + } } - } - - renderer->insertReorderBarrier(false); - } - void doFrame(int frameNr) override { - int curFrame = frameNr % 150; - for (size_t ci = 0; ci < cards.size(); ci++) { - cards[ci]->mutateStagingProperties().setTranslationX(curFrame); - cards[ci]->mutateStagingProperties().setTranslationY(curFrame); - cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - } - } -private: - sp<RenderNode> createCard(int x, int y, int width, int height) { - sp<RenderNode> node = new RenderNode(); - node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); - node->mutateStagingProperties().setElevation(dp(16)); - node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1); - node->mutateStagingProperties().mutableOutline().setShouldClip(true); - node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z); - - DisplayListCanvas* renderer = startRecording(node.get()); - renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); - endRecording(renderer, node.get()); - return node; - } -}; - -class ShadowGrid2Animation : public TreeContentAnimation { -public: - std::vector< sp<RenderNode> > cards; - void createContent(int width, int height, DisplayListCanvas* renderer) override { - renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); - renderer->insertReorderBarrier(true); - - for (int x = dp(8); x < (width - dp(58)); x += dp(58)) { - for (int y = dp(8); y < (height - dp(58)); y += dp(58)) { - sp<RenderNode> card = createCard(x, y, dp(50), dp(50)); - renderer->drawRenderNode(card.get()); - cards.push_back(card); + printf("%-20s %.*s\n", col1, toPrint, col2); + col1 = ""; + col2 += toPrint; + dlen -= toPrint; + while (*col2 == ' ') { + col2++; dlen--; } - } - - renderer->insertReorderBarrier(false); - } - void doFrame(int frameNr) override { - int curFrame = frameNr % 150; - for (size_t ci = 0; ci < cards.size(); ci++) { - cards[ci]->mutateStagingProperties().setTranslationX(curFrame); - cards[ci]->mutateStagingProperties().setTranslationY(curFrame); - cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - } - } -private: - sp<RenderNode> createCard(int x, int y, int width, int height) { - sp<RenderNode> node = new RenderNode(); - node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); - node->mutateStagingProperties().setElevation(dp(16)); - node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1); - node->mutateStagingProperties().mutableOutline().setShouldClip(true); - node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z); - - DisplayListCanvas* renderer = startRecording(node.get()); - renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); - endRecording(renderer, node.get()); - return node; - } -}; - -class RectGridAnimation : public TreeContentAnimation { -public: - sp<RenderNode> card; - void createContent(int width, int height, DisplayListCanvas* renderer) override { - renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); - renderer->insertReorderBarrier(true); - - card = createCard(40, 40, 200, 200); - renderer->drawRenderNode(card.get()); - - renderer->insertReorderBarrier(false); - } - void doFrame(int frameNr) override { - int curFrame = frameNr % 150; - card->mutateStagingProperties().setTranslationX(curFrame); - card->mutateStagingProperties().setTranslationY(curFrame); - card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } while (dlen > 0); + printf("\n"); } -private: - sp<RenderNode> createCard(int x, int y, int width, int height) { - sp<RenderNode> node = new RenderNode(); - node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); - node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - - DisplayListCanvas* renderer = startRecording(node.get()); - renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode); - - SkRegion region; - for (int xOffset = 0; xOffset < width; xOffset+=2) { - for (int yOffset = 0; yOffset < height; yOffset+=2) { - region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op); - } - } - - SkPaint paint; - paint.setColor(0xff00ffff); - renderer->drawRegion(region, paint); +} - endRecording(renderer, node.get()); - return node; - } +static const struct option LONG_OPTIONS[] = { + { "frames", required_argument, nullptr, 'f' }, + { "repeat", required_argument, nullptr, 'r' }, + { "help", no_argument, nullptr, 'h' }, + { "list", no_argument, nullptr, 'l' }, + { 0, 0, 0, 0 } }; -class OvalAnimation : public TreeContentAnimation { -public: - sp<RenderNode> card; - void createContent(int width, int height, DisplayListCanvas* renderer) override { - renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); - renderer->insertReorderBarrier(true); - - card = createCard(40, 40, 400, 400); - renderer->drawRenderNode(card.get()); +static const char* SHORT_OPTIONS = "c:r:h"; - renderer->insertReorderBarrier(false); - } +void parseOptions(int argc, char* argv[]) { + int c; + // temporary variable + int count; + bool error = false; + opterr = 0; - void doFrame(int frameNr) override { - int curFrame = frameNr % 150; - card->mutateStagingProperties().setTranslationX(curFrame); - card->mutateStagingProperties().setTranslationY(curFrame); - card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - } -private: - sp<RenderNode> createCard(int x, int y, int width, int height) { - sp<RenderNode> node = new RenderNode(); - node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); - node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + while (true) { - DisplayListCanvas* renderer = startRecording(node.get()); + /* getopt_long stores the option index here. */ + int option_index = 0; - SkPaint paint; - paint.setAntiAlias(true); - paint.setColor(0xFF000000); - renderer->drawOval(0, 0, width, height, paint); + c = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &option_index); - endRecording(renderer, node.get()); - return node; - } -}; + if (c == -1) + break; -class PartialInvalTest : public TreeContentAnimation { -public: - std::vector< sp<RenderNode> > cards; - void createContent(int width, int height, DisplayListCanvas* renderer) override { - static SkColor COLORS[] = { - 0xFFF44336, - 0xFF9C27B0, - 0xFF2196F3, - 0xFF4CAF50, - }; + switch (c) { + case 0: + // Option set a flag, don't need to do anything + // (although none of the current LONG_OPTIONS do this...) + break; - renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + case 'l': + listTests(); + exit(EXIT_SUCCESS); + break; - for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { - for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { - sp<RenderNode> card = createCard(x, y, dp(100), dp(100), - COLORS[static_cast<int>((y / dp(116))) % 4]); - renderer->drawRenderNode(card.get()); - cards.push_back(card); + case 'c': + count = atoi(optarg); + if (!count) { + fprintf(stderr, "Invalid frames argument '%s'\n", optarg); + error = true; + } else { + gFrameCount = (count > 0 ? count : INT_MAX); + } + break; + + case 'r': + count = atoi(optarg); + if (!count) { + fprintf(stderr, "Invalid repeat argument '%s'\n", optarg); + error = true; + } else { + gRepeatCount = (count > 0 ? count : INT_MAX); } + break; + + case 'h': + printHelp(); + exit(EXIT_SUCCESS); + break; + + case '?': + fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]); + // fall-through + default: + error = true; + break; } } - void doFrame(int frameNr) override { - int curFrame = frameNr % 150; - cards[0]->mutateStagingProperties().setTranslationX(curFrame); - cards[0]->mutateStagingProperties().setTranslationY(curFrame); - cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - DisplayListCanvas* renderer = startRecording(cards[0].get()); - renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0), - SkXfermode::kSrcOver_Mode); - endRecording(renderer, cards[0].get()); + if (error) { + fprintf(stderr, "Try 'hwuitest --help' for more information.\n"); + exit(EXIT_FAILURE); } - static SkColor interpolateColor(float fraction, SkColor start, SkColor end) { - int startA = (start >> 24) & 0xff; - int startR = (start >> 16) & 0xff; - int startG = (start >> 8) & 0xff; - int startB = start & 0xff; - - int endA = (end >> 24) & 0xff; - int endR = (end >> 16) & 0xff; - int endG = (end >> 8) & 0xff; - int endB = end & 0xff; - - return (int)((startA + (int)(fraction * (endA - startA))) << 24) | - (int)((startR + (int)(fraction * (endR - startR))) << 16) | - (int)((startG + (int)(fraction * (endG - startG))) << 8) | - (int)((startB + (int)(fraction * (endB - startB)))); - } -private: - sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) { - sp<RenderNode> node = new RenderNode(); - node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); - node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - - DisplayListCanvas* renderer = startRecording(node.get()); - renderer->drawColor(color, SkXfermode::kSrcOver_Mode); - endRecording(renderer, node.get()); - return node; - } -}; - -struct cstr_cmp { - bool operator()(const char *a, const char *b) const { - return std::strcmp(a, b) < 0; + /* Print any remaining command line arguments (not options). */ + if (optind < argc) { + do { + const char* test = argv[optind++]; + auto pos = testMap().find(test); + if (pos == testMap().end()) { + fprintf(stderr, "Unknown test '%s'\n", test); + exit(EXIT_FAILURE); + } else { + gRunTests.push_back(pos->second); + } + } while (optind < argc); + } else { + gRunTests.push_back(testMap()["shadowgrid"]); } -}; - -typedef void (*testProc)(int); - -std::map<const char*, testProc, cstr_cmp> gTestMap { - {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>}, - {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>}, - {"rectgrid", TreeContentAnimation::run<RectGridAnimation> }, - {"oval", TreeContentAnimation::run<OvalAnimation> }, - {"partialinval", TreeContentAnimation::run<PartialInvalTest> }, -}; +} int main(int argc, char* argv[]) { - const char* testName = argc > 1 ? argv[1] : "shadowgrid"; - testProc proc = gTestMap[testName]; - if(!proc) { - printf("Error: couldn't find test %s\n", testName); - return 1; - } - int loopCount = 1; - if (argc > 2) { - loopCount = atoi(argv[2]); - if (!loopCount) { - printf("Invalid loop count!\n"); - return 1; - } - } - int frameCount = 150; - if (argc > 3) { - frameCount = atoi(argv[3]); - if (frameCount < 1) { - printf("Invalid frame count!\n"); - return 1; + parseOptions(argc, argv); + + BenchmarkOptions opts; + opts.count = gFrameCount; + for (int i = 0; i < gRepeatCount; i++) { + for (auto&& test : gRunTests) { + test.functor(opts); } } - if (loopCount < 0) { - loopCount = INT_MAX; - } - for (int i = 0; i < loopCount; i++) { - proc(frameCount); - } printf("Success!\n"); return 0; } diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index 295cb80ade52..737444585296 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -3,7 +3,6 @@ <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" diff --git a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml index 6dcbb38cb950..ab414a9bb6af 100644 --- a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml +++ b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml @@ -16,6 +16,10 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item + android:state_focused="true" + android:color="@color/platform_blue_a200" + android:alpha="0.1" /> + <item android:state_activated="true" android:color="?android:attr/colorAccent" android:alpha="0.1" /> @@ -25,4 +29,4 @@ android:alpha="0.5" /> <item android:color="@android:color/transparent" /> -</selector>
\ No newline at end of file +</selector> diff --git a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml index 7d7a110864d9..90e2b7e653c6 100644 --- a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml +++ b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml @@ -16,6 +16,10 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item + android:state_focused="true" + android:color="@color/platform_blue_a200" + android:alpha="0.1" /> + <item android:state_activated="true" android:color="?android:attr/colorAccent" android:alpha="0.1" /> diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml index ba92c00b7c1f..3daecc758843 100644 --- a/packages/DocumentsUI/res/values-af/strings.xml +++ b/packages/DocumentsUI/res/values-af/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Lêers"</string> <string name="title_open" msgid="4353228937663917801">"Maak oop vanuit"</string> <string name="title_save" msgid="2433679664882857999">"Stoor na"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nuwe vouer"</string> <string name="menu_grid" msgid="6878021334497835259">"Roosteraansig"</string> <string name="menu_list" msgid="7279285939892417279">"Lysaansig"</string> <string name="menu_sort" msgid="7677740407158414452">"Sorteer volgens"</string> diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml index 7fb3b740e1d3..43db786c582f 100644 --- a/packages/DocumentsUI/res/values-am/strings.xml +++ b/packages/DocumentsUI/res/values-am/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ፋይሎች"</string> <string name="title_open" msgid="4353228937663917801">"ክፈት ከ"</string> <string name="title_save" msgid="2433679664882857999">"አስቀምጥ ወደ"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"አዲስ አቃፊ"</string> <string name="menu_grid" msgid="6878021334497835259">"የፍርግርግ እይታ"</string> <string name="menu_list" msgid="7279285939892417279">"የዝርዝር እይታ"</string> <string name="menu_sort" msgid="7677740407158414452">"ደርድር በ"</string> diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml index b61d190ad4dc..88c42d03d1d6 100644 --- a/packages/DocumentsUI/res/values-ar/strings.xml +++ b/packages/DocumentsUI/res/values-ar/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"الملفات"</string> <string name="title_open" msgid="4353228937663917801">"فتح من"</string> <string name="title_save" msgid="2433679664882857999">"حفظ في"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"مجلد جديد"</string> <string name="menu_grid" msgid="6878021334497835259">"عرض الشبكة"</string> <string name="menu_list" msgid="7279285939892417279">"عرض القائمة"</string> <string name="menu_sort" msgid="7677740407158414452">"ترتيب بحسب"</string> diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml index dc3827ee8e98..f91f2ee5a782 100644 --- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml +++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Fayllar"</string> <string name="title_open" msgid="4353228937663917801">"Vasitəsilə açın"</string> <string name="title_save" msgid="2433679664882857999">"buraya saxlayın"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Yeni qovluq"</string> <string name="menu_grid" msgid="6878021334497835259">"Torlu görünüş"</string> <string name="menu_list" msgid="7279285939892417279">"Siyahı görünüşü"</string> <string name="menu_sort" msgid="7677740407158414452">"Bunlardan biri üzrə sırala"</string> diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml index e8c5822a7308..67a1c6a759c5 100644 --- a/packages/DocumentsUI/res/values-bg/strings.xml +++ b/packages/DocumentsUI/res/values-bg/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Файлове"</string> <string name="title_open" msgid="4353228937663917801">"Отваряне от"</string> <string name="title_save" msgid="2433679664882857999">"Запазване във:"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Нова папка"</string> <string name="menu_grid" msgid="6878021334497835259">"Изглед в мрежа"</string> <string name="menu_list" msgid="7279285939892417279">"Списъчен изглед"</string> <string name="menu_sort" msgid="7677740407158414452">"Сортиране по"</string> diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml index dc20b16ae1cc..b01cc84e5ce5 100644 --- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml +++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ফাইলগুলি"</string> <string name="title_open" msgid="4353228937663917801">"এখান থেকে খুলুন"</string> <string name="title_save" msgid="2433679664882857999">"এতে সংরক্ষণ করুন"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"নতুন ফোল্ডার"</string> <string name="menu_grid" msgid="6878021334497835259">"গ্রিড দৃশ্য"</string> <string name="menu_list" msgid="7279285939892417279">"তালিকা দৃশ্য"</string> <string name="menu_sort" msgid="7677740407158414452">"এই অনুসারে বাছুন"</string> diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml index ab90f1fb4a53..663608ba832f 100644 --- a/packages/DocumentsUI/res/values-ca/strings.xml +++ b/packages/DocumentsUI/res/values-ca/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Fitxers"</string> <string name="title_open" msgid="4353228937663917801">"Obre des de"</string> <string name="title_save" msgid="2433679664882857999">"Desa a"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Carpeta nova"</string> <string name="menu_grid" msgid="6878021334497835259">"Visualització de quadrícula"</string> <string name="menu_list" msgid="7279285939892417279">"Visualització de llista"</string> <string name="menu_sort" msgid="7677740407158414452">"Ordena per"</string> diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml index 9b1b0c8c52f8..81d8dc8caa48 100644 --- a/packages/DocumentsUI/res/values-cs/strings.xml +++ b/packages/DocumentsUI/res/values-cs/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Soubory"</string> <string name="title_open" msgid="4353228937663917801">"Otevřít"</string> <string name="title_save" msgid="2433679664882857999">"Uložit do"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nová složka"</string> <string name="menu_grid" msgid="6878021334497835259">"Mřížkové zobrazení"</string> <string name="menu_list" msgid="7279285939892417279">"Zobrazení seznamu"</string> <string name="menu_sort" msgid="7677740407158414452">"Řadit podle"</string> diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml index 284d87e200c6..e10904847a9f 100644 --- a/packages/DocumentsUI/res/values-da/strings.xml +++ b/packages/DocumentsUI/res/values-da/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Filer"</string> <string name="title_open" msgid="4353228937663917801">"Åbn fra"</string> <string name="title_save" msgid="2433679664882857999">"Gem i"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Ny mappe"</string> <string name="menu_grid" msgid="6878021334497835259">"Gittervisning"</string> <string name="menu_list" msgid="7279285939892417279">"Listevisning"</string> <string name="menu_sort" msgid="7677740407158414452">"Sortér efter"</string> diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml index 1fe53ff88667..7c467ef10d39 100644 --- a/packages/DocumentsUI/res/values-de/strings.xml +++ b/packages/DocumentsUI/res/values-de/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Dateien"</string> <string name="title_open" msgid="4353228937663917801">"Öffnen von"</string> <string name="title_save" msgid="2433679664882857999">"Speichern unter"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Neuer Ordner"</string> <string name="menu_grid" msgid="6878021334497835259">"Rasteransicht"</string> <string name="menu_list" msgid="7279285939892417279">"Listenansicht"</string> <string name="menu_sort" msgid="7677740407158414452">"Sortieren nach"</string> diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml index e9743eee88bc..a350002074a6 100644 --- a/packages/DocumentsUI/res/values-el/strings.xml +++ b/packages/DocumentsUI/res/values-el/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Αρχεία"</string> <string name="title_open" msgid="4353228937663917801">"Άνοιγμα από"</string> <string name="title_save" msgid="2433679664882857999">"Αποθήκευση σε"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Νέος φάκελος"</string> <string name="menu_grid" msgid="6878021334497835259">"Προβολή πλέγματος"</string> <string name="menu_list" msgid="7279285939892417279">"Προβολή λίστας"</string> <string name="menu_sort" msgid="7677740407158414452">"Ταξινόμηση κατά"</string> diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml index 03399e263efd..e9ba34b47e04 100644 --- a/packages/DocumentsUI/res/values-en-rAU/strings.xml +++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Files"</string> <string name="title_open" msgid="4353228937663917801">"Open from"</string> <string name="title_save" msgid="2433679664882857999">"Save to"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"New folder"</string> <string name="menu_grid" msgid="6878021334497835259">"Grid view"</string> <string name="menu_list" msgid="7279285939892417279">"List view"</string> <string name="menu_sort" msgid="7677740407158414452">"Sort by"</string> diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml index 03399e263efd..e9ba34b47e04 100644 --- a/packages/DocumentsUI/res/values-en-rGB/strings.xml +++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Files"</string> <string name="title_open" msgid="4353228937663917801">"Open from"</string> <string name="title_save" msgid="2433679664882857999">"Save to"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"New folder"</string> <string name="menu_grid" msgid="6878021334497835259">"Grid view"</string> <string name="menu_list" msgid="7279285939892417279">"List view"</string> <string name="menu_sort" msgid="7677740407158414452">"Sort by"</string> diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml index 03399e263efd..e9ba34b47e04 100644 --- a/packages/DocumentsUI/res/values-en-rIN/strings.xml +++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Files"</string> <string name="title_open" msgid="4353228937663917801">"Open from"</string> <string name="title_save" msgid="2433679664882857999">"Save to"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"New folder"</string> <string name="menu_grid" msgid="6878021334497835259">"Grid view"</string> <string name="menu_list" msgid="7279285939892417279">"List view"</string> <string name="menu_sort" msgid="7677740407158414452">"Sort by"</string> diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml index cc99d7f0e94d..8e0a18cc633d 100644 --- a/packages/DocumentsUI/res/values-es-rUS/strings.xml +++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Archivos"</string> <string name="title_open" msgid="4353228937663917801">"Abrir desde"</string> <string name="title_save" msgid="2433679664882857999">"Guardar en"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Carpeta nueva"</string> <string name="menu_grid" msgid="6878021334497835259">"Vista de cuadrícula"</string> <string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string> <string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string> diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml index 83403e074ebd..cf0b607a547b 100644 --- a/packages/DocumentsUI/res/values-es/strings.xml +++ b/packages/DocumentsUI/res/values-es/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Archivos"</string> <string name="title_open" msgid="4353228937663917801">"Abrir desde"</string> <string name="title_save" msgid="2433679664882857999">"Guardar en"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nueva carpeta"</string> <string name="menu_grid" msgid="6878021334497835259">"Vista de cuadrícula"</string> <string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string> <string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string> diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml index b38fcd99cf3e..76d1f48e5a06 100644 --- a/packages/DocumentsUI/res/values-et-rEE/strings.xml +++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Failid"</string> <string name="title_open" msgid="4353228937663917801">"Ava:"</string> <string name="title_save" msgid="2433679664882857999">"Salvesta:"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Uus kaust"</string> <string name="menu_grid" msgid="6878021334497835259">"Ruudustikkuva"</string> <string name="menu_list" msgid="7279285939892417279">"Loendikuva"</string> <string name="menu_sort" msgid="7677740407158414452">"Sortimisalus:"</string> diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml index 9939b1d40255..f24cf6027bee 100644 --- a/packages/DocumentsUI/res/values-eu-rES/strings.xml +++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Fitxategiak"</string> <string name="title_open" msgid="4353228937663917801">"Ireki hemendik"</string> <string name="title_save" msgid="2433679664882857999">"Gorde hemen"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Karpeta berria"</string> <string name="menu_grid" msgid="6878021334497835259">"Sareta-ikuspegia"</string> <string name="menu_list" msgid="7279285939892417279">"Zerrenda-ikuspegia"</string> <string name="menu_sort" msgid="7677740407158414452">"Ordenatzeko irizpidea"</string> diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml index 297e4a16fea9..b504db53b779 100644 --- a/packages/DocumentsUI/res/values-fa/strings.xml +++ b/packages/DocumentsUI/res/values-fa/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"فایلها"</string> <string name="title_open" msgid="4353228937663917801">"باز کردن از"</string> <string name="title_save" msgid="2433679664882857999">"ذخیره در"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"پوشه جدید"</string> <string name="menu_grid" msgid="6878021334497835259">"نمای جدولی"</string> <string name="menu_list" msgid="7279285939892417279">"نمای فهرستی"</string> <string name="menu_sort" msgid="7677740407158414452">"مرتبسازی براساس"</string> diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml index a4c742a20489..33f2e3db54fb 100644 --- a/packages/DocumentsUI/res/values-fi/strings.xml +++ b/packages/DocumentsUI/res/values-fi/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Tiedostot"</string> <string name="title_open" msgid="4353228937663917801">"Avaa sijainnista"</string> <string name="title_save" msgid="2433679664882857999">"Tallenna kohteeseen"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Uusi kansio"</string> <string name="menu_grid" msgid="6878021334497835259">"Ruudukkonäkymä"</string> <string name="menu_list" msgid="7279285939892417279">"Luettelonäkymä"</string> <string name="menu_sort" msgid="7677740407158414452">"Lajitteluperuste"</string> diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml index 80cd0b212f29..228dc7a89555 100644 --- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml +++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Fichiers"</string> <string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string> <string name="title_save" msgid="2433679664882857999">"Enregistrer dans"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nouveau dossier"</string> <string name="menu_grid" msgid="6878021334497835259">"Grille"</string> <string name="menu_list" msgid="7279285939892417279">"Liste"</string> <string name="menu_sort" msgid="7677740407158414452">"Trier par"</string> diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml index 9616f634348c..eb9f84ae937e 100644 --- a/packages/DocumentsUI/res/values-fr/strings.xml +++ b/packages/DocumentsUI/res/values-fr/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Fichiers"</string> <string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string> <string name="title_save" msgid="2433679664882857999">"Enregistrer sous"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nouveau dossier"</string> <string name="menu_grid" msgid="6878021334497835259">"Grille"</string> <string name="menu_list" msgid="7279285939892417279">"Liste"</string> <string name="menu_sort" msgid="7677740407158414452">"Trier par"</string> diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml index aebf3a7f6182..07c5d3995a37 100644 --- a/packages/DocumentsUI/res/values-gl-rES/strings.xml +++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Ficheiros"</string> <string name="title_open" msgid="4353228937663917801">"Abrir desde"</string> <string name="title_save" msgid="2433679664882857999">"Gardar en"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Novo cartafol"</string> <string name="menu_grid" msgid="6878021334497835259">"Vista de grade"</string> <string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string> <string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string> diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml index 9694afa56de5..dade7dff8492 100644 --- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml +++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ફાઇલો"</string> <string name="title_open" msgid="4353228937663917801">"અહીંથી ખોલો"</string> <string name="title_save" msgid="2433679664882857999">"આમાં સાચવો"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"નવું ફોલ્ડર"</string> <string name="menu_grid" msgid="6878021334497835259">"ગ્રિડ દૃશ્ય"</string> <string name="menu_list" msgid="7279285939892417279">"સૂચિ દૃશ્ય"</string> <string name="menu_sort" msgid="7677740407158414452">"આ પ્રમાણે સૉર્ટ કરો"</string> diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml index 3ee3d8b821ff..0a5de169b5fc 100644 --- a/packages/DocumentsUI/res/values-hi/strings.xml +++ b/packages/DocumentsUI/res/values-hi/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"फ़ाइलें"</string> <string name="title_open" msgid="4353228937663917801">"यहां से खोलें"</string> <string name="title_save" msgid="2433679664882857999">"यहां सहेजें"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"नया फ़ोल्डर"</string> <string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string> <string name="menu_list" msgid="7279285939892417279">"सूची दृश्य"</string> <string name="menu_sort" msgid="7677740407158414452">"इससे क्रमित करें"</string> diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml index 9d4393ee524f..78c1485c7b6d 100644 --- a/packages/DocumentsUI/res/values-hr/strings.xml +++ b/packages/DocumentsUI/res/values-hr/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Datoteke"</string> <string name="title_open" msgid="4353228937663917801">"Otvori iz"</string> <string name="title_save" msgid="2433679664882857999">"Spremi u"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nova mapa"</string> <string name="menu_grid" msgid="6878021334497835259">"Prikaz rešetke"</string> <string name="menu_list" msgid="7279285939892417279">"Prikaz popisa"</string> <string name="menu_sort" msgid="7677740407158414452">"Poredano po"</string> diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml index 18dcd70b322e..49481161d2e1 100644 --- a/packages/DocumentsUI/res/values-hu/strings.xml +++ b/packages/DocumentsUI/res/values-hu/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Fájlok"</string> <string name="title_open" msgid="4353228937663917801">"Megnyitás innen"</string> <string name="title_save" msgid="2433679664882857999">"Mentés ide"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Új mappa"</string> <string name="menu_grid" msgid="6878021334497835259">"Rács"</string> <string name="menu_list" msgid="7279285939892417279">"Lista"</string> <string name="menu_sort" msgid="7677740407158414452">"Rendezés"</string> diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml index 67d7f6a3ea7a..6dc66b057f1a 100644 --- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml +++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Ֆայլեր"</string> <string name="title_open" msgid="4353228937663917801">"Բացել այստեղից"</string> <string name="title_save" msgid="2433679664882857999">"Պահել այստեղ"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Նոր պանակ"</string> <string name="menu_grid" msgid="6878021334497835259">"Ցանցի տեսքով"</string> <string name="menu_list" msgid="7279285939892417279">"Ցուցակի տեսք"</string> <string name="menu_sort" msgid="7677740407158414452">"Դասավորել ըստ"</string> diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml index a6f69bdf07b8..241248848981 100644 --- a/packages/DocumentsUI/res/values-in/strings.xml +++ b/packages/DocumentsUI/res/values-in/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"File"</string> <string name="title_open" msgid="4353228937663917801">"Buka dari"</string> <string name="title_save" msgid="2433679664882857999">"Simpan ke"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Folder baru"</string> <string name="menu_grid" msgid="6878021334497835259">"Tampilan kisi"</string> <string name="menu_list" msgid="7279285939892417279">"Tampilan daftar"</string> <string name="menu_sort" msgid="7677740407158414452">"Urutkan menurut"</string> diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml index 89d2e03824f4..d09a44bda2d2 100644 --- a/packages/DocumentsUI/res/values-is-rIS/strings.xml +++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Skrár"</string> <string name="title_open" msgid="4353228937663917801">"Opna frá"</string> <string name="title_save" msgid="2433679664882857999">"Vista í"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Ný mappa"</string> <string name="menu_grid" msgid="6878021334497835259">"Töfluyfirlit"</string> <string name="menu_list" msgid="7279285939892417279">"Listayfirlit"</string> <string name="menu_sort" msgid="7677740407158414452">"Raða eftir"</string> diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml index 02a111ef0799..48242055f5e2 100644 --- a/packages/DocumentsUI/res/values-it/strings.xml +++ b/packages/DocumentsUI/res/values-it/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"File"</string> <string name="title_open" msgid="4353228937663917801">"Apri da"</string> <string name="title_save" msgid="2433679664882857999">"Salva in"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nuova cartella"</string> <string name="menu_grid" msgid="6878021334497835259">"Visualizzazione griglia"</string> <string name="menu_list" msgid="7279285939892417279">"Visualizzazione elenco"</string> <string name="menu_sort" msgid="7677740407158414452">"Ordina per"</string> diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml index 3d3c48989138..1a1951029aac 100644 --- a/packages/DocumentsUI/res/values-iw/strings.xml +++ b/packages/DocumentsUI/res/values-iw/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"קבצים"</string> <string name="title_open" msgid="4353228937663917801">"פתח מ-"</string> <string name="title_save" msgid="2433679664882857999">"שמור ב-"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"תיקייה חדשה"</string> <string name="menu_grid" msgid="6878021334497835259">"תצוגת רשת"</string> <string name="menu_list" msgid="7279285939892417279">"תצוגת רשימה"</string> <string name="menu_sort" msgid="7677740407158414452">"מיין לפי"</string> diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml index a58c0a524256..7e356af51ccf 100644 --- a/packages/DocumentsUI/res/values-ja/strings.xml +++ b/packages/DocumentsUI/res/values-ja/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ファイル"</string> <string name="title_open" msgid="4353228937663917801">"次から開く:"</string> <string name="title_save" msgid="2433679664882857999">"次に保存:"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"新しいフォルダ"</string> <string name="menu_grid" msgid="6878021334497835259">"グリッド表示"</string> <string name="menu_list" msgid="7279285939892417279">"リスト表示"</string> <string name="menu_sort" msgid="7677740407158414452">"並べ替え"</string> diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml index cf4b5d64c3d6..6e880aa9453f 100644 --- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml +++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ფაილები"</string> <string name="title_open" msgid="4353228937663917801">"გახსნა აქედან:"</string> <string name="title_save" msgid="2433679664882857999">"შენახვა აქ:"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"ახალი საქაღალდე"</string> <string name="menu_grid" msgid="6878021334497835259">"ბადის ხედი"</string> <string name="menu_list" msgid="7279285939892417279">"სიის ხედი"</string> <string name="menu_sort" msgid="7677740407158414452">"სორტირება:"</string> diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml index 5ec16a9ee729..0d553e0c6843 100644 --- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml +++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Файлдар"</string> <string name="title_open" msgid="4353228937663917801">"Мынадан ашу:"</string> <string name="title_save" msgid="2433679664882857999">"Сақталатын орны"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Жаңа қалта"</string> <string name="menu_grid" msgid="6878021334497835259">"Торлы көрініс"</string> <string name="menu_list" msgid="7279285939892417279">"Тізім көрінісі"</string> <string name="menu_sort" msgid="7677740407158414452">"Белгіге қарай сұрыптау"</string> diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml index 73778823b708..46808794fb66 100644 --- a/packages/DocumentsUI/res/values-km-rKH/strings.xml +++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ឯកសារ"</string> <string name="title_open" msgid="4353228937663917801">"បើកពី"</string> <string name="title_save" msgid="2433679664882857999">"រក្សាទុកទៅ"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"ថតថ្មី"</string> <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាពក្រឡា"</string> <string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាពបញ្ជី"</string> <string name="menu_sort" msgid="7677740407158414452">"តម្រៀបតាម"</string> diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml index a5cad7bd1003..7d5770abd768 100644 --- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml +++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ಫೈಲ್ಗಳು"</string> <string name="title_open" msgid="4353228937663917801">"ಇದರ ಮೂಲಕ ತೆರೆಯಿರಿ"</string> <string name="title_save" msgid="2433679664882857999">"ಇವುಗಳಲ್ಲಿ ಉಳಿಸಿ"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"ಹೊಸ ಫೋಲ್ಡರ್"</string> <string name="menu_grid" msgid="6878021334497835259">"ಗ್ರಿಡ್ ವೀಕ್ಷಣೆ"</string> <string name="menu_list" msgid="7279285939892417279">"ಪಟ್ಟಿ ವೀಕ್ಷಣೆ"</string> <string name="menu_sort" msgid="7677740407158414452">"ಈ ಪ್ರಕಾರ ವಿಂಗಡಿಸು"</string> diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml index 1426bf8225cb..3032eede81ec 100644 --- a/packages/DocumentsUI/res/values-ko/strings.xml +++ b/packages/DocumentsUI/res/values-ko/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"파일"</string> <string name="title_open" msgid="4353228937663917801">"열기:"</string> <string name="title_save" msgid="2433679664882857999">"저장 위치:"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"새 폴더"</string> <string name="menu_grid" msgid="6878021334497835259">"바둑판식 보기"</string> <string name="menu_list" msgid="7279285939892417279">"목록 보기"</string> <string name="menu_sort" msgid="7677740407158414452">"정렬 기준"</string> diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml index c1eadb76f682..14a25bcfc377 100644 --- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml +++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Файлдар"</string> <string name="title_open" msgid="4353228937663917801">"Кийинкиден ачуу:"</string> <string name="title_save" msgid="2433679664882857999">"Кийинкиге сактоо:"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Жаңы куржун"</string> <string name="menu_grid" msgid="6878021334497835259">"Тор көрүнүшү"</string> <string name="menu_list" msgid="7279285939892417279">"Тизмек көрүнүшү"</string> <string name="menu_sort" msgid="7677740407158414452">"Ылгоо"</string> diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml index 5c0ffdb6e401..2a680bf50f7d 100644 --- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml +++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ໄຟລ໌"</string> <string name="title_open" msgid="4353228937663917801">"ເປີດຈາກ"</string> <string name="title_save" msgid="2433679664882857999">"ບັນທຶກໄປທີ່"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"ໂຟລເດີໃໝ່"</string> <string name="menu_grid" msgid="6878021334497835259">"ມຸມມອງແບບຊ່ອງ"</string> <string name="menu_list" msgid="7279285939892417279">"ມຸມມອງແບບລາຍຊື່"</string> <string name="menu_sort" msgid="7677740407158414452">"ຮຽງລຳດັບຕາມ"</string> diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml index 49774e8c1366..8105846f605d 100644 --- a/packages/DocumentsUI/res/values-lt/strings.xml +++ b/packages/DocumentsUI/res/values-lt/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Failai"</string> <string name="title_open" msgid="4353228937663917801">"Atidaryti iš"</string> <string name="title_save" msgid="2433679664882857999">"Išsaugoti į"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Naujas aplankas"</string> <string name="menu_grid" msgid="6878021334497835259">"Tinklelio rodinys"</string> <string name="menu_list" msgid="7279285939892417279">"Sąrašo rodinys"</string> <string name="menu_sort" msgid="7677740407158414452">"Rūšiuoti pagal"</string> diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml index 9cb34250d2c7..48715e6142c7 100644 --- a/packages/DocumentsUI/res/values-lv/strings.xml +++ b/packages/DocumentsUI/res/values-lv/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Faili"</string> <string name="title_open" msgid="4353228937663917801">"Atvēršana no:"</string> <string name="title_save" msgid="2433679664882857999">"Saglabāšana:"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Jauna mape"</string> <string name="menu_grid" msgid="6878021334497835259">"Režģis"</string> <string name="menu_list" msgid="7279285939892417279">"Saraksts"</string> <string name="menu_sort" msgid="7677740407158414452">"Kārtot pēc"</string> diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml index 149d0d43ee9a..950a9b1c7c07 100644 --- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml +++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Датотеки"</string> <string name="title_open" msgid="4353228937663917801">"Отвори од"</string> <string name="title_save" msgid="2433679664882857999">"Зачувај во"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Нова папка"</string> <string name="menu_grid" msgid="6878021334497835259">"Приказ на мрежа"</string> <string name="menu_list" msgid="7279285939892417279">"Приказ на список"</string> <string name="menu_sort" msgid="7677740407158414452">"Подреди по"</string> diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml index c621359a93d0..32411b2097a8 100644 --- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml +++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ഫയലുകൾ"</string> <string name="title_open" msgid="4353228937663917801">"ഇതിൽ നിന്നും തുറക്കുക"</string> <string name="title_save" msgid="2433679664882857999">"ഇതില് സംരക്ഷിക്കുക"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"പുതിയ ഫോൾഡർ"</string> <string name="menu_grid" msgid="6878021334497835259">"ഗ്രിഡ് കാഴ്ച"</string> <string name="menu_list" msgid="7279285939892417279">"ലിസ്റ്റ് കാഴ്ച"</string> <string name="menu_sort" msgid="7677740407158414452">"ഇപ്രകാരം അടുക്കുക"</string> diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml index fb7503e87bd0..0c5c72db314a 100644 --- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml +++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Файл"</string> <string name="title_open" msgid="4353228937663917801">"Нээх"</string> <string name="title_save" msgid="2433679664882857999">"Хадгалах"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Шинэ фолдер"</string> <string name="menu_grid" msgid="6878021334497835259">"Эгнүүлж харах"</string> <string name="menu_list" msgid="7279285939892417279">"Жагсааж харах"</string> <string name="menu_sort" msgid="7677740407158414452">"Эрэмбэлэх"</string> diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml index 8d5885a7466f..15c1291d9205 100644 --- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml +++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"फायली"</string> <string name="title_open" msgid="4353228937663917801">"वरून उघडा"</string> <string name="title_save" msgid="2433679664882857999">"येथे जतन करा"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"नवीन फोल्डर"</string> <string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string> <string name="menu_list" msgid="7279285939892417279">"सूची"</string> <string name="menu_sort" msgid="7677740407158414452">"नुसार क्रमवारी लावा"</string> diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml index 6904e1074b6a..12cf2e3f0472 100644 --- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml +++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Fail"</string> <string name="title_open" msgid="4353228937663917801">"Buka dari"</string> <string name="title_save" msgid="2433679664882857999">"Simpan ke"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Folder baharu"</string> <string name="menu_grid" msgid="6878021334497835259">"Paparan grid"</string> <string name="menu_list" msgid="7279285939892417279">"Paparan senarai"</string> <string name="menu_sort" msgid="7677740407158414452">"Isih mengikut"</string> diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml index f3ed0f7c2823..a1ab012c91ff 100644 --- a/packages/DocumentsUI/res/values-my-rMM/strings.xml +++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ဖိုင်များ"</string> <string name="title_open" msgid="4353228937663917801">"မှ ဖွင့်ပါ"</string> <string name="title_save" msgid="2433679664882857999">"သို့ သိမ်းပါ"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"ဖိုလ်ဒါ အသစ်"</string> <string name="menu_grid" msgid="6878021334497835259">"ဖယားကွက်မြင်ကွင်း"</string> <string name="menu_list" msgid="7279285939892417279">"အစဉ်လိုက်မြင်ကွင်း"</string> <string name="menu_sort" msgid="7677740407158414452">"အစဉ်အလိုက် စီခြင်း"</string> diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml index 5f71805efb07..edeaa6d0f1e5 100644 --- a/packages/DocumentsUI/res/values-nb/strings.xml +++ b/packages/DocumentsUI/res/values-nb/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Filer"</string> <string name="title_open" msgid="4353228937663917801">"Åpne fra"</string> <string name="title_save" msgid="2433679664882857999">"Lagre i"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Ny mappe"</string> <string name="menu_grid" msgid="6878021334497835259">"Rutenettvisning"</string> <string name="menu_list" msgid="7279285939892417279">"Listevisning"</string> <string name="menu_sort" msgid="7677740407158414452">"Sortér etter"</string> diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml index 1bc6a292fe6a..bd5421155862 100644 --- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml +++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"फाइलहरू"</string> <string name="title_open" msgid="4353228937663917801">"यसबाट खोल्नुहोस्"</string> <string name="title_save" msgid="2433679664882857999">"यसमा सुरक्षित गर्नुहोस्"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"नयाँ फोल्डर"</string> <string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string> <string name="menu_list" msgid="7279285939892417279">"सूची दृश्य"</string> <string name="menu_sort" msgid="7677740407158414452">"यसद्वारा क्रमवद्घ गर्नुहोस्"</string> diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml index 79a6de9fd863..f2eda72d5450 100644 --- a/packages/DocumentsUI/res/values-nl/strings.xml +++ b/packages/DocumentsUI/res/values-nl/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Bestanden"</string> <string name="title_open" msgid="4353228937663917801">"Openen vanuit"</string> <string name="title_save" msgid="2433679664882857999">"Opslaan in"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nieuwe map"</string> <string name="menu_grid" msgid="6878021334497835259">"Rasterweergave"</string> <string name="menu_list" msgid="7279285939892417279">"Lijstweergave"</string> <string name="menu_sort" msgid="7677740407158414452">"Sorteren op"</string> diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml index c4fe4e4f2789..e139dfdcaa12 100644 --- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml +++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ਫਾਈਲਾਂ"</string> <string name="title_open" msgid="4353228937663917801">"ਤੋਂ ਖੋਲ੍ਹੋ"</string> <string name="title_save" msgid="2433679664882857999">"ਇਸ ਵਿੱਚ ਸੁਰੱਖਿਅਤ ਕਰੋ"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"ਨਵਾਂ ਫੋਲਡਰ"</string> <string name="menu_grid" msgid="6878021334497835259">"ਗ੍ਰਿਡ ਵਿਊ"</string> <string name="menu_list" msgid="7279285939892417279">"ਸੂਚੀ ਦ੍ਰਿਸ਼"</string> <string name="menu_sort" msgid="7677740407158414452">"ਇਸ ਅਨੁਸਾਰ ਛਾਂਟੋ"</string> diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml index 30b0806574ea..bb81377e5ec0 100644 --- a/packages/DocumentsUI/res/values-pl/strings.xml +++ b/packages/DocumentsUI/res/values-pl/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Pliki"</string> <string name="title_open" msgid="4353228937663917801">"Otwórz z"</string> <string name="title_save" msgid="2433679664882857999">"Zapisz w"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nowy folder"</string> <string name="menu_grid" msgid="6878021334497835259">"Widok siatki"</string> <string name="menu_list" msgid="7279285939892417279">"Widok listy"</string> <string name="menu_sort" msgid="7677740407158414452">"Sortuj według"</string> diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml index 0233e3d7abd5..d72f0c3e585d 100644 --- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml +++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Arquivos"</string> <string name="title_open" msgid="4353228937663917801">"Abrir de"</string> <string name="title_save" msgid="2433679664882857999">"Salvar em"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nova pasta"</string> <string name="menu_grid" msgid="6878021334497835259">"Visualização em grade"</string> <string name="menu_list" msgid="7279285939892417279">"Visualização em lista"</string> <string name="menu_sort" msgid="7677740407158414452">"Classificar por"</string> diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml index 0c41a9152ada..43265a52c6f6 100644 --- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml +++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Ficheiros"</string> <string name="title_open" msgid="4353228937663917801">"Abrir de"</string> <string name="title_save" msgid="2433679664882857999">"Guardar em"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nova pasta"</string> <string name="menu_grid" msgid="6878021334497835259">"Vista de grelha"</string> <string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string> <string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string> diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml index 0233e3d7abd5..d72f0c3e585d 100644 --- a/packages/DocumentsUI/res/values-pt/strings.xml +++ b/packages/DocumentsUI/res/values-pt/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Arquivos"</string> <string name="title_open" msgid="4353228937663917801">"Abrir de"</string> <string name="title_save" msgid="2433679664882857999">"Salvar em"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nova pasta"</string> <string name="menu_grid" msgid="6878021334497835259">"Visualização em grade"</string> <string name="menu_list" msgid="7279285939892417279">"Visualização em lista"</string> <string name="menu_sort" msgid="7677740407158414452">"Classificar por"</string> diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml index 27734d574351..e880bbd5bb5c 100644 --- a/packages/DocumentsUI/res/values-ro/strings.xml +++ b/packages/DocumentsUI/res/values-ro/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Fișiere"</string> <string name="title_open" msgid="4353228937663917801">"Deschideți din"</string> <string name="title_save" msgid="2433679664882857999">"Salvați în"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Dosar nou"</string> <string name="menu_grid" msgid="6878021334497835259">"Afișare tip grilă"</string> <string name="menu_list" msgid="7279285939892417279">"Afișare tip listă"</string> <string name="menu_sort" msgid="7677740407158414452">"Sortați după"</string> diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml index 152992e2d13a..e4755912a6c6 100644 --- a/packages/DocumentsUI/res/values-ru/strings.xml +++ b/packages/DocumentsUI/res/values-ru/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Файлы"</string> <string name="title_open" msgid="4353228937663917801">"Открыть"</string> <string name="title_save" msgid="2433679664882857999">"Сохранить"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Создать папку"</string> <string name="menu_grid" msgid="6878021334497835259">"Сетка"</string> <string name="menu_list" msgid="7279285939892417279">"Список"</string> <string name="menu_sort" msgid="7677740407158414452">"Сортировать"</string> diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml index a8165a1a91a0..e380b0a88383 100644 --- a/packages/DocumentsUI/res/values-si-rLK/strings.xml +++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ගොනු"</string> <string name="title_open" msgid="4353228937663917801">"විවෘත වන්නේ"</string> <string name="title_save" msgid="2433679664882857999">"සුරකින්නේ"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"නව ෆෝල්ඩරය"</string> <string name="menu_grid" msgid="6878021334497835259">"ජාල පෙනුම"</string> <string name="menu_list" msgid="7279285939892417279">"ලැයිස්තු පෙනුම"</string> <string name="menu_sort" msgid="7677740407158414452">"අනුපිළිවෙලට සකසා ඇත්තේ"</string> diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml index 3441f26dad07..6614dcab68c0 100644 --- a/packages/DocumentsUI/res/values-sk/strings.xml +++ b/packages/DocumentsUI/res/values-sk/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Súbory"</string> <string name="title_open" msgid="4353228937663917801">"Otvoriť z"</string> <string name="title_save" msgid="2433679664882857999">"Uložiť do"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nový priečinok"</string> <string name="menu_grid" msgid="6878021334497835259">"Zobrazenie mriežky"</string> <string name="menu_list" msgid="7279285939892417279">"Zobrazenie zoznamu"</string> <string name="menu_sort" msgid="7677740407158414452">"Zoradiť podľa"</string> diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml index fe366641e783..58a4cc046081 100644 --- a/packages/DocumentsUI/res/values-sl/strings.xml +++ b/packages/DocumentsUI/res/values-sl/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Datoteke"</string> <string name="title_open" msgid="4353228937663917801">"Odpri iz mape"</string> <string name="title_save" msgid="2433679664882857999">"Shrani v"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Nova mapa"</string> <string name="menu_grid" msgid="6878021334497835259">"Mrežni pogled"</string> <string name="menu_list" msgid="7279285939892417279">"Pogled seznama"</string> <string name="menu_sort" msgid="7677740407158414452">"Razvrsti glede na"</string> diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml index 18794f589910..7eba92f087e4 100644 --- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml +++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Skedarët"</string> <string name="title_open" msgid="4353228937663917801">"Hap nga"</string> <string name="title_save" msgid="2433679664882857999">"Ruaje te"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Dosje e re"</string> <string name="menu_grid" msgid="6878021334497835259">"Pamje rrjete"</string> <string name="menu_list" msgid="7279285939892417279">"Pamje liste"</string> <string name="menu_sort" msgid="7677740407158414452">"Rendit sipas"</string> diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml index fa047f250b00..0bce68772994 100644 --- a/packages/DocumentsUI/res/values-sr/strings.xml +++ b/packages/DocumentsUI/res/values-sr/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Датотеке"</string> <string name="title_open" msgid="4353228937663917801">"Отвори са"</string> <string name="title_save" msgid="2433679664882857999">"Сачувај у"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Нови директоријум"</string> <string name="menu_grid" msgid="6878021334497835259">"Приказ мреже"</string> <string name="menu_list" msgid="7279285939892417279">"Приказ листе"</string> <string name="menu_sort" msgid="7677740407158414452">"Сортирај према"</string> diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml index 5ac869179591..a4a119c3fad5 100644 --- a/packages/DocumentsUI/res/values-sv/strings.xml +++ b/packages/DocumentsUI/res/values-sv/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Filer"</string> <string name="title_open" msgid="4353228937663917801">"Öppna från"</string> <string name="title_save" msgid="2433679664882857999">"Spara till"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Ny mapp"</string> <string name="menu_grid" msgid="6878021334497835259">"Rutnätsvy"</string> <string name="menu_list" msgid="7279285939892417279">"Listvy"</string> <string name="menu_sort" msgid="7677740407158414452">"Sortera efter"</string> diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml index 3daa18e66892..91151fbe99e2 100644 --- a/packages/DocumentsUI/res/values-sw/strings.xml +++ b/packages/DocumentsUI/res/values-sw/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Faili"</string> <string name="title_open" msgid="4353228937663917801">"Fungua kutoka"</string> <string name="title_save" msgid="2433679664882857999">"Hifadhi kwenye"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Folda mpya"</string> <string name="menu_grid" msgid="6878021334497835259">"Mwonekano gridi"</string> <string name="menu_list" msgid="7279285939892417279">"Mwonekano orodha"</string> <string name="menu_sort" msgid="7677740407158414452">"Panga kwa"</string> diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml index f4bc88ec463c..d415a849d33f 100644 --- a/packages/DocumentsUI/res/values-sw720dp/styles.xml +++ b/packages/DocumentsUI/res/values-sw720dp/styles.xml @@ -16,7 +16,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="DocumentsBaseTheme" parent="@*android:style/Theme.Material.DayNight.Dialog"> + <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Dialog"> <!-- We do not specify width of window here because the max size of floating window specified by windowFixedWidthis is limited. --> <item name="*android:windowFixedHeightMajor">80%</item> diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml index 14fa7020bd25..9a667b846b23 100644 --- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml +++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"கோப்புகள்"</string> <string name="title_open" msgid="4353228937663917801">"இதில் திற"</string> <string name="title_save" msgid="2433679664882857999">"இதில் சேமி"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"புதிய கோப்புறை"</string> <string name="menu_grid" msgid="6878021334497835259">"கட்டக் காட்சி"</string> <string name="menu_list" msgid="7279285939892417279">"பட்டியல்"</string> <string name="menu_sort" msgid="7677740407158414452">"இதன்படி வரிசைப்படுத்து"</string> diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml index 17c0066c441b..91d436a2e43c 100644 --- a/packages/DocumentsUI/res/values-te-rIN/strings.xml +++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ఫైల్లు"</string> <string name="title_open" msgid="4353228937663917801">"ఇక్కడి నుండి తెరువు"</string> <string name="title_save" msgid="2433679664882857999">"ఇందులో సేవ్ చేయి"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"కొత్త ఫోల్డర్"</string> <string name="menu_grid" msgid="6878021334497835259">"గ్రిడ్ వీక్షణ"</string> <string name="menu_list" msgid="7279285939892417279">"జాబితా వీక్షణ"</string> <string name="menu_sort" msgid="7677740407158414452">"ఇలా క్రమబద్ధీకరించు"</string> diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml index 47e12b635317..9d77561d31bc 100644 --- a/packages/DocumentsUI/res/values-th/strings.xml +++ b/packages/DocumentsUI/res/values-th/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"ไฟล์"</string> <string name="title_open" msgid="4353228937663917801">"เปิดจาก"</string> <string name="title_save" msgid="2433679664882857999">"บันทึกไปยัง"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"โฟลเดอร์ใหม่"</string> <string name="menu_grid" msgid="6878021334497835259">"มุมมองตาราง"</string> <string name="menu_list" msgid="7279285939892417279">"มุมมองรายการ"</string> <string name="menu_sort" msgid="7677740407158414452">"จัดเรียงตาม"</string> diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml index b09820c4be79..9ae30aa91faf 100644 --- a/packages/DocumentsUI/res/values-tl/strings.xml +++ b/packages/DocumentsUI/res/values-tl/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Mga File"</string> <string name="title_open" msgid="4353228937663917801">"Buksan mula sa"</string> <string name="title_save" msgid="2433679664882857999">"I-save sa"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Bagong folder"</string> <string name="menu_grid" msgid="6878021334497835259">"View na grid"</string> <string name="menu_list" msgid="7279285939892417279">"View na listahan"</string> <string name="menu_sort" msgid="7677740407158414452">"Uriin ayon sa"</string> diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml index 50fa357e4498..3c2e39c9a4c7 100644 --- a/packages/DocumentsUI/res/values-tr/strings.xml +++ b/packages/DocumentsUI/res/values-tr/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Dosyalar"</string> <string name="title_open" msgid="4353228937663917801">"Şuradan aç:"</string> <string name="title_save" msgid="2433679664882857999">"Şuraya kaydet:"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Yeni klasör"</string> <string name="menu_grid" msgid="6878021334497835259">"Tablo görünümü"</string> <string name="menu_list" msgid="7279285939892417279">"Liste görünümü"</string> <string name="menu_sort" msgid="7677740407158414452">"Sıralama ölçütü"</string> diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml index 7382bf83a7f0..407f1aa9ef8c 100644 --- a/packages/DocumentsUI/res/values-uk/strings.xml +++ b/packages/DocumentsUI/res/values-uk/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Файли"</string> <string name="title_open" msgid="4353228937663917801">"Відкрити"</string> <string name="title_save" msgid="2433679664882857999">"Зберегти в"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Нова папка"</string> <string name="menu_grid" msgid="6878021334497835259">"Режим таблиці"</string> <string name="menu_list" msgid="7279285939892417279">"Режим списку"</string> <string name="menu_sort" msgid="7677740407158414452">"Параметри сортування"</string> diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml index 0e5544f5588a..845d2cb5df25 100644 --- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml +++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"فائلیں"</string> <string name="title_open" msgid="4353228937663917801">"کھولیں از"</string> <string name="title_save" msgid="2433679664882857999">"اس میں محفوظ کریں"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"نیا فولڈر"</string> <string name="menu_grid" msgid="6878021334497835259">"گرڈ منظر"</string> <string name="menu_list" msgid="7279285939892417279">"فہرست منظر"</string> <string name="menu_sort" msgid="7677740407158414452">"ترتیب دیں بلحاظ"</string> diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml index 9cc6bca0405a..f3514db7b288 100644 --- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml +++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Fayllar"</string> <string name="title_open" msgid="4353228937663917801">"Ochish"</string> <string name="title_save" msgid="2433679664882857999">"Saqlash"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Yangi jild"</string> <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> diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml index 1d553388dab0..52a4e821b55a 100644 --- a/packages/DocumentsUI/res/values-vi/strings.xml +++ b/packages/DocumentsUI/res/values-vi/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Tệp"</string> <string name="title_open" msgid="4353228937663917801">"Mở từ"</string> <string name="title_save" msgid="2433679664882857999">"Lưu vào"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Thư mục mới"</string> <string name="menu_grid" msgid="6878021334497835259">"Chế độ xem lưới"</string> <string name="menu_list" msgid="7279285939892417279">"Chế độ xem danh sách"</string> <string name="menu_sort" msgid="7677740407158414452">"Sắp xếp theo"</string> diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml index 7e2e3d501880..8ee90cc1596e 100644 --- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml +++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"文件"</string> <string name="title_open" msgid="4353228937663917801">"打开文件"</string> <string name="title_save" msgid="2433679664882857999">"保存文件"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"新建文件夹"</string> <string name="menu_grid" msgid="6878021334497835259">"网格视图"</string> <string name="menu_list" msgid="7279285939892417279">"列表视图"</string> <string name="menu_sort" msgid="7677740407158414452">"排序依据"</string> diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml index 6cbdf0149e25..dadc02983a40 100644 --- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml +++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"檔案"</string> <string name="title_open" msgid="4353228937663917801">"開啟檔案"</string> <string name="title_save" msgid="2433679664882857999">"儲存至"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"新增資料夾"</string> <string name="menu_grid" msgid="6878021334497835259">"格狀檢視"</string> <string name="menu_list" msgid="7279285939892417279">"清單檢視"</string> <string name="menu_sort" msgid="7677740407158414452">"排序方式"</string> diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml index 850f9b0d8a5d..cdd1288cf027 100644 --- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml +++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"檔案"</string> <string name="title_open" msgid="4353228937663917801">"開啟檔案"</string> <string name="title_save" msgid="2433679664882857999">"儲存至"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"新增資料夾"</string> <string name="menu_grid" msgid="6878021334497835259">"格狀檢視"</string> <string name="menu_list" msgid="7279285939892417279">"清單檢視"</string> <string name="menu_sort" msgid="7677740407158414452">"排序依據"</string> diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml index fdf278d34a84..d6bb2b47ff9a 100644 --- a/packages/DocumentsUI/res/values-zu/strings.xml +++ b/packages/DocumentsUI/res/values-zu/strings.xml @@ -20,8 +20,7 @@ <string name="files_label" msgid="6051402950202690279">"Amafayela"</string> <string name="title_open" msgid="4353228937663917801">"Vula kusuka ku-"</string> <string name="title_save" msgid="2433679664882857999">"Londoloza ku-"</string> - <!-- no translation found for menu_create_dir (2547620241173881754) --> - <skip /> + <string name="menu_create_dir" msgid="2547620241173881754">"Ifolda entsha"</string> <string name="menu_grid" msgid="6878021334497835259">"Ukubuka kwegridi"</string> <string name="menu_list" msgid="7279285939892417279">"Ukubuka uhlu"</string> <string name="menu_sort" msgid="7677740407158414452">"Hlunga nge-"</string> diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index caaa2b9be0a3..9d2d171b05b6 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -39,6 +39,7 @@ import android.provider.DocumentsContract.Root; import android.support.annotation.LayoutRes; import android.support.annotation.Nullable; import android.util.Log; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -465,6 +466,21 @@ abstract class BaseActivity extends Activity { } } + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (DEBUG) Log.d(mTag, "onKeyUp: keycode = " + keyCode); + DirectoryFragment dir = DirectoryFragment.get(getFragmentManager()); + switch (keyCode) { + case KeyEvent.KEYCODE_MOVE_HOME: + dir.focusFirstFile(); + return true; + case KeyEvent.KEYCODE_MOVE_END: + dir.focusLastFile(); + return true; + } + return super.onKeyUp(keyCode, event); + } + public void onStackPicked(DocumentStack stack) { try { // Update the restored stack to ensure we have freshest data diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 0abbf4e4996f..2fe829f266ba 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -873,6 +873,10 @@ public class DirectoryFragment extends Fragment { public DocumentHolder(View view) { super(view); this.view = view; + // Setting this using android:focusable in the item layouts doesn't work for list items. + // So we set it here. Note that touch mode focus is a separate issue - see + // View.setFocusableInTouchMode and View.isInTouchMode for more info. + this.view.setFocusable(true); } } @@ -1337,6 +1341,45 @@ public class DirectoryFragment extends Fragment { } } + /** + * Scrolls to the top of the file list and focuses the first file. + */ + void focusFirstFile() { + focusFile(0); + } + + /** + * Scrolls to the bottom of the file list and focuses the last file. + */ + void focusLastFile() { + focusFile(mAdapter.getItemCount() - 1); + } + + /** + * Scrolls to and then focuses on the file at the given position. + */ + private void focusFile(final int pos) { + // Don't smooth scroll; that taxes the system unnecessarily and makes the scroll handling + // logic below more complicated. + mRecView.scrollToPosition(pos); + + // If the item is already in view, focus it; otherwise, set a one-time listener to focus it + // when the scroll is completed. + RecyclerView.ViewHolder vh = mRecView.findViewHolderForAdapterPosition(pos); + if (vh != null) { + vh.itemView.requestFocus(); + } else { + mRecView.addOnScrollListener( + new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView view, int dx, int dy) { + view.findViewHolderForAdapterPosition(pos).itemView.requestFocus(); + view.removeOnScrollListener(this); + } + }); + } + } + private void setupDragAndDropOnDirectoryView(View view) { // Listen for drops on non-directory items and empty space. view.setOnDragListener(mOnDragListener); @@ -1753,7 +1796,7 @@ public class DirectoryFragment extends Fragment { } } - if (DEBUG) { + if (DEBUG && position != originalPos) { Log.d(TAG, "Item position adjusted for deletion. Original: " + originalPos + " Adjusted: " + position); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java index 70ddf5916f2c..1330b3cbc7cd 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java @@ -89,11 +89,13 @@ public class FilesActivity extends BaseActivity { RootsFragment.show(getFragmentManager(), null); if (mState.restored) { + if (DEBUG) Log.d(TAG, "Restored instance for uri: " + getIntent().getData()); onCurrentDirectoryChanged(ANIM_NONE); } else { Intent intent = getIntent(); Uri uri = intent.getData(); + if (DEBUG) Log.d(TAG, "Creating new instance for uri: " + uri); // 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. diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java index 17a1161f7c25..7426af56ecb1 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java @@ -16,6 +16,7 @@ package com.android.documentsui; +import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.Shared.TAG; import static com.android.documentsui.model.DocumentInfo.getCursorLong; import static com.android.documentsui.model.DocumentInfo.getCursorString; @@ -65,7 +66,9 @@ public class FilteringCursorWrapper extends AbstractCursor { } } - Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount); + if (DEBUG && mCount != cursor.getCount()) { + Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount); + } } @Override diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java index c29937d68cdf..b3d0cf37bbd5 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java @@ -16,15 +16,17 @@ package com.android.documentsui; +import static com.android.documentsui.Shared.DEBUG; + 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 android.util.Log; import java.util.List; @@ -39,39 +41,47 @@ import java.util.List; */ public class LauncherActivity extends Activity { - public static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl"; + private static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl"; + private static final String TAG = "LauncherActivity"; @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(); + Intent intent = findTask(activities); + if (intent != null) { + restoreTask(intent); } else { - raiseFilesTask(activities, raiseTask.getTaskInfo()); + startTask(); } finish(); } - private void launchFilesTask() { + private @Nullable Intent findTask(ActivityManager activities) { + List<AppTask> tasks = activities.getAppTasks(); + for (AppTask task : tasks) { + Intent intent = task.getTaskInfo().baseIntent; + Uri uri = intent.getData(); + if (isLaunchUri(uri)) { + return intent; + } + } + return null; + } + + private void startTask() { Intent intent = createLaunchIntent(this); + if (DEBUG) Log.d(TAG, "Starting new task > " + intent.getData()); startActivity(intent); } - private void raiseFilesTask(ActivityManager activities, RecentTaskInfo task) { - activities.moveTaskToFront(task.id, 0); + private void restoreTask(Intent intent) { + if (DEBUG) Log.d(TAG, "Restoring existing task > " + intent.getData()); + // TODO: This doesn't appear to restore a task once it has stopped running. + startActivity(intent); } static Intent createLaunchIntent(Context context) { @@ -88,6 +98,7 @@ public class LauncherActivity extends Activity { } static boolean isLaunchUri(@Nullable Uri uri) { - return uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority()); + boolean result = uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority()); + return result; } } diff --git a/packages/SystemUI/res/drawable/qs_customizer_background.xml b/packages/SystemUI/res/drawable/qs_customizer_background.xml new file mode 100644 index 000000000000..6bb27cc2a517 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_customizer_background.xml @@ -0,0 +1,19 @@ +<!-- + Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<transition xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@color/qs_detail_transition" /> + <item android:drawable="?android:attr/windowBackground" /> +</transition> diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml index dc928c77baaa..59fed5ba7d2a 100644 --- a/packages/SystemUI/res/layout/qs_customize_panel.xml +++ b/packages/SystemUI/res/layout/qs_customize_panel.xml @@ -20,7 +20,8 @@ android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/navigation_bar_size" - android:background="?android:attr/windowBackground"> + android:background="@drawable/qs_customizer_background" + android:gravity="center_horizontal"> <FrameLayout android:layout_width="match_parent" @@ -76,10 +77,10 @@ </FrameLayout> <com.android.systemui.tuner.AutoScrollView - android:layout_width="match_parent" + android:layout_width="@dimen/notification_panel_width" android:layout_height="0dp" android:layout_weight="1" - android:paddingTop="8dp" + android:paddingTop="12dp" android:paddingBottom="8dp" android:elevation="2dp"> @@ -87,7 +88,9 @@ android:id="@+id/quick_settings_panel" android:background="#0000" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/notification_side_padding" + android:layout_marginRight="@dimen/notification_side_padding" /> </com.android.systemui.tuner.AutoScrollView> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 1d1958943e94..bae801716c19 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -222,7 +222,7 @@ <bool name="doze_pulse_on_notifications">true</bool> <!-- Doze: when to pulse after a buzzworthy notification arrives --> - <string name="doze_pulse_schedule" translatable="false">1s,10s,30s,60s</string> + <string name="doze_pulse_schedule" translatable="false">10s,30s,60s</string> <!-- Doze: maximum number of times the notification pulse schedule can be reset --> <integer name="doze_pulse_schedule_resets">2</integer> diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 36efeaddd004..3370091f53c2 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -88,6 +88,7 @@ public class DozeService extends DreamService { private boolean mPowerSaveActive; private boolean mCarMode; private long mNotificationPulseTime; + private long mLastScheduleResetTime; private long mEarliestPulseDueToLight; private int mScheduleResetsRemaining; @@ -356,13 +357,21 @@ public class DozeService extends DreamService { return; } final long pulseDuration = mDozeParameters.getPulseDuration(false /*pickup*/); - if ((notificationTimeMs - mNotificationPulseTime) < pulseDuration) { + boolean pulseImmediately = System.currentTimeMillis() >= notificationTimeMs; + if ((notificationTimeMs - mLastScheduleResetTime) >= pulseDuration) { + mScheduleResetsRemaining--; + mLastScheduleResetTime = notificationTimeMs; + } else if (!pulseImmediately){ if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule"); return; } - mScheduleResetsRemaining--; if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining); mNotificationPulseTime = notificationTimeMs; + if (pulseImmediately) { + DozeLog.traceNotificationPulse(0); + requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); + } + // schedule the rest of the pulses rescheduleNotificationPulse(true /*predicate*/); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index c612600ac82e..32c906e46afb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -181,6 +181,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { public TilePage(Context context, AttributeSet attrs) { super(context, attrs); mAllowDual = false; + updateResources(); } public void setMaxRows(int maxRows) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index ca38528b05d0..47189b023132 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -140,13 +140,13 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene mDialog.setTitle(getTitle(deviceOwner)); mDialog.setMessage(getMessage(deviceOwner, profileOwner, primaryVpn, profileVpn, managed)); mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); - if (mSecurityController.isVpnEnabled()) { - mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this); + if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) { + mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this); } mDialog.show(); } - private String getNegativeButton() { + private String getSettingsButton() { return mContext.getString(R.string.status_bar_settings_settings_button); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 880349e232fe..18af35ea61cb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -172,7 +172,8 @@ public class QSPanel extends FrameLayout implements Tunable { mCustomizePanel.setHost(mHost); } else { if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) { - mCustomizePanel.hide(); + mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, + mCustomizePanel.getHeight() / 2); } mCustomizePanel = null; } @@ -242,7 +243,7 @@ public class QSPanel extends FrameLayout implements Tunable { public void onCollapse() { if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) { - mCustomizePanel.hide(); + mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2); } } @@ -382,7 +383,12 @@ public class QSPanel extends FrameLayout implements Tunable { public boolean onLongClick(View v) { if (mCustomizePanel != null) { if (!mCustomizePanel.isCustomizing()) { - mCustomizePanel.show(); + int[] loc = new int[2]; + getLocationInWindow(loc); + int x = r.tileView.getLeft() + r.tileView.getWidth() / 2 + loc[0]; + int y = r.tileView.getTop() + mTileLayout.getOffsetTop(r) + + r.tileView.getHeight() / 2 + loc[1]; + mCustomizePanel.show(x, y); } } else { r.tile.longClick(); @@ -409,7 +415,7 @@ public class QSPanel extends FrameLayout implements Tunable { public void closeDetail() { if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) { // Treat this as a detail panel for now, to make things easy. - mCustomizePanel.hide(); + mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2); return; } showDetail(false, mDetailRecord); diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 8bd05fad5699..b8342e2ada7c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -68,9 +68,10 @@ public class TileLayout extends ViewGroup implements QSTileLayout { final Resources res = mContext.getResources(); final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns)); mCellHeight = res.getDimensionPixelSize(R.dimen.qs_tile_height); - mCellWidth = (int)(mCellHeight * TILE_ASPECT); - mLargeCellHeight = res.getDimensionPixelSize(R.dimen.qs_dual_tile_height); - mLargeCellWidth = (int)(mLargeCellHeight * TILE_ASPECT); + mCellWidth = (int) (mCellHeight * TILE_ASPECT); + mLargeCellHeight = mAllowDual ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_height) + : mCellHeight; + mLargeCellWidth = mAllowDual ? (int) (mLargeCellHeight * TILE_ASPECT) : mCellWidth; mDualTileUnderlap = res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical); if (mColumns != columns) { mColumns = columns; 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 f676ea3499cb..3491cb6fdf8a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java @@ -160,6 +160,11 @@ public class CustomQSTileHost extends QSTileHost { } @Override + public boolean isVpnRestricted() { + return false; + } + + @Override public String getPrimaryVpnName() { return null; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 7e7478545a07..601961b41a16 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -15,23 +15,19 @@ */ package com.android.systemui.qs.customize; +import android.animation.Animator; import android.content.ClipData; import android.content.Context; import android.util.AttributeSet; import android.util.TypedValue; -import android.view.ContextThemeWrapper; -import android.view.DragEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; +import android.view.*; import android.view.View.OnClickListener; -import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.Toolbar; import android.widget.Toolbar.OnMenuItemClickListener; - import com.android.systemui.R; import com.android.systemui.SystemUIApplication; +import com.android.systemui.qs.QSDetailClipper; import com.android.systemui.qs.QSTile.Host.Callback; import com.android.systemui.qs.customize.DropButton.OnDropListener; import com.android.systemui.statusbar.phone.PhoneStatusBar; @@ -48,10 +44,11 @@ import java.util.ArrayList; * *someday* do fancy animations to get into/out of it. */ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener, Callback, - OnDropListener, OnClickListener { + OnDropListener, OnClickListener, Animator.AnimatorListener { private static final int MENU_SAVE = Menu.FIRST; private static final int MENU_RESET = Menu.FIRST + 1; + private final QSDetailClipper mClipper; private PhoneStatusBar mPhoneStatusBar; @@ -69,6 +66,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs); mPhoneStatusBar = ((SystemUIApplication) mContext.getApplicationContext()) .getComponent(PhoneStatusBar.class); + mClipper = new QSDetailClipper(this); } public void setHost(QSTileHost host) { @@ -90,8 +88,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mToolbar.setNavigationOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - // TODO: Is this all we want...? - hide(); + hide(0, 0); } }); mToolbar.setOnMenuItemClickListener(this); @@ -115,17 +112,18 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mFab.setOnClickListener(this); } - public void show() { + public void show(int x, int y) { isShown = true; mHost.setSavedTiles(); - // TODO: Fancy shmancy reveal. mPhoneStatusBar.getStatusBarWindow().addView(this); + mQsPanel.setListening(true); + mClipper.animateCircularClip(x, y, true, this); } - public void hide() { + public void hide(int x, int y) { isShown = false; - // TODO: Similarly awesome or better hide. - mPhoneStatusBar.getStatusBarWindow().removeView(this); + mQsPanel.setListening(false); + mClipper.animateCircularClip(x, y, false, this); } public boolean isCustomizing() { @@ -146,7 +144,8 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private void save() { mHost.saveCurrentTiles(); - hide(); + // TODO: At save button. + hide(0, 0); } @Override @@ -197,4 +196,28 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene // TODO: Show list of tiles. } } + + @Override + public void onAnimationEnd(Animator animation) { + if (!isShown) { + mPhoneStatusBar.getStatusBarWindow().removeView(this); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + if (!isShown) { + mPhoneStatusBar.getStatusBarWindow().removeView(this); + } + } + + @Override + public void onAnimationStart(Animator animation) { + // Don't care. + } + + @Override + public void onAnimationRepeat(Animator animation) { + // Don't care. + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index a78351a1ffbc..b1cc27a3445a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -116,9 +116,8 @@ public class Recents extends SystemUI /** Preloads the next task */ public void run() { - // Temporarily skip this if multi stack is enabled - if (mConfig.multiWindowEnabled) return; - + // TODO: Temporarily skip this if multi stack is enabled + /* RecentsConfiguration config = RecentsConfiguration.getInstance(); if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) { RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); @@ -127,7 +126,7 @@ public class Recents extends SystemUI // Load the next task only if we aren't svelte RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); - loader.preloadTasks(plan, true /* isTopTaskHome */); + loader.preloadTasks(plan, true); RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options(); // This callback is made when a new activity is launched and the old one is paused // so ignore the current activity and try and preload the thumbnail for the @@ -141,6 +140,7 @@ public class Recents extends SystemUI launchOpts.onlyLoadPausedActivities = true; loader.loadTasks(mContext, plan, launchOpts); } + */ } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 5c61c5ca66a2..e647c1f6dcfe 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -26,6 +26,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ActivityInfo; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; @@ -41,6 +42,8 @@ import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEven import com.android.systemui.recents.events.ui.DismissTaskEvent; import com.android.systemui.recents.events.ui.ResizeTaskEvent; import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.Console; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; @@ -108,22 +111,11 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void run() { - // Finish Recents - if (mLaunchIntent != null) { - try { - if (mLaunchOpts != null) { - startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT); - } else { - startActivityAsUser(mLaunchIntent, UserHandle.CURRENT); - } - } catch (Exception e) { - Console.logError(RecentsActivity.this, - getString(R.string.recents_launch_error_message, "Home")); - } - } else { - finish(); - overridePendingTransition(R.anim.recents_to_launcher_enter, - R.anim.recents_to_launcher_exit); + try { + startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT); + } catch (Exception e) { + Console.logError(RecentsActivity.this, + getString(R.string.recents_launch_error_message, "Home")); } } } @@ -141,7 +133,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView dismissRecentsToFocusedTaskOrHome(false); } else if (intent.getBooleanExtra(Recents.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) { // Otherwise, dismiss Recents to Home - dismissRecentsToHomeRaw(true); + dismissRecentsToHome(true); } else { // Do nothing } @@ -167,7 +159,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView String action = intent.getAction(); if (action.equals(Intent.ACTION_SCREEN_OFF)) { // When the screen turns off, dismiss Recents to Home - dismissRecentsToHome(false); + dismissRecentsToHomeIfVisible(false); } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) { // When the search activity changes, update the search widget view SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); @@ -283,25 +275,28 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView if (mRecentsView.launchFocusedTask()) return true; // If we launched from Home, then return to Home if (launchState.launchedFromHome) { - dismissRecentsToHomeRaw(true); + dismissRecentsToHome(true); return true; } // Otherwise, try and return to the Task that Recents was launched from if (mRecentsView.launchPreviousTask()) return true; // If none of the other cases apply, then just go Home - dismissRecentsToHomeRaw(true); + dismissRecentsToHome(true); return true; } return false; } - /** Dismisses Recents directly to Home. */ - void dismissRecentsToHomeRaw(boolean animated) { + /** + * Dismisses Recents directly to Home without checking whether it is currently visible. + */ + void dismissRecentsToHome(boolean animated) { if (animated) { ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, null, mFinishLaunchHomeRunnable, null); mRecentsView.startExitToHomeAnimation( new ViewAnimation.TaskViewExitContext(exitTrigger)); + mScrimViews.startExitRecentsAnimation(); } else { mFinishLaunchHomeRunnable.run(); } @@ -314,11 +309,11 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } /** Dismisses Recents directly to Home if we currently aren't transitioning. */ - boolean dismissRecentsToHome(boolean animated) { + boolean dismissRecentsToHomeIfVisible(boolean animated) { SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) { // Return to Home - dismissRecentsToHomeRaw(animated); + dismissRecentsToHome(animated); return true; } return false; @@ -418,7 +413,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView 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); @@ -435,6 +429,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // 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. + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); launchState.launchedFromHome = false; launchState.launchedFromSearchHome = false; launchState.launchedFromAppWithThumbnail = false; @@ -557,7 +552,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void onTaskLaunchFailed() { // Return to Home - dismissRecentsToHomeRaw(true); + dismissRecentsToHome(true); } @Override @@ -612,6 +607,18 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView); } + public final void onBusEvent(DragStartEvent event) { + // Lock the orientation while dragging + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + + // TODO: docking requires custom accessibility actions + } + + public final void onBusEvent(DragEndEvent event) { + // Unlock the orientation when dragging completes + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND); + } + private void refreshSearchWidgetView() { if (mSearchWidgetInfo != null) { SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 52b9521b9952..b6f4a3c61383 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.provider.Settings; import com.android.systemui.R; import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.recents.model.RecentsTaskLoader; /** * Application resources that can be retrieved from the application context and are not specifically @@ -70,13 +71,13 @@ public class RecentsConfiguration { public final int smallestWidth; /** Misc **/ + public boolean hasDockedTasks; public boolean useHardwareLayers; public boolean fakeShadows; public int svelteLevel; public int searchBarSpaceHeightPx; /** Dev options and global settings */ - public boolean multiWindowEnabled; public boolean lockToAppEnabled; /** Private constructor */ @@ -113,7 +114,7 @@ public class RecentsConfiguration { // 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")); + hasDockedTasks = ssp.hasDockedTask(); // Recompute some values based on the given state, since we can not rely on the resource // system to get certain values. diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java index 59df293f71d7..88270650ca1c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java @@ -235,11 +235,9 @@ public class RecentsResizeTaskDialog extends DialogFragment { // In debug mode, we force all task to be resizeable regardless of the // current app configuration. - if (RecentsConfiguration.getInstance().multiWindowEnabled) { - for (int i = additionalTasks; i >= 0; --i) { - if (mTasks[i] != null) { - mSsp.setTaskResizeable(mTasks[i].key.id); - } + for (int i = additionalTasks; i >= 0; --i) { + if (mTasks[i] != null) { + mSsp.setTaskResizeable(mTasks[i].key.id); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java new file mode 100644 index 000000000000..f2c3c338383e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java @@ -0,0 +1,35 @@ +/* + * 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.ui.dragndrop; + +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.model.TaskStack; + +/** + * This event is sent when a user drag enters or exits a dock region. + */ +public class DragDockStateChangedEvent extends EventBus.Event { + + public final Task task; + public final TaskStack.DockState dockState; + + public DragDockStateChangedEvent(Task task, TaskStack.DockState dockState) { + this.task = task; + this.dockState = dockState; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java new file mode 100644 index 000000000000..827998d8529f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java @@ -0,0 +1,45 @@ +/* + * 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.ui.dragndrop; + +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.misc.ReferenceCountedTrigger; +import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.recents.views.DragView; +import com.android.systemui.recents.views.TaskView; + +/** + * This event is sent whenever a drag ends. + */ +public class DragEndEvent extends EventBus.Event { + + public final Task task; + public final TaskView taskView; + public final DragView dragView; + public final TaskStack.DockState dockState; + public final ReferenceCountedTrigger postAnimationTrigger; + + public DragEndEvent(Task task, TaskView taskView, DragView dragView, + TaskStack.DockState dockState, ReferenceCountedTrigger postAnimationTrigger) { + this.task = task; + this.taskView = taskView; + this.dragView = dragView; + this.dockState = dockState; + this.postAnimationTrigger = postAnimationTrigger; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java new file mode 100644 index 000000000000..2d42a0e0103d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java @@ -0,0 +1,38 @@ +/* + * 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.ui.dragndrop; + +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.views.DragView; +import com.android.systemui.recents.views.TaskView; + +/** + * This event is sent whenever a drag starts. + */ +public class DragStartEvent extends EventBus.Event { + + public final Task task; + public final TaskView taskView; + public final DragView dragView; + + public DragStartEvent(Task task, TaskView taskView, DragView dragView) { + this.task = task; + this.taskView = taskView; + this.dragView = dragView; + } +} 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 e0f820d400b6..568d2b1e3f2e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -16,6 +16,9 @@ package com.android.systemui.recents.misc; +import static android.app.ActivityManager.DOCKED_STACK_ID; +import static android.app.ActivityManager.INVALID_STACK_ID; + import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityOptions; @@ -297,7 +300,7 @@ public class SystemServicesProxy { if (mIam == null) return; try { - mIam.moveTaskToDockedStack(taskId, createMode, true); + mIam.startActivityFromRecents(taskId, DOCKED_STACK_ID, null); } catch (RemoteException e) { e.printStackTrace(); } @@ -327,6 +330,21 @@ public class SystemServicesProxy { return mAm.isInHomeStack(taskId); } + /** + * @return whether there are any docked tasks. + */ + public boolean hasDockedTask() { + if (mIam == null) return false; + + ActivityManager.StackInfo stackInfo = null; + try { + stackInfo = mIam.getStackInfo(DOCKED_STACK_ID); + } catch (RemoteException e) { + e.printStackTrace(); + } + return stackInfo != null; + } + /** Returns the top task thumbnail for the given task id */ public Bitmap getTaskThumbnail(int taskId) { if (mAm == null) return null; @@ -707,7 +725,8 @@ public class SystemServicesProxy { ActivityOptions options) { if (mIam != null) { try { - mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle()); + mIam.startActivityFromRecents( + taskId, INVALID_STACK_ID, options == null ? null : options.toBundle()); return true; } catch (Exception e) { Console.logError(context, diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java index e810410cd8bd..93c5ee7c873e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java @@ -21,12 +21,29 @@ import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Rect; import android.view.View; +import android.view.ViewParent; import java.util.ArrayList; /* Common code */ public class Utilities { + /** + * @return the first parent walking up the view hierarchy that has the given class type. + * + * @param parentClass must be a class derived from {@link View} + */ + public static <T extends View> T findParent(View v, Class<T> parentClass) { + ViewParent parent = v.getParent(); + while (parent != null) { + if (parent.getClass().equals(parentClass)) { + return (T) parent; + } + parent = parent.getParent(); + } + return null; + } + /** Scales a rect about its centroid */ public static void scaleRectAboutCenter(Rect r, float scale) { if (scale != 1.0f) { 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 8eec87ee8f76..0fb235b9e23a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -16,8 +16,11 @@ package com.android.systemui.recents.model; +import android.app.ActivityManager; import android.content.Context; import android.graphics.Color; +import android.graphics.Rect; +import android.graphics.RectF; import com.android.systemui.R; import com.android.systemui.recents.Constants; import com.android.systemui.recents.misc.NamedCounter; @@ -30,6 +33,9 @@ import java.util.HashMap; import java.util.List; import java.util.Random; +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; + /** * An interface for a task filter to query whether a particular task should show in a stack. @@ -174,6 +180,63 @@ public class TaskStack { public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks); } + + public enum DockState { + LEFT(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, + new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.5f, 1), new RectF(0.5f, 0, 1, 1)), + TOP(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, + new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.5f), new RectF(0, 0.5f, 1, 1)), + RIGHT(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, + new RectF(0.75f, 0, 1, 1), new RectF(0.5f, 0, 1, 1), new RectF(0, 0, 0.5f, 1)), + BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, + new RectF(0, 0.75f, 1, 1), new RectF(0, 0.5f, 1, 1), new RectF(0, 0, 1, 0.5f)); + + public final int createMode; + private final RectF touchArea; + private final RectF dockArea; + private final RectF stackArea; + + /** + * @param createMode used to pass to ActivityManager to dock the task + * @param touchArea the area in which touch will initiate this dock state + * @param stackArea the area for the stack if a task is docked + */ + DockState(int createMode, RectF touchArea, RectF dockArea, RectF stackArea) { + this.createMode = createMode; + this.touchArea = touchArea; + this.dockArea = dockArea; + this.stackArea = stackArea; + } + + /** + * Returns whether {@param x} and {@param y} are contained in the touch area scaled to the + * given {@param width} and {@param height}. + */ + public boolean touchAreaContainsPoint(int width, int height, float x, float y) { + int left = (int) (touchArea.left * width); + int top = (int) (touchArea.top * height); + int right = (int) (touchArea.right * width); + int bottom = (int) (touchArea.bottom * height); + return x >= left && y >= top && x <= right && y <= bottom; + } + + /** + * Returns the docked task bounds with the given {@param width} and {@param height}. + */ + public Rect getDockedBounds(int width, int height) { + return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height), + (int) (dockArea.right * width), (int) (dockArea.bottom * height)); + } + + /** + * Returns the stack bounds with the given {@param width} and {@param height}. + */ + public Rect getStackBounds(int width, int height) { + return new Rect((int) (stackArea.left * width), (int) (stackArea.top * height), + (int) (stackArea.right * width), (int) (stackArea.bottom * height)); + } + } + // The task offset to apply to a task id as a group affiliation static final int IndividualTaskIdOffset = 1 << 16; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java b/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java new file mode 100644 index 000000000000..96dfaac360c3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java @@ -0,0 +1,39 @@ +/* + * 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.views; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.widget.ImageView; + +public class DragView extends ImageView { + + // The offset from the top-left of the dragBitmap + Point mTopLeftOffset; + + public DragView(Context context, Bitmap dragBitmap, Point tlOffset) { + super(context); + + mTopLeftOffset = tlOffset; + setImageBitmap(dragBitmap); + } + + public Point getTopLeftOffset() { + return mTopLeftOffset; + } +} 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 de8730dd8be7..e4061553e631 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -22,6 +22,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.IRemoteCallback; import android.os.RemoteException; @@ -30,19 +31,25 @@ import android.util.Log; import android.util.SparseArray; import android.view.AppTransitionAnimationSpec; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.WindowInsets; import android.view.WindowManagerGlobal; +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.RecentsActivity; import com.android.systemui.recents.RecentsAppWidgetHostView; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.ui.DismissTaskEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; @@ -78,6 +85,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV TaskStackView mTaskStackView; RecentsAppWidgetHostView mSearchBar; RecentsViewCallbacks mCb; + + RecentsViewTouchHandler mTouchHandler; + DragView mDragView; + ColorDrawable mDockRegionOverlay; + Interpolator mFastOutSlowInInterpolator; Rect mSystemInsets = new Rect(); @@ -96,10 +108,14 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + setWillNotDraw(false); mConfig = RecentsConfiguration.getInstance(); mInflater = LayoutInflater.from(context); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.fast_out_slow_in); + mTouchHandler = new RecentsViewTouchHandler(this); + mDockRegionOverlay = new ColorDrawable(0x66000000); + mDockRegionOverlay.setAlpha(0); } /** Sets the callbacks */ @@ -200,6 +216,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV ArrayList<Task> tasks = stack.getTasks(); // Find the launch task in the stack + // TODO: replace this with an event from RecentsActivity if (!tasks.isEmpty()) { int taskCount = tasks.size(); for (int j = 0; j < taskCount; j++) { @@ -267,6 +284,20 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } } + @Override + protected void onAttachedToWindow() { + EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); + EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 1); + super.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + EventBus.getDefault().unregister(this); + EventBus.getDefault().unregister(mTouchHandler); + } + /** * This is called with the full size of the window since we are handling our own insets. */ @@ -293,6 +324,12 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec); } + if (mDragView != null) { + mDragView.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); + } + setMeasuredDimension(width, height); } @@ -314,6 +351,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) { mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight()); } + + if (mDragView != null) { + mDragView.layout(left, top, left + mDragView.getMeasuredWidth(), + top + mDragView.getMeasuredHeight()); + } } @Override @@ -323,6 +365,24 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV return insets.consumeSystemWindowInsets(); } + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return mTouchHandler.onInterceptTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + return mTouchHandler.onTouchEvent(ev); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (mDockRegionOverlay.getAlpha() > 0) { + mDockRegionOverlay.draw(canvas); + } + } + /** Notifies each task view of the user interaction. */ public void onUserInteraction() { // Get the first stack view @@ -697,4 +757,64 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV .start(); } } + + /**** EventBus Events ****/ + + public final void onBusEvent(DragStartEvent event) { + // Add the drag view + mDragView = event.dragView; + addView(mDragView); + } + + public final void onBusEvent(DragDockStateChangedEvent event) { + // Update the task stack positions, and then + if (event.dockState != null) { + // Draw an overlay on the bounds of the dock task + mDockRegionOverlay.setBounds( + event.dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight())); + mDockRegionOverlay.setAlpha(255); + } else { + mDockRegionOverlay.setAlpha(0); + } + invalidate(); + } + + public final void onBusEvent(final DragEndEvent event) { + event.postAnimationTrigger.increment(); + event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { + @Override + public void run() { + // Remove the drag view + removeView(mDragView); + mDragView = null; + mDockRegionOverlay.setAlpha(0); + invalidate(); + + // Dock the new task if we are hovering over a valid dock state + if (event.dockState != null) { + SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); + ssp.setTaskResizeable(event.task.key.id); + ssp.dockTask(event.task.key.id, event.dockState.createMode); + launchTask(event.task, null); + } + } + }); + if (event.dockState == null) { + // Animate the alpha back to what it was before + Rect taskBounds = mTaskStackView.getStackAlgorithm().getUntransformedTaskViewBounds(); + int left = taskBounds.left + (int) ((1f - event.taskView.getScaleX()) * taskBounds.width()) / 2; + int top = taskBounds.top + (int) ((1f - event.taskView.getScaleY()) * taskBounds.height()) / 2; + event.dragView.animate() + .alpha(1f) + .translationX(left + event.taskView.getTranslationX()) + .translationY(top + event.taskView.getTranslationY()) + .setDuration(175) + .setInterpolator(new AccelerateInterpolator(1.5f)) + .withEndAction(event.postAnimationTrigger.decrementAsRunnable()) + .withLayer() + .start(); + } else { + event.postAnimationTrigger.decrement(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java new file mode 100644 index 000000000000..8dea0cd1ab38 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java @@ -0,0 +1,149 @@ +/* + * 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.views; + +import android.content.res.Configuration; +import android.graphics.Point; +import android.view.MotionEvent; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; +import com.android.systemui.recents.misc.ReferenceCountedTrigger; +import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.model.TaskStack; + + +/** + * Represents the dock regions for each orientation. + */ +class DockRegion { + public static TaskStack.DockState[] LANDSCAPE = { + TaskStack.DockState.LEFT, TaskStack.DockState.RIGHT + }; + public static TaskStack.DockState[] PORTRAIT = { + TaskStack.DockState.TOP, TaskStack.DockState.BOTTOM + }; +} + +/** + * Handles touch events for a RecentsView. + */ +class RecentsViewTouchHandler { + + private RecentsView mRv; + + private Task mDragTask; + private TaskView mTaskView; + private DragView mDragView; + + private Point mDownPos = new Point(); + private boolean mDragging; + private TaskStack.DockState mLastDockState; + + public RecentsViewTouchHandler(RecentsView rv) { + mRv = rv; + } + + /** Touch preprocessing for handling below */ + public boolean onInterceptTouchEvent(MotionEvent ev) { + int action = ev.getAction(); + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + mDownPos.set((int) ev.getX(), (int) ev.getY()); + break; + } + return mDragging; + } + + /** Handles touch events once we have intercepted them */ + public boolean onTouchEvent(MotionEvent ev) { + if (!mDragging) return false; + + boolean isLandscape = mRv.getResources().getConfiguration().orientation == + Configuration.ORIENTATION_LANDSCAPE; + int action = ev.getAction(); + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + mDownPos.set((int) ev.getX(), (int) ev.getY()); + break; + case MotionEvent.ACTION_MOVE: { + int width = mRv.getMeasuredWidth(); + int height = mRv.getMeasuredHeight(); + float evX = ev.getX(); + float evY = ev.getY(); + float x = evX - mDragView.getTopLeftOffset().x; + float y = evY - mDragView.getTopLeftOffset().y; + + // Update the dock state + TaskStack.DockState[] dockStates = isLandscape ? + DockRegion.LANDSCAPE : DockRegion.PORTRAIT; + TaskStack.DockState foundDockState = null; + for (int i = 0; i < dockStates.length; i++) { + TaskStack.DockState state = dockStates[i]; + if (state.touchAreaContainsPoint(width, height, evX, evY)) { + foundDockState = state; + break; + } + } + if (mLastDockState != foundDockState) { + mLastDockState = foundDockState; + EventBus.getDefault().send(new DragDockStateChangedEvent(mDragTask, + foundDockState)); + } + + mDragView.setTranslationX(x); + mDragView.setTranslationY(y); + break; + } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger( + mRv.getContext(), null, null, null); + postAnimationTrigger.increment(); + EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView, mDragView, + mLastDockState, postAnimationTrigger)); + postAnimationTrigger.decrement(); + break; + } + } + return true; + } + + /**** Events ****/ + + public final void onBusEvent(DragStartEvent event) { + mRv.getParent().requestDisallowInterceptTouchEvent(true); + mDragging = true; + mDragTask = event.task; + mTaskView = event.taskView; + mDragView = event.dragView; + + float x = mDownPos.x - mDragView.getTopLeftOffset().x; + float y = mDownPos.y - mDragView.getTopLeftOffset().y; + mDragView.setTranslationX(x); + mDragView.setTranslationY(y); + } + + public final void onBusEvent(DragEndEvent event) { + mDragging = false; + mDragTask = null; + mTaskView = null; + mDragView = null; + mLastDockState = null; + } +} 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 67e3f8271132..78b986263213 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -1296,9 +1296,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal RecentsTaskLoader.getInstance().loadTaskData(task); // If the doze trigger has already fired, then update the state for this task view - if (mUIDozeTrigger.hasTriggered()) { - tv.setNoUserInteractionState(); - } + tv.setNoUserInteractionState(); // If we've finished the start animation, then ensure we always enable the focus animations if (mStartEnterAnimationCompleted) { 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 910db873e0b9..9d08ee907626 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -21,13 +21,17 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Outline; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; import android.view.ViewOutlineProvider; import android.view.animation.AccelerateInterpolator; @@ -36,17 +40,22 @@ import android.view.animation.Interpolator; import android.widget.FrameLayout; import com.android.systemui.R; import com.android.systemui.recents.Constants; +import com.android.systemui.recents.RecentsActivity; import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.ui.DismissTaskEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; +import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; import com.android.systemui.statusbar.phone.PhoneStatusBar; /* A task view */ public class TaskView extends FrameLayout implements Task.TaskCallbacks, - View.OnClickListener { + View.OnClickListener, View.OnLongClickListener { /** The TaskView callbacks */ interface TaskViewCallbacks { @@ -79,6 +88,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View mActionButtonView; TaskViewCallbacks mCb; + Point mDownTouchPos = new Point(); + Interpolator mFastOutSlowInInterpolator; Interpolator mFastOutLinearInInterpolator; Interpolator mQuintOutInterpolator; @@ -169,6 +180,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, } @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY())); + } + return super.onInterceptTouchEvent(ev); + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); @@ -608,8 +627,12 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, /** Compute the dim as a function of the scale of this view. */ int getDimFromTaskProgress() { + // TODO: Temporarily disable the dim on the stack + /* float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress); return (int) (dim * 255); + */ + return 0; } /** Update the dim as a function of the scale of this view. */ @@ -719,6 +742,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mHeaderView.rebindToTask(mTask); // Rebind any listeners mActionButtonView.setOnClickListener(this); + setOnLongClickListener(mConfig.hasDockedTasks ? null : this); } mTaskDataLoaded = true; } @@ -753,4 +777,69 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mCb.onTaskViewClicked(this, mTask, (v == mActionButtonView)); } } + + /**** View.OnLongClickListener Implementation ****/ + + @Override + public boolean onLongClick(View v) { + if (v == this) { + // Start listening for drag events + setClipViewInStack(false); + + int width = (int) (getScaleX() * getWidth()); + int height = (int) (getScaleY() * getHeight()); + Bitmap dragBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(dragBitmap); + c.scale(getScaleX(), getScaleY()); + mThumbnailView.draw(c); + mHeaderView.draw(c); + c.setBitmap(null); + + // Initiate the drag + final DragView dragView = new DragView(getContext(), dragBitmap, mDownTouchPos); + dragView.setOutlineProvider(mViewBounds); + dragView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + // Hide this task view after the drag view is attached + setVisibility(View.INVISIBLE); + // Animate the alpha slightly to indicate dragging + dragView.setElevation(getElevation()); + dragView.setTranslationZ(getTranslationZ()); + dragView.animate() + .alpha(0.75f) + .setDuration(175) + .setInterpolator(new AccelerateInterpolator(1.5f)) + .withLayer() + .start(); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); + EventBus.getDefault().send(new DragStartEvent(mTask, this, dragView)); + return true; + } + return false; + } + + /**** Events ****/ + + public final void onBusEvent(DragEndEvent event) { + event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { + @Override + public void run() { + // If docked state == null: + // Animate the drag view back from where it is, to the view location, then after it returns, + // update the clip state + setClipViewInStack(true); + + // Show this task view + setVisibility(View.VISIBLE); + } + }); + EventBus.getDefault().unregister(this); + } } 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 7de8b7be5134..949d515a75f0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -244,11 +244,9 @@ public class TaskViewHeader extends FrameLayout mLightDismissDrawable : mDarkDismissDrawable); mDismissButton.setContentDescription(String.format(mDismissContentDescription, t.contentDescription)); - mMoveTaskButton.setVisibility((mConfig.multiWindowEnabled) ? View.VISIBLE : View.INVISIBLE); - if (mConfig.multiWindowEnabled) { - updateResizeTaskBarIcon(t); - mMoveTaskButton.setOnClickListener(this); - } + updateResizeTaskBarIcon(t); + mMoveTaskButton.setVisibility(View.VISIBLE); + mMoveTaskButton.setOnClickListener(this); // In accessibility, a single click on the focused app info button will show it AccessibilityManager am = (AccessibilityManager) getContext(). @@ -263,10 +261,7 @@ public class TaskViewHeader extends FrameLayout mTask = null; mApplicationIcon.setImageDrawable(null); mApplicationIcon.setOnClickListener(null); - - if (mConfig.multiWindowEnabled) { - mMoveTaskButton.setOnClickListener(null); - } + mMoveTaskButton.setOnClickListener(null); } /** Updates the resize task bar button. */ @@ -471,7 +466,7 @@ public class TaskViewHeader extends FrameLayout // In accessibility, a single click on the focused app info button will show it EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask)); } else if (v == mDismissButton) { - TaskView tv = (TaskView) getParent().getParent(); + TaskView tv = Utilities.findParent(this, TaskView.class); tv.dismissTask(); // Keep track of deletions by the dismiss button diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java new file mode 100644 index 000000000000..497f04494051 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java @@ -0,0 +1,46 @@ +/* + * 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.statusbar.phone; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.RelativeLayout; +import com.android.systemui.qs.QSPanel; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.NetworkControllerImpl; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.UserInfoController; + +public abstract class BaseStatusBarHeader extends RelativeLayout implements + NetworkControllerImpl.EmergencyListener { + + public BaseStatusBarHeader(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public abstract int getCollapsedHeight(); + public abstract int getExpandedHeight(); + public abstract void setExpanded(boolean b); + public abstract void setExpansion(float headerExpansionFraction); + public abstract void setListening(boolean listening); + public abstract void updateEverything(); + public abstract void setActivityStarter(ActivityStarter activityStarter); + public abstract void setQSPanel(QSPanel qSPanel); + public abstract void setBatteryController(BatteryController batteryController); + public abstract void setNextAlarmController(NextAlarmController nextAlarmController); + public abstract void setUserInfoController(UserInfoController userInfoController); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java index 364e884e816e..86f8f5a9a3e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static android.app.ActivityManager.INVALID_STACK_ID; + import android.animation.LayoutTransition; import android.annotation.Nullable; import android.app.ActivityManager; @@ -742,9 +744,9 @@ class NavigationBarApps extends LinearLayout private void activateTask(int taskPersistentId) { // Launch or bring the activity to front. - IActivityManager manager = ActivityManagerNative.getDefault(); + final IActivityManager iAm = ActivityManagerNative.getDefault(); try { - manager.startActivityFromRecents(taskPersistentId, null /* options */); + iAm.startActivityFromRecents(taskPersistentId, INVALID_STACK_ID, null /* options */); } catch (RemoteException e) { Slog.e(TAG, "Exception when activating a recent task", e); } catch (IllegalArgumentException e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 011889a49c86..33e514d0a79b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -187,15 +187,6 @@ public class NavigationBarView extends LinearLayout { mBarTransitions = new NavigationBarTransitions(this); } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - ViewRootImpl root = getViewRootImpl(); - if (root != null) { - root.setDrawDuringWindowsAnimating(true); - } - } - public BarTransitions getBarTransitions() { return mBarTransitions; } 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 08353cbb51c3..3453652404b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -92,7 +92,7 @@ public class NotificationPanelView extends PanelView implements public static final long DOZE_ANIMATION_DURATION = 700; private KeyguardAffordanceHelper mAfforanceHelper; - private StatusBarHeaderView mHeader; + private BaseStatusBarHeader mHeader; private KeyguardUserSwitcher mKeyguardUserSwitcher; private KeyguardStatusBarView mKeyguardStatusBar; private QSContainer mQsContainer; @@ -232,7 +232,7 @@ public class NotificationPanelView extends PanelView implements @Override protected void onFinishInflate() { super.onFinishInflate(); - mHeader = (StatusBarHeaderView) findViewById(R.id.header); + mHeader = (BaseStatusBarHeader) findViewById(R.id.header); mHeader.setOnClickListener(this); mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header); mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view); 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 37edc28b765d..ba50a415fae2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -308,7 +308,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private QSPanel mQSPanel; // top bar - StatusBarHeaderView mHeader; + BaseStatusBarHeader mHeader; KeyguardStatusBarView mKeyguardStatusBar; View mKeyguardStatusView; KeyguardBottomAreaView mKeyguardBottomArea; @@ -800,7 +800,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mStatusBarView.setScrimController(mScrimController); mDozeScrimController = new DozeScrimController(mScrimController, context); - mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); + mHeader = (BaseStatusBarHeader) mStatusBarWindow.findViewById(R.id.header); mHeader.setActivityStarter(this); mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header); mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java index e9c4e49d7b11..7f5ffafa3b44 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -57,9 +57,9 @@ import java.text.NumberFormat; /** * The view to manage the header area in the expanded status bar. */ -public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener, +public class StatusBarHeaderView extends BaseStatusBarHeader implements View.OnClickListener, BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback, - EmergencyListener { + EmergencyListener, TunerService.Tunable { private boolean mExpanded; private boolean mListening; @@ -128,6 +128,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL private boolean mShowingDetail; private boolean mDetailTransitioning; + private boolean mAllowExpand = true; + public StatusBarHeaderView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -232,6 +234,28 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL updateClockCollapsedMargin(); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + TunerService.get(mContext).addTunable(this, QSPanel.QS_PAGED_PANEL); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + TunerService.get(mContext).removeTunable(this); + } + + @Override + public void onTuningChanged(String key, String newValue) { + if (QSPanel.QS_PAGED_PANEL.equals(key)) { + mAllowExpand = newValue == null || Integer.parseInt(newValue) == 0; + if (!mAllowExpand) { + setExpanded(false); + } + } + } + private void updateClockCollapsedMargin() { Resources res = getResources(); int padding = res.getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin); @@ -290,7 +314,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } public int getExpandedHeight() { - return mExpandedHeight; + return mAllowExpand ? mExpandedHeight : mCollapsedHeight; } public void setListening(boolean listening) { @@ -302,6 +326,9 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } public void setExpanded(boolean expanded) { + if (!mAllowExpand) { + expanded = false; + } boolean changed = expanded != mExpanded; mExpanded = expanded; if (changed) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index cfd33589588c..35a17e45c69c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -144,13 +144,6 @@ public class StatusBarWindowView extends FrameLayout { protected void onAttachedToWindow () { super.onAttachedToWindow(); - // We really need to be able to animate while window animations are going on - // so that activities may be started asynchronously from panel animations - final ViewRootImpl root = getViewRootImpl(); - if (root != null) { - root.setDrawDuringWindowsAnimating(true); - } - // We need to ensure that our window doesn't suffer from overdraw which would normally // occur if our window is translucent. Since we are drawing the whole window anyway with // the scrim, we don't need the window to be cleared in the beginning. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java index 40984d477cff..f06e5d354a12 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java @@ -22,6 +22,7 @@ public interface SecurityController { String getDeviceOwnerName(); String getProfileOwnerName(); boolean isVpnEnabled(); + boolean isVpnRestricted(); String getPrimaryVpnName(); String getProfileVpnName(); void onUserSwitched(int newUserId); 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 e0823b41554d..88f028fa4b7d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -162,6 +162,13 @@ public class SecurityControllerImpl implements SecurityController { } @Override + public boolean isVpnRestricted() { + UserHandle currentUser = new UserHandle(mCurrentUserId); + return mUserManager.getUserInfo(mCurrentUserId).isRestricted() + || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, currentUser); + } + + @Override public void removeCallback(SecurityControllerCallback callback) { synchronized (mCallbacks) { if (callback == null) return; diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java index 703ee6610314..3ac2a943034d 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java @@ -350,6 +350,11 @@ public class QsTuner extends Fragment implements Callback { } @Override + public boolean isVpnRestricted() { + return false; + } + + @Override public String getPrimaryVpnName() { return null; } diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java index 48e05823f625..f0ca44162dad 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java @@ -18,8 +18,11 @@ package com.android.vpndialogs; import android.content.Context; import android.content.DialogInterface; +import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.net.IConnectivityManager; +import android.os.Bundle; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.text.Html; @@ -40,43 +43,47 @@ public class ConfirmDialog extends AlertActivity private IConnectivityManager mService; - private Button mButton; - @Override - protected void onResume() { - super.onResume(); - try { - mPackage = getCallingPackage(); - - mService = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - - if (mService.prepareVpn(mPackage, null, UserHandle.myUserId())) { - setResult(RESULT_OK); - finish(); - return; - } - - View view = View.inflate(this, R.layout.confirm, null); - - ((TextView) view.findViewById(R.id.warning)).setText( - Html.fromHtml( - getString(R.string.warning, VpnConfig.getVpnLabel(this, mPackage)), - this, null /* tagHandler */)); + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPackage = getCallingPackage(); + mService = IConnectivityManager.Stub.asInterface( + ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); + + if (prepareVpn()) { + setResult(RESULT_OK); + finish(); + return; + } + View view = View.inflate(this, R.layout.confirm, null); + ((TextView) view.findViewById(R.id.warning)).setText( + Html.fromHtml(getString(R.string.warning, getVpnLabel()), + this, null /* tagHandler */)); + mAlertParams.mTitle = getText(R.string.prompt); + mAlertParams.mPositiveButtonText = getText(android.R.string.ok); + mAlertParams.mPositiveButtonListener = this; + mAlertParams.mNegativeButtonText = getText(android.R.string.cancel); + mAlertParams.mView = view; + setupAlert(); + + getWindow().setCloseOnTouchOutside(false); + Button button = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); + button.setFilterTouchesWhenObscured(true); + } - mAlertParams.mTitle = getText(R.string.prompt); - mAlertParams.mPositiveButtonText = getText(android.R.string.ok); - mAlertParams.mPositiveButtonListener = this; - mAlertParams.mNegativeButtonText = getText(android.R.string.cancel); - mAlertParams.mView = view; - setupAlert(); + private boolean prepareVpn() { + try { + return mService.prepareVpn(mPackage, null, UserHandle.myUserId()); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + } - getWindow().setCloseOnTouchOutside(false); - mButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); - mButton.setFilterTouchesWhenObscured(true); - } catch (Exception e) { - Log.e(TAG, "onResume", e); - finish(); + private CharSequence getVpnLabel() { + try { + return VpnConfig.getVpnLabel(this, mPackage); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalStateException(e); } } diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index 5a27301badcb..7eb80055c4d5 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -1585,15 +1585,20 @@ public class RenderScript { mMessageThread.mRun = false; // Wait for mMessageThread to join. Try in a loop, in case this thread gets interrupted - // during the wait. - boolean hasJoined = false; + // during the wait. If interrupted, set the "interrupted" status of the current thread. + boolean hasJoined = false, interrupted = false; while (!hasJoined) { try { mMessageThread.join(); hasJoined = true; - } catch(InterruptedException e) { + } catch (InterruptedException e) { + interrupted = true; } } + if (interrupted) { + Log.v(LOG_TAG, "Interrupted during wait for MessageThread to join"); + Thread.currentThread().interrupt(); + } nContextDestroy(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 617264c29efd..91eaf934cd7e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -36,6 +36,7 @@ import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; import static com.android.server.am.ActivityManagerDebugConfig.*; import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; +import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; @@ -2735,7 +2736,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (ActivityManagerService.this) { ActivityStack stack = mStackSupervisor.getStack(stackId); if (stack != null) { - ActivityRecord r = stack.topRunningActivityLocked(null); + ActivityRecord r = stack.topRunningActivityLocked(); if (r != null) { setFocusedActivityLocked(r, "setFocusedStack"); mStackSupervisor.resumeTopActivitiesLocked(stack, null, null); @@ -2752,7 +2753,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (ActivityManagerService.this) { TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); if (task != null) { - ActivityRecord r = task.topRunningActivityLocked(null); + ActivityRecord r = task.topRunningActivityLocked(); if (r != null) { setFocusedActivityLocked(r, "setFocusedTask"); mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null); @@ -4188,27 +4189,39 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public final int startActivityFromRecents(int taskId, Bundle options) { + public final int startActivityFromRecents(int taskId, int launchStackId, Bundle options) { if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: startActivityFromRecents called without " + START_TASKS_FROM_RECENTS; Slog.w(TAG, msg); throw new SecurityException(msg); } - return startActivityFromRecentsInner(taskId, options); + return startActivityFromRecentsInner(taskId, launchStackId, options); } - final int startActivityFromRecentsInner(int taskId, Bundle options) { + final int startActivityFromRecentsInner(int taskId, int launchStackId, Bundle options) { final TaskRecord task; final int callingUid; final String callingPackage; final Intent intent; final int userId; synchronized (this) { - task = mStackSupervisor.anyTaskForIdLocked(taskId); + if (launchStackId == HOME_STACK_ID) { + throw new IllegalArgumentException("startActivityFromRecentsInner: Task " + + taskId + " can't be launch in the home stack."); + } + + task = mStackSupervisor.anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId); if (task == null) { - throw new IllegalArgumentException("Task " + taskId + " not found."); + throw new IllegalArgumentException( + "startActivityFromRecentsInner: Task " + taskId + " not found."); + } + + if (launchStackId != INVALID_STACK_ID && task.stack.mStackId != launchStackId) { + mStackSupervisor.moveTaskToStackUncheckedLocked( + task, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents"); } + if (task.getRootActivity() != null) { moveTaskToFrontLocked(task.taskId, 0, options); return ActivityManager.START_TASK_TO_FRONT; @@ -8550,7 +8563,8 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, "getTaskThumbnail()"); - TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id, false); + final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked( + id, !RESTORE_FROM_RECENTS, INVALID_STACK_ID); if (tr != null) { return tr.getTaskThumbnailLocked(); } @@ -8663,7 +8677,8 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void setTaskResizeable(int taskId, boolean resizeable) { synchronized (this) { - TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, false); + final TaskRecord task = mStackSupervisor.anyTaskForIdLocked( + taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID); if (task == null) { Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found"); return; @@ -8874,7 +8889,8 @@ public final class ActivityManagerService extends ActivityManagerNative * @return Returns true if the given task was found and removed. */ private boolean removeTaskByIdLocked(int taskId, boolean killProcess) { - TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId, false); + final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked( + taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID); if (tr != null) { tr.removeTaskActivitiesLocked(); cleanUpRemovedTaskLocked(tr, killProcess); @@ -9203,7 +9219,8 @@ public final class ActivityManagerService extends ActivityManagerNative long ident = Binder.clearCallingIdentity(); try { synchronized (this) { - TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId, false); + final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked( + taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID); return tr != null && tr.stack != null && tr.stack.isHomeStack(); } } finally { @@ -11336,7 +11353,7 @@ public final class ActivityManagerService extends ActivityManagerNative public boolean isTopActivityImmersive() { enforceNotIsolatedCaller("startActivity"); synchronized (this) { - ActivityRecord r = getFocusedStack().topRunningActivityLocked(null); + ActivityRecord r = getFocusedStack().topRunningActivityLocked(); return (r != null) ? r.immersive : false; } } @@ -17555,6 +17572,24 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public void removeStack(int stackId) { + enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, + "detahStack()"); + if (stackId == HOME_STACK_ID) { + throw new IllegalArgumentException("Removing home stack is not allowed."); + } + synchronized (this) { + ActivityStack stack = mStackSupervisor.getStack(stackId); + if (stack != null) { + ArrayList<TaskRecord> tasks = stack.getAllTasks(); + for (int i = tasks.size() - 1; i >= 0; i--) { + removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */); + } + } + } + } + + @Override public void updatePersistentConfiguration(Configuration values) { enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, "updateConfiguration()"); @@ -17727,7 +17762,7 @@ public final class ActivityManagerService extends ActivityManagerNative // If the configuration changed, and the caller is not already // in the process of starting an activity, then find the top // activity to check if its configuration needs to change. - starting = mainStack.topRunningActivityLocked(null); + starting = mainStack.topRunningActivityLocked(); } if (starting != null) { @@ -21071,7 +21106,7 @@ public final class ActivityManagerService extends ActivityManagerNative public void moveToFront() { checkCaller(); // Will bring task to front if it already has a root activity. - startActivityFromRecentsInner(mTaskId, null); + startActivityFromRecentsInner(mTaskId, INVALID_STACK_ID, null); } @Override diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index d4e798fc45b8..4671cb0a94e3 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -774,7 +774,7 @@ final class ActivityRecord { boolean unsent = true; if ((state == ActivityState.RESUMED || (service.isSleeping() && task.stack != null - && task.stack.topRunningActivityLocked(null) == this)) + && task.stack.topRunningActivityLocked() == this)) && app != null && app.thread != null) { try { ArrayList<ReferrerIntent> ar = new ArrayList<>(1); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index f549af8c0c53..cdb00ef17823 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -403,9 +403,9 @@ final class ActivityStack { return mStackSupervisor.okToShowLocked(r); } - final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { + final ActivityRecord topRunningActivityLocked() { for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { - ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked(notTop); + ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked(); if (r != null) { return r; } @@ -689,7 +689,7 @@ final class ActivityStack { // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is // okay to show the activity when locked. if (mStackSupervisor.isCurrentProfileLocked(task.userId) - || task.topRunningActivityLocked(null) != null) { + || task.topRunningActivityLocked() != null) { if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() + " moving " + task + " to top"); mTaskHistory.remove(i); @@ -1105,7 +1105,7 @@ final class ActivityStack { mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null); } else { mStackSupervisor.checkReadyForSleepLocked(); - ActivityRecord top = topStack.topRunningActivityLocked(null); + ActivityRecord top = topStack.topRunningActivityLocked(); if (top == null || (prev != null && top != prev)) { // If there are no more activities available to run, // do resume anyway to start something. Also if the top @@ -1326,7 +1326,7 @@ final class ActivityStack { if (focusedStackId != HOME_STACK_ID) { return true; } - ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked(null); + ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked(); return topHomeActivity == null || !topHomeActivity.isHomeActivity(); } @@ -1387,7 +1387,7 @@ final class ActivityStack { */ final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, boolean preserveWindows) { - ActivityRecord top = topRunningActivityLocked(null); + ActivityRecord top = topRunningActivityLocked(); if (top == null) { return; } @@ -1643,7 +1643,7 @@ final class ActivityStack { * starting window displayed then remove that starting window. It is possible that the activity * in this state will never resumed in which case that starting window will be orphaned. */ void cancelInitializingActivities() { - final ActivityRecord topActivity = topRunningActivityLocked(null); + final ActivityRecord topActivity = topRunningActivityLocked(); boolean aboveTop = true; for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; @@ -1719,7 +1719,7 @@ final class ActivityStack { cancelInitializingActivities(); // Find the first activity that is not finishing. - final ActivityRecord next = topRunningActivityLocked(null); + final ActivityRecord next = topRunningActivityLocked(); // Remember how we'll process this pause/resume situation, and ensure // that the state is reset however we wind up proceeding. @@ -2037,7 +2037,7 @@ final class ActivityStack { // We should be all done, but let's just make sure our activity // is still at the top and schedule another run if something // weird happened. - ActivityRecord nextNext = topRunningActivityLocked(null); + ActivityRecord nextNext = topRunningActivityLocked(); if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES, "Activity config changed during resume: " + next + ", new next: " + nextNext); @@ -2170,12 +2170,12 @@ final class ActivityStack { // Calculate maximum possible position for this task. int maxPosition = mTaskHistory.size(); if (!mStackSupervisor.isCurrentProfileLocked(task.userId) - && task.topRunningActivityLocked(null) == null) { + && task.topRunningActivityLocked() == null) { // Put non-current user tasks below current user tasks. while (maxPosition > 0) { final TaskRecord tmpTask = mTaskHistory.get(maxPosition - 1); if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId) - || tmpTask.topRunningActivityLocked(null) == null) { + || tmpTask.topRunningActivityLocked() == null) { break; } maxPosition--; @@ -2217,13 +2217,13 @@ final class ActivityStack { int taskNdx = mTaskHistory.size(); final boolean notShownWhenLocked = (newActivity != null && (newActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) == 0) - || (newActivity == null && task.topRunningActivityLocked(null) == null); + || (newActivity == null && task.topRunningActivityLocked() == null); if (!mStackSupervisor.isCurrentProfileLocked(task.userId) && notShownWhenLocked) { // Put non-current user tasks below current user tasks. while (--taskNdx >= 0) { final TaskRecord tmpTask = mTaskHistory.get(taskNdx); if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId) - || tmpTask.topRunningActivityLocked(null) == null) { + || tmpTask.topRunningActivityLocked() == null) { break; } } @@ -2769,7 +2769,7 @@ final class ActivityStack { private void adjustFocusedActivityLocked(ActivityRecord r, String reason) { if (mStackSupervisor.isFrontStack(this) && mService.mFocusedActivity == r) { - ActivityRecord next = topRunningActivityLocked(null); + ActivityRecord next = topRunningActivityLocked(); final String myReason = reason + " adjustFocus"; if (next != r) { if (next != null && (mStackId == FREEFORM_WORKSPACE_STACK_ID @@ -2812,7 +2812,7 @@ final class ActivityStack { if (stack == null) { return false; } - final ActivityRecord top = stack.topRunningActivityLocked(null); + final ActivityRecord top = stack.topRunningActivityLocked(); if (top == null) { return false; } @@ -2913,7 +2913,7 @@ final class ActivityStack { } final void finishTopRunningActivityLocked(ProcessRecord app, String reason) { - ActivityRecord r = topRunningActivityLocked(null); + ActivityRecord r = topRunningActivityLocked(); if (r != null && r.app == app) { // If the top running activity is from this crashing // process, then terminate it to avoid getting in a loop. @@ -3618,7 +3618,7 @@ final class ActivityStack { void releaseBackgroundResources(ActivityRecord r) { if (hasVisibleBehindActivity() && !mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) { - if (r == topRunningActivityLocked(null)) { + if (r == topRunningActivityLocked()) { // Don't release the top activity if it has requested to run behind the next // activity. return; @@ -3767,7 +3767,7 @@ final class ActivityStack { final void updateTransitLocked(int transit, Bundle options) { if (options != null) { - ActivityRecord r = topRunningActivityLocked(null); + ActivityRecord r = topRunningActivityLocked(); if (r != null && r.state != ActivityState.RESUMED) { r.updateOptionsLocked(options); } else { @@ -3840,7 +3840,7 @@ final class ActivityStack { } // Set focus to the top running activity of this stack. - ActivityRecord r = topRunningActivityLocked(null); + ActivityRecord r = topRunningActivityLocked(); mService.setFocusedActivityLocked(r, reason); if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr); @@ -4473,7 +4473,7 @@ final class ActivityStack { } ActivityRecord restartPackage(String packageName) { - ActivityRecord starting = topRunningActivityLocked(null); + ActivityRecord starting = topRunningActivityLocked(); // All activities that came from the package must be // restarted as if there was a config change. @@ -4545,7 +4545,8 @@ final class ActivityStack { if (mTaskHistory.isEmpty()) { if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this); - if (isOnHomeDisplay()) { + // We only need to adjust focused stack if this stack is in focus. + if (isOnHomeDisplay() && mStackSupervisor.isFrontStack(this)) { String myReason = reason + " leftTaskHistoryEmpty"; if (mFullscreen || !adjustFocusToNextVisibleStackLocked(null, myReason)) { mStackSupervisor.moveHomeStackToFront(myReason); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index ac7b9b1400c2..cda9a5dc22bf 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -66,7 +66,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; -import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; @@ -191,6 +190,9 @@ public final class ActivityStackSupervisor implements DisplayListener { // Force the focus to change to the stack we are moving a task to.. static final boolean FORCE_FOCUS = true; + // Restore task from the saved recents if it can't be found in any live stack. + static final boolean RESTORE_FROM_RECENTS = true; + // Activity actions an app cannot start if it uses a permission which is not granted. private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION = new ArrayMap<>(); @@ -541,7 +543,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } TaskRecord anyTaskForIdLocked(int id) { - return anyTaskForIdLocked(id, true); + return anyTaskForIdLocked(id, RESTORE_FROM_RECENTS, INVALID_STACK_ID); } /** @@ -549,8 +551,10 @@ public final class ActivityStackSupervisor implements DisplayListener { * @param id Id of the task we would like returned. * @param restoreFromRecents If the id was not in the active list, but was found in recents, * restore the task from recents to the active list. + * @param stackId The stack to restore the task to (default launch stack will be used + * if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}). */ - TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents) { + TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents, int stackId) { int numDisplays = mActivityDisplays.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; @@ -575,7 +579,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return task; } - if (!restoreRecentTaskLocked(task, INVALID_STACK_ID)) { + if (!restoreRecentTaskLocked(task, stackId)) { if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Couldn't restore task id=" + id + " found in recents"); return null; @@ -610,7 +614,7 @@ public final class ActivityStackSupervisor implements DisplayListener { if (mCurTaskId <= 0) { mCurTaskId = 1; } - } while (anyTaskForIdLocked(mCurTaskId, false) != null); + } while (anyTaskForIdLocked(mCurTaskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID) != null); return mCurTaskId; } @@ -623,7 +627,7 @@ public final class ActivityStackSupervisor implements DisplayListener { if (resumedActivity == null || resumedActivity.app == null) { resumedActivity = stack.mPausingActivity; if (resumedActivity == null || resumedActivity.app == null) { - resumedActivity = stack.topRunningActivityLocked(null); + resumedActivity = stack.topRunningActivityLocked(); } } return resumedActivity; @@ -639,7 +643,7 @@ public final class ActivityStackSupervisor implements DisplayListener { if (!isFrontStack(stack)) { continue; } - ActivityRecord hr = stack.topRunningActivityLocked(null); + ActivityRecord hr = stack.topRunningActivityLocked(); if (hr != null) { if (hr.app == null && app.uid == hr.info.applicationInfo.uid && processName.equals(hr.processName)) { @@ -823,7 +827,7 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityRecord topRunningActivityLocked() { final ActivityStack focusedStack = mFocusedStack; - ActivityRecord r = focusedStack.topRunningActivityLocked(null); + ActivityRecord r = focusedStack.topRunningActivityLocked(); if (r != null) { return r; } @@ -833,7 +837,7 @@ public final class ActivityStackSupervisor implements DisplayListener { for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = stacks.get(stackNdx); if (stack != focusedStack && isFrontStack(stack)) { - r = stack.topRunningActivityLocked(null); + r = stack.topRunningActivityLocked(); if (r != null) { return r; } @@ -1096,7 +1100,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } } while (!outResult.timeout && outResult.who == null); } else if (res == ActivityManager.START_TASK_TO_FRONT) { - ActivityRecord r = stack.topRunningActivityLocked(null); + ActivityRecord r = stack.topRunningActivityLocked(); if (r.nowVisible && r.state == RESUMED) { outResult.timeout = false; outResult.who = new ComponentName(r.info.packageName, r.info.name); @@ -1799,10 +1803,14 @@ public final class ActivityStackSupervisor implements DisplayListener { return container.mStack; } - // The fullscreen stack is the only stack that can contain any task regardless of if - // the task is resizeable or not. So, we let the task go in the fullscreen task if it - // is the focus stack. - if (mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID + // The fullscreen stack can contain any task regardless of if the task is resizeable + // or not. So, we let the task go in the fullscreen task if it is the focus stack. + // If the freeform stack has focus, and the activity to be launched is resizeable, + // we can also put it in the focused stack. + final boolean canUseFocusedStack = + mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID + || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable; + if (canUseFocusedStack && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: Have a focused stack=" + mFocusedStack); @@ -2979,7 +2987,7 @@ public final class ActivityStackSupervisor implements DisplayListener { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId); - ActivityRecord r = stack.topRunningActivityLocked(null); + ActivityRecord r = stack.topRunningActivityLocked(); mTmpBounds.clear(); mTmpConfigs.clear(); @@ -3104,7 +3112,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // to be relaunched due to configuration change. boolean kept = true; if (overrideConfig != null) { - ActivityRecord r = task.topRunningActivityLocked(null); + ActivityRecord r = task.topRunningActivityLocked(); if (r != null) { final ActivityStack stack = task.stack; kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow); @@ -3147,7 +3155,7 @@ public final class ActivityStackSupervisor implements DisplayListener { * Restores a recent task to a stack * @param task The recent task to be restored. * @param stackId The stack to restore the task to (default launch stack will be used - * if stackId is invalid). + * if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}). * @return true if the task has been restored successfully. */ private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) { @@ -3246,7 +3254,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // 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. + // entrance of the new window to be properly animated. ActivityRecord r = task.getTopActivity(); mWindowManager.setReplacingWindow(r.appToken, true /* animate */); } @@ -3735,7 +3743,7 @@ public final class ActivityStackSupervisor implements DisplayListener { final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = stacks.get(stackNdx); - final ActivityRecord r = stack.topRunningActivityLocked(null); + final ActivityRecord r = stack.topRunningActivityLocked(); final ActivityState state = r == null ? DESTROYED : r.state; if (isFrontStack(stack)) { if (r == null) Slog.e(TAG, diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java index c36fd06f633b..26264e5da147 100644 --- a/services/core/java/com/android/server/am/CompatModePackages.java +++ b/services/core/java/com/android/server/am/CompatModePackages.java @@ -196,7 +196,7 @@ public final class CompatModePackages { } public boolean getFrontActivityAskCompatModeLocked() { - ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null); + ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(); if (r == null) { return false; } @@ -208,7 +208,7 @@ public final class CompatModePackages { } public void setFrontActivityAskCompatModeLocked(boolean ask) { - ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null); + ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(); if (r != null) { setPackageAskCompatModeLocked(r.packageName, ask); } @@ -230,7 +230,7 @@ public final class CompatModePackages { } public int getFrontActivityScreenCompatModeLocked() { - ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null); + ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(); if (r == null) { return ActivityManager.COMPAT_MODE_UNKNOWN; } @@ -238,7 +238,7 @@ public final class CompatModePackages { } public void setFrontActivityScreenCompatModeLocked(int mode) { - ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null); + ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(); if (r == null) { Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity"); return; diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 7e14b2b78f0f..77dbad4340ed 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -557,11 +557,11 @@ final class TaskRecord { return null; } - ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { + ActivityRecord topRunningActivityLocked() { if (stack != null) { for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { ActivityRecord r = mActivities.get(activityNdx); - if (!r.finishing && r != notTop && stack.okToShowLocked(r)) { + if (!r.finishing && stack.okToShowLocked(r)) { return r; } } diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java index f03e6c7c545a..359cc360bbc7 100644 --- a/services/core/java/com/android/server/audio/RotationHelper.java +++ b/services/core/java/com/android/server/audio/RotationHelper.java @@ -192,16 +192,18 @@ class RotationHelper { } public void run() { - int newRotation; while (mWaitCounter < WAIT_TIMES_MS.length) { - updateOrientation(); int waitTimeMs; synchronized(mCounterLock) { - waitTimeMs = WAIT_TIMES_MS[mWaitCounter]; + waitTimeMs = mWaitCounter < WAIT_TIMES_MS.length ? + WAIT_TIMES_MS[mWaitCounter] : 0; mWaitCounter++; } try { - sleep(waitTimeMs); + if (waitTimeMs > 0) { + sleep(waitTimeMs); + updateOrientation(); + } } catch (InterruptedException e) { } } } diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index b89a654323f3..c2e434979c01 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -104,7 +104,7 @@ public class ZenModeConditions implements ConditionProviders.Callback { public void onServiceAdded(ComponentName component) { if (DEBUG) Log.d(TAG, "onServiceAdded " + component); if (isAutomaticActive(component)) { - mHelper.setConfig(mHelper.getConfig(), "zmc.onServiceAdded"); + mHelper.setConfigAsync(mHelper.getConfig(), "zmc.onServiceAdded"); } } @@ -120,7 +120,7 @@ public class ZenModeConditions implements ConditionProviders.Callback { updated |= updateSnoozing(automaticRule); } if (updated) { - mHelper.setConfig(config, "conditionChanged"); + mHelper.setConfigAsync(config, "conditionChanged"); } } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 4d41e3a9d734..07c4323c30a7 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -232,6 +232,8 @@ public class ZenModeHelper { ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); if (ruleId == null) { ruleId = newConfig.newRuleId(); + rule.id = ruleId; + rule.creationTime = System.currentTimeMillis(); rule.name = automaticZenRule.getName(); rule.component = automaticZenRule.getOwner(); } else { @@ -312,7 +314,8 @@ public class ZenModeHelper { private AutomaticZenRule createAutomaticZenRule(ZenRule rule) { return new AutomaticZenRule(rule.name, rule.component, rule.conditionId, - NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled); + NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled, + rule.id, rule.creationTime); } public void setManualZenMode(int zenMode, Uri conditionId, String reason) { @@ -446,6 +449,10 @@ public class ZenModeHelper { return setConfig(config, reason, true /*setRingerMode*/); } + public void setConfigAsync(ZenModeConfig config, String reason) { + mHandler.postSetConfig(config, reason); + } + private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) { final long identity = Binder.clearCallingIdentity(); try { @@ -640,7 +647,9 @@ public class ZenModeHelper { rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights); rule1.zenMode = Global.ZEN_MODE_ALARMS; rule1.component = ScheduleConditionProvider.COMPONENT; - config.automaticRules.put(config.newRuleId(), rule1); + rule1.id = config.newRuleId(); + rule1.creationTime = System.currentTimeMillis(); + config.automaticRules.put(rule1.id, rule1); final ScheduleInfo weekends = new ScheduleInfo(); weekends.days = ZenModeConfig.WEEKEND_DAYS; @@ -654,7 +663,9 @@ public class ZenModeHelper { rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends); rule2.zenMode = Global.ZEN_MODE_ALARMS; rule2.component = ScheduleConditionProvider.COMPONENT; - config.automaticRules.put(config.newRuleId(), rule2); + rule2.id = config.newRuleId(); + rule2.creationTime = System.currentTimeMillis(); + config.automaticRules.put(rule2.id, rule2); } private void appendDefaultEventRules(ZenModeConfig config) { @@ -669,7 +680,9 @@ public class ZenModeHelper { rule.conditionId = ZenModeConfig.toEventConditionId(events); rule.zenMode = Global.ZEN_MODE_ALARMS; rule.component = EventConditionProvider.COMPONENT; - config.automaticRules.put(config.newRuleId(), rule); + rule.id = config.newRuleId(); + rule.creationTime = System.currentTimeMillis(); + config.automaticRules.put(rule.id, rule); } private static int zenSeverity(int zen) { @@ -883,6 +896,17 @@ public class ZenModeHelper { private final class H extends Handler { private static final int MSG_DISPATCH = 1; private static final int MSG_METRICS = 2; + private static final int MSG_SET_CONFIG = 3; + + private final class ConfigMessageData { + public final ZenModeConfig config; + public final String reason; + + ConfigMessageData(ZenModeConfig config, String reason) { + this.config = config; + this.reason = reason; + } + } private static final long METRICS_PERIOD_MS = 6 * 60 * 60 * 1000; @@ -900,6 +924,10 @@ public class ZenModeHelper { sendEmptyMessageDelayed(MSG_METRICS, METRICS_PERIOD_MS); } + private void postSetConfig(ZenModeConfig config, String reason) { + sendMessage(obtainMessage(MSG_SET_CONFIG, new ConfigMessageData(config, reason))); + } + @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -909,6 +937,10 @@ public class ZenModeHelper { case MSG_METRICS: mMetrics.emit(); break; + case MSG_SET_CONFIG: + ConfigMessageData configData = (ConfigMessageData)msg.obj; + setConfig(configData.config, configData.reason); + break; } } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 31fc24bef4f9..341410d3522d 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -59,8 +59,6 @@ import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.Xml; -import com.google.android.collect.Sets; - import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.util.FastXmlSerializer; @@ -82,7 +80,6 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.Set; import libcore.io.IoUtils; @@ -147,10 +144,6 @@ public class UserManagerService extends IUserManager.Stub { */ private static final boolean CONFIG_PROFILES_SHARE_CREDENTIAL = true; - // Set of user restrictions, which can only be enforced by the system - private static final Set<String> SYSTEM_CONTROLLED_RESTRICTIONS = Sets.newArraySet( - UserManager.DISALLOW_RECORD_AUDIO); - static final int WRITE_USER_MSG = 1; static final int WRITE_USER_DELAY = 2*1000; // 2 seconds @@ -596,7 +589,7 @@ public class UserManagerService extends IUserManager.Stub { public void setUserRestriction(String key, boolean value, int userId) { checkManageUsersPermission("setUserRestriction"); synchronized (mPackagesLock) { - if (!SYSTEM_CONTROLLED_RESTRICTIONS.contains(key)) { + if (!UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS.contains(key)) { Bundle restrictions = getUserRestrictions(userId); restrictions.putBoolean(key, value); setUserRestrictionsInternalLocked(restrictions, userId); @@ -622,7 +615,7 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mPackagesLock) { final Bundle oldUserRestrictions = mUserRestrictions.get(userId); // Restore the original state of system controlled restrictions from oldUserRestrictions - for (String key : SYSTEM_CONTROLLED_RESTRICTIONS) { + for (String key : UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS) { restrictions.remove(key); if (oldUserRestrictions.containsKey(key)) { restrictions.putBoolean(key, oldUserRestrictions.getBoolean(key)); @@ -815,7 +808,8 @@ public class UserManagerService extends IUserManager.Stub { && type != XmlPullParser.END_TAG) { if (type == XmlPullParser.START_TAG) { if (parser.getName().equals(TAG_RESTRICTIONS)) { - readRestrictionsLocked(parser, mGuestRestrictions); + UserRestrictionsUtils + .readRestrictions(parser, mGuestRestrictions); } break; } @@ -978,7 +972,7 @@ public class UserManagerService extends IUserManager.Stub { serializer.endTag(null, TAG_NAME); Bundle restrictions = mUserRestrictions.get(userInfo.id); if (restrictions != null) { - writeRestrictionsLocked(serializer, restrictions); + UserRestrictionsUtils.writeRestrictions(serializer, restrictions, TAG_RESTRICTIONS); } serializer.endTag(null, TAG_USER); @@ -1016,7 +1010,8 @@ public class UserManagerService extends IUserManager.Stub { serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion)); serializer.startTag(null, TAG_GUEST_RESTRICTIONS); - writeRestrictionsLocked(serializer, mGuestRestrictions); + UserRestrictionsUtils + .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS); serializer.endTag(null, TAG_GUEST_RESTRICTIONS); final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { @@ -1036,45 +1031,6 @@ public class UserManagerService extends IUserManager.Stub { } } - private void writeRestrictionsLocked(XmlSerializer serializer, Bundle restrictions) - throws IOException { - serializer.startTag(null, TAG_RESTRICTIONS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_WIFI); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION); - writeBoolean(serializer, restrictions, - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_REMOVE_USER); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_VPN); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_TETHERING); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_NETWORK_RESET); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_FACTORY_RESET); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADD_USER); - writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_APPS_CONTROL); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_CALLS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_SMS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_FUN); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_BEAM); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_WALLPAPER); - writeBoolean(serializer, restrictions, UserManager.DISALLOW_SAFE_BOOT); - writeBoolean(serializer, restrictions, UserManager.ALLOW_PARENT_PROFILE_APP_LINKING); - serializer.endTag(null, TAG_RESTRICTIONS); - } - private UserInfo readUserLocked(int id) { int flags = 0; int serialNumber = id; @@ -1143,7 +1099,7 @@ public class UserManagerService extends IUserManager.Stub { name = parser.getText(); } } else if (TAG_RESTRICTIONS.equals(tag)) { - readRestrictionsLocked(parser, restrictions); + UserRestrictionsUtils.readRestrictions(parser, restrictions); } } } @@ -1172,60 +1128,6 @@ public class UserManagerService extends IUserManager.Stub { return null; } - private void readRestrictionsLocked(XmlPullParser parser, Bundle restrictions) - throws IOException { - readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_WIFI); - readBoolean(parser, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS); - readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS); - readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS); - readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION); - readBoolean(parser, restrictions, - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); - readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH); - readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); - readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS); - readBoolean(parser, restrictions, UserManager.DISALLOW_REMOVE_USER); - readBoolean(parser, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES); - readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_VPN); - readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_TETHERING); - readBoolean(parser, restrictions, UserManager.DISALLOW_NETWORK_RESET); - readBoolean(parser, restrictions, UserManager.DISALLOW_FACTORY_RESET); - readBoolean(parser, restrictions, UserManager.DISALLOW_ADD_USER); - readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS); - readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS); - readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); - readBoolean(parser, restrictions, UserManager.DISALLOW_APPS_CONTROL); - readBoolean(parser, restrictions, - UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA); - readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE); - readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME); - readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_CALLS); - readBoolean(parser, restrictions, UserManager.DISALLOW_SMS); - readBoolean(parser, restrictions, UserManager.DISALLOW_FUN); - readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS); - readBoolean(parser, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE); - readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_BEAM); - readBoolean(parser, restrictions, UserManager.DISALLOW_WALLPAPER); - readBoolean(parser, restrictions, UserManager.DISALLOW_SAFE_BOOT); - readBoolean(parser, restrictions, UserManager.ALLOW_PARENT_PROFILE_APP_LINKING); - } - - private void readBoolean(XmlPullParser parser, Bundle restrictions, - String restrictionKey) { - String value = parser.getAttributeValue(null, restrictionKey); - if (value != null) { - restrictions.putBoolean(restrictionKey, Boolean.parseBoolean(value)); - } - } - - private void writeBoolean(XmlSerializer xml, Bundle restrictions, String restrictionKey) - throws IOException { - if (restrictions.containsKey(restrictionKey)) { - xml.attribute(null, restrictionKey, - Boolean.toString(restrictions.getBoolean(restrictionKey))); - } - } - private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) { String valueString = parser.getAttributeValue(null, attr); if (valueString == null) return defaultValue; @@ -2142,7 +2044,13 @@ public class UserManagerService extends IUserManager.Stub { sb.append(" ago"); pw.println(sb); } + pw.println(" Restrictions:"); + UserRestrictionsUtils.dumpRestrictions( + pw, " ", mUserRestrictions.get(user.id)); } + pw.println(); + pw.println("Guest restrictions:"); + UserRestrictionsUtils.dumpRestrictions(pw, " ", mGuestRestrictions); } } diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java new file mode 100644 index 000000000000..db1fd2e28dfa --- /dev/null +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -0,0 +1,122 @@ +/* + * 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.pm; + +import com.google.android.collect.Sets; + +import com.android.internal.util.Preconditions; + +import android.os.Bundle; +import android.os.UserManager; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Set; + +public class UserRestrictionsUtils { + private UserRestrictionsUtils() { + } + + public static final String[] USER_RESTRICTIONS = { + UserManager.DISALLOW_CONFIG_WIFI, + UserManager.DISALLOW_MODIFY_ACCOUNTS, + UserManager.DISALLOW_INSTALL_APPS, + UserManager.DISALLOW_UNINSTALL_APPS, + UserManager.DISALLOW_SHARE_LOCATION, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, + UserManager.DISALLOW_CONFIG_BLUETOOTH, + UserManager.DISALLOW_USB_FILE_TRANSFER, + UserManager.DISALLOW_CONFIG_CREDENTIALS, + UserManager.DISALLOW_REMOVE_USER, + UserManager.DISALLOW_DEBUGGING_FEATURES, + UserManager.DISALLOW_CONFIG_VPN, + UserManager.DISALLOW_CONFIG_TETHERING, + UserManager.DISALLOW_NETWORK_RESET, + UserManager.DISALLOW_FACTORY_RESET, + UserManager.DISALLOW_ADD_USER, + UserManager.ENSURE_VERIFY_APPS, + UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, + UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, + UserManager.DISALLOW_APPS_CONTROL, + UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, + UserManager.DISALLOW_UNMUTE_MICROPHONE, + UserManager.DISALLOW_ADJUST_VOLUME, + UserManager.DISALLOW_OUTGOING_CALLS, + UserManager.DISALLOW_SMS, + UserManager.DISALLOW_FUN, + UserManager.DISALLOW_CREATE_WINDOWS, + UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, + UserManager.DISALLOW_OUTGOING_BEAM, + UserManager.DISALLOW_WALLPAPER, + UserManager.DISALLOW_SAFE_BOOT, + UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, + UserManager.DISALLOW_RECORD_AUDIO, + }; + + /** + * Set of user restrictions, which can only be enforced by the system. + */ + public static final Set<String> SYSTEM_CONTROLLED_USER_RESTRICTIONS = Sets.newArraySet( + UserManager.DISALLOW_RECORD_AUDIO); + + /** + * Set of user restriction which we don't want to persist. + */ + public static final Set<String> NON_PERSIST_USER_RESTRICTIONS = Sets.newArraySet( + UserManager.DISALLOW_RECORD_AUDIO); + + public static void writeRestrictions(XmlSerializer serializer, Bundle restrictions, + String tag) throws IOException { + serializer.startTag(null, tag); + for (String key : USER_RESTRICTIONS) { + // + if (restrictions.getBoolean(key) + && !NON_PERSIST_USER_RESTRICTIONS.contains(key)) { + serializer.attribute(null, key, "true"); + } + } + serializer.endTag(null, tag); + } + + public static void readRestrictions(XmlPullParser parser, Bundle restrictions) + throws IOException { + for (String key : USER_RESTRICTIONS) { + final String value = parser.getAttributeValue(null, key); + if (value != null) { + restrictions.putBoolean(key, Boolean.parseBoolean(value)); + } + } + } + + public static void dumpRestrictions(PrintWriter pw, String prefix, Bundle restrictions) { + boolean noneSet = true; + if (restrictions != null) { + for (String key : restrictions.keySet()) { + if (restrictions.getBoolean(key, false)) { + pw.println(prefix + key); + noneSet = false; + } + } + } + if (noneSet) { + pw.println(prefix + "none"); + } + } +} diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index a0a31c0d8a0c..ced04336dfac 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -397,6 +397,10 @@ public final class PowerManagerService extends SystemService // Set this to false to disable. private boolean mUserInactiveOverrideFromWindowManager; + // The next possible user activity timeout after being explicitly told the user is inactive. + // Set to -1 when not told the user is inactive since the last period spent dozing or asleep. + private long mOverriddenTimeout = -1; + // The user activity timeout override from the window manager // to allow the current foreground activity to override the user activity timeout. // Use -1 to disable. @@ -1034,6 +1038,7 @@ public final class PowerManagerService extends SystemService if (mUserInactiveOverrideFromWindowManager) { mUserInactiveOverrideFromWindowManager = false; + mOverriddenTimeout = -1; } if (mWakefulness == WAKEFULNESS_ASLEEP @@ -1251,12 +1256,28 @@ public final class PowerManagerService extends SystemService } } + /** + * Logs the time the device would have spent awake before user activity timeout, + * had the system not been told the user was inactive. + */ + private void logSleepTimeoutRecapturedLocked() { + final long now = SystemClock.uptimeMillis(); + final long savedWakeTimeMs = mOverriddenTimeout - now; + if (savedWakeTimeMs >= 0) { + EventLog.writeEvent(EventLogTags.POWER_SOFT_SLEEP_REQUESTED, savedWakeTimeMs); + mOverriddenTimeout = -1; + } + } + private void finishWakefulnessChangeIfNeededLocked() { if (mWakefulnessChanging && mDisplayReady) { if (mWakefulness == WAKEFULNESS_DOZING && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) { return; // wait until dream has enabled dozing } + if (mWakefulness == WAKEFULNESS_DOZING || mWakefulness == WAKEFULNESS_ASLEEP) { + logSleepTimeoutRecapturedLocked(); + } mWakefulnessChanging = false; mNotifier.onWakefulnessChangeFinished(); } @@ -1579,10 +1600,11 @@ public final class PowerManagerService extends SystemService if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) { if ((mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) { - // Device is being kept awake by recent user activity - long savedWakeTimeMs = now - nextTimeout; - EventLog.writeEvent( - EventLogTags.POWER_SOFT_SLEEP_REQUESTED, savedWakeTimeMs); + // Device is being kept awake by recent user activity + if (nextTimeout >= now && mOverriddenTimeout == -1) { + // Save when the next timeout would have occurred + mOverriddenTimeout = nextTimeout; + } } mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM; nextTimeout = -1; diff --git a/services/core/java/com/android/server/wm/DimBehindController.java b/services/core/java/com/android/server/wm/DimBehindController.java index c56af39f6c93..8870dd1ceef2 100644 --- a/services/core/java/com/android/server/wm/DimBehindController.java +++ b/services/core/java/com/android/server/wm/DimBehindController.java @@ -159,24 +159,32 @@ class DimBehindController { boolean animateDimLayers() { int fullScreen = -1; + int fullScreenAndDimming = -1; + boolean result = false; + for (int i = mState.size() - 1; i >= 0; i--) { - DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i); - if (dimLayerUser.isFullscreen()) { + DimLayer.DimLayerUser user = mState.keyAt(i); + if (user.isFullscreen()) { fullScreen = i; if (mState.valueAt(i).continueDimming) { - break; + fullScreenAndDimming = i; } + } else { + // We always want to animate the non fullscreen windows, they don't share their + // dim layers. + result |= animateDimLayers(user); } } - if (fullScreen != -1) { - return animateDimLayers(mState.keyAt(fullScreen)); - } else { - boolean result = false; - for (int i = mState.size() - 1; i >= 0; i--) { - result |= animateDimLayers(mState.keyAt(i)); - } - return result; + // For the shared, full screen dim layer, we prefer the animation that is causing it to + // appear. + if (fullScreenAndDimming != -1) { + result |= animateDimLayers(mState.keyAt(fullScreenAndDimming)); + } else if (fullScreen != -1) { + // If there is no animation for the full screen dim layer to appear, we can use any of + // the animators that will cause it to disappear. + result |= animateDimLayers(mState.keyAt(fullScreen)); } + return result; } private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) { diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java index 8c479d8ef6aa..bc3127434471 100644 --- a/services/core/java/com/android/server/wm/DimLayer.java +++ b/services/core/java/com/android/server/wm/DimLayer.java @@ -29,6 +29,10 @@ public class DimLayer { private static final String TAG = "DimLayer"; private static final boolean DEBUG = false; + public static final float RESIZING_HINT_ALPHA = 0.5f; + + public static final int RESIZING_HINT_DURATION_MS = 0; + /** Actual surface that dims */ SurfaceControl mDimSurface; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index f13f350cc9d4..438658ee40e4 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -266,9 +266,16 @@ class DisplayContent { final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { final Task task = tasks.get(taskNdx); - task.getBounds(mTmpRect); - if (mTmpRect.contains(x, y)) { - return task.mTaskId; + // We need to use the visible frame on the window for any touch-related tests. + // Can't use the task's bounds because the original task bounds might be adjusted + // to fit the content frame. For example, the presence of the IME adjusting the + // windows frames when the app window is the IME target. + final WindowState win = task.getTopAppMainWindow(); + if (win != null) { + win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH); + if (mTmpRect.contains(x, y)) { + return task.mTaskId; + } } } } @@ -298,7 +305,7 @@ class DisplayContent { // might be adjusted to fit the content frame. (One example is when the // task is put to top-left quadrant, the actual visible frame would not // start at (0,0) after it's adjusted for the status bar.) - WindowState win = task.getTopAppMainWindow(); + final WindowState win = task.getTopAppMainWindow(); if (win != null) { win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH); mTmpRect.inset(-delta, -delta); diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index b9028e39a1c1..610524f50f56 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -25,16 +25,22 @@ import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; 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.DimLayer.RESIZING_HINT_ALPHA; +import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS; +import static com.android.server.wm.TaskPositioner.SIDE_MARGIN_DIP; import static com.android.server.wm.TaskStack.DOCKED_BOTTOM; 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 static com.android.server.wm.WindowManagerService.dipToPixel; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.RemoteException; +import android.util.Slog; +import android.view.DisplayInfo; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -44,11 +50,15 @@ import android.view.WindowManagerGlobal; /** * Controls showing and hiding of a docked stack divider on the display. */ -public class DockedStackDividerController implements View.OnTouchListener { +public class DockedStackDividerController implements View.OnTouchListener, DimLayer.DimLayerUser { private static final String TAG = "DockedStackDivider"; private final Context mContext; private final int mDividerWidth; private final DisplayContent mDisplayContent; + private final int mSideMargin; + private final DimLayer mDimLayer; + private final int mDisplayWidth; + private final int mDisplayHeight; private View mView; private Rect mTmpRect = new Rect(); private Rect mLastResizeRect = new Rect(); @@ -57,12 +67,18 @@ public class DockedStackDividerController implements View.OnTouchListener { private TaskStack mTaskStack; private Rect mOriginalRect = new Rect(); private int mDockSide; + private boolean mDimLayerVisible; DockedStackDividerController(Context context, DisplayContent displayContent) { mContext = context; mDisplayContent = displayContent; + final DisplayInfo info = displayContent.getDisplayInfo(); + mDisplayWidth = info.logicalWidth; + mDisplayHeight = info.logicalHeight; mDividerWidth = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_divider_thickness); + mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayContent.getDisplayMetrics()); + mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId()); } private void addDivider(Configuration configuration) { @@ -154,22 +170,175 @@ public class DockedStackDividerController implements View.OnTouchListener { break; case MotionEvent.ACTION_MOVE: if (mTaskStack != null) { - resizeStack(event); + final int x = (int) event.getRawX(); + final int y = (int) event.getRawY(); + resizeStack(x, y); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - mTaskStack = null; + if (mTaskStack != null) { + final int x = (int) event.getRawX(); + final int y = (int) event.getRawY(); + // At most one of these will be executed, the other one will exit early. + maybeDismissTaskStack(x, y); + maybeMaximizeTaskStack(x, y); + mTaskStack = null; + } + setDimLayerVisible(false); mDockSide = TaskStack.DOCKED_INVALID; break; } return true; } - private void resizeStack(MotionEvent event) { + private void maybeMaximizeTaskStack(int x, int y) { + final int distance = distanceFromFullScreen(mDockSide, x, y); + if (distance == -1) { + Slog.wtf(TAG, "maybeMaximizeTaskStack: Unknown dock side=" + mDockSide); + return; + } + if (distance <= mSideMargin) { + try { + mDisplayContent.mService.mActivityManager.resizeStack(mTaskStack.mStackId, null); + } catch (RemoteException e) { + // This can't happen because we are in the same process. + } + } + } + + private void maybeDismissTaskStack(int x, int y) { + final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y); + if (distance == -1) { + Slog.wtf(TAG, "maybeDismissTaskStack: Unknown dock side=" + mDockSide); + return; + } + if (distance <= mSideMargin) { + try { + mDisplayContent.mService.mActivityManager.removeStack(mTaskStack.mStackId); + } catch (RemoteException e) { + // This can't happen because we are in the same process. + } + } + } + + private void updateDimLayer(int x, int y) { + final int dismissDistance = distanceFromDockSide(mDockSide, mOriginalRect, x, y); + final int maximizeDistance = distanceFromFullScreen(mDockSide, x, y); + if (dismissDistance == -1 || maximizeDistance == -1) { + Slog.wtf(TAG, "updateDimLayer: Unknown dock side=" + mDockSide); + return; + } + if (dismissDistance <= mSideMargin && maximizeDistance <= mSideMargin) { + Slog.wtf(TAG, "Both dismiss and maximize distances would trigger dim layer."); + return; + } + if (dismissDistance <= mSideMargin) { + setDismissDimLayerVisible(x, y); + } else if (maximizeDistance <= mSideMargin) { + setMaximizeDimLayerVisible(x, y); + } else { + setDimLayerVisible(false); + } + } + + /** + * Provides the distance from the point to the docked side of a rectangle. + * + * @return non negative distance or -1 on error + */ + private static int distanceFromDockSide(int dockSide, Rect bounds, int x, int y) { + switch (dockSide) { + case DOCKED_LEFT: + return x - bounds.left; + case DOCKED_TOP: + return y - bounds.top; + case DOCKED_RIGHT: + return bounds.right - x; + case DOCKED_BOTTOM: + return bounds.bottom - y; + } + return -1; + } + + private int distanceFromFullScreen(int dockSide, int x, int y) { + switch (dockSide) { + case DOCKED_LEFT: + return mDisplayWidth - x; + case DOCKED_TOP: + return mDisplayHeight - y; + case DOCKED_RIGHT: + return x; + case DOCKED_BOTTOM: + return y; + } + return -1; + } + + private void setDismissDimLayerVisible(int x, int y) { mTmpRect.set(mOriginalRect); - final int deltaX = (int) event.getRawX() - mStartX; - final int deltaY = (int) event.getRawY() - mStartY; + switch (mDockSide) { + case DOCKED_LEFT: + mTmpRect.right = x; + break; + case DOCKED_TOP: + mTmpRect.bottom = y; + break; + case DOCKED_RIGHT: + mTmpRect.left = x; + break; + case DOCKED_BOTTOM: + mTmpRect.top = y; + break; + default: + Slog.wtf(TAG, "setDismissDimLayerVisible: Unknown dock side when setting dim " + + "layer=" + mDockSide); + return; + } + mDimLayer.setBounds(mTmpRect); + setDimLayerVisible(true); + } + + private void setMaximizeDimLayerVisible(int x, int y) { + mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight); + switch (mDockSide) { + case DOCKED_LEFT: + mTmpRect.left = x; + break; + case DOCKED_TOP: + mTmpRect.top = y; + break; + case DOCKED_RIGHT: + mTmpRect.right = x; + break; + case DOCKED_BOTTOM: + mTmpRect.top = y; + break; + default: + Slog.wtf(TAG, "setMaximizeDimLayerVisible: Unknown dock side when setting dim " + + "layer=" + mDockSide); + } + mDimLayer.setBounds(mTmpRect); + setDimLayerVisible(true); + } + + private void setDimLayerVisible(boolean visible) { + if (mDimLayerVisible == visible) { + return; + } + mDimLayerVisible = visible; + if (mDimLayerVisible) { + mDimLayer.show(mDisplayContent.mService.getDragLayerLocked(), RESIZING_HINT_ALPHA, + RESIZING_HINT_DURATION_MS); + } else { + mDimLayer.hide(); + } + } + + private void resizeStack(int x, int y) { + mTmpRect.set(mOriginalRect); + final int deltaX = x - mStartX; + final int deltaY = y - mStartY; switch (mDockSide) { case DOCKED_LEFT: mTmpRect.right += deltaX; @@ -191,7 +360,9 @@ public class DockedStackDividerController implements View.OnTouchListener { try { mDisplayContent.mService.mActivityManager.resizeStack(DOCKED_STACK_ID, mTmpRect); } catch (RemoteException e) { + // This can't happen because we are in the same process. } + updateDimLayer(x, y); } boolean isResizing() { @@ -201,4 +372,24 @@ public class DockedStackDividerController implements View.OnTouchListener { int getWidthAdjustment() { return getWidth() / 2; } + + @Override + public boolean isFullscreen() { + return false; + } + + @Override + public DisplayInfo getDisplayInfo() { + return mDisplayContent.getDisplayInfo(); + } + + @Override + public void getBounds(Rect outBounds) { + // This dim layer user doesn't need this. + } + + @Override + public String toShortString() { + return TAG; + } } diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 207da47773f8..227b3f01fac2 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -23,8 +23,11 @@ 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.DimLayer.RESIZING_HINT_ALPHA; +import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS; 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.WindowManagerService.dipToPixel; import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP; import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP; @@ -60,7 +63,7 @@ class TaskPositioner implements DimLayer.DimLayerUser { // The margin the pointer position has to be within the side of the screen to be // considered at the side of the screen. - private static final int SIDE_MARGIN_DIP = 100; + static final int SIDE_MARGIN_DIP = 100; @IntDef(flag = true, value = { @@ -246,7 +249,7 @@ class TaskPositioner implements DimLayer.DimLayerUser { mDisplay.getDisplayId()); mDragWindowHandle.name = TAG; mDragWindowHandle.inputChannel = mServerChannel; - mDragWindowHandle.layer = getDragLayerLocked(); + mDragWindowHandle.layer = mService.getDragLayerLocked(); mDragWindowHandle.layoutParamsFlags = 0; mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; mDragWindowHandle.dispatchingTimeoutNanos = @@ -279,9 +282,9 @@ class TaskPositioner implements DimLayer.DimLayerUser { mService.pauseRotationLocked(); mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId()); - mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics); - mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics); - mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics); + mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics); + mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics); + mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics); mDragEnded = false; } @@ -455,7 +458,8 @@ class TaskPositioner implements DimLayer.DimLayerUser { } mDimLayer.setBounds(mTmpRect); - mDimLayer.show(getDragLayerLocked(), 0.5f, 0); + mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA, + RESIZING_HINT_DURATION_MS); } @Override /** {@link DimLayer.DimLayerUser} */ @@ -477,10 +481,4 @@ class TaskPositioner implements DimLayer.DimLayerUser { public String toShortString() { return TAG; } - - private int getDragLayerLocked() { - return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG) - * WindowManagerService.TYPE_LAYER_MULTIPLIER - + WindowManagerService.TYPE_LAYER_OFFSET; - } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index a63099301571..ca358b15bbb8 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -19,6 +19,7 @@ package com.android.server.wm; 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.H.UNUSED; import static com.android.server.wm.WindowManagerService.TAG; import android.annotation.IntDef; @@ -177,9 +178,9 @@ public class TaskStack implements DimLayer.DimLayerUser { if (mDisplayContent != null) { mDisplayContent.mDimBehindController.updateDimLayer(this); + mAnimationBackgroundSurface.setBounds(bounds); } - mAnimationBackgroundSurface.setBounds(bounds); mBounds.set(bounds); mRotation = rotation; return true; @@ -238,7 +239,7 @@ public class TaskStack implements DimLayer.DimLayerUser { // 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)); + mService.mH.obtainMessage(RESIZE_STACK, mStackId, UNUSED, mBounds)); } } } @@ -488,7 +489,7 @@ public class TaskStack implements DimLayer.DimLayerUser { && otherStackId >= FIRST_STATIC_STACK_ID && otherStackId <= LAST_STATIC_STACK_ID) { mService.mH.sendMessage( - mService.mH.obtainMessage(RESIZE_STACK, otherStackId, -1, bounds)); + mService.mH.obtainMessage(RESIZE_STACK, otherStackId, UNUSED, bounds)); } } } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index ab1bf202f666..513826995e5e 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -272,25 +272,6 @@ public class WindowAnimator { winAnimator.mWasAnimating = nowAnimating; mAnimating |= nowAnimating; - boolean appWindowAnimating = winAnimator.mAppAnimator != null - && winAnimator.mAppAnimator.animating; - boolean wasAppWindowAnimating = winAnimator.mAppAnimator != null - && winAnimator.mAppAnimator.wasAnimating; - boolean anyAnimating = appWindowAnimating || nowAnimating; - boolean anyWasAnimating = wasAppWindowAnimating || wasAnimating; - - try { - if (anyAnimating && !anyWasAnimating) { - win.mClient.onAnimationStarted(winAnimator.mAnimatingMove ? -1 - : winAnimator.mKeyguardGoingAwayAnimation ? 1 - : 0); - } else if (!anyAnimating && anyWasAnimating) { - win.mClient.onAnimationStopped(); - } - } catch (RemoteException e) { - Slog.w(TAG, "Failed to dispatch window animation state change.", e); - } - if (WindowManagerService.DEBUG_WALLPAPER) { Slog.v(TAG, win + ": wasAnimating=" + wasAnimating + ", nowAnimating=" + nowAnimating); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 1b72876dcb14..de3d83636bdb 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -18,6 +18,9 @@ package com.android.server.wm; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.DOCKED_STACK_ID; +import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; @@ -223,7 +226,6 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean HIDE_STACK_CRAWLS = true; static final int LAYOUT_REPEAT_THRESHOLD = 4; - static final boolean PROFILE_ORIENTATION = false; static final boolean localLOGV = DEBUG; @@ -467,6 +469,11 @@ public class WindowManagerService extends IWindowManager.Stub static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; + int getDragLayerLocked() { + return mPolicy.windowTypeToLayerLw(LayoutParams.TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + + TYPE_LAYER_OFFSET; + } + class RotationWatcher { IRotationWatcher watcher; IBinder.DeathRecipient deathRecipient; @@ -1005,7 +1012,7 @@ public class WindowManagerService extends IWindowManager.Stub * @param displayContent The display we are interested in. * @return List of windows from token that are on displayContent. */ - WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) { + private WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) { final WindowList windowList = new WindowList(); final int count = token.windows.size(); for (int i = 0; i < count; i++) { @@ -1039,55 +1046,19 @@ public class WindowManagerService extends IWindowManager.Stub } private int addAppWindowToListLocked(final WindowState win) { - final IWindow client = win.mClient; - final WindowToken token = win.mToken; final DisplayContent displayContent = win.getDisplayContent(); if (displayContent == null) { // It doesn't matter this display is going away. return 0; } + final IWindow client = win.mClient; + final WindowToken token = win.mToken; - final WindowList windows = win.getWindowList(); - final int N = windows.size(); + final WindowList windows = displayContent.getWindowList(); WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent); int tokenWindowsPos = 0; - int windowListPos = tokenWindowList.size(); if (!tokenWindowList.isEmpty()) { - // If this application has existing windows, we - // simply place the new window on top of them... but - // keep the starting window on top. - if (win.mAttrs.type == TYPE_BASE_APPLICATION) { - // Base windows go behind everything else. - WindowState lowestWindow = tokenWindowList.get(0); - placeWindowBefore(lowestWindow, win); - tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows); - } else { - AppWindowToken atoken = win.mAppToken; - WindowState lastWindow = tokenWindowList.get(windowListPos - 1); - if (atoken != null && lastWindow == atoken.startingWindow) { - placeWindowBefore(lastWindow, win); - tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows); - } else { - int newIdx = findIdxBasedOnAppTokens(win); - //there is a window above this one associated with the same - //apptoken note that the window could be a floating window - //that was created later or a window at the top of the list of - //windows associated with this token. - if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, - "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " + - N); - windows.add(newIdx + 1, win); - if (newIdx < 0) { - // No window from token found on win's display. - tokenWindowsPos = 0; - } else { - tokenWindowsPos = indexOfWinInWindowList( - windows.get(newIdx), token.windows) + 1; - } - mWindowsChanged = true; - } - } - return tokenWindowsPos; + return addAppWindowToTokenListLocked(win, token, windows, tokenWindowList); } // No windows from this token on this display @@ -1189,19 +1160,63 @@ public class WindowManagerService extends IWindowManager.Stub // Just search for the start of this layer. final int myLayer = win.mBaseLayer; int i; - for (i = N - 1; i >= 0; --i) { + for (i = windows.size() - 1; i >= 0; --i) { WindowState w = windows.get(i); - if (w.mBaseLayer <= myLayer) { + // Dock divider shares the base layer with application windows, but we want to always + // keep it above the application windows. + if (w.mBaseLayer <= myLayer && w.mAttrs.type != TYPE_DOCK_DIVIDER) { break; } } if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, - "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + N); + "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + + windows.size()); windows.add(i + 1, win); mWindowsChanged = true; return tokenWindowsPos; } + private int addAppWindowToTokenListLocked(WindowState win, WindowToken token, + WindowList windows, WindowList tokenWindowList) { + int tokenWindowsPos; + // If this application has existing windows, we + // simply place the new window on top of them... but + // keep the starting window on top. + if (win.mAttrs.type == TYPE_BASE_APPLICATION) { + // Base windows go behind everything else. + WindowState lowestWindow = tokenWindowList.get(0); + placeWindowBefore(lowestWindow, win); + tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows); + } else { + AppWindowToken atoken = win.mAppToken; + final int windowListPos = tokenWindowList.size(); + WindowState lastWindow = tokenWindowList.get(windowListPos - 1); + if (atoken != null && lastWindow == atoken.startingWindow) { + placeWindowBefore(lastWindow, win); + tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows); + } else { + int newIdx = findIdxBasedOnAppTokens(win); + //there is a window above this one associated with the same + //apptoken note that the window could be a floating window + //that was created later or a window at the top of the list of + //windows associated with this token. + if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " + + windows.size()); + windows.add(newIdx + 1, win); + if (newIdx < 0) { + // No window from token found on win's display. + tokenWindowsPos = 0; + } else { + tokenWindowsPos = indexOfWinInWindowList( + windows.get(newIdx), token.windows) + 1; + } + mWindowsChanged = true; + } + } + return tokenWindowsPos; + } + private void addFreeWindowToListLocked(final WindowState win) { final WindowList windows = win.getWindowList(); @@ -2442,7 +2457,6 @@ public class WindowManagerService extends IWindowManager.Stub boolean configChanged; boolean surfaceChanged = false; boolean dragResizing = false; - boolean animating; boolean hasStatusBarPermission = mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) == PackageManager.PERMISSION_GRANTED; @@ -2593,16 +2607,12 @@ public class WindowManagerService extends IWindowManager.Stub // If we're starting a drag-resize, we'll be changing the surface size as well as // notifying the client to render to with an offset from the surface's top-left. - // Do a screen freeze, and keep the old surface until the the first frame drawn to - // the new surface comes back, so that we avoid a flash due to mismatching surface - // setups on window manager side and client side. if (win.isDragResizeChanged()) { win.setDragResizing(); if (win.mHasSurface) { winAnimator.mDestroyPendingSurfaceUponRedraw = true; winAnimator.mSurfaceDestroyDeferred = true; winAnimator.destroySurfaceLocked(); - startFreezingDisplayLocked(false, 0, 0); toBeDisplayed = true; } } @@ -3180,9 +3190,9 @@ public class WindowManagerService extends IWindowManager.Stub public int getOrientationLocked() { if (mDisplayFrozen) { - if (mLastWindowForcedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { - if (DEBUG_ORIENTATION) Slog.v(TAG, "Display is frozen, return " - + mLastWindowForcedOrientation); + if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { + if (DEBUG_ORIENTATION) Slog.v(TAG, + "Display is frozen, return " + mLastWindowForcedOrientation); // If the display is frozen, some activities may be in the middle // of restarting, and thus have removed their old window. If the // window has the flag to hide the lock screen, then the lock screen @@ -3204,8 +3214,7 @@ public class WindowManagerService extends IWindowManager.Stub continue; } int req = win.mAttrs.screenOrientation; - if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) || - (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ + if(req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND) { continue; } @@ -3215,7 +3224,7 @@ public class WindowManagerService extends IWindowManager.Stub } return (mLastWindowForcedOrientation = req); } - mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED; if (mPolicy.isKeyguardLocked()) { // The screen is locked and no top system window is requesting an orientation. @@ -3226,7 +3235,7 @@ public class WindowManagerService extends IWindowManager.Stub null : winShowWhenLocked.mAppToken; if (appShowWhenLocked != null) { int req = appShowWhenLocked.requestedOrientation; - if (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + if (req == SCREEN_ORIENTATION_BEHIND) { req = mLastKeyguardForcedOrientation; } if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + appShowWhenLocked @@ -3239,8 +3248,21 @@ public class WindowManagerService extends IWindowManager.Stub } } + final TaskStack dockedStack = mStackIdToStack.get(DOCKED_STACK_ID); + final TaskStack freeformStack = mStackIdToStack.get(FREEFORM_WORKSPACE_STACK_ID); + if ((dockedStack != null && dockedStack.isVisibleLocked()) + || (freeformStack != null && freeformStack.isVisibleLocked())) { + // We don't let app affect the system orientation when in freeform or docked mode since + // they don't occupy the entire display and their request can conflict with other apps. + return SCREEN_ORIENTATION_UNSPECIFIED; + } + // Top system windows are not requesting an orientation. Start searching from apps. - int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + return getAppSpecifiedOrientation(); + } + + private int getAppSpecifiedOrientation() { + int lastOrientation = SCREEN_ORIENTATION_UNSPECIFIED; boolean findingBehind = false; boolean lastFullscreen = false; // TODO: Multi window. @@ -3257,19 +3279,16 @@ public class WindowManagerService extends IWindowManager.Stub // 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 (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken - + " -- going to hide"); + if (DEBUG_ORIENTATION) Slog.v(TAG, + "Skipping " + atoken + " -- going to hide"); continue; } if (tokenNdx == firstToken) { - // If we have hit a new Task, and the bottom - // of the previous group didn't explicitly say to use - // the orientation behind it, and the last app was - // full screen, then we'll stick with the - // user's orientation. - if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND - && lastFullscreen) { + // If we have hit a new Task, and the bottom of the previous group didn't + // explicitly say to use the orientation behind it, and the last app was + // full screen, then we'll stick with the user's orientation. + if (lastOrientation != SCREEN_ORIENTATION_BEHIND && lastFullscreen) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken + " -- end of group, return " + lastOrientation); return lastOrientation; @@ -3278,8 +3297,8 @@ public class WindowManagerService extends IWindowManager.Stub // We ignore any hidden applications on the top. if (atoken.hiddenRequested) { - if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken - + " -- hidden on top"); + if (DEBUG_ORIENTATION) Slog.v(TAG, + "Skipping " + atoken + " -- hidden on top"); continue; } @@ -3293,23 +3312,22 @@ public class WindowManagerService extends IWindowManager.Stub // to use the orientation behind it, then just take whatever // orientation it has and ignores whatever is under it. lastFullscreen = atoken.appFullscreen; - if (lastFullscreen && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { - if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken - + " -- full screen, return " + or); + if (lastFullscreen && or != SCREEN_ORIENTATION_BEHIND) { + if (DEBUG_ORIENTATION) Slog.v(TAG, + "Done at " + atoken + " -- full screen, return " + or); return or; } // If this application has requested an explicit orientation, then use it. - if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED - && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { - if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken - + " -- explicitly set, return " + or); + if (or != SCREEN_ORIENTATION_UNSPECIFIED && or != SCREEN_ORIENTATION_BEHIND) { + if (DEBUG_ORIENTATION) Slog.v(TAG, + "Done at " + atoken + " -- explicitly set, return " + or); return or; } - findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); + findingBehind |= (or == SCREEN_ORIENTATION_BEHIND); } } - if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation, return " - + mForcedAppOrientation); + if (DEBUG_ORIENTATION) Slog.v(TAG, + "No app is requesting an orientation, return " + mForcedAppOrientation); // The next app has not been requested to be visible, so we keep the current orientation // to prevent freezing/unfreezing the display too early. return mForcedAppOrientation; @@ -3412,7 +3430,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override public void setNewConfiguration(Configuration config) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setNewConfiguration()")) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8ace99053cf4..383ad8cb4b07 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -583,12 +583,6 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (mContainingFrame.isEmpty()) { mContainingFrame.set(cf); } - } else { - // Make sure the containing frame is within the content frame so we don't layout - // resized window under screen decorations. - if (!mContainingFrame.intersect(cf)) { - mContainingFrame.set(cf); - } } mDisplayFrame.set(mContainingFrame); } else { @@ -713,6 +707,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mStableFrame.set(mContentFrame); } else if (mAttrs.type == TYPE_DOCK_DIVIDER) { mDisplayContent.mDividerControllerLocked.positionDockedStackedDivider(mFrame); + mContentFrame.set(mFrame); } else { mContentFrame.set(Math.max(mContentFrame.left, mFrame.left), Math.max(mContentFrame.top, mFrame.top), diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index ed53501ff443..722ef9f0d302 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -290,6 +290,9 @@ class WindowStateAnimator { // This must be called while inside a transaction. Returns true if // there is more animation to run. boolean stepAnimationLocked(long currentTime) { + // Save the animation state as it was before this step so WindowManagerService can tell if + // we just started or just stopped animating by comparing mWasAnimating with isAnimating(). + mWasAnimating = mAnimating; final DisplayContent displayContent = mWin.getDisplayContent(); if (displayContent != null && mService.okToDisplay()) { // We will run animations as long as the display isn't frozen. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 2dd7cdea7305..cb5ab1bc8569 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -124,6 +124,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo; +import com.android.server.pm.UserRestrictionsUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -277,7 +278,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final LocalService mLocalService; // Stores and loads state on device and profile owners. - private final Owners mOwners; + @VisibleForTesting + final Owners mOwners; private final Binder mToken = new Binder(); @@ -433,6 +435,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String TAG_PROVIDER = "provider"; private static final String TAG_PACKAGE_LIST_ITEM = "item"; + private static final String TAG_USER_RESTRICTIONS = "user-restrictions"; + final DeviceAdminInfo info; int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; @@ -512,6 +516,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { List<String> crossProfileWidgetProviders; + Bundle userRestrictions; + ActiveAdmin(DeviceAdminInfo _info) { info = _info; } @@ -686,6 +692,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES, permittedAccessiblityServices); writePackageListToXml(out, TAG_PERMITTED_IMES, permittedInputMethods); + if (hasUserRestrictions()) { + UserRestrictionsUtils.writeRestrictions( + out, userRestrictions, TAG_USER_RESTRICTIONS); + } } void writePackageListToXml(XmlSerializer out, String outerTag, @@ -795,6 +805,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { permittedAccessiblityServices = readPackageList(parser, tag); } else if (TAG_PERMITTED_IMES.equals(tag)) { permittedInputMethods = readPackageList(parser, tag); + } else if (TAG_USER_RESTRICTIONS.equals(tag)) { + UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions()); } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -915,6 +927,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return result; } + boolean hasUserRestrictions() { + return userRestrictions != null && userRestrictions.size() > 0; + } + + Bundle ensureUserRestrictions() { + if (userRestrictions == null) { + userRestrictions = new Bundle(); + } + return userRestrictions; + } + void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("uid="); pw.println(getUid()); pw.print(prefix); pw.println("policies:"); @@ -984,6 +1007,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.print(prefix); pw.print("permittedInputMethods="); pw.println(permittedInputMethods.toString()); } + pw.print(prefix); pw.println("userRestrictions:"); + UserRestrictionsUtils.dumpRestrictions(pw, prefix + " ", userRestrictions); } } @@ -1116,6 +1141,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return getCallingUid() == Process.myUid(); } + final int userHandleGetCallingUserId() { + return UserHandle.getUserId(binderGetCallingUid()); + } + File environmentGetUserSystemDirectory(int userId) { return Environment.getUserSystemDirectory(userId); } @@ -1151,6 +1180,42 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String getDevicePolicyFilePathForSystemUser() { return "/data/system/"; } + + int settingsSecureGetIntForUser(String name, int def, int userHandle) { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + name, def, userHandle); + } + + void settingsSecurePutIntForUser(String name, int value, int userHandle) { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + name, value, userHandle); + } + + void settingsSecurePutStringForUser(String name, String value, int userHandle) { + Settings.Secure.putStringForUser(mContext.getContentResolver(), + name, value, userHandle); + } + + void settingsGlobalPutStringForUser(String name, String value, int userHandle) { + Settings.Global.putStringForUser(mContext.getContentResolver(), + name, value, userHandle); + } + + void settingsSecurePutInt(String name, int value) { + Settings.Secure.putInt(mContext.getContentResolver(), name, value); + } + + void settingsGlobalPutInt(String name, int value) { + Settings.Global.putInt(mContext.getContentResolver(), name, value); + } + + void settingsSecurePutString(String name, String value) { + Settings.Secure.putString(mContext.getContentResolver(), name, value); + } + + void settingsGlobalPutString(String name, String value) { + Settings.Global.putString(mContext.getContentResolver(), name, value); + } } /** @@ -1381,17 +1446,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean ownsProfile = (getProfileOwner(userId) != null && getProfileOwner(userId).getPackageName() .equals(admin.info.getPackageName())); - boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName()) - && !hasUserSetupCompleted(userId); if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) { - if ((userId == UserHandle.USER_SYSTEM && (ownsDevice || ownsInitialization)) - || (ownsDevice && ownsProfile)) { + if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || (ownsDevice && ownsProfile)) { return true; } } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) { - if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || ownsProfile - || ownsInitialization) { + if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || ownsProfile) { return true; } } else { @@ -1798,7 +1859,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { validatePasswordOwnerLocked(policy); syncDeviceCapabilitiesLocked(policy); updateMaximumTimeToLockLocked(policy); - addDeviceInitializerToLockTaskPackagesLocked(userHandle); updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle); if (policy.mStatusBarDisabled) { setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle); @@ -3076,7 +3136,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } - boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwnerOrInitializer(callingUid); + boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwner(callingUid); boolean doNotAskCredentialsOnBoot = (flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0; if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) { @@ -3163,8 +3223,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else { // Make sure KEEP_SCREEN_ON is disabled, since that // would allow bypassing of the maximum time to lock. - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); + mInjector.settingsGlobalPutInt(Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); } policy.mLastMaximumTimeToLock = timeMs; @@ -3383,7 +3442,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // If there is a profile owner, redirect to that; otherwise query the device owner. ComponentName aliasChooser = getProfileOwner(caller.getIdentifier()); if (aliasChooser == null && caller.isOwner()) { - ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdmin(); + ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked(); if (deviceOwnerAdmin != null) { aliasChooser = deviceOwnerAdmin.info.getComponent(); } @@ -3484,11 +3543,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long ident = mInjector.binderClearCallingIdentity(); try { if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { - boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName()) - && !hasUserSetupCompleted(userHandle); if (userHandle != UserHandle.USER_SYSTEM - || !(isDeviceOwner(admin.info.getPackageName()) - || ownsInitialization)) { + || !isDeviceOwner(admin.info.getPackageName())) { throw new SecurityException( "Only device owner admins can set WIPE_RESET_PROTECTION_DATA"); } @@ -3846,16 +3902,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } catch (NumberFormatException e) {} } exclusionList = exclusionList.trim(); - ContentResolver res = mContext.getContentResolver(); ProxyInfo proxyProperties = new ProxyInfo(data[0], proxyPort, exclusionList); if (!proxyProperties.isValid()) { Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString()); return; } - Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]); - Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort); - Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, + mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]); + mInjector.settingsGlobalPutInt(Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort); + mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList); } @@ -4079,8 +4134,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (required) { long ident = mInjector.binderClearCallingIdentity(); try { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */); + mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */); } finally { mInjector.binderRestoreCallingIdentity(ident); } @@ -4096,7 +4150,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } synchronized (this) { - ActiveAdmin deviceOwner = getDeviceOwnerAdmin(); + ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); return (deviceOwner != null) ? deviceOwner.requireAutoTime : false; } } @@ -4317,7 +4371,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } // Returns the active device owner or null if there is no device owner. - private ActiveAdmin getDeviceOwnerAdmin() { + @VisibleForTesting + ActiveAdmin getDeviceOwnerAdminLocked() { String deviceOwnerPackageName = getDeviceOwner(); if (deviceOwnerPackageName == null) { return null; @@ -4367,126 +4422,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public boolean setDeviceInitializer(ComponentName who, ComponentName initializer) { - if (!mHasFeature) { - return false; - } - if (initializer == null || - !mOwners.hasDeviceOwner() || - !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(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); - } - if (!isInitializerSystemApp) { - throw new IllegalArgumentException("Only system app can be set as device initializer."); - } - synchronized (this) { - enforceCanSetDeviceInitializer(who); - - if (mOwners.hasDeviceInitializer()) { - throw new IllegalStateException( - "Trying to set device initializer but device initializer is already set."); - } - - mOwners.setDeviceInitializer(initializer); - - addDeviceInitializerToLockTaskPackagesLocked(mOwners.getDeviceOwnerUserId()); - mOwners.writeDeviceOwner(); - return true; - } - } - - private void enforceCanSetDeviceInitializer(ComponentName who) { - if (who == null) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_DEVICE_ADMINS, null); - if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) { - throw new IllegalStateException( - "Trying to set device initializer but device is already provisioned."); - } - } else { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - } - } - - @Override - public boolean isDeviceInitializer(String packageName) { - if (!mHasFeature) { - return false; - } - synchronized (this) { - return mOwners.hasDeviceInitializer() - && mOwners.getDeviceInitializerPackageName().equals(packageName); - } - } - - @Override - public String getDeviceInitializer() { - if (!mHasFeature) { - return null; - } - synchronized (this) { - if (mOwners.hasDeviceInitializer()) { - return mOwners.getDeviceInitializerPackageName(); - } - } - return null; - } - - @Override - public ComponentName getDeviceInitializerComponent() { - if (!mHasFeature) { - return null; - } - synchronized (this) { - if (mOwners.hasDeviceInitializer()) { - return mOwners.getDeviceInitializerComponent(); - } - } - return null; - } - - @Override - public void clearDeviceInitializer(ComponentName who) { - if (!mHasFeature) { - return; - } - Preconditions.checkNotNull(who, "ComponentName is null"); - - ActiveAdmin admin = getActiveAdminUncheckedLocked(who, UserHandle.getCallingUserId()); - - if (admin.getUid() != mInjector.binderGetCallingUid()) { - throw new SecurityException("Admin " + who + " is not owned by uid " - + mInjector.binderGetCallingUid()); - } - - if (!isDeviceInitializer(admin.info.getPackageName()) - && !isDeviceOwner(admin.info.getPackageName())) { - throw new SecurityException( - "clearDeviceInitializer can only be called by the device initializer/owner"); - } - synchronized (this) { - long ident = mInjector.binderClearCallingIdentity(); - try { - mOwners.clearDeviceInitializer(); - mOwners.writeDeviceOwner(); - } finally { - mInjector.binderRestoreCallingIdentity(ident); - } - } - } - - @Override public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) { if (!mHasFeature) { return false; @@ -4576,50 +4511,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public boolean setUserEnabled(ComponentName who) { - if (!mHasFeature) { - return false; - } - synchronized (this) { - if (who == null) { - throw new NullPointerException("ComponentName is null"); - } - int userId = UserHandle.getCallingUserId(); - - ActiveAdmin activeAdmin = - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - if (!isDeviceInitializer(activeAdmin.info.getPackageName())) { - throw new SecurityException( - "This method can only be called by device initializers"); - } - - long id = mInjector.binderClearCallingIdentity(); - try { - if (!isDeviceOwner(activeAdmin.info.getPackageName())) { - mIPackageManager.setComponentEnabledSetting(who, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED, - PackageManager.DONT_KILL_APP, userId); - - removeActiveAdmin(who, userId); - } - - if (userId == UserHandle.USER_SYSTEM) { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, 1); - } - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, 1, userId); - } catch (RemoteException e) { - Log.i(LOG_TAG, "Can't talk to package manager", e); - return false; - } finally { - mInjector.binderRestoreCallingIdentity(id); - } - return true; - } - } - - @Override public void setProfileEnabled(ComponentName who) { if (!mHasFeature) { return; @@ -4674,7 +4565,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Returns the active profile owner for this user or null if the current user has no // profile owner. - private ActiveAdmin getProfileOwnerAdmin(int userHandle) { + @VisibleForTesting + ActiveAdmin getProfileOwnerAdminLocked(int userHandle) { ComponentName profileOwner = mOwners.getProfileOwnerComponent(userHandle); if (profileOwner == null) { return null; @@ -5546,7 +5438,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void setUserRestriction(ComponentName who, String key, boolean enabled) { Preconditions.checkNotNull(who, "ComponentName is null"); - final int userHandle = UserHandle.getCallingUserId(); + final int userHandle = mInjector.userHandleGetCallingUserId(); final UserHandle user = new UserHandle(userHandle); synchronized (this) { ActiveAdmin activeAdmin = @@ -5571,37 +5463,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mInjector.getIAudioService() .setMasterMute(true, 0, mContext.getPackageName(), userHandle); } else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), + mInjector.settingsSecurePutIntForUser( Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userHandle); } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), + mInjector.settingsSecurePutIntForUser( Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userHandle); - Settings.Secure.putStringForUser(mContext.getContentResolver(), + mInjector.settingsSecurePutStringForUser( Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "", userHandle); } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) { // Only disable adb if changing for system user, since it is global // TODO: should this be admin user? if (userHandle == UserHandle.USER_SYSTEM) { - Settings.Global.putStringForUser(mContext.getContentResolver(), + mInjector.settingsGlobalPutStringForUser( Settings.Global.ADB_ENABLED, "0", userHandle); } } else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) { - Settings.Global.putStringForUser(mContext.getContentResolver(), + mInjector.settingsGlobalPutStringForUser( Settings.Global.PACKAGE_VERIFIER_ENABLE, "1", userHandle); - Settings.Global.putStringForUser(mContext.getContentResolver(), + mInjector.settingsGlobalPutStringForUser( Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1", userHandle); } else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), + mInjector.settingsSecurePutIntForUser( Settings.Secure.INSTALL_NON_MARKET_APPS, 0, userHandle); } } mUserManager.setUserRestriction(key, enabled, user); + activeAdmin.ensureUserRestrictions().putBoolean(key, enabled); + saveSettingsLocked(userHandle); + if (enabled != alreadyRestricted) { if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) { // Send out notifications however as some clients may want to reread the @@ -5902,7 +5797,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // TODO: Should there be a check to make sure this relationship is within a profile group? //enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system"); synchronized (this) { - ActiveAdmin admin = getProfileOwnerAdmin(userId); + ActiveAdmin admin = getProfileOwnerAdminLocked(userId); return (admin != null) ? admin.disableCallerId : false; } } @@ -5995,7 +5890,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // within a profile group? // enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system"); synchronized (this) { - ActiveAdmin admin = getProfileOwnerAdmin(userId); + ActiveAdmin admin = getProfileOwnerAdminLocked(userId); return (admin != null) ? admin.disableBluetoothContactSharing : false; } } @@ -6098,7 +5993,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void setGlobalSetting(ComponentName who, String setting, String value) { - final ContentResolver contentResolver = mContext.getContentResolver(); Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { @@ -6126,7 +6020,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long id = mInjector.binderClearCallingIdentity(); try { - Settings.Global.putString(contentResolver, setting, value); + mInjector.settingsGlobalPutString(setting, value); } finally { mInjector.binderRestoreCallingIdentity(id); } @@ -6155,7 +6049,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long id = mInjector.binderClearCallingIdentity(); try { - Settings.Secure.putStringForUser(contentResolver, setting, value, callingUserId); + mInjector.settingsSecurePutStringForUser(setting, value, callingUserId); } finally { mInjector.binderRestoreCallingIdentity(id); } @@ -6278,18 +6172,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ void updateUserSetupComplete() { List<UserInfo> users = mUserManager.getUsers(true); - ContentResolver resolver = mContext.getContentResolver(); final int N = users.size(); for (int i = 0; i < N; i++) { int userHandle = users.get(i).id; - if (Settings.Secure.getIntForUser(resolver, Settings.Secure.USER_SETUP_COMPLETE, 0, + if (mInjector.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, userHandle) != 0) { DevicePolicyData policy = getUserData(userHandle); if (!policy.mUserSetupComplete) { policy.mUserSetupComplete = true; synchronized (this) { - // The DeviceInitializer was whitelisted but now should be removed. - removeDeviceInitializerFromLockTaskPackages(userHandle); saveSettingsLocked(userHandle); } } @@ -6297,35 +6188,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void addDeviceInitializerToLockTaskPackagesLocked(int userHandle) { - if (hasUserSetupCompleted(userHandle)) { - return; - } - - final String deviceInitializerPackage = getDeviceInitializer(); - if (deviceInitializerPackage == null) { - return; - } - - final List<String> packages = getLockTaskPackagesLocked(userHandle); - if (!packages.contains(deviceInitializerPackage)) { - packages.add(deviceInitializerPackage); - setLockTaskPackagesLocked(userHandle, packages); - } - } - - private void removeDeviceInitializerFromLockTaskPackages(int userHandle) { - final String deviceInitializerPackage = getDeviceInitializer(); - if (deviceInitializerPackage == null) { - return; - } - - List<String> packages = getLockTaskPackagesLocked(userHandle); - if (packages.remove(deviceInitializerPackage)) { - setLockTaskPackagesLocked(userHandle, packages); - } - } - private class SetupContentObserver extends ContentObserver { private final Uri mUserSetupComplete = Settings.Secure.getUriFor( @@ -6450,15 +6312,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * Checks if the caller of the method is the device owner app or device initialization app. + * Checks if the caller of the method is the device owner app. * * @param callerUid UID of the caller. - * @return true if the caller is the device owner app or device initializer. + * @return true if the caller is the device owner app */ - private boolean isCallerDeviceOwnerOrInitializer(int callerUid) { + private boolean isCallerDeviceOwner(int callerUid) { String[] pkgs = mContext.getPackageManager().getPackagesForUid(callerUid); for (String pkg : pkgs) { - if (isDeviceOwner(pkg) || isDeviceInitializer(pkg)) { + if (isDeviceOwner(pkg)) { return true; } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 370cf48b4655..f9543c22e086 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -16,16 +16,11 @@ package com.android.server.devicepolicy; -import android.app.AppGlobals; import android.app.admin.SystemUpdatePolicy; import android.content.ComponentName; import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.os.Environment; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; @@ -90,9 +85,6 @@ class Owners { private int mDeviceOwnerUserId = UserHandle.USER_NULL; - // Internal state for the device initializer package. - private OwnerInfo mDeviceInitializer; - // Internal state for the profile owner packages. private final ArrayMap<Integer, OwnerInfo> mProfileOwners = new ArrayMap<>(); @@ -167,26 +159,6 @@ class Owners { mDeviceOwnerUserId = UserHandle.USER_NULL; } - ComponentName getDeviceInitializerComponent() { - return mDeviceInitializer.admin; - } - - String getDeviceInitializerPackageName() { - return mDeviceInitializer != null ? mDeviceInitializer.packageName : null; - } - - void setDeviceInitializer(ComponentName admin) { - mDeviceInitializer = new OwnerInfo(null, admin); - } - - void clearDeviceInitializer() { - mDeviceInitializer = null; - } - - boolean hasDeviceInitializer() { - return mDeviceInitializer != null; - } - void setProfileOwner(ComponentName admin, String ownerName, int userId) { mProfileOwners.put(userId, new OwnerInfo(ownerName, admin)); } @@ -252,18 +224,7 @@ class Owners { mDeviceOwner = new OwnerInfo(name, packageName); mDeviceOwnerUserId = UserHandle.USER_SYSTEM; } else if (tag.equals(TAG_DEVICE_INITIALIZER)) { - String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); - String initializerComponentStr = - parser.getAttributeValue(null, ATTR_COMPONENT_NAME); - ComponentName admin = - ComponentName.unflattenFromString(initializerComponentStr); - if (admin != null) { - mDeviceInitializer = new OwnerInfo(null, admin); - } else { - mDeviceInitializer = new OwnerInfo(null, packageName); - Slog.e(TAG, "Error parsing device-owner file. Bad component name " + - initializerComponentStr); - } + // Deprecated tag } else if (tag.equals(TAG_PROFILE_OWNER)) { String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE); String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME); @@ -444,8 +405,7 @@ class Owners { @Override boolean shouldWrite() { - return (mDeviceOwner != null) || (mDeviceInitializer != null) - || (mSystemUpdatePolicy != null); + return (mDeviceOwner != null) || (mSystemUpdatePolicy != null); } @Override @@ -457,9 +417,6 @@ class Owners { out.endTag(null, TAG_DEVICE_OWNER_CONTEXT); } - if (mDeviceInitializer != null) { - mDeviceInitializer.writeToXml(out, TAG_DEVICE_INITIALIZER); - } if (mSystemUpdatePolicy != null) { out.startTag(null, TAG_SYSTEM_UPDATE_POLICY); mSystemUpdatePolicy.saveToXml(out); @@ -488,7 +445,7 @@ class Owners { break; } case TAG_DEVICE_INITIALIZER: - mDeviceInitializer = OwnerInfo.readFromXml(parser); + // Deprecated tag break; case TAG_SYSTEM_UPDATE_POLICY: mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser); @@ -607,11 +564,6 @@ class Owners { pw.println(prefix + " User ID: " + mDeviceOwnerUserId); pw.println(); } - if (mDeviceInitializer != null) { - pw.println(prefix + "Device Initializer: "); - mDeviceInitializer.dump(prefix + " ", pw); - pw.println(); - } if (mSystemUpdatePolicy != null) { pw.println(prefix + "System Update Policy: " + mSystemUpdatePolicy); pw.println(); 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 f4ffe2ee1d27..b109e7bc9d03 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -225,5 +225,45 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi boolean userManagerIsSplitSystemUser() { return context.userManagerForMock.isSplitSystemUser(); } + + @Override + int settingsSecureGetIntForUser(String name, int def, int userHandle) { + return context.settings.settingsSecureGetIntForUser(name, def, userHandle); + } + + @Override + void settingsSecurePutIntForUser(String name, int value, int userHandle) { + context.settings.settingsSecurePutIntForUser(name, value, userHandle); + } + + @Override + void settingsSecurePutStringForUser(String name, String value, int userHandle) { + context.settings.settingsSecurePutStringForUser(name, value, userHandle); + } + + @Override + void settingsGlobalPutStringForUser(String name, String value, int userHandle) { + context.settings.settingsGlobalPutStringForUser(name, value, userHandle); + } + + @Override + void settingsSecurePutInt(String name, int value) { + context.settings.settingsSecurePutInt(name, value); + } + + @Override + void settingsGlobalPutInt(String name, int value) { + context.settings.settingsGlobalPutInt(name, value); + } + + @Override + void settingsSecurePutString(String name, String value) { + context.settings.settingsSecurePutString(name, value); + } + + @Override + void settingsGlobalPutString(String name, String value) { + context.settings.settingsGlobalPutString(name, value); + } } } 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 5b2379833ad6..03b892e55c41 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -31,8 +31,8 @@ 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.os.UserManager; import android.util.Pair; import org.mockito.ArgumentCaptor; @@ -65,8 +65,6 @@ import static org.mockito.Mockito.when; (mmma frameworks/base/services/tests/servicestests/ for non-ninja build) */ public class DevicePolicyManagerTest extends DpmTestBase { - - private DpmMockContext mContext; public DevicePolicyManager dpm; public DevicePolicyManagerServiceTestable dpms; @@ -207,9 +205,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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. + // PO needs to be an DA. dpm.setActiveAdmin(admin, /* replace =*/ false); // Fire! @@ -625,4 +621,97 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setApplicationRestrictions(admin1, "pkg2", new Bundle()); assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size()); } + + public void testSetUserRestriction_asDo() 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); + + // First, set DO. + + // Call from a process on the system user. + 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); + + // Call. + dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM); + assertTrue(dpm.setDeviceOwner(admin1.getPackageName(), "owner-name", + UserHandle.USER_SYSTEM)); + + assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_SMS)); + assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); + + dpm.addUserRestriction(admin1, UserManager.DISALLOW_SMS); + dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS); + + assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_SMS)); + assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); + + dpm.clearUserRestriction(admin1, UserManager.DISALLOW_SMS); + + assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_SMS)); + assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); + + dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS); + + assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_SMS)); + assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); + + // TODO Check inner calls. + // TODO Make sure restrictions are written to the file. + } + + public void testSetUserRestriction_asPo() { + setAsProfileOwner(admin1); + + assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)); + assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); + + dpm.addUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); + dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS); + + assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)); + assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); + + dpm.clearUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); + + assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)); + assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); + + dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS); + + assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)); + assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .ensureUserRestrictions() + .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); + + // TODO Check inner calls. + // TODO Make sure restrictions are written to the file. + } } 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 7b36e88c5e29..73d63ea64026 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -158,6 +158,33 @@ public class DpmMockContext extends MockContext { } } + public static class SettingsForMock { + int settingsSecureGetIntForUser(String name, int def, int userHandle) { + return 0; + } + + void settingsSecurePutIntForUser(String name, int value, int userHandle) { + } + + void settingsSecurePutStringForUser(String name, String value, int userHandle) { + } + + void settingsGlobalPutStringForUser(String name, String value, int userHandle) { + } + + void settingsSecurePutInt(String name, int value) { + } + + void settingsGlobalPutInt(String name, int value) { + } + + void settingsSecurePutString(String name, String value) { + } + + void settingsGlobalPutString(String name, String value) { + } + } + public final Context realTestContext; /** @@ -184,6 +211,7 @@ public class DpmMockContext extends MockContext { public final IBackupManager ibackupManager; public final IAudioService iaudioService; public final LockPatternUtils lockPatternUtils; + public final SettingsForMock settings; /** Note this is a partial mock, not a real mock. */ public final PackageManager packageManager; @@ -212,6 +240,7 @@ public class DpmMockContext extends MockContext { ibackupManager = mock(IBackupManager.class); iaudioService = mock(IAudioService.class); lockPatternUtils = mock(LockPatternUtils.class); + settings = mock(SettingsForMock.class); // Package manager is huge, so we use a partial mock instead. packageManager = spy(context.getPackageManager()); 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 4a39614aed99..a210d4652fe3 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java @@ -21,6 +21,7 @@ import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.Owners import android.content.ComponentName; import android.content.pm.UserInfo; import android.os.UserHandle; +import android.os.UserManager; import android.util.Log; import java.io.BufferedReader; @@ -28,6 +29,8 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import static org.mockito.Mockito.when; @@ -94,7 +97,6 @@ public class OwnersTest extends DpmTestBase { assertFalse(owners.hasDeviceOwner()); assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertFalse(owners.hasDeviceInitializer()); assertNull(owners.getSystemUpdatePolicy()); assertEquals(0, owners.getProfileOwnerKeys().size()); } @@ -106,7 +108,6 @@ public class OwnersTest extends DpmTestBase { assertFalse(owners.hasDeviceOwner()); assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertFalse(owners.hasDeviceInitializer()); assertNull(owners.getSystemUpdatePolicy()); assertEquals(0, owners.getProfileOwnerKeys().size()); } @@ -139,7 +140,6 @@ public class OwnersTest extends DpmTestBase { assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName()); assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId()); - assertFalse(owners.hasDeviceInitializer()); assertNull(owners.getSystemUpdatePolicy()); assertEquals(0, owners.getProfileOwnerKeys().size()); } @@ -154,7 +154,6 @@ public class OwnersTest extends DpmTestBase { assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName()); assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId()); - assertFalse(owners.hasDeviceInitializer()); assertNull(owners.getSystemUpdatePolicy()); assertEquals(0, owners.getProfileOwnerKeys().size()); } @@ -184,7 +183,6 @@ public class OwnersTest extends DpmTestBase { assertFalse(owners.hasDeviceOwner()); assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertFalse(owners.hasDeviceInitializer()); assertNull(owners.getSystemUpdatePolicy()); assertEquals(2, owners.getProfileOwnerKeys().size()); @@ -207,7 +205,6 @@ public class OwnersTest extends DpmTestBase { assertFalse(owners.hasDeviceOwner()); assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertFalse(owners.hasDeviceInitializer()); assertNull(owners.getSystemUpdatePolicy()); assertEquals(2, owners.getProfileOwnerKeys().size()); @@ -251,8 +248,6 @@ public class OwnersTest extends DpmTestBase { assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName()); assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId()); - assertTrue(owners.hasDeviceInitializer()); - assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName()); assertNotNull(owners.getSystemUpdatePolicy()); assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType()); @@ -279,8 +274,6 @@ public class OwnersTest extends DpmTestBase { assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName()); assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId()); - assertTrue(owners.hasDeviceInitializer()); - assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName()); assertNotNull(owners.getSystemUpdatePolicy()); assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType()); @@ -322,8 +315,6 @@ public class OwnersTest extends DpmTestBase { assertFalse(owners.hasDeviceOwner()); assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertTrue(owners.hasDeviceInitializer()); - assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName()); assertNull(owners.getSystemUpdatePolicy()); assertEquals(0, owners.getProfileOwnerKeys().size()); @@ -337,8 +328,6 @@ public class OwnersTest extends DpmTestBase { assertFalse(owners.hasDeviceOwner()); assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertTrue(owners.hasDeviceInitializer()); - assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName()); assertNull(owners.getSystemUpdatePolicy()); assertEquals(0, owners.getProfileOwnerKeys().size()); @@ -368,7 +357,6 @@ public class OwnersTest extends DpmTestBase { assertFalse(owners.hasDeviceOwner()); assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertFalse(owners.hasDeviceInitializer()); assertEquals(0, owners.getProfileOwnerKeys().size()); assertNotNull(owners.getSystemUpdatePolicy()); @@ -382,7 +370,6 @@ public class OwnersTest extends DpmTestBase { assertFalse(owners.hasDeviceOwner()); assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertFalse(owners.hasDeviceInitializer()); assertEquals(0, owners.getProfileOwnerKeys().size()); assertNotNull(owners.getSystemUpdatePolicy()); @@ -408,7 +395,6 @@ public class OwnersTest extends DpmTestBase { assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists()); // Then clear all information and save. - owners.clearDeviceInitializer(); owners.clearDeviceOwner(); owners.clearSystemUpdatePolicy(); owners.removeProfileOwner(10); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index e57964a96182..ba91254b01a8 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -304,6 +304,31 @@ public class CarrierConfigManager { "carrier_instant_lettering_escaped_chars_string"; /** + * When IMS instant lettering is available for a carrier (see + * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the character encoding + * which will be used when determining the length of messages. Used in the InCall UI to limit + * the number of characters the user may type. If empty-string, the instant lettering + * message size limit will be enforced on a 1:1 basis. That is, each character will count + * towards the messages size limit as a single bye. If a character encoding is specified, the + * message size limit will be based on the number of bytes in the message per the specified + * encoding. + * @hide + */ + public static final String KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING = + "carrier_instant_lettering_encoding_string"; + + /** + * When IMS instant lettering is available for a carrier (see + * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), the length limit for messages. Used + * in the InCall UI to ensure the user cannot enter more characters than allowed by the carrier. + * See also {@link #KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING} for more information on how + * the length of the message is calculated. + * @hide + */ + public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT = + "carrier_instant_lettering_length_limit_int"; + + /** * If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0 * this is the value that should be used instead. A configuration value of * RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default @@ -508,6 +533,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, ""); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, ""); + sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, ""); + sDefaults.putInt(KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT, 64); sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false); sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false); sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true); diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java index e3e1d34f193e..b4e0c707d3f3 100644 --- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java +++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java @@ -106,12 +106,12 @@ public class SurfaceCompositionMeasuringActivity extends Activity implements OnC class CompositorScore { double mSurfaces; - double mBitrate; + double mBandwidth; @Override public String toString() { return DOUBLE_FORMAT.format(mSurfaces) + " surfaces. " + - "Bitrate: " + getReadableMemory((long)mBitrate) + "/s"; + "Bandwidth: " + getReadableMemory((long)mBandwidth) + "/s"; } } @@ -131,7 +131,7 @@ public class SurfaceCompositionMeasuringActivity extends Activity implements OnC score.mSurfaces = measureCompositionScore(new Measurement(0, 60.0), new Measurement(mViews.size() + 1, 0.0f), pixelFormat); // Assume 32 bits per pixel. - score.mBitrate = score.mSurfaces * mTargetFPS * mWidth * mHeight * 4.0; + score.mBandwidth = score.mSurfaces * mTargetFPS * mWidth * mHeight * 4.0; //memAccessTask.stop(); return score; } diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java index 6e9e7390c2c1..3f04888da637 100644 --- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java +++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java @@ -15,7 +15,9 @@ */ package android.surfacecomposition; +import android.app.Activity; import android.graphics.PixelFormat; +import android.os.Bundle; import android.surfacecomposition.SurfaceCompositionMeasuringActivity.AllocationScore; import android.surfacecomposition.SurfaceCompositionMeasuringActivity.CompositorScore; import android.test.ActivityInstrumentationTestCase2; @@ -25,6 +27,16 @@ import android.util.Log; public class SurfaceCompositionTest extends ActivityInstrumentationTestCase2<SurfaceCompositionMeasuringActivity> { private final static String TAG = "SurfaceCompositionTest"; + private final static String KEY_SURFACE_COMPOSITION_PERFORMANCE = + "surface-compoistion-peformance-sps"; + private final static String KEY_SURFACE_COMPOSITION_BANDWITH = + "surface-compoistion-bandwidth-gbps"; + private final static String KEY_SURFACE_ALLOCATION_PERFORMANCE_MEDIAN = + "surface-allocation-performance-median-sps"; + private final static String KEY_SURFACE_ALLOCATION_PERFORMANCE_MIN = + "surface-allocation-performance-min-sps"; + private final static String KEY_SURFACE_ALLOCATION_PERFORMANCE_MAX = + "surface-allocation-performance-max-sps"; // Pass threshold for major pixel formats. private final static int[] TEST_PIXEL_FORMATS = new int[] { @@ -53,6 +65,7 @@ public class SurfaceCompositionTest extends @SmallTest public void testSurfaceCompositionPerformance() { + Bundle status = new Bundle(); for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) { int pixelFormat = TEST_PIXEL_FORMATS[i]; String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat); @@ -62,11 +75,20 @@ public class SurfaceCompositionTest extends "performance score. " + score.mSurfaces + " < " + MIN_ACCEPTED_COMPOSITION_SCORE[i] + ".", score.mSurfaces >= MIN_ACCEPTED_COMPOSITION_SCORE[i]); + // Send status only for TRANSLUCENT format. + if (pixelFormat == PixelFormat.TRANSLUCENT) { + status.putDouble(KEY_SURFACE_COMPOSITION_PERFORMANCE, score.mSurfaces); + // Put bandwidth in GBPS. + status.putDouble(KEY_SURFACE_COMPOSITION_BANDWITH, score.mBandwidth / + (1024.0 * 1024.0 * 1024.0)); + } } + getInstrumentation().sendStatus(Activity.RESULT_OK, status); } @SmallTest public void testSurfaceAllocationPerformance() { + Bundle status = new Bundle(); for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) { int pixelFormat = TEST_PIXEL_FORMATS[i]; String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat); @@ -76,6 +98,13 @@ public class SurfaceCompositionTest extends "performance score. " + score.mMedian + " < " + MIN_ACCEPTED_ALLOCATION_SCORE[i] + ".", score.mMedian >= MIN_ACCEPTED_ALLOCATION_SCORE[i]); + // Send status only for TRANSLUCENT format. + if (pixelFormat == PixelFormat.TRANSLUCENT) { + status.putDouble(KEY_SURFACE_ALLOCATION_PERFORMANCE_MEDIAN, score.mMedian); + status.putDouble(KEY_SURFACE_ALLOCATION_PERFORMANCE_MIN, score.mMin); + status.putDouble(KEY_SURFACE_ALLOCATION_PERFORMANCE_MAX, score.mMax); + } } + getInstrumentation().sendStatus(Activity.RESULT_OK, status); } } diff --git a/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java b/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java deleted file mode 100644 index 4475fa483015..000000000000 --- a/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java +++ /dev/null @@ -1,59 +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 android.animation; - -import com.android.tools.layoutlib.annotations.LayoutlibDelegate; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.Resources.NotFoundException; -import android.content.res.Resources.Theme; -import android.util.AttributeSet; - -/** - * Delegate providing alternate implementation to static methods in {@link AnimatorInflater}. - */ -public class AnimatorInflater_Delegate { - - @LayoutlibDelegate - /*package*/ static Animator loadAnimator(Context context, int id) - throws NotFoundException { - return loadAnimator(context.getResources(), context.getTheme(), id); - } - - @LayoutlibDelegate - /*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id) - throws NotFoundException { - return loadAnimator(resources, theme, id, 1); - } - - @LayoutlibDelegate - /*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id, - float pathErrorScale) throws NotFoundException { - // This is a temporary fix to http://b.android.com/77865. This skips loading the - // animation altogether. - // TODO: Remove this override when Path.approximate() is supported. - return new FakeAnimator(); - } - - @LayoutlibDelegate - /*package*/ static ValueAnimator loadAnimator(Resources res, Theme theme, - AttributeSet attrs, ValueAnimator anim, float pathErrorScale) - throws NotFoundException { - return AnimatorInflater.loadAnimator_Original(res, theme, attrs, anim, pathErrorScale); - } -} diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index f4b1f2cd363f..0e392436d748 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -18,6 +18,7 @@ package android.content.res; import com.android.SdkConstants; import com.android.ide.common.rendering.api.ArrayResourceValue; +import com.android.ide.common.rendering.api.DensityBasedResourceValue; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.ResourceValue; @@ -661,13 +662,18 @@ public final class BridgeResources extends Resources { Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag); if (value != null) { - String v = value.getSecond().getValue(); + ResourceValue resVal = value.getSecond(); + String v = resVal.getValue(); if (v != null) { if (ResourceHelper.parseFloatAttribute(value.getFirst(), v, outValue, false /*requireUnit*/)) { return; } + if (resVal instanceof DensityBasedResourceValue) { + outValue.density = + ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue(); + } // else it's a string outValue.type = TypedValue.TYPE_STRING; diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java index d858953b956c..60514b658649 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java @@ -59,6 +59,7 @@ import java.util.Set; if (opts.inPremultiplied) { bitmapCreateFlags.add(BitmapCreateFlags.PREMULTIPLIED); } + opts.inScaled = false; } try { diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index f8b3739b6a89..64cd5031346e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -35,6 +35,8 @@ import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; @@ -707,6 +709,12 @@ public final class Canvas_Delegate { @Override public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) { Shape shape = pathDelegate.getJavaShape(); + Rectangle2D bounds = shape.getBounds2D(); + if (bounds.isEmpty()) { + // Apple JRE 1.6 doesn't like drawing empty shapes. + // http://b.android.com/178278 + return; + } int style = paintDelegate.getStyle(); if (style == Paint.Style.FILL.nativeInt || diff --git a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java new file mode 100644 index 000000000000..dd2978f5c414 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java @@ -0,0 +1,210 @@ +/* + * 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 com.android.ide.common.rendering.api.LayoutLog; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; + +/** + * Delegate implementing the native methods of {@link android.graphics.PathMeasure} + * <p/> + * Through the layoutlib_create tool, the original native methods of PathMeasure have been + * replaced by + * calls to methods of the same name in this delegate class. + * <p/> + * This class behaves like the original native implementation, but in Java, keeping previously + * native data into its own objects and mapping them to int that are sent back and forth between it + * and the original PathMeasure class. + * + * @see DelegateManager + */ +public final class PathMeasure_Delegate { + // ---- delegate manager ---- + private static final DelegateManager<PathMeasure_Delegate> sManager = + new DelegateManager<PathMeasure_Delegate>(PathMeasure_Delegate.class); + + // ---- delegate data ---- + // This governs how accurate the approximation of the Path is. + private static final float PRECISION = 0.002f; + + /** + * Array containing the path points components. There are three components for each point: + * <ul> + * <li>Fraction along the length of the path that the point resides</li> + * <li>The x coordinate of the point</li> + * <li>The y coordinate of the point</li> + * </ul> + */ + private float mPathPoints[]; + private long mNativePath; + + private PathMeasure_Delegate(long native_path, boolean forceClosed) { + mNativePath = native_path; + if (forceClosed && mNativePath != 0) { + // Copy the path and call close + mNativePath = Path_Delegate.init2(native_path); + Path_Delegate.native_close(mNativePath); + } + + mPathPoints = + mNativePath != 0 ? Path_Delegate.native_approximate(mNativePath, PRECISION) : null; + } + + @LayoutlibDelegate + /*package*/ static long native_create(long native_path, boolean forceClosed) { + return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed)); + } + + @LayoutlibDelegate + /*package*/ static void native_destroy(long native_instance) { + sManager.removeJavaReferenceFor(native_instance); + } + + @LayoutlibDelegate + /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[], + float tan[]) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "PathMeasure.getPostTan is not supported.", null, null); + return false; + } + + @LayoutlibDelegate + /*package*/ static boolean native_getMatrix(long native_instance, float distance, long + native_matrix, int flags) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "PathMeasure.getMatrix is not supported.", null, null); + return false; + } + + @LayoutlibDelegate + /*package*/ static boolean native_nextContour(long native_instance) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "PathMeasure.nextContour is not supported.", null, null); + return false; + } + + @LayoutlibDelegate + /*package*/ static void native_setPath(long native_instance, long native_path, boolean + forceClosed) { + PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); + assert pathMeasure != null; + + if (forceClosed && native_path != 0) { + // Copy the path and call close + native_path = Path_Delegate.init2(native_path); + Path_Delegate.native_close(native_path); + } + pathMeasure.mNativePath = native_path; + pathMeasure.mPathPoints = Path_Delegate.native_approximate(native_path, PRECISION); + } + + @LayoutlibDelegate + /*package*/ static float native_getLength(long native_instance) { + PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); + assert pathMeasure != null; + + if (pathMeasure.mPathPoints == null) { + return 0; + } + + float length = 0; + int nPoints = pathMeasure.mPathPoints.length / 3; + for (int i = 1; i < nPoints; i++) { + length += Point2D.distance( + pathMeasure.mPathPoints[(i - 1) * 3 + 1], + pathMeasure.mPathPoints[(i - 1) * 3 + 2], + pathMeasure.mPathPoints[i*3 + 1], + pathMeasure.mPathPoints[i*3 + 2]); + } + + return length; + } + + @LayoutlibDelegate + /*package*/ static boolean native_isClosed(long native_instance) { + PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); + assert pathMeasure != null; + + Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath); + if (path == null) { + return false; + } + + PathIterator pathIterator = path.getJavaShape().getPathIterator(null); + + int type = 0; + float segment[] = new float[6]; + while (!pathIterator.isDone()) { + type = pathIterator.currentSegment(segment); + pathIterator.next(); + } + + // A path is a closed path if the last element is SEG_CLOSE + return type == PathIterator.SEG_CLOSE; + } + + @LayoutlibDelegate + /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD, + long native_dst_path, boolean startWithMoveTo) { + if (startD < 0) { + startD = 0; + } + + if (startD >= stopD) { + return false; + } + + PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); + assert pathMeasure != null; + + if (pathMeasure.mPathPoints == null) { + return false; + } + + float accLength = 0; + boolean isZeroLength = true; // Whether the output has zero length or not + int nPoints = pathMeasure.mPathPoints.length / 3; + for (int i = 0; i < nPoints; i++) { + float x = pathMeasure.mPathPoints[i * 3 + 1]; + float y = pathMeasure.mPathPoints[i * 3 + 2]; + if (accLength >= startD && accLength <= stopD) { + if (startWithMoveTo) { + startWithMoveTo = false; + Path_Delegate.native_moveTo(native_dst_path, x, y); + } else { + isZeroLength = false; + Path_Delegate.native_lineTo(native_dst_path, x, y); + } + } + + if (i > 0) { + accLength += Point2D.distance( + pathMeasure.mPathPoints[(i - 1) * 3 + 1], + pathMeasure.mPathPoints[(i - 1) * 3 + 2], + pathMeasure.mPathPoints[i * 3 + 1], + pathMeasure.mPathPoints[i * 3 + 2]); + } + } + + return !isZeroLength; + } +} diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 3c9a062719e2..a2a53fef7f1f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -36,6 +36,7 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; +import java.util.ArrayList; /** * Delegate implementing the native methods of android.graphics.Path @@ -173,11 +174,8 @@ public final class Path_Delegate { @LayoutlibDelegate /*package*/ static boolean native_isEmpty(long nPath) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); - if (pathDelegate == null) { - return true; - } + return pathDelegate == null || pathDelegate.isEmpty(); - return pathDelegate.isEmpty(); } @LayoutlibDelegate @@ -488,54 +486,44 @@ public final class Path_Delegate { @LayoutlibDelegate /*package*/ static float[] native_approximate(long nPath, float error) { - Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not fully supported", - null); Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { return null; } - PathIterator pathIterator = pathDelegate.mPath.getPathIterator(null); - float[] tmp = new float[6]; - float[] coords = new float[6]; - boolean isFirstPoint = true; - while (!pathIterator.isDone()) { - int type = pathIterator.currentSegment(tmp); - switch (type) { - case PathIterator.SEG_MOVETO: - case PathIterator.SEG_LINETO: - store(tmp, coords, 1, isFirstPoint); - break; - case PathIterator.SEG_QUADTO: - store(tmp, coords, 2, isFirstPoint); - break; - case PathIterator.SEG_CUBICTO: - store(tmp, coords, 3, isFirstPoint); - break; - case PathIterator.SEG_CLOSE: - // No points returned. + // Get a FlatteningIterator + PathIterator iterator = pathDelegate.getJavaShape().getPathIterator(null, error); + + float segment[] = new float[6]; + float totalLength = 0; + ArrayList<Point2D.Float> points = new ArrayList<Point2D.Float>(); + Point2D.Float previousPoint = null; + while (!iterator.isDone()) { + int type = iterator.currentSegment(segment); + Point2D.Float currentPoint = new Point2D.Float(segment[0], segment[1]); + // MoveTo shouldn't affect the length + if (previousPoint != null && type != PathIterator.SEG_MOVETO) { + totalLength += currentPoint.distance(previousPoint); } - isFirstPoint = false; - pathIterator.next(); + previousPoint = currentPoint; + points.add(currentPoint); + iterator.next(); } - if (isFirstPoint) { - // No points found - return new float[0]; - } else { - return coords; - } - } - private static void store(float[] src, float[] dst, int count, boolean isFirst) { - if (isFirst) { - dst[0] = 0; // fraction - dst[1] = src[0]; // abscissa - dst[2] = src[1]; // ordinate - } - if (count > 1 || !isFirst) { - dst[3] = 1; - dst[4] = src[2 * count - 2]; - dst[5] = src[2 * count - 1]; + int nPoints = points.size(); + float[] result = new float[nPoints * 3]; + previousPoint = null; + for (int i = 0; i < nPoints; i++) { + Point2D.Float point = points.get(i); + float distance = previousPoint != null ? (float) previousPoint.distance(point) : .0f; + result[i * 3] = distance / totalLength; + result[i * 3 + 1] = point.x; + result[i * 3 + 2] = point.y; + + totalLength += distance; + previousPoint = point; } + + return result; } // ---- Private helper methods ---- @@ -735,6 +723,9 @@ public final class Path_Delegate { */ private void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) { + if (isEmpty()) { + mPath.moveTo(0, 0); + } mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3); } diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java new file mode 100644 index 000000000000..a3ad2aac7d3d --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java @@ -0,0 +1,73 @@ +/* + * 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.drawable; + +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import android.graphics.Path; +import android.graphics.drawable.GradientDrawable.GradientState; + +import java.lang.reflect.Field; + +/** + * Delegate implementing the native methods of {@link GradientDrawable} + * + * Through the layoutlib_create tool, the original native methods of GradientDrawable have been + * replaced by calls to methods of the same name in this delegate class. + */ +public class GradientDrawable_Delegate { + + /** + * The ring can be built either by drawing full circles, or by drawing arcs in case the + * circle isn't complete. LayoutLib cannot handle drawing full circles (requires path + * subtraction). So, if we need to draw full circles, we switch to drawing 99% circle. + */ + @LayoutlibDelegate + /*package*/ static Path buildRing(GradientDrawable thisDrawable, GradientState st) { + boolean useLevel = st.mUseLevelForShape; + int level = thisDrawable.getLevel(); + // 10000 is the max level. See android.graphics.drawable.Drawable#getLevel() + float sweep = useLevel ? (360.0f * level / 10000.0f) : 360f; + Field mLevel = null; + if (sweep >= 360 || sweep <= -360) { + st.mUseLevelForShape = true; + // Use reflection to set the value of the field to prevent setting the drawable to + // dirty again. + try { + mLevel = Drawable.class.getDeclaredField("mLevel"); + mLevel.setAccessible(true); + mLevel.setInt(thisDrawable, 9999); // set to one less than max. + } catch (NoSuchFieldException e) { + // The field has been removed in a recent framework change. Fall back to old + // buggy behaviour. + } catch (IllegalAccessException e) { + // We've already set the field to be accessible. + assert false; + } + } + Path path = thisDrawable.buildRing_Original(st); + st.mUseLevelForShape = useLevel; + if (mLevel != null) { + try { + mLevel.setInt(thisDrawable, level); + } catch (IllegalAccessException e) { + assert false; + } + } + return path; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java index 771c3c85d2f5..c2d8d0c3c77b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java @@ -91,14 +91,6 @@ public final class BridgeWindow implements IWindow { } @Override - public void onAnimationStarted(int remainingFrameCount) { - } - - @Override - public void onAnimationStopped() { - } - - @Override public void dispatchWindowShown() { } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 499bea42007f..e480eadc0cf6 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -152,7 +152,6 @@ public final class CreateInfo implements ICreateInfo { * The list of methods to rewrite as delegates. */ public final static String[] DELEGATE_METHODS = new String[] { - "android.animation.AnimatorInflater#loadAnimator", // TODO: remove when Path.approximate() is supported. "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;", "android.content.res.Resources$Theme#obtainStyledAttributes", "android.content.res.Resources$Theme#resolveAttribute", @@ -162,6 +161,7 @@ public final class CreateInfo implements ICreateInfo { "android.content.res.TypedArray#getValueAt", "android.content.res.TypedArray#obtain", "android.graphics.BitmapFactory#finishDecode", + "android.graphics.drawable.GradientDrawable#buildRing", "android.graphics.Typeface#getSystemFontConfigLocation", "android.os.Handler#sendMessageAtTime", "android.os.HandlerThread#run", @@ -228,6 +228,7 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.Path", "android.graphics.PathDashPathEffect", "android.graphics.PathEffect", + "android.graphics.PathMeasure", "android.graphics.PixelXorXfermode", "android.graphics.PorterDuffColorFilter", "android.graphics.PorterDuffXfermode", |