Merge "Reduce, reuse, recycle SNAPSHOTS!"
diff --git a/api/current.txt b/api/current.txt
index c3a4a0c..09361f2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5629,7 +5629,7 @@
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 +22848,7 @@
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 642d2a8..f12e61e 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -6,6 +6,15 @@
}
+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 325aa28b..23805a3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5750,7 +5750,7 @@
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 +5792,8 @@
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 +24792,7 @@
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/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index ebf5085..393956f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -624,7 +624,7 @@
}
}
- String grp = nextOption();
+ String grp = nextArg();
ArrayList<String> groupList = new ArrayList<String>();
if (groups) {
List<PermissionGroupInfo> infos =
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index cb1a89f..42888cf 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -330,9 +330,11 @@
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;
@@ -2984,11 +2986,13 @@
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 {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4b8efab..9f24de8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3956,13 +3956,12 @@
}
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/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 478fdd1..ae82bd6 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -92,7 +92,8 @@
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;
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index a1bb40c..84b6d39 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -227,24 +227,6 @@
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 @@
/**
* 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 @@
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 a118f16..50764f7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -497,96 +497,6 @@
"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 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 @@
/**
* @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.
+ * @deprecated Do not use
+ * @removed
*/
- 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
@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 55a21c6..74fb11d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -211,13 +211,6 @@
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 04b1a3b..1a8602b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3201,15 +3201,12 @@
}
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/os/Build.java b/core/java/android/os/Build.java
index 2374899..85041ad 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -634,6 +634,11 @@
* 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 @@
* @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/util/MathUtils.java b/core/java/android/util/MathUtils.java
index acca3ed..32aac13 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -156,6 +156,27 @@
return start + (stop - start) * amount;
}
+ /**
+ * Returns an interpolated angle in degrees between a set of start and end
+ * angles.
+ * <p>
+ * Unlike {@link #lerp(float, float, float)}, the direction and distance of
+ * travel is determined by the shortest angle between the start and end
+ * angles. For example, if the starting angle is 0 and the ending angle is
+ * 350, then the interpolated angle will be in the range [0,-10] rather
+ * than [0,350].
+ *
+ * @param start the starting angle in degrees
+ * @param end the ending angle in degrees
+ * @param amount the position between start and end in the range [0,1]
+ * where 0 is the starting angle and 1 is the ending angle
+ * @return the interpolated angle in degrees
+ */
+ public static float lerpDeg(float start, float end, float amount) {
+ final float minAngle = (((end - start) + 180) % 360) - 180;
+ return minAngle * amount + start;
+ }
+
public static float norm(float start, float stop, float value) {
return (value - start) / (stop - start);
}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index acad496..cc4bcb6 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -80,20 +80,6 @@
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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7cf23e7..72bbf40 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -238,11 +238,7 @@
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;
@@ -1978,8 +1974,6 @@
}
}
- boolean skipDraw = false;
-
if (mFirst) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
@@ -1994,11 +1988,6 @@
+ mView.findFocus());
}
}
- } else if (mWindowsAnimating) {
- if (mRemainingFrameCount <= 0) {
- skipDraw = true;
- }
- mRemainingFrameCount--;
}
final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
@@ -2043,16 +2032,14 @@
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
@@ -2787,16 +2774,6 @@
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 +3137,6 @@
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 +3180,6 @@
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 +3400,6 @@
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 +4012,6 @@
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 +4038,6 @@
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 +5252,6 @@
}
}
- 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();
}
@@ -6224,15 +6163,6 @@
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.
@@ -6802,22 +6732,6 @@
}
@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) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 5f4e7af..13544851 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -28,9 +28,8 @@
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 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/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 9571109..7220256 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -16,12 +16,7 @@
package android.widget;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.Keyframe;
import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -35,6 +30,7 @@
import android.graphics.Typeface;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.util.IntArray;
import android.util.Log;
import android.util.MathUtils;
@@ -50,7 +46,6 @@
import com.android.internal.R;
import com.android.internal.widget.ExploreByTouchHelper;
-import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
@@ -60,6 +55,7 @@
* @hide
*/
public class RadialTimePickerView extends View {
+
private static final String TAG = "RadialTimePickerView";
private static final int HOURS = 0;
@@ -73,12 +69,6 @@
private static final int AM = 0;
private static final int PM = 1;
- // Opaque alpha level
- private static final int ALPHA_OPAQUE = 255;
-
- // Transparent alpha level
- private static final int ALPHA_TRANSPARENT = 0;
-
private static final int HOURS_IN_CIRCLE = 12;
private static final int MINUTES_IN_CIRCLE = 60;
private static final int DEGREES_FOR_ONE_HOUR = 360 / HOURS_IN_CIRCLE;
@@ -88,8 +78,8 @@
private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
private static final int[] MINUTES_NUMBERS = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55};
- private static final int FADE_OUT_DURATION = 500;
- private static final int FADE_IN_DURATION = 500;
+ private static final int ANIM_DURATION_NORMAL = 500;
+ private static final int ANIM_DURATION_TOUCH = 60;
private static final int[] SNAP_PREFER_30S_MAP = new int[361];
@@ -97,6 +87,9 @@
private static final float[] COS_30 = new float[NUM_POSITIONS];
private static final float[] SIN_30 = new float[NUM_POSITIONS];
+ /** "Something is wrong" color used when a color attribute is missing. */
+ private static final int MISSING_COLOR = Color.MAGENTA;
+
static {
// Prepare mapping to snap touchable degrees to selectable degrees.
preparePrefer30sMap();
@@ -110,8 +103,19 @@
}
}
- private final InvalidateUpdateListener mInvalidateUpdateListener =
- new InvalidateUpdateListener();
+ private final FloatProperty<RadialTimePickerView> HOURS_TO_MINUTES =
+ new FloatProperty<RadialTimePickerView>("hoursToMinutes") {
+ @Override
+ public Float get(RadialTimePickerView radialTimePickerView) {
+ return radialTimePickerView.mHoursToMinutes;
+ }
+
+ @Override
+ public void setValue(RadialTimePickerView object, float value) {
+ object.mHoursToMinutes = value;
+ object.invalidate();
+ }
+ };
private final String[] mHours12Texts = new String[12];
private final String[] mOuterHours24Texts = new String[12];
@@ -119,15 +123,8 @@
private final String[] mMinutesTexts = new String[12];
private final Paint[] mPaint = new Paint[2];
- private final IntHolder[] mAlpha = new IntHolder[2];
-
private final Paint mPaintCenter = new Paint();
-
- private final Paint[][] mPaintSelector = new Paint[2][3];
-
- private final int mSelectorColor;
- private final int mSelectorDotColor;
-
+ private final Paint[] mPaintSelector = new Paint[3];
private final Paint mPaintBackground = new Paint();
private final Typeface mTypeface;
@@ -144,9 +141,6 @@
private final int[] mSelectionDegrees = new int[2];
- private final ArrayList<Animator> mHoursToMinutesAnims = new ArrayList<>();
- private final ArrayList<Animator> mMinuteToHoursAnims = new ArrayList<>();
-
private final RadialPickerTouchHelper mTouchHelper;
private final Path mSelectorPath = new Path();
@@ -154,6 +148,9 @@
private boolean mIs24HourMode;
private boolean mShowHours;
+ private ObjectAnimator mHoursToMinutesAnimator;
+ private float mHoursToMinutes;
+
/**
* When in 24-hour mode, indicates that the current hour is between
* 1 and 12 (inclusive).
@@ -165,6 +162,9 @@
private int mSelectorDotRadius;
private int mCenterDotRadius;
+ private int mSelectorColor;
+ private int mSelectorDotColor;
+
private int mXCenter;
private int mYCenter;
private int mCircleRadius;
@@ -176,7 +176,6 @@
private String[] mOuterTextHours;
private String[] mInnerTextHours;
private String[] mMinutesText;
- private AnimatorSet mTransition;
private int mAmOrPm;
@@ -304,27 +303,15 @@
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs);
+ applyAttributes(attrs, defStyleAttr, defStyleRes);
+
// Pull disabled alpha from theme.
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
mDisabledAlpha = outValue.getFloat();
- // process style attributes
- final Resources res = getResources();
- final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.TimePicker,
- defStyleAttr, defStyleRes);
-
mTypeface = Typeface.create("sans-serif", Typeface.NORMAL);
- // Initialize all alpha values to opaque.
- for (int i = 0; i < mAlpha.length; i++) {
- mAlpha[i] = new IntHolder(ALPHA_OPAQUE);
- }
-
- mTextColor[HOURS] = a.getColorStateList(R.styleable.TimePicker_numbersTextColor);
- mTextColor[HOURS_INNER] = a.getColorStateList(R.styleable.TimePicker_numbersInnerTextColor);
- mTextColor[MINUTES] = mTextColor[HOURS];
-
mPaint[HOURS] = new Paint();
mPaint[HOURS].setAntiAlias(true);
mPaint[HOURS].setTextAlign(Paint.Align.CENTER);
@@ -333,44 +320,21 @@
mPaint[MINUTES].setAntiAlias(true);
mPaint[MINUTES].setTextAlign(Paint.Align.CENTER);
- final ColorStateList selectorColors = a.getColorStateList(
- R.styleable.TimePicker_numbersSelectorColor);
- final int selectorActivatedColor = selectorColors.getColorForState(
- StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
-
- mPaintCenter.setColor(selectorActivatedColor);
mPaintCenter.setAntiAlias(true);
- final int[] activatedStateSet = StateSet.get(
- StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
+ mPaintSelector[SELECTOR_CIRCLE] = new Paint();
+ mPaintSelector[SELECTOR_CIRCLE].setAntiAlias(true);
- mSelectorColor = selectorActivatedColor;
- mSelectorDotColor = mTextColor[HOURS].getColorForState(activatedStateSet, 0);
+ mPaintSelector[SELECTOR_DOT] = new Paint();
+ mPaintSelector[SELECTOR_DOT].setAntiAlias(true);
- mPaintSelector[HOURS][SELECTOR_CIRCLE] = new Paint();
- mPaintSelector[HOURS][SELECTOR_CIRCLE].setAntiAlias(true);
+ mPaintSelector[SELECTOR_LINE] = new Paint();
+ mPaintSelector[SELECTOR_LINE].setAntiAlias(true);
+ mPaintSelector[SELECTOR_LINE].setStrokeWidth(2);
- mPaintSelector[HOURS][SELECTOR_DOT] = new Paint();
- mPaintSelector[HOURS][SELECTOR_DOT].setAntiAlias(true);
-
- mPaintSelector[HOURS][SELECTOR_LINE] = new Paint();
- mPaintSelector[HOURS][SELECTOR_LINE].setAntiAlias(true);
- mPaintSelector[HOURS][SELECTOR_LINE].setStrokeWidth(2);
-
- mPaintSelector[MINUTES][SELECTOR_CIRCLE] = new Paint();
- mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAntiAlias(true);
-
- mPaintSelector[MINUTES][SELECTOR_DOT] = new Paint();
- mPaintSelector[MINUTES][SELECTOR_DOT].setAntiAlias(true);
-
- mPaintSelector[MINUTES][SELECTOR_LINE] = new Paint();
- mPaintSelector[MINUTES][SELECTOR_LINE].setAntiAlias(true);
- mPaintSelector[MINUTES][SELECTOR_LINE].setStrokeWidth(2);
-
- mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
- context.getColor(R.color.timepicker_default_numbers_background_color_material)));
mPaintBackground.setAntiAlias(true);
+ final Resources res = getResources();
mSelectorRadius = res.getDimensionPixelSize(R.dimen.timepicker_selector_radius);
mSelectorStroke = res.getDimensionPixelSize(R.dimen.timepicker_selector_stroke);
mSelectorDotRadius = res.getDimensionPixelSize(R.dimen.timepicker_selector_dot_radius);
@@ -385,6 +349,7 @@
mTextInset[HOURS_INNER] = res.getDimensionPixelSize(R.dimen.timepicker_text_inset_inner);
mShowHours = true;
+ mHoursToMinutes = HOURS;
mIs24HourMode = false;
mAmOrPm = AM;
@@ -399,8 +364,6 @@
initHoursAndMinutesText();
initData();
- a.recycle();
-
// Initial values
final Calendar calendar = Calendar.getInstance(Locale.getDefault());
final int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
@@ -412,6 +375,48 @@
setHapticFeedbackEnabled(true);
}
+ void applyAttributes(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ final Context context = getContext();
+ final TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.TimePicker, defStyleAttr, defStyleRes);
+
+ final ColorStateList numbersTextColor = a.getColorStateList(
+ R.styleable.TimePicker_numbersTextColor);
+ final ColorStateList numbersInnerTextColor = a.getColorStateList(
+ R.styleable.TimePicker_numbersInnerTextColor);
+ mTextColor[HOURS] = numbersTextColor == null ?
+ ColorStateList.valueOf(MISSING_COLOR) : numbersTextColor;
+ mTextColor[HOURS_INNER] = numbersInnerTextColor == null ?
+ ColorStateList.valueOf(MISSING_COLOR) : numbersInnerTextColor;
+ mTextColor[MINUTES] = mTextColor[HOURS];
+
+ // Set up various colors derived from the selector "activated" state.
+ final ColorStateList selectorColors = a.getColorStateList(
+ R.styleable.TimePicker_numbersSelectorColor);
+ final int selectorActivatedColor;
+ if (selectorColors != null) {
+ final int[] stateSetEnabledActivated = StateSet.get(
+ StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
+ selectorActivatedColor = selectorColors.getColorForState(
+ stateSetEnabledActivated, 0);
+ } else {
+ selectorActivatedColor = MISSING_COLOR;
+ }
+
+ mPaintCenter.setColor(selectorActivatedColor);
+
+ final int[] stateSetActivated = StateSet.get(
+ StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
+
+ mSelectorColor = selectorActivatedColor;
+ mSelectorDotColor = mTextColor[HOURS].getColorForState(stateSetActivated, 0);
+
+ mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
+ context.getColor(R.color.timepicker_default_numbers_background_color_material)));
+
+ a.recycle();
+ }
+
public void initialize(int hour, int minute, boolean is24HourMode) {
if (mIs24HourMode != is24HourMode) {
mIs24HourMode = is24HourMode;
@@ -569,35 +574,11 @@
}
public void showHours(boolean animate) {
- if (mShowHours) {
- return;
- }
-
- mShowHours = true;
-
- if (animate) {
- startMinutesToHoursAnimation();
- }
-
- initData();
- invalidate();
- mTouchHelper.invalidateRoot();
+ showPicker(true, animate);
}
public void showMinutes(boolean animate) {
- if (!mShowHours) {
- return;
- }
-
- mShowHours = false;
-
- if (animate) {
- startHoursToMinutesAnimation();
- }
-
- initData();
- invalidate();
- mTouchHelper.invalidateRoot();
+ showPicker(false, animate);
}
private void initHoursAndMinutesText() {
@@ -620,12 +601,6 @@
}
mMinutesText = mMinutesTexts;
-
- final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT;
- mAlpha[HOURS].setValue(hoursAlpha);
-
- final int minutesAlpha = mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE;
- mAlpha[MINUTES].setValue(minutesAlpha);
}
@Override
@@ -653,90 +628,144 @@
final float alphaMod = mInputEnabled ? 1 : mDisabledAlpha;
drawCircleBackground(canvas);
- drawHours(canvas, alphaMod);
- drawMinutes(canvas, alphaMod);
+
+ final Path selectorPath = mSelectorPath;
+ drawSelector(canvas, selectorPath);
+ drawHours(canvas, selectorPath, alphaMod);
+ drawMinutes(canvas, selectorPath, alphaMod);
drawCenter(canvas, alphaMod);
}
+ private void showPicker(boolean hours, boolean animate) {
+ if (mShowHours == hours) {
+ return;
+ }
+
+ mShowHours = hours;
+
+ if (animate) {
+ animatePicker(hours, ANIM_DURATION_NORMAL);
+ }
+
+ initData();
+ invalidate();
+ mTouchHelper.invalidateRoot();
+ }
+
+ private void animatePicker(boolean hoursToMinutes, long duration) {
+ final float target = hoursToMinutes ? HOURS : MINUTES;
+ if (mHoursToMinutes == target) {
+ // If we have a pending or running animator, cancel it.
+ if (mHoursToMinutesAnimator != null && mHoursToMinutesAnimator.isStarted()) {
+ mHoursToMinutesAnimator.cancel();
+ mHoursToMinutesAnimator = null;
+ }
+
+ // We're already showing the correct picker.
+ return;
+ }
+
+ mHoursToMinutesAnimator = ObjectAnimator.ofFloat(this, HOURS_TO_MINUTES, target);
+ mHoursToMinutesAnimator.setAutoCancel(true);
+ mHoursToMinutesAnimator.setDuration(duration);
+ mHoursToMinutesAnimator.start();
+ }
+
private void drawCircleBackground(Canvas canvas) {
canvas.drawCircle(mXCenter, mYCenter, mCircleRadius, mPaintBackground);
}
- private void drawHours(Canvas canvas, float alphaMod) {
- final int hoursAlpha = (int) (mAlpha[HOURS].getValue() * alphaMod + 0.5f);
+ private void drawHours(Canvas canvas, Path selectorPath, float alphaMod) {
+ final int hoursAlpha = (int) (255f * (1f - mHoursToMinutes) * alphaMod + 0.5f);
if (hoursAlpha > 0) {
- // Draw the hour selector under the elements.
- drawSelector(canvas, mIsOnInnerCircle ? HOURS_INNER : HOURS, null, alphaMod);
-
- // Draw outer hours.
- drawTextElements(canvas, mTextSize[HOURS], mTypeface, mTextColor[HOURS],
- mOuterTextHours, mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS],
- hoursAlpha, !mIsOnInnerCircle, mSelectionDegrees[HOURS], false);
-
- // Draw inner hours (13-00) for 24-hour time.
- if (mIs24HourMode && mInnerTextHours != null) {
- drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mTextColor[HOURS_INNER],
- mInnerTextHours, mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha,
- mIsOnInnerCircle, mSelectionDegrees[HOURS], false);
- }
- }
- }
-
- private void drawMinutes(Canvas canvas, float alphaMod) {
- final int minutesAlpha = (int) (mAlpha[MINUTES].getValue() * alphaMod + 0.5f);
- if (minutesAlpha > 0) {
- // Draw the minute selector under the elements.
- drawSelector(canvas, MINUTES, mSelectorPath, alphaMod);
-
- // Exclude the selector region, then draw minutes with no
+ // Exclude the selector region, then draw inner/outer hours with no
// activated states.
canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipPath(mSelectorPath, Region.Op.DIFFERENCE);
- drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
- mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
- minutesAlpha, false, 0, false);
+ canvas.clipPath(selectorPath, Region.Op.DIFFERENCE);
+ drawHoursClipped(canvas, hoursAlpha, false);
canvas.restore();
// Intersect the selector region, then draw minutes with only
// activated states.
canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipPath(mSelectorPath, Region.Op.INTERSECT);
- drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
- mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
- minutesAlpha, true, mSelectionDegrees[MINUTES], true);
+ canvas.clipPath(selectorPath, Region.Op.INTERSECT);
+ drawHoursClipped(canvas, hoursAlpha, true);
canvas.restore();
}
}
+ private void drawHoursClipped(Canvas canvas, int hoursAlpha, boolean showActivated) {
+ // Draw outer hours.
+ drawTextElements(canvas, mTextSize[HOURS], mTypeface, mTextColor[HOURS], mOuterTextHours,
+ mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS], hoursAlpha,
+ showActivated && !mIsOnInnerCircle, mSelectionDegrees[HOURS], showActivated);
+
+ // Draw inner hours (13-00) for 24-hour time.
+ if (mIs24HourMode && mInnerTextHours != null) {
+ drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mTextColor[HOURS_INNER],
+ mInnerTextHours, mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha,
+ showActivated && mIsOnInnerCircle, mSelectionDegrees[HOURS], showActivated);
+ }
+ }
+
+ private void drawMinutes(Canvas canvas, Path selectorPath, float alphaMod) {
+ final int minutesAlpha = (int) (255f * mHoursToMinutes * alphaMod + 0.5f);
+ if (minutesAlpha > 0) {
+ // Exclude the selector region, then draw minutes with no
+ // activated states.
+ canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipPath(selectorPath, Region.Op.DIFFERENCE);
+ drawMinutesClipped(canvas, minutesAlpha, false);
+ canvas.restore();
+
+ // Intersect the selector region, then draw minutes with only
+ // activated states.
+ canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipPath(selectorPath, Region.Op.INTERSECT);
+ drawMinutesClipped(canvas, minutesAlpha, true);
+ canvas.restore();
+ }
+ }
+
+ private void drawMinutesClipped(Canvas canvas, int minutesAlpha, boolean showActivated) {
+ drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES], mMinutesText,
+ mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], minutesAlpha,
+ showActivated, mSelectionDegrees[MINUTES], showActivated);
+ }
+
private void drawCenter(Canvas canvas, float alphaMod) {
mPaintCenter.setAlpha((int) (255 * alphaMod + 0.5f));
canvas.drawCircle(mXCenter, mYCenter, mCenterDotRadius, mPaintCenter);
}
- private int applyAlpha(int argb, int alpha) {
- final int srcAlpha = (argb >> 24) & 0xFF;
- final int dstAlpha = (int) (srcAlpha * (alpha / 255.0) + 0.5f);
- return (0xFFFFFF & argb) | (dstAlpha << 24);
- }
-
private int getMultipliedAlpha(int argb, int alpha) {
return (int) (Color.alpha(argb) * (alpha / 255.0) + 0.5);
}
- private void drawSelector(Canvas canvas, int index, Path selectorPath, float alphaMod) {
- final int alpha = (int) (mAlpha[index % 2].getValue() * alphaMod + 0.5f);
- final int color = applyAlpha(mSelectorColor, alpha);
+ private void drawSelector(Canvas canvas, Path selectorPath) {
+ // Determine the current length, angle, and dot scaling factor.
+ final int hoursIndex = mIsOnInnerCircle ? HOURS_INNER : HOURS;
+ final int hoursInset = mTextInset[hoursIndex];
+ final int hoursAngleDeg = mSelectionDegrees[hoursIndex % 2];
+ final float hoursDotScale = mSelectionDegrees[hoursIndex % 2] % 30 != 0 ? 1 : 0;
+
+ final int minutesIndex = MINUTES;
+ final int minutesInset = mTextInset[minutesIndex];
+ final int minutesAngleDeg = mSelectionDegrees[minutesIndex];
+ final float minutesDotScale = mSelectionDegrees[minutesIndex] % 30 != 0 ? 1 : 0;
// Calculate the current radius at which to place the selection circle.
final int selRadius = mSelectorRadius;
- final int selLength = mCircleRadius - mTextInset[index];
- final double selAngleRad = Math.toRadians(mSelectionDegrees[index % 2]);
+ final float selLength =
+ mCircleRadius - MathUtils.lerp(hoursInset, minutesInset, mHoursToMinutes);
+ final double selAngleRad =
+ Math.toRadians(MathUtils.lerpDeg(hoursAngleDeg, minutesAngleDeg, mHoursToMinutes));
final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad);
final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad);
// Draw the selection circle.
- final Paint paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
- paint.setColor(color);
+ final Paint paint = mPaintSelector[SELECTOR_CIRCLE];
+ paint.setColor(mSelectorColor);
canvas.drawCircle(selCenterX, selCenterY, selRadius, paint);
// If needed, set up the clip path for later.
@@ -746,26 +775,26 @@
}
// Draw the dot if we're between two items.
- final boolean shouldDrawDot = mSelectionDegrees[index % 2] % 30 != 0;
- if (shouldDrawDot) {
- final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT];
+ final float dotScale = MathUtils.lerp(hoursDotScale, minutesDotScale, mHoursToMinutes);
+ if (dotScale > 0) {
+ final Paint dotPaint = mPaintSelector[SELECTOR_DOT];
dotPaint.setColor(mSelectorDotColor);
- canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint);
+ canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius * dotScale, dotPaint);
}
// Shorten the line to only go from the edge of the center dot to the
// edge of the selection circle.
final double sin = Math.sin(selAngleRad);
final double cos = Math.cos(selAngleRad);
- final int lineLength = selLength - selRadius;
+ final float lineLength = selLength - selRadius;
final int centerX = mXCenter + (int) (mCenterDotRadius * sin);
final int centerY = mYCenter - (int) (mCenterDotRadius * cos);
final float linePointX = centerX + (int) (lineLength * sin);
final float linePointY = centerY - (int) (lineLength * cos);
// Draw the line.
- final Paint linePaint = mPaintSelector[index % 2][SELECTOR_LINE];
- linePaint.setColor(color);
+ final Paint linePaint = mPaintSelector[SELECTOR_LINE];
+ linePaint.setColor(mSelectorColor);
linePaint.setStrokeWidth(mSelectorStroke);
canvas.drawLine(mXCenter, mYCenter, linePointX, linePointY, linePaint);
}
@@ -842,73 +871,6 @@
}
}
- private static ObjectAnimator getFadeOutAnimator(IntHolder target, int startAlpha, int endAlpha,
- InvalidateUpdateListener updateListener) {
- final ObjectAnimator animator = ObjectAnimator.ofInt(target, "value", startAlpha, endAlpha);
- animator.setDuration(FADE_OUT_DURATION);
- animator.addUpdateListener(updateListener);
- return animator;
- }
-
- private static ObjectAnimator getFadeInAnimator(IntHolder target, int startAlpha, int endAlpha,
- InvalidateUpdateListener updateListener) {
- final float delayMultiplier = 0.25f;
- final float transitionDurationMultiplier = 1f;
- final float totalDurationMultiplier = transitionDurationMultiplier + delayMultiplier;
- final int totalDuration = (int) (FADE_IN_DURATION * totalDurationMultiplier);
- final float delayPoint = (delayMultiplier * FADE_IN_DURATION) / totalDuration;
-
- final Keyframe kf0, kf1, kf2;
- kf0 = Keyframe.ofInt(0f, startAlpha);
- kf1 = Keyframe.ofInt(delayPoint, startAlpha);
- kf2 = Keyframe.ofInt(1f, endAlpha);
- final PropertyValuesHolder fadeIn = PropertyValuesHolder.ofKeyframe("value", kf0, kf1, kf2);
-
- final ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(target, fadeIn);
- animator.setDuration(totalDuration);
- animator.addUpdateListener(updateListener);
- return animator;
- }
-
- private class InvalidateUpdateListener implements ValueAnimator.AnimatorUpdateListener {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- RadialTimePickerView.this.invalidate();
- }
- }
-
- private void startHoursToMinutesAnimation() {
- if (mHoursToMinutesAnims.size() == 0) {
- mHoursToMinutesAnims.add(getFadeOutAnimator(mAlpha[HOURS],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeInAnimator(mAlpha[MINUTES],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- }
-
- if (mTransition != null && mTransition.isRunning()) {
- mTransition.end();
- }
- mTransition = new AnimatorSet();
- mTransition.playTogether(mHoursToMinutesAnims);
- mTransition.start();
- }
-
- private void startMinutesToHoursAnimation() {
- if (mMinuteToHoursAnims.size() == 0) {
- mMinuteToHoursAnims.add(getFadeOutAnimator(mAlpha[MINUTES],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeInAnimator(mAlpha[HOURS],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- }
-
- if (mTransition != null && mTransition.isRunning()) {
- mTransition.end();
- }
- mTransition = new AnimatorSet();
- mTransition.playTogether(mMinuteToHoursAnims);
- mTransition.start();
- }
-
private int getDegreesFromXY(float x, float y, boolean constrainOutside) {
// Ensure the point is inside the touchable area.
final int innerBound;
@@ -992,6 +954,9 @@
return false;
}
+ // Ensure we're showing the correct picker.
+ animatePicker(mShowHours, ANIM_DURATION_TOUCH);
+
final int type;
final int newValue;
final boolean valueChanged;
@@ -1356,20 +1321,4 @@
return id >>> SHIFT_VALUE & MASK_VALUE;
}
}
-
- private static class IntHolder {
- private int mValue;
-
- public IntHolder(int value) {
- mValue = value;
- }
-
- public void setValue(int value) {
- mValue = value;
- }
-
- public int getValue() {
- return mValue;
- }
- }
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 61ef6dc..6d41c1d 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -200,8 +200,8 @@
a.recycle();
- mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(
- R.id.radial_picker);
+ mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(R.id.radial_picker);
+ mRadialTimePickerView.applyAttributes(attrs, defStyleAttr, defStyleRes);
setupListeners();
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index c9b8119..23c4047 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -488,7 +488,7 @@
}
public void clearContentView() {
- if (mNonClientDecorView.getChildCount() > 1) {
+ if (mNonClientDecorView != null && mNonClientDecorView.getChildCount() > 1) {
mNonClientDecorView.removeViewAt(1);
}
}
@@ -5413,16 +5413,10 @@
* @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 3eeabcd..07bfce7 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -100,14 +100,6 @@
}
@Override
- public void onAnimationStarted(int remainingFrameCount) {
- }
-
- @Override
- public void onAnimationStopped() {
- }
-
- @Override
public void dispatchWindowShown() {
}
}
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 32a877a..3d96fab 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -749,57 +749,57 @@
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 437bd19..24eb961 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -267,7 +267,7 @@
{ "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 460c1a1..b64acc3 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -174,24 +174,24 @@
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/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 07ca45a..c179a2e 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -646,7 +646,8 @@
<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 @@
<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/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 8e7efb4..a9d1e42 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 @@
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 @@
if (index == 0) {
bounds = tr.transformedBounds();
} else {
- bounds.intersect(tr.transformedBounds());
+ bounds.doIntersect(tr.transformedBounds());
}
}
return bounds;
@@ -275,10 +266,7 @@
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 38fefe5..f88fd92 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -33,7 +33,7 @@
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 c63b559..227271d 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -58,7 +58,7 @@
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 9f24e37..cd03ac4 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -488,7 +488,8 @@
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 @@
// 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::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 @@
// 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 @@
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 4c4cd3d..50199db 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,25 +125,32 @@
}
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 @@
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 71589c8..f824cc0 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -549,7 +549,7 @@
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/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ddfd621..38f6e53 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -339,7 +339,7 @@
// 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/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 295cb80..73744458 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 6dcbb38..ab414a9 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 7d7a110..90e2b7e 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-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
index f4bc88e..d415a84 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/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 0abbf4e..a24c936 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -873,6 +873,10 @@
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);
}
}
@@ -1753,7 +1757,7 @@
}
}
- 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 70ddf59..1330b3c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -89,11 +89,13 @@
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 17a1161..7426af5 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 @@
}
}
- 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 c29937d..b3d0cf3 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 @@
*/
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 @@
}
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/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1d19589..bae8017 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 36efead..3370091 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 @@
private boolean mPowerSaveActive;
private boolean mCarMode;
private long mNotificationPulseTime;
+ private long mLastScheduleResetTime;
private long mEarliestPulseDueToLight;
private int mScheduleResetsRemaining;
@@ -356,13 +357,21 @@
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/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index ca38528..47189b0 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 @@
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/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
index f676ea3..3491cb6 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 @@
}
@Override
+ public boolean isVpnRestricted() {
+ return false;
+ }
+
+ @Override
public String getPrimaryVpnName() {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index a78351a..b1cc27a 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 @@
/** 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 @@
// 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 @@
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 5c61c5ca6..e647c1f 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.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.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 @@
@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 @@
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 @@
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 @@
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 @@
}
/** 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 @@
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 @@
// 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 @@
@Override
public void onTaskLaunchFailed() {
// Return to Home
- dismissRecentsToHomeRaw(true);
+ dismissRecentsToHome(true);
}
@Override
@@ -612,6 +607,18 @@
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 52b9521..b6f4a3c 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.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 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 @@
// 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 59df293..8827065 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 @@
// 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 0000000..f2c3c33
--- /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 0000000..827998d
--- /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 0000000..2d42a0e
--- /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 e0f820d..568d2b1 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 @@
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 @@
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 @@
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 e810410..93c5ee7 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.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 8eec87e..0fb235b 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.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 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 0000000..96dfaac
--- /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 de8730d..e406155 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.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.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 @@
TaskStackView mTaskStackView;
RecentsAppWidgetHostView mSearchBar;
RecentsViewCallbacks mCb;
+
+ RecentsViewTouchHandler mTouchHandler;
+ DragView mDragView;
+ ColorDrawable mDockRegionOverlay;
+
Interpolator mFastOutSlowInInterpolator;
Rect mSystemInsets = new Rect();
@@ -96,10 +108,14 @@
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 @@
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 @@
}
}
+ @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 @@
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 @@
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 @@
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 @@
.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 0000000..8dea0cd
--- /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 67e3f82..78b9862 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 @@
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 910db87..9d08ee9 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.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.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 @@
View mActionButtonView;
TaskViewCallbacks mCb;
+ Point mDownTouchPos = new Point();
+
Interpolator mFastOutSlowInInterpolator;
Interpolator mFastOutLinearInInterpolator;
Interpolator mQuintOutInterpolator;
@@ -169,6 +180,14 @@
}
@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 @@
/** 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 @@
mHeaderView.rebindToTask(mTask);
// Rebind any listeners
mActionButtonView.setOnClickListener(this);
+ setOnLongClickListener(mConfig.hasDockedTasks ? null : this);
}
mTaskDataLoaded = true;
}
@@ -753,4 +777,69 @@
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 7de8b7b..949d515 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 @@
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 @@
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 @@
// 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/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 364e884..86f8f5a 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 @@
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 011889a..33e514d 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 @@
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/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index cfd3358..35a17e4 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 @@
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 40984d4..f06e5d3 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 @@
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 e0823b4..88f028f 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 @@
}
@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 703ee661..3ac2a94 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 @@
}
@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 48e0582..f0ca441 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,8 +18,11 @@
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 @@
private IConnectivityManager mService;
- private Button mButton;
-
@Override
- protected void onResume() {
- super.onResume();
- try {
- mPackage = getCallingPackage();
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mPackage = getCallingPackage();
+ mService = IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
- 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 */));
-
- 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);
- mButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
- mButton.setFilterTouchesWhenObscured(true);
- } catch (Exception e) {
- Log.e(TAG, "onResume", e);
+ 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);
+ }
+
+ private boolean prepareVpn() {
+ try {
+ return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ 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 5a27301..7eb8005 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -1585,15 +1585,20 @@
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 617264c..dac317a 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.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 @@
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 @@
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 @@
}
@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 (task == null) {
- throw new IllegalArgumentException("Task " + taskId + " not found.");
+ 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(
+ "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 @@
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 @@
@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 @@
* @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 @@
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 boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("startActivity");
synchronized (this) {
- ActivityRecord r = getFocusedStack().topRunningActivityLocked(null);
+ ActivityRecord r = getFocusedStack().topRunningActivityLocked();
return (r != null) ? r.immersive : false;
}
}
@@ -17727,7 +17744,7 @@
// 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 +21088,7 @@
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 d4e798f..4671cb0 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 @@
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 f549af8..cdb00ef 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 @@
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 @@
// 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 @@
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 @@
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 void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
boolean preserveWindows) {
- ActivityRecord top = topRunningActivityLocked(null);
+ ActivityRecord top = topRunningActivityLocked();
if (top == null) {
return;
}
@@ -1643,7 +1643,7 @@
* 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 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 @@
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 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 @@
}
// 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 @@
}
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 @@
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 ac7b9b1..cda9a5d 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.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 @@
// 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 @@
}
TaskRecord anyTaskForIdLocked(int id) {
- return anyTaskForIdLocked(id, true);
+ return anyTaskForIdLocked(id, RESTORE_FROM_RECENTS, INVALID_STACK_ID);
}
/**
@@ -549,8 +551,10 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
ActivityRecord topRunningActivityLocked() {
final ActivityStack focusedStack = mFocusedStack;
- ActivityRecord r = focusedStack.topRunningActivityLocked(null);
+ ActivityRecord r = focusedStack.topRunningActivityLocked();
if (r != null) {
return r;
}
@@ -833,7 +837,7 @@
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 @@
}
} 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 @@
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 @@
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 @@
// 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 @@
* 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 @@
// 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 @@
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 c36fd06..26264e5 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 boolean getFrontActivityAskCompatModeLocked() {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
+ ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
if (r == null) {
return false;
}
@@ -208,7 +208,7 @@
}
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 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 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 7e14b2b..77dbad4 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 @@
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 f03e6c7..359cc36 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 @@
}
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/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 31fc24b..341410d 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.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.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import libcore.io.IoUtils;
@@ -147,10 +144,6 @@
*/
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 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 @@
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 @@
&& 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 @@
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 @@
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 @@
}
}
- 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 @@
name = parser.getText();
}
} else if (TAG_RESTRICTIONS.equals(tag)) {
- readRestrictionsLocked(parser, restrictions);
+ UserRestrictionsUtils.readRestrictions(parser, restrictions);
}
}
}
@@ -1172,60 +1128,6 @@
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 @@
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 0000000..db1fd2e
--- /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 a0a31c0..ced0433 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 @@
// 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 @@
if (mUserInactiveOverrideFromWindowManager) {
mUserInactiveOverrideFromWindowManager = false;
+ mOverriddenTimeout = -1;
}
if (mWakefulness == WAKEFULNESS_ASLEEP
@@ -1251,12 +1256,28 @@
}
}
+ /**
+ * 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 @@
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 c56af39..8870dd1 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 @@
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/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 8c5d319..b9028e3 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -25,16 +26,15 @@
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.server.wm.TaskStack.DOCKED_BOTTOM;
-import static com.android.server.wm.TaskStack.DOCKED_INVALID;
import static com.android.server.wm.TaskStack.DOCKED_LEFT;
import static com.android.server.wm.TaskStack.DOCKED_RIGHT;
import static com.android.server.wm.TaskStack.DOCKED_TOP;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.RemoteException;
-import android.util.Slog;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -58,7 +58,6 @@
private Rect mOriginalRect = new Rect();
private int mDockSide;
-
DockedStackDividerController(Context context, DisplayContent displayContent) {
mContext = context;
mDisplayContent = displayContent;
@@ -66,13 +65,16 @@
com.android.internal.R.dimen.docked_stack_divider_thickness);
}
- private void addDivider() {
+ private void addDivider(Configuration configuration) {
View view = LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.docked_stack_divider, null);
view.setOnTouchListener(this);
WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
+ final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
+ final int width = landscape ? mDividerWidth : MATCH_PARENT;
+ final int height = landscape ? MATCH_PARENT : mDividerWidth;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- mDividerWidth, MATCH_PARENT, TYPE_DOCK_DIVIDER,
+ width, height, TYPE_DOCK_DIVIDER,
FLAG_TOUCHABLE_WHEN_WAKING | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
| FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH,
PixelFormat.OPAQUE);
@@ -92,10 +94,13 @@
return mView != null;
}
- void update() {
+ void update(Configuration configuration, boolean forceUpdate) {
+ if (forceUpdate && mView != null) {
+ removeDivider();
+ }
TaskStack stack = mDisplayContent.getDockedStackLocked();
if (stack != null && mView == null) {
- addDivider();
+ addDivider(configuration);
} else if (stack == null && mView != null) {
removeDivider();
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index ab1bf20..5138269 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 @@
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 c4201d9..da4af92 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 @@
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 @@
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;
@@ -1005,7 +1007,7 @@
* @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 +1041,19 @@
}
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 +1155,63 @@
// 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();
@@ -3180,9 +3190,9 @@
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 @@
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 @@
}
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 @@
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 @@
}
}
+ 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 @@
// 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 @@
// 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 @@
// 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 @@
}
}
- @Override
public void setNewConfiguration(Configuration config) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setNewConfiguration()")) {
@@ -3420,12 +3437,20 @@
}
synchronized(mWindowMap) {
+ final boolean orientationChanged = mCurConfiguration.orientation != config.orientation;
mCurConfiguration = new Configuration(config);
if (mWaitingForConfig) {
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
mWindowPlacerLocked.performSurfacePlacement();
+ if (orientationChanged) {
+ for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
+ DisplayContent content = mDisplayContents.valueAt(i);
+ Message.obtain(mH, H.UPDATE_DOCKED_STACK_DIVIDER, H.DOCK_DIVIDER_FORCE_UPDATE,
+ H.UNUSED, content).sendToTarget();
+ }
+ }
}
}
@@ -7165,6 +7190,21 @@
public static final int RESIZE_STACK = 43;
public static final int RESIZE_TASK = 44;
+ /**
+ * Used to indicate in the message that the dock divider needs to be updated only if it's
+ * necessary.
+ */
+ static final int DOCK_DIVIDER_NO_FORCE_UPDATE = 0;
+ /**
+ * Used to indicate in the message that the dock divider should be force-removed before
+ * updating, so new configuration can be applied.
+ */
+ static final int DOCK_DIVIDER_FORCE_UPDATE = 1;
+ /**
+ * Used to denote that an integer field in a message will not be used.
+ */
+ public static final int UNUSED = 0;
+
@Override
public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) {
@@ -7706,8 +7746,9 @@
break;
case UPDATE_DOCKED_STACK_DIVIDER: {
DisplayContent content = (DisplayContent) msg.obj;
+ final boolean forceUpdate = msg.arg1 == DOCK_DIVIDER_FORCE_UPDATE;
synchronized (mWindowMap) {
- content.mDividerControllerLocked.update();
+ content.mDividerControllerLocked.update(mCurConfiguration, forceUpdate);
}
}
break;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ed53501..722ef9f 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 @@
// 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/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index db9fcf1..112646a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -965,7 +965,8 @@
}
mService.mPolicy.finishLayoutLw();
- mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget();
+ mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER,
+ DOCK_DIVIDER_NO_FORCE_UPDATE, UNUSED, displayContent).sendToTarget();
}
/**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2dd7cde..cb5ab1b 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.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 @@
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 @@
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 @@
List<String> crossProfileWidgetProviders;
+ Bundle userRestrictions;
+
ActiveAdmin(DeviceAdminInfo _info) {
info = _info;
}
@@ -686,6 +692,10 @@
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 @@
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 @@
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 @@
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 @@
return getCallingUid() == Process.myUid();
}
+ final int userHandleGetCallingUserId() {
+ return UserHandle.getUserId(binderGetCallingUid());
+ }
+
File environmentGetUserSystemDirectory(int userId) {
return Environment.getUserSystemDirectory(userId);
}
@@ -1151,6 +1180,42 @@
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 @@
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 @@
validatePasswordOwnerLocked(policy);
syncDeviceCapabilitiesLocked(policy);
updateMaximumTimeToLockLocked(policy);
- addDeviceInitializerToLockTaskPackagesLocked(userHandle);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
@@ -3076,7 +3136,7 @@
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 @@
} 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 @@
// 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 @@
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 @@
} 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 @@
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 @@
return false;
}
synchronized (this) {
- ActiveAdmin deviceOwner = getDeviceOwnerAdmin();
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
return (deviceOwner != null) ? deviceOwner.requireAutoTime : false;
}
}
@@ -4317,7 +4371,8 @@
}
// 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 @@
}
@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 @@
}
@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 @@
// 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 @@
@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 @@
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 @@
// 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 @@
// 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 @@
@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 @@
long id = mInjector.binderClearCallingIdentity();
try {
- Settings.Global.putString(contentResolver, setting, value);
+ mInjector.settingsGlobalPutString(setting, value);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -6155,7 +6049,7 @@
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 @@
*/
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 @@
}
}
- 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 @@
}
/**
- * 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 370cf48..f9543c2 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 @@
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 @@
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 @@
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 @@
@Override
boolean shouldWrite() {
- return (mDeviceOwner != null) || (mDeviceInitializer != null)
- || (mSystemUpdatePolicy != null);
+ return (mDeviceOwner != null) || (mSystemUpdatePolicy != null);
}
@Override
@@ -457,9 +417,6 @@
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 @@
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 @@
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 f4ffe2e..b109e7b 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 @@
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 5b23798..03b892e 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.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 @@
(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 @@
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 @@
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 7b36e88..73d63ea 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 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 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 @@
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 4a39614..a210d46 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 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.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 @@
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
}
@@ -106,7 +108,6 @@
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
}
@@ -139,7 +140,6 @@
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 @@
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 @@
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(2, owners.getProfileOwnerKeys().size());
@@ -207,7 +205,6 @@
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(2, owners.getProfileOwnerKeys().size());
@@ -251,8 +248,6 @@
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 @@
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 @@
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 @@
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 @@
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertEquals(0, owners.getProfileOwnerKeys().size());
assertNotNull(owners.getSystemUpdatePolicy());
@@ -382,7 +370,6 @@
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertEquals(0, owners.getProfileOwnerKeys().size());
assertNotNull(owners.getSystemUpdatePolicy());
@@ -408,7 +395,6 @@
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 e57964a..ba91254 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -304,6 +304,31 @@
"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 @@
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 e3e1d34..b4e0c70 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
@@ -106,12 +106,12 @@
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 @@
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 6e9e739..3f04888 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 @@
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 @@
@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 @@
"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 @@
"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/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index f4b1f2c..0e39243 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 @@
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 @@
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 d858953..60514b6 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 @@
if (opts.inPremultiplied) {
bitmapCreateFlags.add(BitmapCreateFlags.PREMULTIPLIED);
}
+ opts.inScaled = false;
}
try {
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 771c3c8..c2d8d0c 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 @@
}
@Override
- public void onAnimationStarted(int remainingFrameCount) {
- }
-
- @Override
- public void onAnimationStopped() {
- }
-
- @Override
public void dispatchWindowShown() {
}