Merge "Update docs for DisplayMetrics pixel size fields" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 878a2a9..e253ae1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -879,6 +879,7 @@
field public static final int nextFocusLeft = 16842977; // 0x10100e1
field public static final int nextFocusRight = 16842978; // 0x10100e2
field public static final int nextFocusUp = 16842979; // 0x10100e3
+ field public static final int nfcAntennaPositionDrawable = 16844063; // 0x101051f
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -9326,6 +9327,7 @@
field public int flags;
field public int largestWidthLimitDp;
field public java.lang.String manageSpaceActivityName;
+ field public java.lang.String minSdkVersion;
field public java.lang.String nativeLibraryDir;
field public java.lang.String permission;
field public java.lang.String processName;
@@ -13775,6 +13777,7 @@
public static abstract class CameraCaptureSession.CaptureCallback {
ctor public CameraCaptureSession.CaptureCallback();
+ method public void onCaptureBufferLost(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.view.Surface, long);
method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2846f58..c9176ec 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -974,6 +974,7 @@
field public static final int nextFocusLeft = 16842977; // 0x10100e1
field public static final int nextFocusRight = 16842978; // 0x10100e2
field public static final int nextFocusUp = 16842979; // 0x10100e3
+ field public static final int nfcAntennaPositionDrawable = 16844063; // 0x101051f
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -9648,6 +9649,7 @@
field public int flags;
field public int largestWidthLimitDp;
field public java.lang.String manageSpaceActivityName;
+ field public java.lang.String minSdkVersion;
field public java.lang.String nativeLibraryDir;
field public java.lang.String permission;
field public java.lang.String processName;
@@ -9929,6 +9931,7 @@
method public void setAppLabel(java.lang.CharSequence);
method public void setAppPackageName(java.lang.String);
method public void setGrantedRuntimePermissions(java.lang.String[]);
+ method public void setInstallFlagsDowngrade();
method public void setInstallLocation(int);
method public void setOriginatingUid(int);
method public void setOriginatingUri(android.net.Uri);
@@ -14176,6 +14179,7 @@
public static abstract class CameraCaptureSession.CaptureCallback {
ctor public CameraCaptureSession.CaptureCallback();
+ method public void onCaptureBufferLost(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.view.Surface, long);
method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
diff --git a/api/test-current.txt b/api/test-current.txt
index 6cdaef3..a7b434c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -879,6 +879,7 @@
field public static final int nextFocusLeft = 16842977; // 0x10100e1
field public static final int nextFocusRight = 16842978; // 0x10100e2
field public static final int nextFocusUp = 16842979; // 0x10100e3
+ field public static final int nfcAntennaPositionDrawable = 16844063; // 0x101051f
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -9335,6 +9336,7 @@
field public int flags;
field public int largestWidthLimitDp;
field public java.lang.String manageSpaceActivityName;
+ field public java.lang.String minSdkVersion;
field public java.lang.String nativeLibraryDir;
field public java.lang.String permission;
field public java.lang.String processName;
@@ -13785,6 +13787,7 @@
public static abstract class CameraCaptureSession.CaptureCallback {
ctor public CameraCaptureSession.CaptureCallback();
+ method public void onCaptureBufferLost(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.view.Surface, long);
method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 5ab2c1d..663f297 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -972,7 +972,14 @@
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
startAnimation();
- setCurrentFraction(mSeekFraction == -1 ? 0 : mSeekFraction);
+ if (mSeekFraction == -1) {
+ // No seek, start at play time 0. Note that the reason we are not using fraction 0
+ // is because for animations with 0 duration, we want to be consistent with pre-N
+ // behavior: skip to the final value immediately.
+ setCurrentPlayTime(0);
+ } else {
+ setCurrentFraction(mSeekFraction);
+ }
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2f6907e..2d33a2c 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3329,6 +3329,23 @@
}
}
+ /**
+ * Logs out current current foreground user by switching to the system user and stopping the
+ * user being switched from.
+ * @hide
+ */
+ public static void logoutCurrentUser() {
+ int currentUser = ActivityManager.getCurrentUser();
+ if (currentUser != UserHandle.USER_SYSTEM) {
+ try {
+ ActivityManagerNative.getDefault().switchUser(UserHandle.USER_SYSTEM);
+ ActivityManagerNative.getDefault().stopUser(currentUser, /* force= */ false, null);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
/** {@hide} */
public static final int FLAG_OR_STOPPED = 1 << 0;
/** {@hide} */
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6fbb430..8b62ef9 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -838,6 +838,12 @@
resizePinnedStack(bounds, tempPinnedTaskBounds);
return true;
}
+ case SWAP_DOCKED_AND_FULLSCREEN_STACK: {
+ data.enforceInterface(IActivityManager.descriptor);
+ swapDockedAndFullscreenStack();
+ reply.writeNoException();
+ return true;
+ }
case RESIZE_DOCKED_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final boolean hasBounds = data.readInt() != 0;
@@ -3895,6 +3901,17 @@
reply.recycle();
}
@Override
+ public void swapDockedAndFullscreenStack() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(SWAP_DOCKED_AND_FULLSCREEN_STACK, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ @Override
public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds)
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 82c4c51..64586a6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1267,8 +1267,15 @@
/** @hide */
public void setUserRestriction(int code, boolean restricted, IBinder token) {
+ setUserRestriction(code, restricted, token, /*exceptionPackages*/null);
+ }
+
+ /** @hide */
+ public void setUserRestriction(int code, boolean restricted, IBinder token,
+ String[] exceptionPackages) {
try {
- mService.setUserRestriction(code, restricted, token, mContext.getUserId());
+ mService.setUserRestriction(code, restricted, token, mContext.getUserId(),
+ exceptionPackages);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index eadf497..cdbf598 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -151,6 +151,12 @@
boolean preserveWindows, boolean animate) throws RemoteException;
/**
+ * Moves all tasks from the docked stack in the fullscreen stack and puts the top task of the
+ * fullscreen stack into the docked stack.
+ */
+ public void swapDockedAndFullscreenStack() throws RemoteException;
+
+ /**
* Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
*
* @param dockedBounds The bounds for the docked stack.
@@ -998,4 +1004,5 @@
int GET_MEMORY_TRIM_LEVEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+369;
int RESIZE_PINNED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 370;
int IS_VR_PACKAGE_ENABLED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 371;
+ int SWAP_DOCKED_AND_FULLSCREEN_STACK = IBinder.FIRST_CALL_TRANSACTION + 372;
}
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index 181c907..70a5e15 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -19,6 +19,7 @@
import com.android.internal.R;
import com.android.internal.app.MediaRouteDialogPresenter;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.TypedArray;
@@ -279,7 +280,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == mRemoteIndicator;
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index ad174f6..58d75f7 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -707,6 +707,12 @@
public int uid;
/**
+ * The minimum SDK version this application can run on. It will not run
+ * on earlier versions.
+ */
+ public String minSdkVersion;
+
+ /**
* The minimum SDK version this application targets. It may run on earlier
* versions, but it knows how to work with any new behavior added at this
* version. Will be {@link android.os.Build.VERSION_CODES#CUR_DEVELOPMENT}
@@ -790,7 +796,9 @@
pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
}
}
- pw.println(prefix + "enabled=" + enabled + " targetSdkVersion=" + targetSdkVersion
+ pw.println(prefix + "enabled=" + enabled
+ + " minSdkVersion=" + minSdkVersion
+ + " targetSdkVersion=" + targetSdkVersion
+ " versionCode=" + versionCode);
if ((flags&DUMP_FLAG_DETAILS) != 0) {
if (manageSpaceActivityName != null) {
@@ -884,6 +892,7 @@
deviceEncryptedDataDir = orig.deviceEncryptedDataDir;
credentialEncryptedDataDir = orig.credentialEncryptedDataDir;
uid = orig.uid;
+ minSdkVersion = orig.minSdkVersion;
targetSdkVersion = orig.targetSdkVersion;
versionCode = orig.versionCode;
enabled = orig.enabled;
@@ -938,6 +947,7 @@
dest.writeString(deviceEncryptedDataDir);
dest.writeString(credentialEncryptedDataDir);
dest.writeInt(uid);
+ dest.writeString(minSdkVersion);
dest.writeInt(targetSdkVersion);
dest.writeInt(versionCode);
dest.writeInt(enabled ? 1 : 0);
@@ -992,6 +1002,7 @@
deviceEncryptedDataDir = source.readString();
credentialEncryptedDataDir = source.readString();
uid = source.readInt();
+ minSdkVersion = source.readString();
targetSdkVersion = source.readInt();
versionCode = source.readInt();
enabled = source.readInt() != 0;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 1f603ef..0f5ec91 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1053,6 +1053,12 @@
}
/** {@hide} */
+ @SystemApi
+ public void setInstallFlagsDowngrade() {
+ installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ }
+
+ /** {@hide} */
public void setInstallFlagsExternal() {
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bc28ff1..7d7be9a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1524,6 +1524,7 @@
childPkg.baseRevisionCode = parentPkg.baseRevisionCode;
childPkg.mVersionName = parentPkg.mVersionName;
childPkg.applicationInfo.targetSdkVersion = parentPkg.applicationInfo.targetSdkVersion;
+ childPkg.applicationInfo.minSdkVersion = parentPkg.applicationInfo.minSdkVersion;
childPkg = parseBaseApkCommon(childPkg, CHILD_PACKAGE_TAGS, res, parser, flags, outError);
if (childPkg == null) {
@@ -1854,10 +1855,16 @@
com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
- targetCode = minCode = val.string.toString();
+ targetCode = val.string.toString();
+ if (minCode == null) {
+ minCode = targetCode;
+ }
} else {
// If it's not a string, it's an integer.
targetVers = val.data;
+ if (minVers == 0) {
+ minVers = targetVers;
+ }
}
}
@@ -1883,11 +1890,14 @@
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
+ pkg.applicationInfo.minSdkVersion = minCode;
} else if (minVers > SDK_VERSION) {
outError[0] = "Requires newer sdk version #" + minVers
+ " (current version is #" + SDK_VERSION + ")";
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
+ } else {
+ pkg.applicationInfo.minSdkVersion = Integer.toString(minVers);
}
if (targetCode != null) {
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 83a70cd..e41136c 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -160,10 +160,12 @@
mIcon = b.mIcon;
mTitle = b.mTitle;
mIntent = b.mIntent;
- final Bundle intentExtras = mIntent.getExtras();
- if (intentExtras != null) {
- mIntent.replaceExtras((Bundle) null);
- mIntentPersistableExtras = new PersistableBundle(intentExtras);
+ if (mIntent != null) {
+ final Bundle intentExtras = mIntent.getExtras();
+ if (intentExtras != null) {
+ mIntent.replaceExtras((Bundle) null);
+ mIntentPersistableExtras = new PersistableBundle(intentExtras);
+ }
}
mWeight = b.mWeight;
mExtras = b.mExtras;
@@ -194,6 +196,7 @@
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
mIcon = source.mIcon;
+ mBitmapPath = source.mBitmapPath;
}
mTitle = source.mTitle;
@@ -204,8 +207,6 @@
mWeight = source.mWeight;
mExtras = source.mExtras;
mIconResourceId = source.mIconResourceId;
- mBitmapPath = source.mBitmapPath;
-
} else {
// Set this bit.
mFlags |= FLAG_KEY_FIELDS_ONLY;
@@ -231,9 +232,9 @@
* @hide
*/
public void copyNonNullFieldsFrom(ShortcutInfo source) {
- Preconditions.checkState(mId == source.mId, "ID must match");
+ Preconditions.checkState(mId.equals(source.mId), "ID must match");
Preconditions.checkState(mPackageName.equals(source.mPackageName),
- "Package namae must match");
+ "Package name must match");
if (source.mActivityComponent != null) {
mActivityComponent = source.mActivityComponent;
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 8724a96..38279a4 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -990,6 +990,30 @@
int sequenceId) {
// default empty implementation
}
+
+ /**
+ * <p>This method is called if a single buffer for a capture could not be sent to its
+ * destination surface.</p>
+ *
+ * <p>If the whole capture failed, then {@link #onCaptureFailed} will be called instead. If
+ * some but not all buffers were captured but the result metadata will not be available,
+ * then onCaptureFailed will be invoked with {@link CaptureFailure#wasImageCaptured}
+ * returning true, along with one or more calls to {@link #onCaptureBufferLost} for the
+ * failed outputs.</p>
+ *
+ * @param session
+ * The session returned by {@link CameraDevice#createCaptureSession}
+ * @param request
+ * The request that was given to the CameraDevice
+ * @param target
+ * The target Surface that the buffer will not be produced for
+ * @param frameNumber
+ * The frame number for the request
+ */
+ public void onCaptureBufferLost(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, @NonNull Surface target, long frameNumber) {
+ // default empty implementation
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 37d2ea2..d84a6fc 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1116,6 +1116,11 @@
int sequenceId) {
// default empty implementation
}
+
+ public void onCaptureBufferLost(CameraDevice camera,
+ CaptureRequest request, Surface target, long frameNumber) {
+ // default empty implementation
+ }
}
/**
@@ -1887,48 +1892,66 @@
final CaptureRequest request = holder.getRequest(subsequenceId);
- // No way to report buffer errors right now
+ Runnable failureDispatch = null;
if (errorCode == ERROR_CAMERA_BUFFER) {
- Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber));
- return;
- }
-
- boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
-
- // This is only approximate - exact handling needs the camera service and HAL to
- // disambiguate between request failures to due abort and due to real errors.
- // For now, assume that if the session believes we're mid-abort, then the error
- // is due to abort.
- int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
- CaptureFailure.REASON_FLUSHED :
- CaptureFailure.REASON_ERROR;
-
- final CaptureFailure failure = new CaptureFailure(
- request,
- reason,
- /*dropped*/ mayHaveBuffers,
- requestId,
- frameNumber);
-
- Runnable failureDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()){
- holder.getCallback().onCaptureFailed(
- CameraDeviceImpl.this,
- request,
- failure);
- }
+ final Surface outputSurface =
+ mConfiguredOutputs.get(resultExtras.getErrorStreamId()).getSurface();
+ if (DEBUG) {
+ Log.v(TAG, String.format("Lost output buffer reported for frame %d, target %s",
+ frameNumber, outputSurface));
}
- };
- holder.getHandler().post(failureDispatch);
+ failureDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getCallback().onCaptureBufferLost(
+ CameraDeviceImpl.this,
+ request,
+ outputSurface,
+ frameNumber);
+ }
+ }
+ };
+ } else {
+ boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
- // Fire onCaptureSequenceCompleted if appropriate
- if (DEBUG) {
- Log.v(TAG, String.format("got error frame %d", frameNumber));
+ // This is only approximate - exact handling needs the camera service and HAL to
+ // disambiguate between request failures to due abort and due to real errors. For
+ // now, assume that if the session believes we're mid-abort, then the error is due
+ // to abort.
+ int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
+ CaptureFailure.REASON_FLUSHED :
+ CaptureFailure.REASON_ERROR;
+
+ final CaptureFailure failure = new CaptureFailure(
+ request,
+ reason,
+ /*dropped*/ mayHaveBuffers,
+ requestId,
+ frameNumber);
+
+ failureDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getCallback().onCaptureFailed(
+ CameraDeviceImpl.this,
+ request,
+ failure);
+ }
+ }
+ };
+
+ // Fire onCaptureSequenceCompleted if appropriate
+ if (DEBUG) {
+ Log.v(TAG, String.format("got error frame %d", frameNumber));
+ }
+ mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
+ checkAndFireSequenceComplete();
}
- mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
- checkAndFireSequenceComplete();
+
+ // Dispatch the failure callback
+ holder.getHandler().post(failureDispatch);
}
} // public class CameraDeviceCallbacks
diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
index d859da7..40535e2 100644
--- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
+++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
@@ -28,6 +28,7 @@
private int precaptureTriggerId;
private long frameNumber;
private int partialResultCount;
+ private int errorStreamId;
public static final Parcelable.Creator<CaptureResultExtras> CREATOR =
new Parcelable.Creator<CaptureResultExtras>() {
@@ -48,13 +49,14 @@
public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId,
int precaptureTriggerId, long frameNumber,
- int partialResultCount) {
+ int partialResultCount, int errorStreamId) {
this.requestId = requestId;
this.subsequenceId = subsequenceId;
this.afTriggerId = afTriggerId;
this.precaptureTriggerId = precaptureTriggerId;
this.frameNumber = frameNumber;
this.partialResultCount = partialResultCount;
+ this.errorStreamId = errorStreamId;
}
@Override
@@ -70,6 +72,7 @@
dest.writeInt(precaptureTriggerId);
dest.writeLong(frameNumber);
dest.writeInt(partialResultCount);
+ dest.writeInt(errorStreamId);
}
public void readFromParcel(Parcel in) {
@@ -79,6 +82,7 @@
precaptureTriggerId = in.readInt();
frameNumber = in.readLong();
partialResultCount = in.readInt();
+ errorStreamId = in.readInt();
}
public int getRequestId() {
@@ -104,4 +108,8 @@
public int getPartialResultCount() {
return partialResultCount;
}
+
+ public int getErrorStreamId() {
+ return errorStreamId;
+ }
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 4c4adea..661edd7 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -91,11 +91,11 @@
private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
if (holder == null) {
return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
- ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
+ ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
}
return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
/*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
- /*partialResultCount*/1);
+ /*partialResultCount*/1, /*errorStreamId*/-1);
}
/**
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index d73deb6..ea8ba2f 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -70,6 +70,9 @@
private static final boolean CHECK_PARCEL_SIZE = false;
static final String TAG = "Binder";
+ /** @hide */
+ public static boolean LOG_RUNTIME_EXCEPTION = false; // DO NOT SUBMIT WITH TRUE
+
/**
* Control whether dump() calls are allowed.
*/
@@ -560,17 +563,16 @@
// If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
try {
res = onTransact(code, data, reply, flags);
- } catch (RemoteException e) {
- if ((flags & FLAG_ONEWAY) != 0) {
- Log.w(TAG, "Binder call failed.", e);
- } else {
- reply.setDataPosition(0);
- reply.writeException(e);
- }
- res = true;
- } catch (RuntimeException e) {
- if ((flags & FLAG_ONEWAY) != 0) {
+ } catch (RemoteException|RuntimeException e) {
+ if (LOG_RUNTIME_EXCEPTION) {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
+ }
+ if ((flags & FLAG_ONEWAY) != 0) {
+ if (e instanceof RemoteException) {
+ Log.w(TAG, "Binder call failed.", e);
+ } else {
+ Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
+ }
} else {
reply.setDataPosition(0);
reply.writeException(e);
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 60c7270..dcf987b 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -84,8 +84,19 @@
if (fileSize > Integer.MAX_VALUE) {
return false;
}
- MappedByteBuffer apkContents =
- apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
+ MappedByteBuffer apkContents;
+ try {
+ apkContents = apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
+ } catch (IOException e) {
+ if (e.getCause() instanceof OutOfMemoryError) {
+ // TODO: Remove this temporary workaround once verifying large APKs is
+ // supported. Very large APKs cannot be memory-mapped. This verification code
+ // needs to change to use a different approach for verifying such APKs.
+ return false; // Pretend that this APK does not have a v2 signature.
+ } else {
+ throw new IOException("Failed to memory-map APK", e);
+ }
+ }
// ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order.
apkContents.order(ByteOrder.LITTLE_ENDIAN);
@@ -134,11 +145,26 @@
if (fileSize > Integer.MAX_VALUE) {
throw new IOException("File too large: " + apk.length() + " bytes");
}
- MappedByteBuffer apkContents =
- apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
- // Attempt to preload the contents into memory for faster overall verification (v2 and
- // older) at the expense of somewhat increased latency for rejecting malformed APKs.
- apkContents.load();
+ MappedByteBuffer apkContents;
+ try {
+ apkContents = apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
+ // Attempt to preload the contents into memory for faster overall verification (v2 and
+ // older) at the expense of somewhat increased latency for rejecting malformed APKs.
+ apkContents.load();
+ } catch (IOException e) {
+ if (e.getCause() instanceof OutOfMemoryError) {
+ // TODO: Remove this temporary workaround once verifying large APKs is supported.
+ // Very large APKs cannot be memory-mapped. This verification code needs to change
+ // to use a different approach for verifying such APKs.
+ // This workaround pretends that this APK does not have a v2 signature. This works
+ // fine provided the APK is not actually v2-signed. If the APK is v2 signed, v2
+ // signature stripping protection inside v1 signature verification code will reject
+ // this APK.
+ throw new SignatureNotFoundException("Failed to memory-map APK", e);
+ } else {
+ throw new IOException("Failed to memory-map APK", e);
+ }
+ }
return verify(apkContents);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8048301..f912e51 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -363,6 +363,12 @@
void setDockedStackResizing(boolean resizing);
/**
+ * Sets the region the user can touch the divider. This region will be excluded from the region
+ * which is used to cause a focus switch when dispatching touch.
+ */
+ void setDockedStackDividerTouchRegion(in Rect touchableRegion);
+
+ /**
* Registers a listener that will be called when the dock divider changes its visibility or when
* the docked stack gets added/removed.
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9bd3df0..57ab6d4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17505,7 +17505,7 @@
* {@link SystemClock#uptimeMillis} timebase.
*/
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
if (verifyDrawable(who) && what != null) {
final long delay = when - SystemClock.uptimeMillis();
if (mAttachInfo != null) {
@@ -17527,7 +17527,7 @@
* @param what the action to cancel
*/
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
if (verifyDrawable(who) && what != null) {
if (mAttachInfo != null) {
mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
@@ -17637,7 +17637,7 @@
* @see #drawableStateChanged()
*/
@CallSuper
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
// Avoid verifying the scroll bar drawable so that we don't end up in
// an invalidation loop. This effectively prevents the scroll bar
// drawable from triggering invalidations and scheduling runnables.
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 0d05c54..69c30ba 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -170,7 +170,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || (mDrawables != null && mDrawables.contains(who));
}
@@ -229,7 +229,7 @@
}
@Override
- public void invalidateDrawable(Drawable drawable) {
+ public void invalidateDrawable(@NonNull Drawable drawable) {
invalidate(drawable.getBounds());
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d2aef0a..7cbe8de 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2864,7 +2864,7 @@
}
@Override
- public boolean verifyDrawable(Drawable dr) {
+ public boolean verifyDrawable(@NonNull Drawable dr) {
return mSelector == dr || super.verifyDrawable(dr);
}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 34f3a47..878a9eb 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -18,6 +18,7 @@
import com.android.internal.R;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -485,7 +486,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return who == mThumb || who == mTickMark || super.verifyDrawable(who);
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 9f94005..df506ca 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -308,7 +308,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return who == mCheckMarkDrawable || super.verifyDrawable(who);
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index b19fe17..5d7585f 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -474,7 +474,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == mButtonDrawable;
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 6959137..3b6ba3a 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -290,7 +290,6 @@
boolean mIsInsertionActionModeStartPending = false;
private final SuggestionHelper mSuggestionHelper = new SuggestionHelper();
- private SuggestionInfo[] mSuggestionInfosInContextMenu;
Editor(TextView textView) {
mTextView = textView;
@@ -926,7 +925,8 @@
}
void onLocaleChanged() {
- // Will be re-created on demand in getWordIterator with the proper new locale
+ // Will be re-created on demand in getWordIterator and getWordIteratorWithText with the
+ // proper new locale
mWordIterator = null;
mWordIteratorWithText = null;
}
@@ -2453,21 +2453,24 @@
}
if (shouldOfferToShowSuggestions()) {
- if (mSuggestionInfosInContextMenu == null) {
- mSuggestionInfosInContextMenu =
- new SuggestionInfo[SuggestionSpan.SUGGESTIONS_MAX_SIZE];
- for (int i = 0; i < mSuggestionInfosInContextMenu.length; i++) {
- mSuggestionInfosInContextMenu[i] = new SuggestionInfo();
- }
+ final SuggestionInfo[] suggestionInfoArray =
+ new SuggestionInfo[SuggestionSpan.SUGGESTIONS_MAX_SIZE];
+ for (int i = 0; i < suggestionInfoArray.length; i++) {
+ suggestionInfoArray[i] = new SuggestionInfo();
}
final SubMenu subMenu = menu.addSubMenu(Menu.NONE, Menu.NONE, MENU_ITEM_ORDER_REPLACE,
com.android.internal.R.string.replace);
- mSuggestionHelper.getSuggestionInfo(mSuggestionInfosInContextMenu);
- int i = 0;
- for (final SuggestionInfo info : mSuggestionInfosInContextMenu) {
- info.mSuggestionEnd = info.mText.length();
- subMenu.add(Menu.NONE, Menu.NONE, i++, info.mText)
- .setOnMenuItemClickListener(mOnContextMenuReplaceItemClickListener);
+ final int numItems = mSuggestionHelper.getSuggestionInfo(suggestionInfoArray);
+ for (int i = 0; i < numItems; i++) {
+ final SuggestionInfo info = suggestionInfoArray[i];
+ subMenu.add(Menu.NONE, Menu.NONE, i, info.mText)
+ .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ replaceWithSuggestion(info);
+ return true;
+ }
+ });
}
}
@@ -2608,27 +2611,6 @@
}
};
- private final MenuItem.OnMenuItemClickListener mOnContextMenuReplaceItemClickListener =
- new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- int index = item.getOrder();
- if (index < 0 || index >= mSuggestionInfosInContextMenu.length) {
- clear();
- return false;
- }
- replaceWithSuggestion(mSuggestionInfosInContextMenu[index]);
- clear();
- return true;
- }
-
- private void clear() {
- for (final SuggestionInfo info : mSuggestionInfosInContextMenu) {
- info.clear();
- }
- }
- };
-
/**
* Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
* pop-up should be displayed.
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index f601f7d..3400873 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -212,7 +212,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable dr) {
+ protected boolean verifyDrawable(@NonNull Drawable dr) {
return mDrawable == dr || super.verifyDrawable(dr);
}
@@ -223,7 +223,7 @@
}
@Override
- public void invalidateDrawable(Drawable dr) {
+ public void invalidateDrawable(@NonNull Drawable dr) {
if (dr == mDrawable) {
if (dr != null) {
// update cached drawable dimensions if they've changed
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 72a50ec1..ce94870 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1229,7 +1229,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return who == mProgressDrawable || who == mIndeterminateDrawable
|| super.verifyDrawable(who);
}
@@ -1692,7 +1692,7 @@
}
@Override
- public void invalidateDrawable(Drawable dr) {
+ public void invalidateDrawable(@NonNull Drawable dr) {
if (!mInDrawing) {
if (verifyDrawable(dr)) {
final Rect dirty = dr.getBounds();
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index 8880217..11eab2a 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -18,6 +18,7 @@
import com.android.internal.widget.ScrollBarUtils;
+import android.annotation.NonNull;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
@@ -362,17 +363,17 @@
}
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
invalidateSelf();
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
scheduleSelf(what, when);
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
unscheduleSelf(what);
}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 8a7ce12..43cf5a1 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -33,6 +33,7 @@
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.IntArray;
+import android.util.Log;
import android.util.MathUtils;
import android.util.StateSet;
import android.view.KeyEvent;
@@ -69,6 +70,9 @@
private static final int SELECTED_HIGHLIGHT_ALPHA = 0xB0;
+ /** Temporary until we figure out why the date gets messed up. */
+ private static final boolean DEBUG_WRONG_DATE = true;
+
private final TextPaint mMonthPaint = new TextPaint();
private final TextPaint mDayOfWeekPaint = new TextPaint();
private final TextPaint mDayPaint = new TextPaint();
@@ -189,6 +193,12 @@
}
private void updateDayOfWeekLabels() {
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "enter updateDayOfWeekLabels()", new Exception());
+ Log.d(LOG_TAG, "mLocale => " + mLocale);
+ Log.d(LOG_TAG, "mWeekStart => " + mWeekStart);
+ }
+
final Calendar calendar = Calendar.getInstance(mLocale);
calendar.setFirstDayOfWeek(mWeekStart);
@@ -197,6 +207,10 @@
calendar.set(Calendar.DAY_OF_WEEK, i);
mDayOfWeekLabels[i] = formatter.format(calendar.getTime());
}
+
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "mDayOfWeekLabels <= " + Arrays.toString(mDayOfWeekLabels));
+ }
}
/**
@@ -760,12 +774,20 @@
* {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
*/
public void setFirstDayOfWeek(int weekStart) {
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "enter setFirstDayOfWeek(" + weekStart + ")", new Exception());
+ }
+
if (isValidDayOfWeek(weekStart)) {
mWeekStart = weekStart;
} else {
mWeekStart = mCalendar.getFirstDayOfWeek();
}
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "mWeekStart <=" + mWeekStart);
+ }
+
updateDayOfWeekLabels();
// Invalidate cached accessibility information.
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 434516d..c4a1771 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -18,6 +18,7 @@
import android.animation.ObjectAnimator;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.content.Context;
@@ -1371,7 +1372,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4c461ad..e971f86 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5470,7 +5470,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
final boolean verified = super.verifyDrawable(who);
if (!verified && mDrawables != null) {
for (Drawable dr : mDrawables.mShowing) {
@@ -5495,7 +5495,7 @@
}
@Override
- public void invalidateDrawable(Drawable drawable) {
+ public void invalidateDrawable(@NonNull Drawable drawable) {
boolean handled = false;
if (verifyDrawable(drawable)) {
@@ -8923,8 +8923,7 @@
}
void onLocaleChanged() {
- // Will be re-created on demand in getWordIterator with the proper new locale
- mEditor.mWordIterator = null;
+ mEditor.onLocaleChanged();
}
/**
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index b13be97..3a31b37 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -45,6 +45,6 @@
void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages);
void setUserRestrictions(in Bundle restrictions, IBinder token, int userHandle);
- void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle);
+ void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in String[] exceptionPackages);
void removeUser(int userHandle);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index f04bcf2..90ee05e 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -505,7 +505,6 @@
final int numSubtypes = subtypes.size();
// Handle overridesImplicitlyEnabledSubtype mechanism.
- final String systemLanguage = systemLocales.get(0).getLanguage();
final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<>();
for (int i = 0; i < numSubtypes; ++i) {
// scan overriding implicitly enabled subtypes.
@@ -521,25 +520,20 @@
return new ArrayList<>(applicableModeAndSubtypesMap.values());
}
+ final HashMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
+ new HashMap<>();
final ArrayList<InputMethodSubtype> keyboardSubtypes = new ArrayList<>();
+
for (int i = 0; i < numSubtypes; ++i) {
final InputMethodSubtype subtype = subtypes.get(i);
- if (TextUtils.equals(SUBTYPE_MODE_KEYBOARD, subtype.getMode())) {
+ final String mode = subtype.getMode();
+ if (SUBTYPE_MODE_KEYBOARD.equals(mode)) {
keyboardSubtypes.add(subtype);
} else {
- final Locale locale = subtype.getLocaleObject();
- final String mode = subtype.getMode();
- // TODO: Take secondary system locales into consideration.
- if (locale != null && locale.equals(systemLanguage)) {
- final InputMethodSubtype applicableSubtype =
- applicableModeAndSubtypesMap.get(mode);
- // If more applicable subtypes are contained, skip.
- if (applicableSubtype != null) {
- if (systemLocale.equals(applicableSubtype.getLocaleObject())) continue;
- if (!systemLocale.equals(locale)) continue;
- }
- applicableModeAndSubtypesMap.put(mode, subtype);
+ if (!nonKeyboardSubtypesMap.containsKey(mode)) {
+ nonKeyboardSubtypesMap.put(mode, new ArrayList<>());
}
+ nonKeyboardSubtypesMap.get(mode).add(subtype);
}
}
@@ -578,7 +572,12 @@
}
}
- applicableSubtypes.addAll(applicableModeAndSubtypesMap.values());
+ // For each non-keyboard mode, extract subtypes with system locales.
+ for (final ArrayList<InputMethodSubtype> subtypeList : nonKeyboardSubtypesMap.values()) {
+ LocaleUtils.filterByLanguage(subtypeList, sSubtypeToLocale, systemLocales,
+ applicableSubtypes);
+ }
+
return applicableSubtypes;
}
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index 398bbe7..baf3188 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -147,7 +147,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return (who == mBackground && !mIsSplit) || (who == mStackedBackground && mIsStacked) ||
(who == mSplitBackground && mIsSplit) || super.verifyDrawable(who);
}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 948a6bb..277fafd 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -17,6 +17,7 @@
package com.android.internal.widget;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -746,7 +747,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == mMarginDrawable;
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9ccd7f0..50c7bfb 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -593,6 +593,9 @@
the appearance matches the branding of the app requesting the fingerprint scan.-->
<attr name="fingerprintAuthDrawable" format="reference" />
+ <!-- Asset that should be used to show users the position of the NFC antenna on the device. -->
+ <attr name="nfcAntennaPositionDrawable" format="reference" />
+
<!-- ============ -->
<!-- Panel styles -->
<!-- ============ -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ce5d07c..51cd029 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -805,6 +805,13 @@
-->
<integer name="config_longPressOnPowerBehavior">1</integer>
+ <!-- Control the behavior when the user long presses the back button. Non-zero values are only
+ valid for watches as part of CDD/CTS.
+ 0 - Nothing
+ 1 - Go to voice assist
+ -->
+ <integer name="config_longPressOnBackBehavior">0</integer>
+
<!-- Control the behavior when the user short presses the power button.
0 - Nothing
1 - Go to sleep (doze)
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 06e2248..2b0ef42 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2705,6 +2705,7 @@
<public type="attr" name="countDown" />
<public type="attr" name="canRecord" />
<public type="attr" name="tunerCount" />
+ <public type="attr" name="nfcAntennaPositionDrawable" />
<public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
<public type="style" name="Widget.Material.SeekBar.Discrete" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 70f4f54..1470741 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -382,6 +382,7 @@
<java-symbol type="integer" name="config_extraFreeKbytesAbsolute" />
<java-symbol type="integer" name="config_immersive_mode_confirmation_panic" />
<java-symbol type="integer" name="config_longPressOnPowerBehavior" />
+ <java-symbol type="integer" name="config_longPressOnBackBehavior" />
<java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAdjust" />
<java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAbsolute" />
<java-symbol type="integer" name="config_max_pan_devices" />
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index c92863d..b908d92 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -905,6 +905,10 @@
a1.start();
a2.reverse();
a3.start();
+ // Check that the animators' values are immediately set to end value in the case of
+ // 0-duration.
+ assertEquals(A1_END_VALUE, a1.getAnimatedValue());
+ assertEquals(A2_START_VALUE, a2.getAnimatedValue());
}
});
Thread.sleep(POLL_INTERVAL);
@@ -951,6 +955,10 @@
a1.start();
a2.start();
+
+ // In the case of 0 duration scale applied to a non-0 duration, check that the
+ // value is immediately set to the start value.
+ assertEquals(A2_START_VALUE, a2.getAnimatedValue());
}
});
Thread.sleep(POLL_INTERVAL);
@@ -962,6 +970,8 @@
assertTrue(l2.startCalled);
assertTrue(l1.endCalled);
assertTrue(l2.endCalled);
+ assertEquals(A1_END_VALUE, a1.getAnimatedValue());
+ assertEquals(A2_END_VALUE, a2.getAnimatedValue());
}
});
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 00df87d..923b829 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -143,6 +143,8 @@
onView(withId(R.id.textview)).check(hasSelection(""));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("i")));
+
+ // TODO: Add tests for suggestions
}
@SmallTest
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index 719b274..6b5e4ad 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -219,9 +219,28 @@
final InputMethodSubtype nonAutoHi = createDummyInputMethodSubtype("hi",
SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
!IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoSrCyrl = createDummyInputMethodSubtype("sr",
+ "sr-Cyrl", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
+ !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+ !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoSrLatn = createDummyInputMethodSubtype("sr_ZZ",
+ "sr-Latn", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
+ !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
+ !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
final InputMethodSubtype nonAutoHandwritingEn = createDummyInputMethodSubtype("en",
SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
!IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoHandwritingFr = createDummyInputMethodSubtype("fr",
+ SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+ !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoHandwritingSrCyrl = createDummyInputMethodSubtype("sr",
+ "sr-Cyrl", SUBTYPE_MODE_HANDWRITING, !IS_AUX,
+ !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+ !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoHandwritingSrLatn = createDummyInputMethodSubtype("sr_ZZ",
+ "sr-Latn", SUBTYPE_MODE_HANDWRITING, !IS_AUX,
+ !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+ !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype =
createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
@@ -242,6 +261,8 @@
subtypes.add(autoSubtype); // overridesImplicitlyEnabledSubtype == true
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -264,6 +285,8 @@
subtypes.add(nonAutoFil);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -271,7 +294,9 @@
final ArrayList<InputMethodSubtype> result =
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(LOCALE_EN_US), imi);
+ assertEquals(2, result.size());
verifyEquality(nonAutoEnUS, result.get(0));
+ verifyEquality(nonAutoHandwritingEn, result.get(1));
}
// Make sure that a subtype whose locale is exactly equal to the specified locale is
@@ -284,6 +309,8 @@
subtypes.add(nonAutoJa);
subtypes.add(nonAutoFil);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -291,8 +318,9 @@
final ArrayList<InputMethodSubtype> result =
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(LOCALE_EN_GB), imi);
- assertEquals(1, result.size());
+ assertEquals(2, result.size());
verifyEquality(nonAutoEnGB, result.get(0));
+ verifyEquality(nonAutoHandwritingEn, result.get(1));
}
// If there is no automatic subtype (overridesImplicitlyEnabledSubtype:true) and
@@ -306,6 +334,8 @@
subtypes.add(nonAutoFil);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -313,8 +343,9 @@
final ArrayList<InputMethodSubtype> result =
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(LOCALE_FR), imi);
- assertEquals(1, result.size());
+ assertEquals(2, result.size());
verifyEquality(nonAutoFrCA, result.get(0));
+ verifyEquality(nonAutoHandwritingFr, result.get(1));
}
// Then make sure that a subtype (locale: "fr") can be found with locale: "fr_CA".
{
@@ -324,6 +355,8 @@
subtypes.add(nonAutoFil);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -331,8 +364,9 @@
final ArrayList<InputMethodSubtype> result =
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(LOCALE_FR_CA), imi);
- assertEquals(1, result.size());
+ assertEquals(2, result.size());
verifyEquality(nonAutoFrCA, result.get(0));
+ verifyEquality(nonAutoHandwritingFr, result.get(1));
}
// Make sure that subtypes which have "EnabledWhenDefaultIsNotAsciiCapable" in its
@@ -343,6 +377,8 @@
subtypes.add(nonAutoJa); // not ASCII capable
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -363,6 +399,7 @@
subtypes.add(nonAutoHi);
subtypes.add(nonAutoEnUS);
subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
@@ -379,6 +416,7 @@
subtypes.add(nonAutoEnUS);
subtypes.add(nonAutoHi);
subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
@@ -393,6 +431,7 @@
{
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
subtypes.add(nonAutoEnUS);
subtypes.add(nonAutoHi);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
@@ -407,6 +446,85 @@
verifyEquality(nonAutoEnUS, result.get(0));
}
+ // Make sure that both language and script are taken into account to find the best matching
+ // subtype.
+ {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
+ subtypes.add(nonAutoEnUS);
+ subtypes.add(nonAutoSrCyrl);
+ subtypes.add(nonAutoSrLatn);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
+ subtypes.add(nonAutoHandwritingSrCyrl);
+ subtypes.add(nonAutoHandwritingSrLatn);
+ final InputMethodInfo imi = createDummyInputMethodInfo(
+ "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+ subtypes);
+ final ArrayList<InputMethodSubtype> result =
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ getResourcesForLocales(Locale.forLanguageTag("sr-Latn-RS")), imi);
+ assertEquals(2, result.size());
+ assertThat(nonAutoSrLatn, isIn(result));
+ assertThat(nonAutoHandwritingSrLatn, isIn(result));
+ }
+ {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
+ subtypes.add(nonAutoEnUS);
+ subtypes.add(nonAutoSrCyrl);
+ subtypes.add(nonAutoSrLatn);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
+ subtypes.add(nonAutoHandwritingSrCyrl);
+ subtypes.add(nonAutoHandwritingSrLatn);
+ final InputMethodInfo imi = createDummyInputMethodInfo(
+ "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+ subtypes);
+ final ArrayList<InputMethodSubtype> result =
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ getResourcesForLocales(Locale.forLanguageTag("sr-Cyrl-RS")), imi);
+ assertEquals(2, result.size());
+ assertThat(nonAutoSrCyrl, isIn(result));
+ assertThat(nonAutoHandwritingSrCyrl, isIn(result));
+ }
+
+ // Make sure that secondary locales are taken into account to find the best matching
+ // subtype.
+ {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
+ subtypes.add(nonAutoEnUS);
+ subtypes.add(nonAutoEnGB);
+ subtypes.add(nonAutoSrCyrl);
+ subtypes.add(nonAutoSrLatn);
+ subtypes.add(nonAutoFr);
+ subtypes.add(nonAutoFrCA);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
+ subtypes.add(nonAutoHandwritingSrCyrl);
+ subtypes.add(nonAutoHandwritingSrLatn);
+ final InputMethodInfo imi = createDummyInputMethodInfo(
+ "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+ subtypes);
+ final ArrayList<InputMethodSubtype> result =
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ getResourcesForLocales(
+ Locale.forLanguageTag("sr-Latn-RS-x-android"),
+ Locale.forLanguageTag("ja-JP"),
+ Locale.forLanguageTag("fr-FR"),
+ Locale.forLanguageTag("en-GB"),
+ Locale.forLanguageTag("en-US")),
+ imi);
+ assertEquals(6, result.size());
+ assertThat(nonAutoEnGB, isIn(result));
+ assertThat(nonAutoFr, isIn(result));
+ assertThat(nonAutoSrLatn, isIn(result));
+ assertThat(nonAutoHandwritingEn, isIn(result));
+ assertThat(nonAutoHandwritingFr, isIn(result));
+ assertThat(nonAutoHandwritingSrLatn, isIn(result));
+ }
+
// Make sure that 3-letter language code can be handled.
{
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
@@ -755,7 +873,15 @@
private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode,
boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype,
boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable) {
+ return createDummyInputMethodSubtype(locale, null /* languageTag */, mode, isAuxiliary,
+ overridesImplicitlyEnabledSubtype, isAsciiCapable,
+ isEnabledWhenDefaultIsNotAsciiCapable);
+ }
+ private static InputMethodSubtype createDummyInputMethodSubtype(String locale,
+ String languageTag, String mode, boolean isAuxiliary,
+ boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable,
+ boolean isEnabledWhenDefaultIsNotAsciiCapable) {
final StringBuilder subtypeExtraValue = new StringBuilder();
if (isEnabledWhenDefaultIsNotAsciiCapable) {
subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR);
@@ -773,6 +899,7 @@
.setSubtypeNameResId(0)
.setSubtypeIconResId(0)
.setSubtypeLocale(locale)
+ .setLanguageTag(languageTag)
.setSubtypeMode(mode)
.setSubtypeExtraValue(subtypeExtraValue.toString())
.setIsAuxiliary(isAuxiliary)
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index de741b3..dc85046 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -95,3 +95,14 @@
build-one-font-module :=
font_src_files :=
+
+
+# Run sanity tests on fonts on checkbuild
+checkbuild: fontchain_lint
+
+FONTCHAIN_LINTER := frameworks/base/tools/fonts/fontchain_lint.py
+
+.PHONY: fontchain_lint
+fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml
+ PYTHONPATH=$$PYTHONPATH:external/fonttools/Lib \
+ python $(FONTCHAIN_LINTER) $(TARGET_OUT)
\ No newline at end of file
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 99fa9fe..d312454 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -32,13 +32,15 @@
* @see Drawable#getOutline(Outline)
*/
public final class Outline {
+ private static final float RADIUS_UNDEFINED = -1.0f;
+
/** @hide */
public Path mPath;
/** @hide */
public Rect mRect;
/** @hide */
- public float mRadius;
+ public float mRadius = RADIUS_UNDEFINED;
/** @hide */
public float mAlpha;
@@ -63,7 +65,7 @@
public void setEmpty() {
mPath = null;
mRect = null;
- mRadius = 0;
+ mRadius = RADIUS_UNDEFINED;
}
/**
@@ -223,6 +225,7 @@
mPath.reset();
mPath.addOval(left, top, right, bottom, Path.Direction.CW);
mRect = null;
+ mRadius = RADIUS_UNDEFINED;
}
/**
@@ -249,7 +252,7 @@
mPath.set(convexPath);
mRect = null;
- mRadius = -1.0f;
+ mRadius = RADIUS_UNDEFINED;
}
/**
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 219bca8..39ea205 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -260,6 +260,13 @@
return mAnimatedVectorState.mVectorDrawable.setLayoutDirection(layoutDirection);
}
+ /**
+ * AnimatedVectorDrawable is running on render thread now. Therefore, if the root alpha is being
+ * animated, then the root alpha value we get from this call could be out of sync with alpha
+ * value used in the render thread. Otherwise, the root alpha should be always the same value.
+ *
+ * @return the containing vector drawable's root alpha value.
+ */
@Override
public int getAlpha() {
return mAnimatedVectorState.mVectorDrawable.getAlpha();
@@ -276,6 +283,11 @@
}
@Override
+ public ColorFilter getColorFilter() {
+ return mAnimatedVectorState.mVectorDrawable.getColorFilter();
+ }
+
+ @Override
public void setTintList(ColorStateList tint) {
mAnimatedVectorState.mVectorDrawable.setTintList(tint);
}
@@ -703,17 +715,17 @@
private final Callback mCallback = new Callback() {
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
invalidateSelf();
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
scheduleSelf(what, when);
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
unscheduleSelf(what);
}
};
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 3d8437d..f106c68 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -308,7 +308,7 @@
* to supply your implementation of the interface to the drawable; it uses
* this interface to schedule and execute animation changes.
*/
- public static interface Callback {
+ public interface Callback {
/**
* Called when the drawable needs to be redrawn. A view at this point
* should invalidate itself (or at least the part of itself where the
@@ -316,7 +316,7 @@
*
* @param who The drawable that is requesting the update.
*/
- public void invalidateDrawable(Drawable who);
+ void invalidateDrawable(@NonNull Drawable who);
/**
* A Drawable can call this to schedule the next frame of its
@@ -330,7 +330,7 @@
* @param when The time (in milliseconds) to run. The timebase is
* {@link android.os.SystemClock#uptimeMillis}
*/
- public void scheduleDrawable(Drawable who, Runnable what, long when);
+ void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when);
/**
* A Drawable can call this to unschedule an action previously
@@ -342,7 +342,7 @@
* @param who The drawable being unscheduled.
* @param what The action being unscheduled.
*/
- public void unscheduleDrawable(Drawable who, Runnable what);
+ void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what);
}
/**
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 3b0e7e8..a91d1f0 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -373,21 +373,21 @@
}
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().invalidateDrawable(this);
}
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().scheduleDrawable(this, what, when);
}
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().unscheduleDrawable(this, what);
}
@@ -804,6 +804,7 @@
mConstantPadding = null;
mCheckedPadding = false;
mCheckedConstantSize = false;
+ mCheckedConstantState = false;
return pos;
}
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index c427870..4df2d57 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -198,7 +198,7 @@
}
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
@@ -206,7 +206,7 @@
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
@@ -214,7 +214,7 @@
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index e2150c0..d142f95 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -944,17 +944,17 @@
}
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
invalidateSelf();
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
scheduleSelf(what, when);
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
unscheduleSelf(what);
}
diff --git a/graphics/java/android/graphics/drawable/shapes/ArcShape.java b/graphics/java/android/graphics/drawable/shapes/ArcShape.java
index 84731b0..c4b239f 100644
--- a/graphics/java/android/graphics/drawable/shapes/ArcShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/ArcShape.java
@@ -17,6 +17,7 @@
package android.graphics.drawable.shapes;
import android.graphics.Canvas;
+import android.graphics.Outline;
import android.graphics.Paint;
/**
@@ -46,5 +47,11 @@
public void draw(Canvas canvas, Paint paint) {
canvas.drawArc(rect(), mStart, mSweep, true, paint);
}
+
+ @Override
+ public void getOutline(Outline outline) {
+ // Since we don't support concave outlines, arc shape does not attempt
+ // to provide an outline.
+ }
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 936c7e8..f6e3b50 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -240,6 +240,7 @@
tests/unit/DamageAccumulatorTests.cpp \
tests/unit/DeviceInfoTests.cpp \
tests/unit/FatVectorTests.cpp \
+ tests/unit/GlopBuilderTests.cpp \
tests/unit/GpuMemoryTrackerTests.cpp \
tests/unit/LayerUpdateQueueTests.cpp \
tests/unit/LinearAllocatorTests.cpp \
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
index 97dec88..9a39ec2 100644
--- a/libs/hwui/FloatColor.h
+++ b/libs/hwui/FloatColor.h
@@ -17,6 +17,7 @@
#define FLOATCOLOR_H
#include "utils/Macros.h"
+#include "utils/MathUtils.h"
#include <stdint.h>
@@ -38,6 +39,17 @@
|| b > 0.0f;
}
+ bool operator==(const FloatColor& other) const {
+ return MathUtils::areEqual(r, other.r)
+ && MathUtils::areEqual(g, other.g)
+ && MathUtils::areEqual(b, other.b)
+ && MathUtils::areEqual(a, other.a);
+ }
+
+ bool operator!=(const FloatColor& other) const {
+ return !(*this == other);
+ }
+
float r;
float g;
float b;
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index e72f396..704bd69 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -81,8 +81,10 @@
* vertex/index/Texture/RoundRectClipState pointers prevent this from
* being safe.
*/
-// TODO: PREVENT_COPY_AND_ASSIGN(...) or similar
struct Glop {
+ PREVENT_COPY_AND_ASSIGN(Glop);
+public:
+ Glop() { }
struct Mesh {
GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported
@@ -149,7 +151,7 @@
}
} transform;
- const RoundRectClipState* roundRectClipState;
+ const RoundRectClipState* roundRectClipState = nullptr;
/**
* Blending to be used by this draw - both GL_NONE if blending is disabled.
@@ -165,7 +167,7 @@
* Bounds of the drawing command in layer space. Only mapped into layer
* space once GlopBuilder::build() is called.
*/
- Rect bounds;
+ Rect bounds; // TODO: remove for HWUI_NEW_OPS
/**
* Additional render state to enumerate:
diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp
new file mode 100644
index 0000000..949c541
--- /dev/null
+++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "BakedOpRenderer.h"
+#include "Glop.h"
+#include "GlopBuilder.h"
+#include "Rect.h"
+#include "tests/common/TestUtils.h"
+#include "utils/Color.h"
+
+#include <SkPaint.h>
+
+using namespace android::uirenderer;
+
+static void expectFillEq(Glop::Fill& expectedFill, Glop::Fill& builtFill) {
+ EXPECT_EQ(expectedFill.colorEnabled, builtFill.colorEnabled);
+ if (expectedFill.colorEnabled)
+ EXPECT_EQ(expectedFill.color, builtFill.color);
+
+ EXPECT_EQ(expectedFill.filterMode, builtFill.filterMode);
+ if (expectedFill.filterMode == ProgramDescription::ColorFilterMode::Blend) {
+ EXPECT_EQ(expectedFill.filter.color, builtFill.filter.color);
+ } else if (expectedFill.filterMode == ProgramDescription::ColorFilterMode::Matrix) {
+ Glop::Fill::Filter::Matrix& expectedMatrix = expectedFill.filter.matrix;
+ Glop::Fill::Filter::Matrix& builtMatrix = expectedFill.filter.matrix;
+ EXPECT_TRUE(std::memcmp(expectedMatrix.matrix, builtMatrix.matrix,
+ sizeof(Glop::Fill::Filter::Matrix::matrix)));
+ EXPECT_TRUE(std::memcmp(expectedMatrix.vector, builtMatrix.vector,
+ sizeof(Glop::Fill::Filter::Matrix::vector)));
+ }
+ EXPECT_EQ(expectedFill.skiaShaderData.skiaShaderType, builtFill.skiaShaderData.skiaShaderType);
+ EXPECT_EQ(expectedFill.texture.clamp, builtFill.texture.clamp);
+ EXPECT_EQ(expectedFill.texture.filter, builtFill.texture.filter);
+ EXPECT_EQ(expectedFill.texture.target, builtFill.texture.target);
+ EXPECT_EQ(expectedFill.texture.textureTransform, builtFill.texture.textureTransform);
+}
+
+static void expectBlendEq(Glop::Blend& expectedBlend, Glop::Blend& builtBlend) {
+ EXPECT_EQ(expectedBlend.src, builtBlend.src);
+ EXPECT_EQ(expectedBlend.dst, builtBlend.dst);
+}
+
+static void expectMeshEq(Glop::Mesh& expectedMesh, Glop::Mesh& builtMesh) {
+ EXPECT_EQ(expectedMesh.elementCount, builtMesh.elementCount);
+ EXPECT_EQ(expectedMesh.primitiveMode, builtMesh.primitiveMode);
+ EXPECT_EQ(expectedMesh.indices.indices, builtMesh.indices.indices);
+ EXPECT_EQ(expectedMesh.indices.bufferObject, builtMesh.indices.bufferObject);
+ EXPECT_EQ(expectedMesh.vertices.attribFlags, builtMesh.vertices.attribFlags);
+ EXPECT_EQ(expectedMesh.vertices.bufferObject, builtMesh.vertices.bufferObject);
+ EXPECT_EQ(expectedMesh.vertices.color, builtMesh.vertices.color);
+ EXPECT_EQ(expectedMesh.vertices.position, builtMesh.vertices.position);
+ EXPECT_EQ(expectedMesh.vertices.stride, builtMesh.vertices.stride);
+ EXPECT_EQ(expectedMesh.vertices.texCoord, builtMesh.vertices.texCoord);
+
+ if (builtMesh.vertices.position) {
+ for (int i = 0; i < 4; i++) {
+ TextureVertex& expectedVertex = expectedMesh.mappedVertices[i];
+ TextureVertex& builtVertex = builtMesh.mappedVertices[i];
+ EXPECT_EQ(expectedVertex.u, builtVertex.u);
+ EXPECT_EQ(expectedVertex.v, builtVertex.v);
+ EXPECT_EQ(expectedVertex.x, builtVertex.x);
+ EXPECT_EQ(expectedVertex.y, builtVertex.y);
+ }
+ }
+}
+
+static void expectTransformEq(Glop::Transform& expectedTransform, Glop::Transform& builtTransform) {
+ EXPECT_EQ(expectedTransform.canvas, builtTransform.canvas);
+ EXPECT_EQ(expectedTransform.modelView, builtTransform.modelView);
+ EXPECT_EQ(expectedTransform.transformFlags, expectedTransform.transformFlags);
+}
+
+static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) {
+ EXPECT_EQ(expectedGlop.bounds, builtGlop.bounds);
+ expectBlendEq(expectedGlop.blend, builtGlop.blend);
+ expectFillEq(expectedGlop.fill, builtGlop.fill);
+ expectMeshEq(expectedGlop.mesh, builtGlop.mesh);
+ expectTransformEq(expectedGlop.transform, builtGlop.transform);
+}
+
+static std::unique_ptr<Glop> blackUnitQuadGlop(RenderState& renderState) {
+ std::unique_ptr<Glop> glop(new Glop());
+ glop->blend = { GL_ZERO, GL_ZERO };
+ glop->mesh.elementCount = 4;
+ glop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+ glop->mesh.indices.indices = nullptr;
+ glop->mesh.indices.bufferObject = GL_ZERO;
+ glop->mesh.vertices = {
+ renderState.meshState().getUnitQuadVBO(),
+ VertexAttribFlags::None,
+ nullptr, nullptr, nullptr,
+ kTextureVertexStride };
+ glop->transform.modelView.loadIdentity();
+ glop->fill.colorEnabled = true;
+ glop->fill.color.set(Color::Black);
+ glop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType;
+ glop->fill.filterMode = ProgramDescription::ColorFilterMode::None;
+ glop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ return glop;
+}
+
+RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) {
+ RenderState& renderState = renderThread.renderState();
+ Caches& caches = Caches::getInstance();
+ SkPaint paint;
+ Rect dest(1, 1, 100, 100);
+ Matrix4 simpleTranslate;
+ simpleTranslate.loadTranslate(0.7, 0.7, 0);
+ Glop glop;
+ GlopBuilder(renderState, caches, &glop)
+ .setRoundRectClipState(nullptr)
+ .setMeshUnitQuad()
+ .setFillPaint(paint, 1.0f)
+ .setTransform(simpleTranslate, TransformFlags::None)
+ .setModelViewMapUnitToRectSnap(dest)
+ .build();
+
+ std::unique_ptr<Glop> goldenGlop(blackUnitQuadGlop(renderState));
+ // Rect(1,1,100,100) is the set destination,
+ // so unit quad should be translated by (1,1) and scaled by (99, 99)
+ // Tricky part: because translate (0.7, 0.7) and snapping were set in glopBuilder,
+ // unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels.
+ goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0);
+ goldenGlop->transform.modelView.scale(99, 99, 1);
+ goldenGlop->bounds = android::uirenderer::Rect(1.70, 1.70, 100.70, 100.70);
+ goldenGlop->transform.canvas = simpleTranslate;
+ goldenGlop->fill.texture.filter = GL_NEAREST;
+ expectGlopEq(*goldenGlop, glop);
+}
diff --git a/packages/DocumentsUI/res/animator-ldrtl/dir_leave.xml b/packages/DocumentsUI/res/animator-ldrtl/dir_leave.xml
deleted file mode 100644
index 8e2925c..0000000
--- a/packages/DocumentsUI/res/animator-ldrtl/dir_leave.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="-1"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime"
- android:interpolator="@android:interpolator/accelerate_quad" />
diff --git a/packages/DocumentsUI/res/animator/dir_enter.xml b/packages/DocumentsUI/res/animator/dir_enter.xml
index 7f547f1..43c50bd 100644
--- a/packages/DocumentsUI/res/animator/dir_enter.xml
+++ b/packages/DocumentsUI/res/animator/dir_enter.xml
@@ -13,10 +13,24 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="1"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime"
- android:interpolator="@android:interpolator/decelerate_quad" />
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="together">
+
+ <objectAnimator
+ android:valueFrom="0f"
+ android:valueTo="1f"
+ android:propertyName="alpha"
+ android:valueType="floatType"
+ android:duration="@android:integer/config_mediumAnimTime"
+ android:interpolator="@android:interpolator/decelerate_quad" />
+
+ <!-- position property maps to AnimationView.setPosition -->
+ <objectAnimator
+ android:propertyName="position"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:duration="@android:integer/config_mediumAnimTime"
+ android:interpolator="@android:interpolator/decelerate_quad" />
+
+</set>
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/packages/DocumentsUI/res/animator/dir_frozen.xml
deleted file mode 100644
index b541d13..0000000
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
diff --git a/packages/DocumentsUI/res/animator/dir_leave.xml b/packages/DocumentsUI/res/animator/dir_leave.xml
index fda0faf..7574655 100644
--- a/packages/DocumentsUI/res/animator/dir_leave.xml
+++ b/packages/DocumentsUI/res/animator/dir_leave.xml
@@ -13,10 +13,24 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="1"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime"
- android:interpolator="@android:interpolator/accelerate_quad" />
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="together">
+
+ <objectAnimator
+ android:valueFrom="1f"
+ android:valueTo="0f"
+ android:propertyName="alpha"
+ android:valueType="floatType"
+ android:duration="@android:integer/config_mediumAnimTime"
+ android:interpolator="@android:interpolator/decelerate_quad" />
+
+ <!-- position property maps to AnimationView.setPosition -->
+ <objectAnimator
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:propertyName="position"
+ android:valueType="floatType"
+ android:duration="@android:integer/config_mediumAnimTime"
+ android:interpolator="@android:interpolator/accelerate_quad" />
+
+</set>
\ No newline at end of file
diff --git a/packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml b/packages/DocumentsUI/res/animator/fade_in.xml
similarity index 90%
rename from packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml
rename to packages/DocumentsUI/res/animator/fade_in.xml
index 6c7e224..3ce012b 100644
--- a/packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml
+++ b/packages/DocumentsUI/res/animator/fade_in.xml
@@ -14,9 +14,9 @@
-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="-1"
- android:valueTo="0"
- android:propertyName="position"
+ android:valueFrom="0f"
+ android:valueTo="1f"
+ android:propertyName="alpha"
android:valueType="floatType"
android:duration="@android:integer/config_mediumAnimTime"
android:interpolator="@android:interpolator/decelerate_quad" />
diff --git a/packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml b/packages/DocumentsUI/res/animator/fade_out.xml
similarity index 90%
copy from packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml
copy to packages/DocumentsUI/res/animator/fade_out.xml
index 6c7e224..8d02c77 100644
--- a/packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml
+++ b/packages/DocumentsUI/res/animator/fade_out.xml
@@ -14,9 +14,9 @@
-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="-1"
- android:valueTo="0"
- android:propertyName="position"
+ android:valueFrom="1f"
+ android:valueTo="0f"
+ android:propertyName="alpha"
android:valueType="floatType"
android:duration="@android:integer/config_mediumAnimTime"
android:interpolator="@android:interpolator/decelerate_quad" />
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index 03c6a83..8eb46dd 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -14,7 +14,8 @@
limitations under the License.
-->
-<com.android.documentsui.DirectoryView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.dirlist.AnimationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/directory_background"
@@ -99,4 +100,4 @@
</FrameLayout>
-</com.android.documentsui.DirectoryView>
+</com.android.documentsui.dirlist.AnimationView>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index c9d18b3..1a8ce18 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -18,10 +18,6 @@
import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.State.MODE_GRID;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_ENTER;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_LEAVE;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_SIDE;
import android.app.Activity;
import android.app.Fragment;
@@ -48,6 +44,7 @@
import com.android.documentsui.SearchViewManager.SearchManagerListener;
import com.android.documentsui.State.ViewMode;
+import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
@@ -225,7 +222,7 @@
// Otherwise we delegate loading data from disk to a task
// to ensure a responsive ui.
if (mRoots.isRecentsRoot(root)) {
- refreshCurrentRootAndDirectory(ANIM_NONE);
+ refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
} else {
new PickRootTask(this, root).executeOnExecutor(getExecutorForCurrentDirectory());
}
@@ -327,7 +324,7 @@
// previous directory. Especially after opening a root document, pressing
// back, wouldn't go to the previous root, but close the activity.
final int anim = (mState.hasLocationChanged() && mState.stack.size() > 1)
- ? ANIM_ENTER : ANIM_NONE;
+ ? AnimationView.ANIM_ENTER : AnimationView.ANIM_NONE;
refreshCurrentRootAndDirectory(anim);
}
@@ -543,7 +540,7 @@
// Update the restored stack to ensure we have freshest data
stack.updateDocuments(getContentResolver());
mState.setStack(stack);
- refreshCurrentRootAndDirectory(ANIM_SIDE);
+ refreshCurrentRootAndDirectory(AnimationView.ANIM_SIDE);
} catch (FileNotFoundException e) {
Log.w(mTag, "Failed to restore stack: " + e);
@@ -644,7 +641,7 @@
private boolean popDir() {
if (mState.stack.size() > 1) {
mState.stack.pop();
- refreshCurrentRootAndDirectory(ANIM_LEAVE);
+ refreshCurrentRootAndDirectory(AnimationView.ANIM_LEAVE);
return true;
}
return false;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
deleted file mode 100644
index 000b92a..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.documentsui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.LinearLayout;
-
-public class DirectoryView extends LinearLayout {
- private float mPosition = 0f;
-
- private int mWidth;
-
- public DirectoryView(Context context) {
- super(context);
- }
-
- public DirectoryView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mWidth = w;
- setPosition(mPosition);
- }
-
- public float getPosition() {
- return mPosition;
- }
-
- public void setPosition(float position) {
- mPosition = position;
- setX((mWidth > 0) ? (mPosition * mWidth) : 0);
-
- if (mPosition != 0) {
- setTranslationZ(getResources().getDimensionPixelSize(R.dimen.dir_elevation));
- } else {
- setTranslationZ(0);
- }
- }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index f8b32a0..ba593dc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -22,7 +22,6 @@
import static com.android.documentsui.State.ACTION_OPEN;
import static com.android.documentsui.State.ACTION_OPEN_TREE;
import static com.android.documentsui.State.ACTION_PICK_COPY_DESTINATION;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import android.app.Activity;
import android.app.Fragment;
@@ -46,6 +45,7 @@
import com.android.documentsui.RecentsProvider.RecentColumns;
import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
@@ -492,7 +492,7 @@
protected void finish(Void result) {
mState.restored = true;
mState.external = mExternal;
- mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
+ mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 573e4f3..0af8aa2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -18,7 +18,6 @@
import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_UNKNOWN;
import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import android.app.Activity;
import android.app.FragmentManager;
@@ -39,6 +38,7 @@
import com.android.documentsui.OperationDialogFragment.DialogType;
import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
@@ -97,7 +97,7 @@
if (DEBUG) Log.d(TAG, "Launching with non-empty stack.");
assert(uri == null || uri.getAuthority() == null ||
LauncherActivity.isLaunchUri(uri));
- refreshCurrentRootAndDirectory(ANIM_NONE);
+ refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
} else if (intent.getAction() == Intent.ACTION_VIEW) {
assert(uri != null);
new OpenUriForViewTask(this).executeOnExecutor(
@@ -470,7 +470,7 @@
@Override
protected void finish(Void result) {
- mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
+ mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index c520204..30c1020 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -19,7 +19,6 @@
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_LEAVE;
import android.annotation.Nullable;
import android.graphics.drawable.Drawable;
@@ -30,10 +29,10 @@
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
-import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
+import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
@@ -105,7 +104,7 @@
while (mState.stack.size() > position + 1) {
mState.popDocument();
}
- mEnv.refreshCurrentRootAndDirectory(ANIM_LEAVE);
+ mEnv.refreshCurrentRootAndDirectory(AnimationView.ANIM_LEAVE);
}
void update() {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/AnimationView.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/AnimationView.java
new file mode 100644
index 0000000..a666456
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/AnimationView.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui.dirlist;
+
+import android.annotation.IntDef;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import com.android.documentsui.R;
+import com.android.documentsui.Shared;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class exists solely to support animated transition of our directory fragment.
+ * The structure of this class is tightly coupled with the static animations defined in
+ * res/animator, specifically the "position" property referenced by
+ * res/animator/dir_{enter,leave}.xml.
+ */
+public class AnimationView extends LinearLayout {
+
+ @IntDef(flag = true, value = {
+ ANIM_NONE,
+ ANIM_SIDE,
+ ANIM_LEAVE,
+ ANIM_ENTER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AnimationType {}
+ public static final int ANIM_NONE = 1;
+ public static final int ANIM_SIDE = 2;
+ public static final int ANIM_LEAVE = 3;
+ public static final int ANIM_ENTER = 4;
+
+ private float mPosition = 0f;
+
+ // The distance the animation will cover...currently matches the height of the
+ // content area.
+ private int mSpan;
+
+ public AnimationView(Context context) {
+ super(context);
+ }
+
+ public AnimationView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mSpan = h;
+ setPosition(mPosition);
+ }
+
+ public float getPosition() {
+ return mPosition;
+ }
+
+ public void setPosition(float position) {
+ mPosition = position;
+ // Warning! If we ever decide to switch this to setX (slide left/right)
+ // please remember to add RLT variations of the animations under res/animator-ldrtl.
+ setY((mSpan > 0) ? (mPosition * mSpan) : 0);
+
+ if (mPosition != 0) {
+ setTranslationZ(getResources().getDimensionPixelSize(R.dimen.dir_elevation));
+ } else {
+ setTranslationZ(0);
+ }
+ }
+
+ /**
+ * Configures custom animations on the transaction according to the specified
+ * @AnimationType.
+ */
+ static void setupAnimations(
+ FragmentTransaction ft, @AnimationType int anim, Bundle args) {
+ switch (anim) {
+ case AnimationView.ANIM_SIDE:
+ args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
+ break;
+ case AnimationView.ANIM_ENTER:
+ // TODO: Document which behavior is being tailored
+ // by passing this bit. Remove if possible.
+ args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
+ ft.setCustomAnimations(R.animator.dir_enter, R.animator.fade_out);
+ break;
+ case AnimationView.ANIM_LEAVE:
+ ft.setCustomAnimations(R.animator.fade_in, R.animator.dir_leave);
+ break;
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 461bade..bfc8d71 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -84,7 +84,6 @@
import com.android.documentsui.Events.MotionInputEvent;
import com.android.documentsui.Menus;
import com.android.documentsui.MessageBar;
-import com.android.documentsui.MimePredicate;
import com.android.documentsui.R;
import com.android.documentsui.RecentsLoader;
import com.android.documentsui.RootsCache;
@@ -125,19 +124,6 @@
public static final int TYPE_RECENT_OPEN = 2;
@IntDef(flag = true, value = {
- ANIM_NONE,
- ANIM_SIDE,
- ANIM_LEAVE,
- ANIM_ENTER
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AnimationType {}
- public static final int ANIM_NONE = 1;
- public static final int ANIM_SIDE = 2;
- public static final int ANIM_LEAVE = 3;
- public static final int ANIM_ENTER = 4;
-
- @IntDef(flag = true, value = {
REQUEST_COPY_DESTINATION
})
@Retention(RetentionPolicy.SOURCE)
@@ -1485,18 +1471,7 @@
args.putParcelable(Shared.EXTRA_SELECTION, new Selection());
final FragmentTransaction ft = fm.beginTransaction();
- switch (anim) {
- case ANIM_SIDE:
- args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
- break;
- case ANIM_ENTER:
- args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
- ft.setCustomAnimations(R.animator.dir_enter, R.animator.dir_frozen);
- break;
- case ANIM_LEAVE:
- ft.setCustomAnimations(R.animator.dir_frozen, R.animator.dir_leave);
- break;
- }
+ AnimationView.setupAnimations(ft, anim, args);
final DirectoryFragment fragment = new DirectoryFragment();
fragment.setArguments(args);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 637551c..334035c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -187,6 +187,15 @@
android:process=":screenshot"
android:exported="false" />
+ <!-- Called from PhoneWindowManager -->
+ <receiver android:name=".screenshot.ScreenshotServiceErrorReceiver"
+ android:process=":screenshot"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.systemui.screenshot.SHOW_ERROR" />
+ </intent-filter>
+ </receiver>
+
<service android:name=".LoadAverageService"
android:exported="true" />
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 0491ea0..7af247e 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -24,26 +24,4 @@
android:background="@drawable/qs_customizer_background"
android:gravity="center_horizontal">
- <Toolbar
- android:id="@*android:id/action_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="28dp"
- android:navigationContentDescription="@*android:string/action_bar_up_description"
- style="?android:attr/toolbarStyle" />
-
- <android.support.v7.widget.RecyclerView
- android:id="@android:id/list"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:scrollIndicators="top"
- android:scrollbars="vertical" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="@dimen/navigation_bar_size"
- android:layout_gravity="bottom"
- android:background="#ff000000" />
-
</com.android.systemui.qs.customize.QSCustomizer>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
new file mode 100644
index 0000000..75f8fa4
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <Toolbar
+ android:id="@*android:id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="28dp"
+ android:navigationContentDescription="@*android:string/action_bar_up_description"
+ style="?android:attr/toolbarStyle" />
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@android:id/list"
+ android:layout_width="@dimen/notification_panel_width"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:scrollIndicators="top"
+ android:scrollbars="vertical" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/navigation_bar_size"
+ android:layout_gravity="bottom"
+ android:background="#ff000000" />
+</merge>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 30acc72..d65ab04 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -165,4 +165,7 @@
<!-- Keyboard shortcuts colors -->
<color name="ksh_system_group_color">#ff00bcd4</color>
<color name="ksh_application_group_color">#fff44336</color>
+
+ <!-- Background color of edit overflow -->
+ <color name="qs_edit_overflow_bg">#455A64</color>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7838fea..8af413c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -185,7 +185,9 @@
<string name="screenshot_saved_text">Touch to view your screenshot.</string>
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
<string name="screenshot_failed_title">Couldn\'t capture screenshot.</string>
- <!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
+ <!-- Notification text displayed when we fail to save a screenshot for unknown reasons. [CHAR LIMIT=100] -->
+ <string name="screenshot_failed_to_save_unknown_text">Problem encountered while saving screenshot.</string>
+ <!-- Notification text displayed when we fail to save a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_save_text">Can\'t save screenshot due to limited storage space.</string>
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_capture_text">Taking screenshots is not allowed by the app or your organization.</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 89890d6..2660926 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -339,4 +339,8 @@
<item name="android:colorAccent">@color/switch_accent_color</item>
</style>
+ <style name="edit_theme" parent="@android:style/Theme.Material">
+ <item name="android:colorBackground">@color/qs_edit_overflow_bg</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 068efa6..72a59d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -25,6 +25,7 @@
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -66,26 +67,11 @@
private QSContainer mQsContainer;
public QSCustomizer(Context context, AttributeSet attrs) {
- super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
+ super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
mClipper = new QSDetailClipper(this);
- }
- public void setHost(QSTileHost host) {
- mHost = host;
- mPhoneStatusBar = host.getPhoneStatusBar();
- }
+ LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
- public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
- mNotifQsContainer = notificationsQsContainer;
- }
-
- public void setQsContainer(QSContainer qsContainer) {
- mQsContainer = qsContainer;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
mToolbar = (Toolbar) findViewById(com.android.internal.R.id.action_bar);
TypedValue value = new TypedValue();
mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
@@ -115,6 +101,19 @@
mRecyclerView.setItemAnimator(animator);
}
+ public void setHost(QSTileHost host) {
+ mHost = host;
+ mPhoneStatusBar = host.getPhoneStatusBar();
+ }
+
+ public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
+ mNotifQsContainer = notificationsQsContainer;
+ }
+
+ public void setQsContainer(QSContainer qsContainer) {
+ mQsContainer = qsContainer;
+ }
+
public void show(int x, int y) {
if (!isShown) {
isShown = true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 73ce26f..2b6ed44 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -45,6 +45,7 @@
import com.android.systemui.RecentsComponent;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
@@ -619,6 +620,12 @@
}
}
+ public final void onBusEvent(ConfigurationChangedEvent event) {
+ // Update the configuration for the Recents component when the activity configuration
+ // changes as well
+ mImpl.onConfigurationChanged();
+ }
+
/**
* Attempts to register with the system user.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 4e11bca..d864df8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -759,6 +759,7 @@
TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
TaskStackViewScroller stackScroller = stackView.getScroller();
+ stackView.updateLayoutAlgorithm(true /* boundScroll */);
stackView.updateToInitialState();
for (int i = tasks.size() - 1; i >= 0; i--) {
@@ -825,6 +826,7 @@
}
// Get the transform for the running task
+ stackView.updateLayoutAlgorithm(true /* boundScroll */);
stackView.updateToInitialState();
mTmpTransform = stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
stackView.getScroller().getStackScroll(), mTmpTransform, 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 e2830a1..6c410c3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -813,7 +813,7 @@
*
* @see #updateLayoutAlgorithm(boolean, ArraySet<Task.TaskKey>)
*/
- void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
+ public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
updateLayoutAlgorithm(boundScrollToNewMinMax, mIgnoreTasks);
}
@@ -822,7 +822,7 @@
*
* @param ignoreTasksSet the set of tasks to ignore in the relayout
*/
- void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
+ public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
ArraySet<Task.TaskKey> ignoreTasksSet) {
// Compute the min and max scroll values
mLayoutAlgorithm.update(mStack, ignoreTasksSet);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index e64354c..eb08947 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -93,13 +93,13 @@
/**
* An AsyncTask that saves an image to the media store in the background.
*/
-class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void,
- SaveImageInBackgroundData> {
+class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private static final String SCREENSHOTS_DIR_NAME = "Screenshots";
private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
+ private final SaveImageInBackgroundData mParams;
private final NotificationManager mNotificationManager;
private final Notification.Builder mNotificationBuilder, mPublicNotificationBuilder;
private final File mScreenshotDir;
@@ -122,6 +122,7 @@
Resources r = context.getResources();
// Prepare all the output metadata
+ mParams = data;
mImageTime = System.currentTimeMillis();
String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
@@ -210,17 +211,17 @@
}
@Override
- protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) {
+ protected Void doInBackground(Void... params) {
if (isCancelled()) {
- return params[0];
+ return null;
}
// By default, AsyncTask sets the worker thread to have background thread priority, so bump
// it back up so that we save a little quicker.
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
- Context context = params[0].context;
- Bitmap image = params[0].image;
+ Context context = mParams.context;
+ Bitmap image = mParams.image;
Resources r = context.getResources();
try {
@@ -284,14 +285,14 @@
r.getString(com.android.internal.R.string.delete), deleteAction);
mNotificationBuilder.addAction(deleteActionBuilder.build());
- params[0].imageUri = uri;
- params[0].image = null;
- params[0].errorMsgResId = 0;
+ mParams.imageUri = uri;
+ mParams.image = null;
+ mParams.errorMsgResId = 0;
} catch (Exception e) {
// IOException/UnsupportedOperationException may be thrown if external storage is not
// mounted
- params[0].clearImage();
- params[0].errorMsgResId = R.string.screenshot_failed_to_save_text;
+ mParams.clearImage();
+ mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
}
// Recycle the bitmap data
@@ -299,23 +300,23 @@
image.recycle();
}
- return params[0];
+ return null;
}
@Override
- protected void onPostExecute(SaveImageInBackgroundData params) {
- if (params.errorMsgResId != 0) {
+ protected void onPostExecute(Void params) {
+ if (mParams.errorMsgResId != 0) {
// Show a message that we've failed to save the image to disk
- GlobalScreenshot.notifyScreenshotError(params.context, mNotificationManager,
- params.errorMsgResId);
+ GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,
+ mParams.errorMsgResId);
} else {
// Show the final notification to indicate screenshot saved
- Context context = params.context;
+ Context context = mParams.context;
Resources r = context.getResources();
// Create the intent to show the screenshot in gallery
Intent launchIntent = new Intent(Intent.ACTION_VIEW);
- launchIntent.setDataAndType(params.imageUri, "image/png");
+ launchIntent.setDataAndType(mParams.imageUri, "image/png");
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final long now = System.currentTimeMillis();
@@ -324,7 +325,7 @@
mPublicNotificationBuilder
.setContentTitle(r.getString(R.string.screenshot_saved_title))
.setContentText(r.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0))
+ .setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
.setWhen(now)
.setAutoCancel(true)
.setColor(context.getColor(
@@ -332,7 +333,7 @@
mNotificationBuilder
.setContentTitle(r.getString(R.string.screenshot_saved_title))
.setContentText(r.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0))
+ .setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
.setWhen(now)
.setAutoCancel(true)
.setColor(context.getColor(
@@ -342,15 +343,18 @@
mNotificationManager.notify(R.id.notification_screenshot, mNotificationBuilder.build());
}
- params.finisher.run();
- params.clearContext();
+ mParams.finisher.run();
+ mParams.clearContext();
}
@Override
- protected void onCancelled(SaveImageInBackgroundData params) {
- params.finisher.run();
- params.clearImage();
- params.clearContext();
+ protected void onCancelled(Void params) {
+ // If we are cancelled while the task is running in the background, we may get null params.
+ // The finisher is expected to always be called back, so just use the baked-in params from
+ // the ctor in any case.
+ mParams.finisher.run();
+ mParams.clearImage();
+ mParams.clearContext();
// Cancel the posted notification
mNotificationManager.cancel(R.id.notification_screenshot);
@@ -419,7 +423,7 @@
private float mBgPadding;
private float mBgPaddingScale;
- private AsyncTask<SaveImageInBackgroundData, Void, SaveImageInBackgroundData> mSaveInBgTask;
+ private AsyncTask<Void, Void, Void> mSaveInBgTask;
private MediaActionSound mCameraSound;
@@ -510,7 +514,7 @@
mSaveInBgTask.cancel(false);
}
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager)
- .execute(data);
+ .execute();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
new file mode 100644
index 0000000..fc2a1e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.screenshot;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.systemui.R;
+
+/**
+ * Performs a number of miscellaneous, non-system-critical actions
+ * after the system has finished booting.
+ */
+public class ScreenshotServiceErrorReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ // Show a message that we've failed to save the image to disk
+ NotificationManager nm = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ GlobalScreenshot.notifyScreenshotError(context, nm,
+ R.string.screenshot_failed_to_save_unknown_text);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 7cb1f24..e015666 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -16,6 +16,9 @@
package com.android.systemui.stackdivider;
+import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -31,6 +34,9 @@
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnDoubleTapListener;
+import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.VelocityTracker;
@@ -39,6 +45,7 @@
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -52,17 +59,16 @@
import com.android.internal.policy.DockedDividerUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
-import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
-
/**
* Docked stack divider.
*/
@@ -136,6 +142,7 @@
private boolean mGrowRecents;
private ValueAnimator mCurrentAnimator;
private boolean mEntranceAnimationRunning;
+ private GestureDetector mGestureDetector;
private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
@Override
@@ -213,12 +220,35 @@
landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW));
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
mHandle.setAccessibilityDelegate(mHandleDelegate);
+ mGestureDetector = new GestureDetector(mContext, new SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ updateDockSide();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (mDockSide != WindowManager.DOCKED_INVALID
+ && !ssp.isRecentsTopMost(ssp.getTopMostTask(), null /* isTopHome */)) {
+ mWindowManagerProxy.swapTasks();
+ return true;
+ }
+ return false;
+ }
+ });
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
+ getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+
+ @Override
+ public void onGlobalLayout() {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(),
+ mHandle.getLeft() + mHandle.getWidth(),
+ mHandle.getTop() + mHandle.getHeight()));
+ }
+ });
}
@Override
@@ -320,6 +350,7 @@
@Override
public boolean onTouch(View v, MotionEvent event) {
convertToScreenCoordinates(event);
+ mGestureDetector.onTouchEvent(event);
final int action = event.getAction() & MotionEvent.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN:
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 15bcaf8..e312fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -16,6 +16,9 @@
package com.android.systemui.stackdivider;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.view.WindowManager.DOCKED_INVALID;
+
import android.app.ActivityManagerNative;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -27,9 +30,6 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.view.WindowManager.DOCKED_INVALID;
-
/**
* Proxy to simplify calls into window manager/activity manager
*/
@@ -39,7 +39,7 @@
private static final WindowManagerProxy sInstance = new WindowManagerProxy();
- @GuardedBy("mResizeRect")
+ @GuardedBy("mDockedRect")
private final Rect mDockedRect = new Rect();
private final Rect mTempDockedTaskRect = new Rect();
private final Rect mTempDockedInsetRect = new Rect();
@@ -52,6 +52,9 @@
private final Rect mTmpRect4 = new Rect();
private final Rect mTmpRect5 = new Rect();
+ @GuardedBy("mDockedRect")
+ private final Rect mTouchableRegion = new Rect();
+
private boolean mDimLayerVisible;
private int mDimLayerTargetStack;
private float mDimLayerAlpha;
@@ -117,6 +120,32 @@
}
};
+ private final Runnable mSwapRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManagerNative.getDefault().swapDockedAndFullscreenStack();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to resize stack: " + e);
+ }
+ }
+ };
+
+ private final Runnable mSetTouchableRegionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ synchronized (mDockedRect) {
+ mTmpRect1.set(mTouchableRegion);
+ }
+ WindowManagerGlobal.getWindowManagerService().setDockedStackDividerTouchRegion(
+ mTmpRect1);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to set touchable region: " + e);
+ }
+ }
+ };
+
private WindowManagerProxy() {
}
@@ -188,4 +217,15 @@
mDimLayerAlpha = alpha;
mExecutor.execute(mDimLayerRunnable);
}
+
+ public void swapTasks() {
+ mExecutor.execute(mSwapRunnable);
+ }
+
+ public void setTouchRegion(Rect region) {
+ synchronized (mDockedRect) {
+ mTouchableRegion.set(region);
+ }
+ mExecutor.execute(mSetTouchableRegionRunnable);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index cedc3c7..ab44b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -315,8 +315,8 @@
public void logoutCurrentUser() {
int currentUser = ActivityManager.getCurrentUser();
if (currentUser != UserHandle.USER_SYSTEM) {
- switchToUserId(UserHandle.USER_SYSTEM);
- stopUserId(currentUser);
+ pauseRefreshUsers();
+ ActivityManager.logoutCurrentUser();
}
}
@@ -384,14 +384,6 @@
}
}
- private void stopUserId(int id) {
- try {
- ActivityManagerNative.getDefault().stopUser(id, /* force= */ false, null);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't stop user.", e);
- }
- }
-
private void showExitGuestDialog(int id) {
if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
mExitGuestDialog.cancel();
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 32f2d59..bfe9e8e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -25,6 +25,7 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -57,6 +58,7 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -107,8 +109,21 @@
private final SparseArray<UidState> mUidStates = new SparseArray<>();
- /** These are app op restrictions imposed per user from various parties */
- private final ArrayMap<IBinder, SparseArray<boolean[]>> mOpUserRestrictions = new ArrayMap<>();
+ /*
+ * These are app op restrictions imposed per user from various parties.
+ *
+ * This is organized as follows:
+ *
+ * ArrayMap w/ mapping:
+ * IBinder (for client imposing restriction) --> SparseArray w/ mapping:
+ * User handle --> Pair containing:
+ * - Array w/ index = AppOp code, value = restricted status boolean
+ * - SparseArray w/ mapping:
+ * AppOp code --> Set of packages that are not restricted for this code
+ *
+ */
+ private final ArrayMap<IBinder, SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>>
+ mOpUserRestrictions = new ArrayMap<>();
private static final class UidState {
public final int uid;
@@ -1267,11 +1282,35 @@
private boolean isOpRestricted(int uid, int code, String packageName) {
int userHandle = UserHandle.getUserId(uid);
final int restrictionSetCount = mOpUserRestrictions.size();
+
for (int i = 0; i < restrictionSetCount; i++) {
- SparseArray<boolean[]> perUserRestrictions = mOpUserRestrictions.valueAt(i);
- boolean[] opRestrictions = perUserRestrictions.get(userHandle);
- if (opRestrictions != null && opRestrictions[code]) {
+ // For each client, check that the given op is not restricted, or that the given
+ // package is exempt from the restriction.
+
+ SparseArray<Pair<boolean[],SparseArray<ArraySet<String>>>> perUserRestrictions =
+ mOpUserRestrictions.valueAt(i);
+
+ Pair<boolean[],SparseArray<ArraySet<String>>> restrictions =
+ perUserRestrictions.get(userHandle);
+ if (restrictions == null) {
+ continue; // No restrictions set by this client
+ }
+
+ boolean[] opRestrictions = restrictions.first;
+ SparseArray<ArraySet<String>> opExceptions = restrictions.second;
+
+ if (opRestrictions == null) {
+ continue; // No restrictions set by this client
+ }
+
+ if (opRestrictions[code]) {
+ if (opExceptions != null && opExceptions.get(code) != null &&
+ opExceptions.get(code).contains(packageName)) {
+ continue; // AppOps code is restricted, but this package is exempt
+ }
+
if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
+ // If we are the system, bypass user restrictions for certain codes
synchronized (this) {
Ops ops = getOpsLocked(uid, packageName, true);
if ((ops != null) && ops.isPrivileged) {
@@ -1279,6 +1318,7 @@
}
}
}
+
return true;
}
}
@@ -2069,7 +2109,8 @@
}
@Override
- public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle) {
+ public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle,
+ String[] exceptionPackages) {
if (Binder.getCallingPid() != Process.myPid()) {
mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
@@ -2085,12 +2126,37 @@
}
verifyIncomingOp(code);
Preconditions.checkNotNull(token);
- setUserRestrictionNoCheck(code, restricted, token, userHandle);
+ setUserRestrictionNoCheck(code, restricted, token, userHandle, exceptionPackages);
}
private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
int userHandle) {
+ setUserRestrictionNoCheck(code, restricted, token, userHandle, /*exceptionPackages*/null);
+ }
+
+ private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
+ int userHandle, String[] exceptionPackages) {
+
final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle);
+
+ if (restricted) {
+ final SparseArray<ArraySet<String>> opExceptions =
+ getUserPackageExemptionsForToken(token, userHandle);
+
+ // If exceptionPackages is not null, update the exception packages for this AppOps code
+ ArraySet<String> exceptions = opExceptions.get(code);
+ if (exceptionPackages != null) {
+ if (exceptions == null) {
+ exceptions = new ArraySet<>(exceptionPackages.length);
+ opExceptions.put(code, exceptions);
+ } else {
+ exceptions.clear();
+ }
+
+ exceptions.addAll(Arrays.asList(exceptionPackages));
+ }
+ }
+
if (opRestrictions[code] == restricted) {
return;
}
@@ -2132,7 +2198,8 @@
checkSystemUid("removeUser");
final int tokenCount = mOpUserRestrictions.size();
for (int i = tokenCount - 1; i >= 0; i--) {
- SparseArray<boolean[]> opRestrictions = mOpUserRestrictions.valueAt(i);
+ SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> opRestrictions =
+ mOpUserRestrictions.valueAt(i);
if (opRestrictions != null) {
opRestrictions.remove(userHandle);
if (opRestrictions.size() <= 0) {
@@ -2144,15 +2211,23 @@
private void pruneUserRestrictionsForToken(IBinder token, int userHandle) {
- SparseArray<boolean[]> perTokenRestrictions = mOpUserRestrictions.get(token);
+ SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions =
+ mOpUserRestrictions.get(token);
if (perTokenRestrictions != null) {
- final boolean[] opRestrictions = perTokenRestrictions.get(userHandle);
- if (opRestrictions != null) {
- for (boolean restriction : opRestrictions) {
- if (restriction) {
- return;
+ final Pair<boolean[], SparseArray<ArraySet<String>>> restrictions =
+ perTokenRestrictions.get(userHandle);
+
+ if (restrictions != null) {
+ final boolean[] opRestrictions = restrictions.first;
+ if (opRestrictions != null) {
+ for (boolean restriction : opRestrictions) {
+ if (restriction) {
+ return;
+ }
}
}
+
+ // No restrictions set for this client
perTokenRestrictions.remove(userHandle);
if (perTokenRestrictions.size() <= 0) {
mOpUserRestrictions.remove(token);
@@ -2161,18 +2236,61 @@
}
}
+ /**
+ * Get or create the user restrictions array for a given client if it doesn't already exist.
+ *
+ * @param token the binder client creating the restriction.
+ * @param userHandle the user handle to create a restriction for.
+ *
+ * @return the array of restriction states for each AppOps code.
+ */
private boolean[] getOrCreateUserRestrictionsForToken(IBinder token, int userHandle) {
- SparseArray<boolean[]> perTokenRestrictions = mOpUserRestrictions.get(token);
+ SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions =
+ mOpUserRestrictions.get(token);
+
if (perTokenRestrictions == null) {
- perTokenRestrictions = new SparseArray<>();
+ perTokenRestrictions =
+ new SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>();
mOpUserRestrictions.put(token, perTokenRestrictions);
}
- boolean[] opRestrictions = perTokenRestrictions.get(userHandle);
- if (opRestrictions == null) {
- opRestrictions = new boolean[AppOpsManager._NUM_OP];
- perTokenRestrictions.put(userHandle, opRestrictions);
+
+ Pair<boolean[], SparseArray<ArraySet<String>>> restrictions =
+ perTokenRestrictions.get(userHandle);
+
+ if (restrictions == null) {
+ restrictions = new Pair<boolean[], SparseArray<ArraySet<String>>>(
+ new boolean[AppOpsManager._NUM_OP], new SparseArray<ArraySet<String>>());
+ perTokenRestrictions.put(userHandle, restrictions);
}
- return opRestrictions;
+
+ return restrictions.first;
+ }
+
+ /**
+ * Get the per-package exemptions for each AppOps code for a given client and userHandle.
+ *
+ * @param token the binder client to get the exemptions for.
+ * @param userHandle the user handle to get the exemptions for.
+ *
+ * @return a mapping from the AppOps code to a set of packages exempt for that code.
+ */
+ private SparseArray<ArraySet<String>> getUserPackageExemptionsForToken(IBinder token,
+ int userHandle) {
+ SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions =
+ mOpUserRestrictions.get(token);
+
+ if (perTokenRestrictions == null) {
+ return null; // Don't create user restrictions accidentally
+ }
+
+ Pair<boolean[], SparseArray<ArraySet<String>>> restrictions =
+ perTokenRestrictions.get(userHandle);
+
+ if (restrictions == null) {
+ return null; // Don't create user restrictions accidentally
+ }
+
+ return restrictions.second;
}
private void checkSystemUid(String function) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index a93b4d8..9b8f2d2 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3588,6 +3588,7 @@
private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue";
private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
+ private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
private final AtomicFile mAdditionalInputMethodSubtypeFile;
private final HashMap<String, InputMethodInfo> mMethodMap;
private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
@@ -3684,6 +3685,8 @@
out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
out.attribute(null, ATTR_IS_AUXILIARY,
String.valueOf(subtype.isAuxiliary() ? 1 : 0));
+ out.attribute(null, ATTR_IS_ASCII_CAPABLE,
+ String.valueOf(subtype.isAsciiCapable() ? 1 : 0));
out.endTag(null, NODE_SUBTYPE);
}
out.endTag(null, NODE_IMI);
@@ -3749,6 +3752,8 @@
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE);
final boolean isAuxiliary = "1".equals(String.valueOf(
parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
+ final boolean isAsciiCapable = "1".equals(String.valueOf(
+ parser.getAttributeValue(null, ATTR_IS_ASCII_CAPABLE)));
final InputMethodSubtype subtype = new InputMethodSubtypeBuilder()
.setSubtypeNameResId(label)
.setSubtypeIconResId(icon)
@@ -3757,6 +3762,7 @@
.setSubtypeMode(imeSubtypeMode)
.setSubtypeExtraValue(imeSubtypeExtraValue)
.setIsAuxiliary(isAuxiliary)
+ .setIsAsciiCapable(isAsciiCapable)
.build();
tempSubtypesArray.add(subtype);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 61290e8..07f668b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -352,6 +352,11 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
+import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -3262,9 +3267,9 @@
boolean isNextTransitionForward() {
int transit = mWindowManager.getPendingAppTransition();
- return transit == AppTransition.TRANSIT_ACTIVITY_OPEN
- || transit == AppTransition.TRANSIT_TASK_OPEN
- || transit == AppTransition.TRANSIT_TASK_TO_FRONT;
+ return transit == TRANSIT_ACTIVITY_OPEN
+ || transit == TRANSIT_TASK_OPEN
+ || transit == TRANSIT_TASK_TO_FRONT;
}
int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
@@ -9102,7 +9107,8 @@
preserveWindow = false;
}
- mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode, preserveWindow);
+ mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode, preserveWindow,
+ false /* deferResume */);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -9167,7 +9173,7 @@
throw new IllegalArgumentException("Expected in-place ActivityOption " +
"with valid animation");
}
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_IN_PLACE, false);
+ mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
mWindowManager.overridePendingAppTransitionInPlace(opts.getPackageName(),
opts.getCustomInPlaceResId());
mWindowManager.executeAppTransition();
@@ -9546,6 +9552,55 @@
}
}
+ @Override
+ public void swapDockedAndFullscreenStack() throws RemoteException {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "swapDockedAndFullscreenStack()");
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ final ActivityStack fullscreenStack = mStackSupervisor.getStack(
+ FULLSCREEN_WORKSPACE_STACK_ID);
+ final TaskRecord topTask = fullscreenStack != null ? fullscreenStack.topTask()
+ : null;
+ final ActivityStack dockedStack = mStackSupervisor.getStack(DOCKED_STACK_ID);
+ final ArrayList<TaskRecord> tasks = dockedStack != null ? dockedStack.getAllTasks()
+ : null;
+ if (topTask == null || tasks == null || tasks.size() == 0) {
+ Slog.w(TAG,
+ "Unable to swap tasks, either docked or fullscreen stack is empty.");
+ return;
+ }
+
+ // TODO: App transition
+ mWindowManager.prepareAppTransition(TRANSIT_ACTIVITY_RELAUNCH, false);
+
+ // Defer the resume so resume/pausing while moving stacks is dangerous.
+ mStackSupervisor.moveTaskToStackLocked(topTask.taskId, DOCKED_STACK_ID,
+ false /* toTop */, !FORCE_FOCUS, "swapDockedAndFullscreenStack",
+ ANIMATE, true /* deferResume */);
+ final int size = tasks.size();
+ for (int i = 0; i < size; i++) {
+ final int id = tasks.get(i).taskId;
+ if (id == topTask.taskId) {
+ continue;
+ }
+ mStackSupervisor.moveTaskToStackLocked(id,
+ FULLSCREEN_WORKSPACE_STACK_ID, true /* toTop */, !FORCE_FOCUS,
+ "swapDockedAndFullscreenStack", ANIMATE, true /* deferResume */);
+ }
+
+ // Because we deferred the resume, to avoid conflicts with stack switches while
+ // resuming, we need to do it after all the tasks are moved.
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
+
+ mWindowManager.executeAppTransition();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
/**
* Moves the input task to the docked stack.
*
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5561456..09542cb 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1809,6 +1809,12 @@
r.app.pendingUiClean = true;
r.app.thread.scheduleWindowVisibility(r.appToken, true);
r.stopFreezingScreenLocked(false);
+
+ // The activity may be waiting for stop, but that is no longer
+ // appropriate for it.
+ mStackSupervisor.mStoppingActivities.remove(r);
+ mStackSupervisor.mGoingToSleepActivities.remove(r);
+ mStackSupervisor.mWaitingVisibleActivities.remove(r);
} catch (Exception e) {
// Just skip on any failure; we'll make it
// visible when it next restarts.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 48f31f9..d364d85 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2062,7 +2062,8 @@
}
}
- boolean resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
+ boolean resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow,
+ boolean deferResume) {
if (!task.isResizeable()) {
Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
return true;
@@ -2105,10 +2106,14 @@
if (r != null) {
final ActivityStack stack = task.stack;
kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
- // All other activities must be made visible with their correct configuration.
- ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
- if (!kept) {
- resumeFocusedStackTopActivityLocked();
+
+ if (!deferResume) {
+
+ // All other activities must be made visible with their correct configuration.
+ ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
+ if (!kept) {
+ resumeFocusedStackTopActivityLocked();
+ }
}
}
}
@@ -2247,6 +2252,12 @@
boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
String reason, boolean animate) {
+ return moveTaskToStackLocked(taskId, stackId, toTop, forceFocus, reason, animate,
+ false /* deferResume */);
+ }
+
+ boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
+ String reason, boolean animate, boolean deferResume) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
@@ -2297,16 +2308,19 @@
// Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
- kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow);
+ kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
+ !mightReplaceWindow, deferResume);
} else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
Rect bounds = task.getLaunchBounds();
if (bounds == null) {
stack.layoutTaskInStack(task, null);
bounds = task.mBounds;
}
- kept = resizeTaskLocked(task, bounds, RESIZE_MODE_FORCED, !mightReplaceWindow);
+ kept = resizeTaskLocked(task, bounds, RESIZE_MODE_FORCED, !mightReplaceWindow,
+ deferResume);
} else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
- kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow);
+ kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
+ !mightReplaceWindow, deferResume);
}
} finally {
mWindowManager.continueSurfaceLayout();
@@ -2319,10 +2333,13 @@
mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken, !kept);
}
- // The task might have already been running and its visibility needs to be synchronized with
- // the visibility of the stack / windows.
- ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
- resumeFocusedStackTopActivityLocked();
+ if (!deferResume) {
+
+ // The task might have already been running and its visibility needs to be synchronized with
+ // the visibility of the stack / windows.
+ ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
+ resumeFocusedStackTopActivityLocked();
+ }
showNonResizeableDockToastIfNeeded(task, preferredLaunchStackId, stackId);
@@ -2372,6 +2389,11 @@
if (task.mActivities.size() == 1) {
// There is only one activity in the task. So, we can just move the task over to
// the stack without re-parenting the activity in a different task.
+ if (task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
+ // Move the home stack forward if the task we just moved to the pinned stack
+ // was launched from home so home should be visible behind it.
+ moveHomeStackToFront(reason);
+ }
moveTaskToStackLocked(
task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS, reason, !ANIMATE);
} else {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 004be34..4eae45c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -759,6 +759,14 @@
if (r.isPersistable()) {
mService.notifyTaskPersisterLocked(this, false);
}
+
+ if (stack != null && stack.mStackId == PINNED_STACK_ID) {
+ // We normally notify listeners of task stack changes on pause, however pinned stack
+ // activities are normally in the paused state so no notification will be sent there
+ // before the activity is removed. We send it here so instead.
+ mService.notifyTaskStackChangedLocked();
+ }
+
if (mActivities.isEmpty()) {
return !mReuseTask;
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index e90fb32..b7cd318 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -51,6 +51,7 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
@@ -76,6 +77,7 @@
publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
}
+ @VisibleForTesting
static class LauncherAppsImpl extends ILauncherApps.Stub {
private static final boolean DEBUG = false;
private static final String TAG = "LauncherAppsService";
@@ -167,7 +169,8 @@
/**
* Checks if the caller is in the same group as the userToCheck.
*/
- private void ensureInUserProfiles(UserHandle userToCheck, String message) {
+ @VisibleForTesting // We override it in unit tests
+ void ensureInUserProfiles(UserHandle userToCheck, String message) {
final int callingUserId = UserHandle.getCallingUserId();
final int targetUserId = userToCheck.getIdentifier();
@@ -187,12 +190,14 @@
}
}
- private void verifyCallingPackage(String callingPackage) {
+ @VisibleForTesting // We override it in unit tests
+ void verifyCallingPackage(String callingPackage) {
int packageUid = -1;
try {
- packageUid = mPm.getPackageUid(callingPackage,
+ packageUid = mPm.getPackageUidAsUser(callingPackage,
PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
- | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ UserHandle.getUserId(getCallingUid()));
} catch (NameNotFoundException e) {
Log.e(TAG, "Package not found: " + callingPackage);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bf5a8f6..4c77f28 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4291,6 +4291,7 @@
}
pw.print(prefix); pw.print(" versionCode="); pw.print(ps.versionCode);
if (ps.pkg != null) {
+ pw.print(" minSdk="); pw.print(ps.pkg.applicationInfo.minSdkVersion);
pw.print(" targetSdk="); pw.print(ps.pkg.applicationInfo.targetSdkVersion);
}
pw.println();
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 1cd0592..0b0f7ab 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -88,6 +88,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.function.Predicate;
/**
@@ -149,6 +150,7 @@
static final String DIRECTORY_BITMAPS = "bitmaps";
static final String TAG_ROOT = "root";
+ static final String TAG_USER = "user";
static final String TAG_PACKAGE = "package";
static final String TAG_LAST_RESET_TIME = "last_reset_time";
static final String TAG_INTENT_EXTRAS = "intent-extras";
@@ -221,11 +223,10 @@
private long mRawLastResetTime;
/**
- * User ID -> package name -> list of ShortcutInfos.
+ * User ID -> UserShortcuts
*/
@GuardedBy("mLock")
- private final SparseArray<ArrayMap<String, PackageShortcuts>> mShortcuts =
- new SparseArray<>();
+ private final SparseArray<UserShortcuts> mUsers = new SparseArray<>();
/**
* Max number of dynamic shortcuts that each application can have at a time.
@@ -313,7 +314,7 @@
/** lifecycle event */
void onCleanupUserInner(int userId) {
// Unload
- mShortcuts.delete(userId);
+ mUsers.delete(userId);
}
/** Return the base state file name */
@@ -583,20 +584,9 @@
XmlSerializer out = new FastXmlSerializer();
out.setOutput(outs, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
- out.startTag(null, TAG_ROOT);
- final ArrayMap<String, PackageShortcuts> packages = getUserShortcutsLocked(userId);
+ getUserShortcutsLocked(userId).saveToXml(out);
- // Body.
- for (int i = 0; i < packages.size(); i++) {
- final String packageName = packages.keyAt(i);
- final PackageShortcuts packageShortcuts = packages.valueAt(i);
-
- packageShortcuts.saveToXml(out);
- }
-
- // Epilogue.
- out.endTag(null, TAG_ROOT);
out.endDocument();
// Close.
@@ -612,7 +602,7 @@
}
@Nullable
- private ArrayMap<String, PackageShortcuts> loadUserLocked(@UserIdInt int userId) {
+ private UserShortcuts loadUserLocked(@UserIdInt int userId) {
final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
if (DEBUG) {
Slog.i(TAG, "Loading from " + path);
@@ -628,13 +618,11 @@
}
return null;
}
- final ArrayMap<String, PackageShortcuts> ret = new ArrayMap<>();
+ UserShortcuts ret = null;
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, StandardCharsets.UTF_8.name());
- PackageShortcuts shortcuts = null;
-
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
@@ -647,22 +635,9 @@
Slog.d(TAG, String.format("depth=%d type=%d name=%s",
depth, type, tag));
}
- switch (depth) {
- case 1: {
- if (TAG_ROOT.equals(tag)) {
- continue;
- }
- break;
- }
- case 2: {
- switch (tag) {
- case TAG_PACKAGE:
- shortcuts = PackageShortcuts.loadFromXml(parser, userId);
- ret.put(shortcuts.mPackageName, shortcuts);
- continue;
- }
- break;
- }
+ if ((depth == 1) && TAG_USER.equals(tag)) {
+ ret = UserShortcuts.loadFromXml(parser, userId);
+ continue;
}
throwForInvalidTag(depth, tag);
}
@@ -731,14 +706,14 @@
/** Return the per-user state. */
@GuardedBy("mLock")
@NonNull
- private ArrayMap<String, PackageShortcuts> getUserShortcutsLocked(@UserIdInt int userId) {
- ArrayMap<String, PackageShortcuts> userPackages = mShortcuts.get(userId);
+ private UserShortcuts getUserShortcutsLocked(@UserIdInt int userId) {
+ UserShortcuts userPackages = mUsers.get(userId);
if (userPackages == null) {
userPackages = loadUserLocked(userId);
if (userPackages == null) {
- userPackages = new ArrayMap<>();
+ userPackages = new UserShortcuts(userId);
}
- mShortcuts.put(userId, userPackages);
+ mUsers.put(userId, userPackages);
}
return userPackages;
}
@@ -748,11 +723,11 @@
@NonNull
private PackageShortcuts getPackageShortcutsLocked(
@NonNull String packageName, @UserIdInt int userId) {
- final ArrayMap<String, PackageShortcuts> userPackages = getUserShortcutsLocked(userId);
- PackageShortcuts shortcuts = userPackages.get(packageName);
+ final UserShortcuts userPackages = getUserShortcutsLocked(userId);
+ PackageShortcuts shortcuts = userPackages.getPackages().get(packageName);
if (shortcuts == null) {
shortcuts = new PackageShortcuts(userId, packageName);
- userPackages.put(packageName, shortcuts);
+ userPackages.getPackages().put(packageName, shortcuts);
}
return shortcuts;
}
@@ -1335,7 +1310,7 @@
userId, ret, cloneFlag);
} else {
final ArrayMap<String, PackageShortcuts> packages =
- getUserShortcutsLocked(userId);
+ getUserShortcutsLocked(userId).getPackages();
for (int i = packages.size() - 1; i >= 0; i--) {
getShortcutsInnerLocked(
packages.keyAt(i),
@@ -1512,38 +1487,14 @@
pw.print(mIconPersistQuality);
pw.println();
- pw.println();
- for (int i = 0; i < mShortcuts.size(); i++) {
- dumpUserLocked(pw, mShortcuts.keyAt(i));
+ for (int i = 0; i < mUsers.size(); i++) {
+ pw.println();
+ mUsers.valueAt(i).dump(this, pw, " ");
}
}
}
- private void dumpUserLocked(PrintWriter pw, int userId) {
- pw.print(" User: ");
- pw.print(userId);
- pw.println();
-
- final ArrayMap<String, PackageShortcuts> packages = mShortcuts.get(userId);
- if (packages == null) {
- return;
- }
- for (int j = 0; j < packages.size(); j++) {
- dumpPackageLocked(pw, userId, packages.keyAt(j));
- }
- pw.println();
- }
-
- private void dumpPackageLocked(PrintWriter pw, int userId, String packageName) {
- final PackageShortcuts packageShortcuts = mShortcuts.get(userId).get(packageName);
- if (packageShortcuts == null) {
- return;
- }
-
- packageShortcuts.dump(this, pw, " ");
- }
-
static String formatTime(long time) {
Time tobj = new Time();
tobj.set(time);
@@ -1694,8 +1645,8 @@
}
@VisibleForTesting
- SparseArray<ArrayMap<String, PackageShortcuts>> getShortcutsForTest() {
- return mShortcuts;
+ SparseArray<UserShortcuts> getShortcutsForTest() {
+ return mUsers;
}
@VisibleForTesting
@@ -1736,6 +1687,73 @@
}
}
+class UserShortcuts {
+ private static final String TAG = ShortcutService.TAG;
+
+ @UserIdInt
+ final int mUserId;
+
+ private final ArrayMap<String, PackageShortcuts> mPackages = new ArrayMap<>();
+
+ public UserShortcuts(int userId) {
+ mUserId = userId;
+ }
+
+ public ArrayMap<String, PackageShortcuts> getPackages() {
+ return mPackages;
+ }
+
+ public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+ out.startTag(null, ShortcutService.TAG_USER);
+
+ for (int i = 0; i < mPackages.size(); i++) {
+ final String packageName = mPackages.keyAt(i);
+ final PackageShortcuts packageShortcuts = mPackages.valueAt(i);
+
+ packageShortcuts.saveToXml(out);
+ }
+
+ out.endTag(null, ShortcutService.TAG_USER);
+ }
+
+ public static UserShortcuts loadFromXml(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+ final UserShortcuts ret = new UserShortcuts(userId);
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ final String tag = parser.getName();
+ switch (tag) {
+ case ShortcutService.TAG_PACKAGE:
+ final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId);
+
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.getPackages().put(shortcuts.mPackageName, shortcuts);
+ continue;
+ }
+ throw ShortcutService.throwForInvalidTag(depth, tag);
+ }
+ return ret;
+ }
+
+ public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.print(" ");
+ pw.print("User: ");
+ pw.print(mUserId);
+ pw.println();
+
+ for (int i = 0; i < mPackages.size(); i++) {
+ mPackages.valueAt(i).dump(s, pw, prefix + " ");
+ }
+ }
+}
+
/**
* All the information relevant to shortcuts from a single package (per-user).
*/
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0cd69c4..9af1304 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -191,6 +191,9 @@
static final int LONG_PRESS_POWER_SHUT_OFF = 2;
static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3;
+ static final int LONG_PRESS_BACK_NOTHING = 0;
+ static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1;
+
static final int MULTI_PRESS_POWER_NOTHING = 0;
static final int MULTI_PRESS_POWER_THEATER_MODE = 1;
static final int MULTI_PRESS_POWER_BRIGHTNESS_BOOST = 2;
@@ -251,6 +254,12 @@
// app shows again. If that doesn't happen for 30s we drop the gesture.
private static final long PANIC_GESTURE_EXPIRATION = 30000;
+ private static final String SYSUI_PACKAGE = "com.android.systemui";
+ private static final String SYSUI_SCREENSHOT_SERVICE =
+ "com.android.systemui.screenshot.TakeScreenshotService";
+ private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
+ "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
+
/**
* Keyguard stuff
*/
@@ -385,6 +394,7 @@
// handler thread. We'll need to resolve this someday by teaching the input dispatcher
// to hold wakelocks during dispatch and eliminating the critical path.
volatile boolean mPowerKeyHandled;
+ volatile boolean mBackKeyHandled;
volatile boolean mBeganFromNonInteractive;
volatile int mPowerKeyPressCounter;
volatile boolean mEndCallKeyHandled;
@@ -437,6 +447,7 @@
int mLongPressOnPowerBehavior;
int mDoublePressOnPowerBehavior;
int mTriplePressOnPowerBehavior;
+ int mLongPressOnBackBehavior;
int mShortPressOnSleepBehavior;
int mShortPressWindowBehavior;
boolean mAwake;
@@ -693,6 +704,7 @@
private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15;
private static final int MSG_REQUEST_TRANSIENT_BARS = 16;
private static final int MSG_REQUEST_TV_PICTURE_IN_PICTURE = 17;
+ private static final int MSG_BACK_LONG_PRESS = 18;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -757,6 +769,9 @@
case MSG_REQUEST_TV_PICTURE_IN_PICTURE:
requestTvPictureInPictureInternal();
break;
+ case MSG_BACK_LONG_PRESS:
+ backLongPress();
+ break;
}
}
}
@@ -1103,6 +1118,13 @@
}
}
+ private void cancelPendingBackKeyAction() {
+ if (!mBackKeyHandled) {
+ mBackKeyHandled = true;
+ mHandler.removeMessages(MSG_BACK_LONG_PRESS);
+ }
+ }
+
private void powerPress(long eventTime, boolean interactive, int count) {
if (mScreenOnEarly && !mScreenOnFully) {
Slog.i(TAG, "Suppressed redundant power key press while "
@@ -1210,6 +1232,19 @@
}
}
+ private void backLongPress() {
+ mBackKeyHandled = true;
+
+ switch (mLongPressOnBackBehavior) {
+ case LONG_PRESS_BACK_NOTHING:
+ break;
+ case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST:
+ Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ break;
+ }
+ }
+
private void sleepPress(long eventTime) {
if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
@@ -1238,6 +1273,10 @@
return getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;
}
+ private boolean hasLongPressOnBackBehavior() {
+ return mLongPressOnBackBehavior != LONG_PRESS_BACK_NOTHING;
+ }
+
private void interceptScreenshotChord() {
if (mScreenshotChordEnabled
&& mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
@@ -1567,6 +1606,9 @@
mSupportLongPressPowerWhenNonInteractive = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_supportLongPressPowerWhenNonInteractive);
+ mLongPressOnBackBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnBackBehavior);
+
mShortPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnPowerBehavior);
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
@@ -5186,6 +5228,7 @@
if (mScreenshotConnection != null) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
+ notifyScreenshotError();
}
}
}
@@ -5197,10 +5240,10 @@
if (mScreenshotConnection != null) {
return;
}
- ComponentName cn = new ComponentName("com.android.systemui",
- "com.android.systemui.screenshot.TakeScreenshotService");
- Intent intent = new Intent();
- intent.setComponent(cn);
+ final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
+ SYSUI_SCREENSHOT_SERVICE);
+ final Intent serviceIntent = new Intent();
+ serviceIntent.setComponent(serviceComponent);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -5235,17 +5278,35 @@
}
}
}
+
@Override
- public void onServiceDisconnected(ComponentName name) {}
+ public void onServiceDisconnected(ComponentName name) {
+ notifyScreenshotError();
+ }
};
- if (mContext.bindServiceAsUser(
- intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
+ if (mContext.bindServiceAsUser(serviceIntent, conn,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+ UserHandle.CURRENT)) {
mScreenshotConnection = conn;
mHandler.postDelayed(mScreenshotTimeout, 10000);
}
}
}
+ /**
+ * Notifies the screenshot service to show an error.
+ */
+ private void notifyScreenshotError() {
+ // If the service process is killed, then ask it to clean up after itself
+ final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
+ SYSUI_SCREENSHOT_ERROR_RECEIVER);
+ Intent errorIntent = new Intent();
+ errorIntent.setComponent(errorComponent);
+ errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(errorIntent, UserHandle.ALL);
+ }
+
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
@@ -5316,6 +5377,29 @@
// Handle special keys.
switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK: {
+ if (down) {
+ mBackKeyHandled = false;
+ if (hasLongPressOnBackBehavior()) {
+ Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg,
+ ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
+ }
+ } else {
+ boolean handled = mBackKeyHandled;
+
+ // Reset back key state
+ cancelPendingBackKeyAction();
+
+ // Don't pass back press to app if we've already handled it
+ if (handled) {
+ result &= ~ACTION_PASS_TO_USER;
+ }
+ }
+ break;
+ }
+
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
@@ -7365,6 +7449,8 @@
pw.print(" mLidControlsScreenLock="); pw.println(mLidControlsScreenLock);
pw.print(" mLidControlsSleep="); pw.println(mLidControlsSleep);
pw.print(prefix);
+ pw.print(" mLongPressOnBackBehavior="); pw.println(mLongPressOnBackBehavior);
+ pw.print(prefix);
pw.print("mShortPressOnPowerBehavior="); pw.print(mShortPressOnPowerBehavior);
pw.print(" mLongPressOnPowerBehavior="); pw.println(mLongPressOnPowerBehavior);
pw.print(prefix);
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index f5914faf..d0ee6e0 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -201,13 +201,16 @@
}
}
- private void updateOverlayStateLocked() {
+ private void updateOverlayStateLocked(ComponentName exemptedComponent) {
final long identity = Binder.clearCallingIdentity();
try {
AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
if (appOpsManager != null) {
+ String[] exemptions = (exemptedComponent == null) ? new String[0] :
+ new String[] { exemptedComponent.getPackageName() };
+
appOpsManager.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
- mVrModeEnabled, mOverlayToken);
+ mVrModeEnabled, mOverlayToken, exemptions);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -230,12 +233,12 @@
private boolean updateCurrentVrServiceLocked(boolean enabled,
@NonNull ComponentName component, int userId) {
- // Always send mode change events.
- changeVrModeLocked(enabled);
-
boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
EnabledComponentsObserver.NO_ERROR);
+ // Always send mode change events.
+ changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null);
+
if (!enabled || !validUserComponent) {
// Unbind whatever is running
if (mCurrentVrService != null) {
@@ -275,8 +278,9 @@
* Note: Must be called while holding {@code mLock}.
*
* @param enabled new state of the VR mode.
+ * @param exemptedComponent a component to exempt from AppOps restrictions for overlays.
*/
- private void changeVrModeLocked(boolean enabled) {
+ private void changeVrModeLocked(boolean enabled, ComponentName exemptedComponent) {
if (mVrModeEnabled != enabled) {
mVrModeEnabled = enabled;
@@ -284,7 +288,7 @@
Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled"));
setVrModeNative(mVrModeEnabled);
- updateOverlayStateLocked();
+ updateOverlayStateLocked(exemptedComponent);
onVrModeChangedLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 73cea52..5212211 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -29,6 +29,7 @@
import android.app.ActivityManager.StackId;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.Region.Op;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.Display;
@@ -426,6 +427,10 @@
win.getTouchableRegion(mTmpRegion);
mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
}
+ if (getDockedStackVisibleForUserLocked() != null) {
+ mDividerControllerLocked.getTouchRegion(mTmpRect);
+ mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
+ }
if (mTapDetector != null) {
mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion, mNonResizeableRegion);
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 9bceee7..36e8bbb 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -95,6 +95,7 @@
private long mAnimationDuration;
private final Interpolator mMinimizedDockInterpolator;
private float mMaximizeMeetFraction;
+ private final Rect mTouchRegion = new Rect();
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -129,6 +130,15 @@
}
}
+ void setTouchRegion(Rect touchRegion) {
+ mTouchRegion.set(touchRegion);
+ }
+
+ void getTouchRegion(Rect outRegion) {
+ outRegion.set(mTouchRegion);
+ outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+ }
+
private void resetDragResizingChangeReported() {
final WindowList windowList = mDisplayContent.getWindowList();
for (int i = windowList.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a998bc3..304449d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -10438,6 +10438,15 @@
}
@Override
+ public void setDockedStackDividerTouchRegion(Rect touchRegion) {
+ synchronized (mWindowMap) {
+ getDefaultDisplayContentLocked().getDockedDividerController()
+ .setTouchRegion(touchRegion);
+ setFocusTaskRegionLocked();
+ }
+ }
+
+ @Override
public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
synchronized (mWindowMap) {
getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer(
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index 2f4beaa..f2c42db 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -15,13 +15,18 @@
*/
package com.android.server.pm;
+import static org.mockito.Mockito.mock;
+
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ILauncherApps;
+import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
@@ -30,10 +35,12 @@
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
+import android.os.BaseBundle;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
+import android.os.UserManager;
import android.test.AndroidTestCase;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
@@ -43,6 +50,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutService.ConfigConstants;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
@@ -75,7 +83,11 @@
-w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
* TODO: Add checks with assertAllNotHaveIcon()
+ *
+ * TODO: separate, detailed tests for ShortcutInfo (CTS?) *
+ *
* TODO: Cross-user test (do in CTS?)
+ *
*/
@SmallTest
public class ShortcutManagerTest extends AndroidTestCase {
@@ -87,11 +99,19 @@
*/
private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH true
- /** Context used in the client side */
- private final class ClientContext extends MockContext {
+ private class BaseContext extends MockContext {
@Override
- public String getPackageName() {
- return mInjectedClientPackage;
+ public Object getSystemService(String name) {
+ switch (name) {
+ case Context.USER_SERVICE:
+ return mMockUserManager;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mMockPackageManager;
}
@Override
@@ -100,14 +120,18 @@
}
}
- /** Context used in the service side */
- private final class ServiceContext extends MockContext {
+ /** Context used in the client side */
+ private class ClientContext extends BaseContext {
@Override
- public Resources getResources() {
- return ShortcutManagerTest.this.getContext().getResources();
+ public String getPackageName() {
+ return mInjectedClientPackage;
}
}
+ /** Context used in the service side */
+ private final class ServiceContext extends BaseContext {
+ }
+
/** ShortcutService with injection override methods. */
private final class ShortcutServiceTestable extends ShortcutService {
public ShortcutServiceTestable(Context context) {
@@ -170,7 +194,7 @@
}
/** ShortcutManager with injection override methods. */
- private final class ShortcutManagerTestable extends ShortcutManager {
+ private class ShortcutManagerTestable extends ShortcutManager {
public ShortcutManagerTestable(Context context, ShortcutServiceTestable service) {
super(context, service);
}
@@ -181,6 +205,27 @@
}
}
+ private class LauncherAppImplTestable extends LauncherAppsImpl {
+ public LauncherAppImplTestable(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void ensureInUserProfiles(UserHandle userToCheck, String message) {
+ // SKIP
+ }
+
+ @Override
+ public void verifyCallingPackage(String callingPackage) {
+ // SKIP
+ }
+ }
+
+ private class LauncherAppsTestable extends LauncherApps {
+ public LauncherAppsTestable(Context context, ILauncherApps service) {
+ super(context, service);
+ }
+ }
public static class ShortcutActivity extends Activity {
}
@@ -198,6 +243,9 @@
private ShortcutManagerTestable mManager;
private ShortcutServiceInternal mInternal;
+ private LauncherAppImplTestable mLauncherAppImpl;
+ private LauncherAppsTestable mLauncherApps;
+
private File mInjectedFilePathRoot;
private long mInjectedCurrentTimeLillis;
@@ -209,6 +257,9 @@
private Map<String, Integer> mInjectedPackageUidMap;
+ private PackageManager mMockPackageManager;
+ private UserManager mMockUserManager;
+
private static final String CALLING_PACKAGE_1 = "com.android.test.1";
private static final int CALLING_UID_1 = 10001;
@@ -246,6 +297,9 @@
mServiceContext = new ServiceContext();
mClientContext = new ClientContext();
+ mMockPackageManager = mock(PackageManager.class);
+ mMockUserManager = mock(UserManager.class);
+
// Prepare injection values.
mInjectedCurrentTimeLillis = START_TIME;
@@ -280,6 +334,9 @@
mInternal = LocalServices.getService(ShortcutServiceInternal.class);
+ mLauncherAppImpl = new LauncherAppImplTestable(mServiceContext);
+ mLauncherApps = new LauncherAppsTestable(mClientContext, mLauncherAppImpl);
+
// Load the setting file.
mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
}
@@ -315,6 +372,10 @@
return UserHandle.getUserId(mInjectedCallingUid);
}
+ private UserHandle getCallingUser() {
+ return UserHandle.of(getCallingUserId());
+ }
+
/** For debugging */
private void dumpsysOnLogcat() {
if (!ENABLE_DUMP) return;
@@ -434,7 +495,6 @@
return s;
}
-
/**
* Make multiple shortcuts with IDs.
*/
@@ -446,6 +506,10 @@
return ret;
}
+ private ShortcutInfo.Builder makeShortcutBuilder() {
+ return new ShortcutInfo.Builder(mClientContext);
+ }
+
/**
* Make a shortcut with details.
*/
@@ -663,6 +727,10 @@
}
}
+ private void assertBundleEmpty(BaseBundle b) {
+ assertTrue(b == null || b.size() == 0);
+ }
+
private ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) {
return mService.getPackageShortcutForTest(packageName, shortcutId, userId);
}
@@ -1132,65 +1200,61 @@
setCaller(LAUNCHER_1);
// Check hasIconResource()/hasIconFile().
- assertShortcutIds(assertAllHaveIconResId(mInternal.getShortcutInfo(
- getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("res32x32"),
- getCallingUserId())), "res32x32");
+ assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("res32x32"),
+ getCallingUser())), "res32x32");
- assertShortcutIds(assertAllHaveIconResId(mInternal.getShortcutInfo(
- getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("res64x64"),
- getCallingUserId())), "res64x64");
+ assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("res64x64"), getCallingUser())),
+ "res64x64");
- assertShortcutIds(assertAllHaveIconFile(mInternal.getShortcutInfo(
- getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("bmp32x32"),
- getCallingUserId())), "bmp32x32");
- assertShortcutIds(assertAllHaveIconFile(mInternal.getShortcutInfo(
- getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("bmp64x64"),
- getCallingUserId())), "bmp64x64");
- assertShortcutIds(assertAllHaveIconFile(mInternal.getShortcutInfo(
- getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("bmp512x512"),
- getCallingUserId())), "bmp512x512");
+ assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("bmp32x32"), getCallingUser())),
+ "bmp32x32");
+
+ assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("bmp64x64"), getCallingUser())),
+ "bmp64x64");
+
+ assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("bmp512x512"), getCallingUser())),
+ "bmp512x512");
// Check
assertEquals(
R.drawable.black_32x32,
- mInternal.getShortcutIconResId(getCallingPackage(),
- makePackageShortcut(CALLING_PACKAGE_1, "res32x32"), getCallingUserId()));
+ mLauncherApps.getShortcutIconResId(
+ makePackageShortcut(CALLING_PACKAGE_1, "res32x32"), getCallingUser()));
assertEquals(
R.drawable.black_64x64,
- mInternal.getShortcutIconResId(
- getCallingPackage(),
- makePackageShortcut(CALLING_PACKAGE_1, "res64x64"), getCallingUserId()));
+ mLauncherApps.getShortcutIconResId(
+
+ makePackageShortcut(CALLING_PACKAGE_1, "res64x64"), getCallingUser()));
assertEquals(
0, // because it's not a resource
- mInternal.getShortcutIconResId(
- getCallingPackage(),
- makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUserId()));
+ mLauncherApps.getShortcutIconResId(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
assertEquals(
0, // because it's not a resource
- mInternal.getShortcutIconResId(
- getCallingPackage(),
- makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUserId()));
+ mLauncherApps.getShortcutIconResId(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
assertEquals(
0, // because it's not a resource
- mInternal.getShortcutIconResId(
- getCallingPackage(),
- makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUserId()));
+ mLauncherApps.getShortcutIconResId(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
- bmp = pfdToBitmap(mInternal.getShortcutIconFd(
- getCallingPackage(),
- makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUserId()));
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
assertBitmapSize(32, 32, bmp);
- bmp = pfdToBitmap(mInternal.getShortcutIconFd(
- getCallingPackage(),
- makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUserId()));
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
assertBitmapSize(64, 64, bmp);
- bmp = pfdToBitmap(mInternal.getShortcutIconFd(
- getCallingPackage(),
- makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUserId()));
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
assertBitmapSize(128, 128, bmp);
// TODO Test the content URI case too.
@@ -1300,11 +1364,146 @@
assertFalse(p11_1_3.getName().contains("_"));
}
+ public void testUpdateShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ )));
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ )));
+ });
+ runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s2", "s3"),
+ getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s4", "s5"),
+ getCallingUser());
+ });
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ mManager.deleteDynamicShortcut("s1");
+ mManager.deleteDynamicShortcut("s2");
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ mManager.deleteDynamicShortcut("s1");
+ mManager.deleteDynamicShortcut("s3");
+ mManager.deleteDynamicShortcut("s5");
+ });
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mManager.getDynamicShortcuts()),
+ "s3", "s4", "s5");
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s2", "s3");
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mManager.getDynamicShortcuts()),
+ "s2", "s4");
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s4", "s5");
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ ShortcutInfo s2 = makeShortcutBuilder()
+ .setId("s2")
+ .setIcon(Icon.createWithResource(mContext, R.drawable.black_32x32))
+ .build();
+
+ ShortcutInfo s4 = makeShortcutBuilder()
+ .setId("s4")
+ .setTitle("new title")
+ .build();
+
+ mManager.updateShortcuts(Arrays.asList(s2, s4));
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ ShortcutInfo s2 = makeShortcutBuilder()
+ .setId("s2")
+ .setIntent(makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
+ "key1", "val1"))
+ .build();
+
+ ShortcutInfo s4 = makeShortcutBuilder()
+ .setId("s4")
+ .setIntent(new Intent(Intent.ACTION_ALL_APPS))
+ .build();
+
+ mManager.updateShortcuts(Arrays.asList(s2, s4));
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mManager.getDynamicShortcuts()),
+ "s3", "s4", "s5");
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s2", "s3");
+
+ ShortcutInfo s = getCallerShortcut("s2");
+ assertTrue(s.hasIconResource());
+ assertEquals(R.drawable.black_32x32, s.getIconResourceId());
+ assertEquals("Title-s2", s.getTitle());
+
+ s = getCallerShortcut("s4");
+ assertFalse(s.hasIconResource());
+ assertEquals(0, s.getIconResourceId());
+ assertEquals("new title", s.getTitle());
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mManager.getDynamicShortcuts()),
+ "s2", "s4");
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s4", "s5");
+
+ ShortcutInfo s = getCallerShortcut("s2");
+ assertFalse(s.hasIconResource());
+ assertEquals(0, s.getIconResourceId());
+ assertEquals("Title-s2", s.getTitle());
+ assertEquals(Intent.ACTION_ANSWER, s.getIntent().getAction());
+ assertEquals(1, s.getIntent().getExtras().size());
+
+ s = getCallerShortcut("s4");
+ assertFalse(s.hasIconResource());
+ assertEquals(0, s.getIconResourceId());
+ assertEquals("Title-s4", s.getTitle());
+ assertEquals(Intent.ACTION_ALL_APPS, s.getIntent().getAction());
+ assertBundleEmpty(s.getIntent().getExtras());
+ });
+ // TODO Check with other fields too.
+
+ // TODO Check bitmap removal too.
+ }
+
// TODO: updateShortcuts()
// TODO: getPinnedShortcuts()
// === Test for launcher side APIs ===
+ private static ShortcutQuery buildQuery(long changedSince,
+ String packageName, ComponentName componentName,
+ /* @ShortcutQuery.QueryFlags */ int flags) {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setChangedSince(changedSince);
+ q.setPackage(packageName);
+ q.setActivity(componentName);
+ q.setQueryFlags(flags);
+ return q;
+ }
+
public void testGetShortcuts() {
// Set up shortcuts.
@@ -1330,56 +1529,55 @@
// Get dynamic
assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllNotKeyFieldsOnly(
- mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_1,
- /* activity =*/ null,
- ShortcutQuery.FLAG_GET_DYNAMIC, getCallingUserId())),
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s1", "s2"))));
// Get pinned
assertShortcutIds(
- mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_1,
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null,
- ShortcutQuery.FLAG_GET_PINNED, getCallingUserId())
+ ShortcutQuery.FLAG_GET_PINNED), getCallingUser())
/* none */);
// Get both, with timestamp
assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
- assertAllNotKeyFieldsOnly(mInternal.getShortcuts(getCallingPackage(),
+ assertAllNotKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2,
/* activity =*/ null,
- ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC,
- getCallingUserId())),
+ ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC),
+ getCallingUser())),
"s2", "s3"))));
// FLAG_GET_KEY_FIELDS_ONLY
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
- assertAllKeyFieldsOnly(mInternal.getShortcuts(getCallingPackage(),
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2,
/* activity =*/ null,
- ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY,
- getCallingUserId())),
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
"s2", "s3"))));
// Pin some shortcuts.
- mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_2,
- Arrays.asList("s3", "s4"), getCallingUserId());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ Arrays.asList("s3", "s4"), getCallingUser());
// Pinned ones only
assertAllPinned(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
- assertAllNotKeyFieldsOnly(mInternal.getShortcuts(getCallingPackage(),
+ assertAllNotKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2,
/* activity =*/ null,
- ShortcutQuery.FLAG_GET_PINNED,
- getCallingUserId())),
+ ShortcutQuery.FLAG_GET_PINNED),
+ getCallingUser())),
"s3"))));
// All packages.
assertShortcutIds(assertAllNotKeyFieldsOnly(
- mInternal.getShortcuts(getCallingPackage(),
+ mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 5000, /* package= */ null,
/* activity =*/ null,
- ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED,
- getCallingUserId())),
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED),
+ getCallingUser())),
"s1", "s3");
// TODO More tests: pinned but dynamic, filter by activity
@@ -1423,8 +1621,8 @@
// Pin some.
setCaller(LAUNCHER_1);
- mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_1,
- Arrays.asList("s2"), getCallingUserId());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ Arrays.asList("s2"), getCallingUser());
dumpsysOnLogcat();
@@ -1442,20 +1640,20 @@
// Note we don't guarantee the orders.
list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
assertAllNotKeyFieldsOnly(
- mInternal.getShortcutInfo(getCallingPackage(), CALLING_PACKAGE_1,
- Arrays.asList("s2", "s1", "s3", null), getCallingUserId())))),
+ mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
+ Arrays.asList("s2", "s1", "s3", null), getCallingUser())))),
"s1", "s2");
assertEquals("Title 1", findById(list, "s1").getTitle());
assertEquals("Title 2", findById(list, "s2").getTitle());
assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
- mInternal.getShortcutInfo(getCallingPackage(), CALLING_PACKAGE_1,
- Arrays.asList("s3"), getCallingUserId())))
+ mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
+ Arrays.asList("s3"), getCallingUser())))
/* none */);
list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
- mInternal.getShortcutInfo(getCallingPackage(), CALLING_PACKAGE_2,
- Arrays.asList("s1", "s2", "s3"), getCallingUserId()))),
+ mLauncherApps.getShortcutInfo(CALLING_PACKAGE_2,
+ Arrays.asList("s1", "s2", "s3"), getCallingUser()))),
"s1");
assertEquals("ABC", findById(list, "s1").getTitle());
}
@@ -1481,14 +1679,14 @@
// Pin some.
setCaller(LAUNCHER_1);
- mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_1,
- Arrays.asList("s2", "s3"), getCallingUserId());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ Arrays.asList("s2", "s3"), getCallingUser());
- mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_2,
- Arrays.asList("s3", "s4", "s5"), getCallingUserId());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ Arrays.asList("s3", "s4", "s5"), getCallingUser());
- mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_3,
- Arrays.asList("s3"), getCallingUserId()); // Note ID doesn't exist
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
+ Arrays.asList("s3"), getCallingUser()); // Note ID doesn't exist
// Delete some.
setCaller(CALLING_PACKAGE_1);
@@ -1511,18 +1709,18 @@
// CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
- mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_1,
- /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId()))),
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s2");
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
- mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_2,
- /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId()))),
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s3", "s4");
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
- mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_3,
- /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId())))
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_3,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
/* none */);
}
@@ -1561,11 +1759,11 @@
// Pin all.
setCaller(LAUNCHER_1);
- mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_1,
- Arrays.asList("s1", "s2"), getCallingUserId());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ Arrays.asList("s1", "s2"), getCallingUser());
- mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_2,
- Arrays.asList("s1"), getCallingUserId());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ Arrays.asList("s1"), getCallingUser());
// Just to make it complicated, delete some.
setCaller(CALLING_PACKAGE_1);
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index bba357e..7f90731 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -92,6 +92,11 @@
int NO_SMS_TO_ACK = 48; /* ACK received when there is no SMS to ack */
int NETWORK_ERR = 49; /* Received error from network */
int REQUEST_RATE_LIMITED = 50; /* Operation denied due to overly-frequent requests */
+ int SIM_BUSY = 51; /* SIM is busy */
+ int SIM_FULL = 52; /* The target EF is full */
+ int NETWORK_REJECT = 53; /* Request is rejected by network */
+ int OPERATION_NOT_ALLOWED = 54; /* Not allowed the request now */
+ int EMPTY_RECORD = 55; /* The request record is empty */
// Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
// reveal particular replacement for Generic failure
int OEM_ERROR_1 = 501;
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index d311b3d..85d22ff 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -125,10 +125,12 @@
libexpat \
libziparchive-host \
libpng \
- libbase
+ libbase \
+ libprotobuf-cpp-lite_static
-hostSharedLibs := \
- libprotobuf-cpp-lite
+# Do not add any shared libraries. AAPT2 is built to run on many
+# environments that may not have the required dependencies.
+hostSharedLibs :=
ifneq ($(strip $(USE_MINGW)),)
hostStaticLibs += libz
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index b100e84..9704d970 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -81,6 +81,12 @@
// Recursively adds resources to the ResourceTable.
static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
+ StringPiece16 trimmedComment = util::trimWhitespace(res->comment);
+ if (trimmedComment.size() != res->comment.size()) {
+ // Only if there was a change do we re-assign.
+ res->comment = trimmedComment.toString();
+ }
+
if (res->symbolState) {
Symbol symbol;
symbol.state = res->symbolState.value();
diff --git a/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml b/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml
index 2b24544..d09a485 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml
+++ b/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml
@@ -15,8 +15,13 @@
-->
<resources>
+ <!-- An attribute from StaticLibOne -->
<attr name="StaticLibOne_attr" format="string" />
<string name="Foo">Foo</string>
<string name="Foo" product="tablet">Bar</string>
+
+ <declare-styleable name="Widget">
+ <attr name="StaticLibOne_attr" />
+ </declare-styleable>
</resources>
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 9c25d4e..496e92e 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -38,7 +38,7 @@
mComment << "/**";
}
- mComment << "\n" << " * " << std::move(comment);
+ mComment << "\n * " << std::move(comment);
}
void AnnotationProcessor::appendComment(const StringPiece16& comment) {
@@ -60,6 +60,10 @@
}
}
+void AnnotationProcessor::appendNewLine() {
+ mComment << "\n *";
+}
+
void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) {
if (mHasComments) {
std::string result = mComment.str();
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index e7f2be0..fadf584 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -61,6 +61,8 @@
void appendComment(const StringPiece16& comment);
void appendComment(const StringPiece& comment);
+ void appendNewLine();
+
/**
* Writes the comments and annotations to the stream, with the given prefix before each line.
*/
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 1076ffe..01330dc 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -23,6 +23,7 @@
#include "java/AnnotationProcessor.h"
#include "java/ClassDefinitionWriter.h"
#include "java/JavaClassGenerator.h"
+#include "process/SymbolTable.h"
#include "util/StringPiece.h"
#include <algorithm>
@@ -33,8 +34,9 @@
namespace aapt {
-JavaClassGenerator::JavaClassGenerator(ResourceTable* table, JavaClassGeneratorOptions options) :
- mTable(table), mOptions(options) {
+JavaClassGenerator::JavaClassGenerator(IAaptContext* context, ResourceTable* table,
+ const JavaClassGeneratorOptions& options) :
+ mContext(context), mTable(table), mOptions(options) {
}
static void generateHeader(const StringPiece16& packageNameToGenerate, std::ostream* out) {
@@ -103,6 +105,85 @@
return output;
}
+static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
+ const uint32_t typeMask = attr->typeMask;
+ if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
+ processor->appendComment(
+ "<p>May be a reference to another resource, in the form\n"
+ "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
+ "attribute in the form\n"
+ "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_STRING) {
+ processor->appendComment(
+ "<p>May be a string value, using '\\\\;' to escape characters such as\n"
+ "'\\\\n' or '\\\\uxxxx' for a unicode character;");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_INTEGER) {
+ processor->appendComment("<p>May be an integer value, such as \"<code>100</code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
+ processor->appendComment(
+ "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
+ "\"<code>false</code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_COLOR) {
+ processor->appendComment(
+ "<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
+ "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
+ "\"<code>#<i>aarrggbb</i></code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_FLOAT) {
+ processor->appendComment(
+ "<p>May be a floating point value, such as \"<code>1.2</code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
+ processor->appendComment(
+ "<p>May be a dimension value, which is a floating point number appended with a\n"
+ "unit such as \"<code>14.5sp</code>\".\n"
+ "Available units are: px (pixels), dp (density-independent pixels),\n"
+ "sp (scaled pixels based on preferred font size), in (inches), and\n"
+ "mm (millimeters).");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_FRACTION) {
+ processor->appendComment(
+ "<p>May be a fractional value, which is a floating point number appended with\n"
+ "either % or %p, such as \"<code>14.5%</code>\".\n"
+ "The % suffix always means a percentage of the base size;\n"
+ "the optional %p suffix provides a size relative to some parent container.");
+ }
+
+ if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
+ if (typeMask & android::ResTable_map::TYPE_FLAGS) {
+ processor->appendComment(
+ "<p>Must be one or more (separated by '|') of the following "
+ "constant values.</p>");
+ } else {
+ processor->appendComment("<p>Must be one of the following constant values.</p>");
+ }
+
+ processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
+ for (const Attribute::Symbol& symbol : attr->symbols) {
+ std::stringstream line;
+ line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
+ << "<td>" << std::hex << symbol.value << std::dec << "</td>"
+ << "<td>" << util::trimWhitespace(symbol.symbol.getComment()) << "</td></tr>";
+ processor->appendComment(line.str());
+ }
+ processor->appendComment("</table>");
+ }
+}
+
bool JavaClassGenerator::skipSymbol(SymbolState state) {
switch (mOptions.types) {
case JavaClassGeneratorOptions::SymbolTypes::kAll:
@@ -117,6 +198,7 @@
struct StyleableAttr {
const Reference* attrRef;
+ std::shared_ptr<Attribute> attribute;
std::string fieldName;
};
@@ -148,8 +230,29 @@
assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
assert(attr.name && "no name set for Styleable entry");
- sortedAttributes.emplace_back(StyleableAttr{
- &attr, transformNestedAttr(attr.name.value(), className, packageNameToGenerate) });
+ StyleableAttr styleableAttr = {};
+ styleableAttr.attrRef = &attr;
+ styleableAttr.fieldName = transformNestedAttr(attr.name.value(), className,
+ packageNameToGenerate);
+
+ Reference mangledReference;
+ mangledReference.id = attr.id;
+ mangledReference.name = attr.name;
+ if (mangledReference.name.value().package.empty()) {
+ mangledReference.name.value().package = mContext->getCompilationPackage();
+ }
+
+ if (Maybe<ResourceName> mangledName =
+ mContext->getNameMangler()->mangleName(mangledReference.name.value())) {
+ mangledReference.name = mangledName;
+ }
+
+ const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
+ mangledReference);
+ if (symbol) {
+ styleableAttr.attribute = symbol->attribute;
+ }
+ sortedAttributes.push_back(std::move(styleableAttr));
}
std::sort(sortedAttributes.begin(), sortedAttributes.end(), lessStyleableAttr);
@@ -159,16 +262,34 @@
// Build the comment string for the Styleable. It includes details about the
// child attributes.
std::stringstream styleableComment;
- styleableComment << "Attributes that can be used with a " << className << ".\n";
- styleableComment << "<table>\n"
+ if (!styleable->getComment().empty()) {
+ styleableComment << styleable->getComment() << "\n";
+ } else {
+ styleableComment << "Attributes that can be used with a " << className << ".\n";
+ }
+ styleableComment <<
+ "<p>Includes the following attributes:</p>\n"
+ "<table>\n"
"<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\">\n"
+ "<colgroup align=\"left\" />\n"
"<tr><th>Attribute</th><th>Description</th></tr>\n";
+
for (const auto& entry : sortedAttributes) {
const ResourceName& attrName = entry.attrRef->name.value();
- styleableComment << "<tr><td><code>{@link #" << entry.fieldName << " "
- << attrName.package << ":" << attrName.entry
- << "}</code></td><td></td></tr>\n";
+ styleableComment << "<tr><td>";
+ styleableComment << "<code>{@link #"
+ << entry.fieldName << " "
+ << (!attrName.package.empty()
+ ? attrName.package : mContext->getCompilationPackage())
+ << ":" << attrName.entry
+ << "}</code>";
+ styleableComment << "</td>";
+
+ styleableComment << "<td>";
+ if (entry.attribute) {
+ styleableComment << entry.attribute->getComment();
+ }
+ styleableComment << "</td></tr>\n";
}
styleableComment << "</table>\n";
for (const auto& entry : sortedAttributes) {
@@ -189,96 +310,45 @@
// Now we emit the indices into the array.
for (size_t i = 0; i < attrCount; i++) {
- const ResourceName& attrName = sortedAttributes[i].attrRef->name.value();
+ const StyleableAttr& styleableAttr = sortedAttributes[i];
+ const ResourceName& attrName = styleableAttr.attrRef->name.value();
+
+ StringPiece16 packageName = attrName.package;
+ if (packageName.empty()) {
+ packageName = mContext->getCompilationPackage();
+ }
AnnotationProcessor attrProcessor;
- std::stringstream doclavaComments;
- doclavaComments << "@attr name ";
- if (!attrName.package.empty()) {
- doclavaComments << attrName.package << ":";
+
+ StringPiece16 comment = styleableAttr.attrRef->getComment();
+ if (styleableAttr.attribute && comment.empty()) {
+ comment = styleableAttr.attribute->getComment();
}
- doclavaComments << attrName.entry;
- attrProcessor.appendComment(doclavaComments.str());
- outClassDef->addIntMember(sortedAttributes[i].fieldName, &attrProcessor, i);
- }
-}
-static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
- const uint32_t typeMask = attr->typeMask;
- if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
- processor->appendComment(
- "<p>May be a reference to another resource, in the form\n"
- "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
- "attribute in the form\n"
- "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_STRING) {
- processor->appendComment(
- "<p>May be a string value, using '\\\\;' to escape characters such as\n"
- "'\\\\n' or '\\\\uxxxx' for a unicode character;");
- }
-
- if (typeMask & android::ResTable_map::TYPE_INTEGER) {
- processor->appendComment("<p>May be an integer value, such as \"<code>100</code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- processor->appendComment(
- "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
- "\"<code>false</code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_COLOR) {
- processor->appendComment(
- "<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
- "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
- "\"<code>#<i>aarrggbb</i></code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_FLOAT) {
- processor->appendComment(
- "<p>May be a floating point value, such as \"<code>1.2</code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
- processor->appendComment(
- "<p>May be a dimension value, which is a floating point number appended with a\n"
- "unit such as \"<code>14.5sp</code>\".\n"
- "Available units are: px (pixels), dp (density-independent pixels),\n"
- "sp (scaled pixels based on preferred font size), in (inches), and\n"
- "mm (millimeters).");
- }
-
- if (typeMask & android::ResTable_map::TYPE_FRACTION) {
- processor->appendComment(
- "<p>May be a fractional value, which is a floating point number appended with\n"
- "either % or %p, such as \"<code>14.5%</code>\".\n"
- "The % suffix always means a percentage of the base size;\n"
- "the optional %p suffix provides a size relative to some parent container.");
- }
-
- if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
- if (typeMask & android::ResTable_map::TYPE_FLAGS) {
- processor->appendComment(
- "<p>Must be one or more (separated by '|') of the following "
- "constant values.</p>");
+ if (!comment.empty()) {
+ attrProcessor.appendComment("<p>\n@attr description");
+ attrProcessor.appendComment(comment);
} else {
- processor->appendComment("<p>Must be one of the following constant values.</p>");
+ std::stringstream defaultComment;
+ defaultComment
+ << "<p>This symbol is the offset where the "
+ << "{@link " << packageName << ".R.attr#" << transform(attrName.entry) << "}\n"
+ << "attribute's value can be found in the "
+ << "{@link #" << className << "} array.";
+ attrProcessor.appendComment(defaultComment.str());
}
- processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
- for (const Attribute::Symbol& symbol : attr->symbols) {
- std::stringstream line;
- line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
- << "<td>" << std::hex << symbol.value << std::dec << "</td>"
- << "<td>" << util::trimWhitespace(symbol.symbol.getComment()) << "</td></tr>";
- processor->appendComment(line.str());
+ attrProcessor.appendNewLine();
+
+ if (styleableAttr.attribute) {
+ addAttributeFormatDoc(&attrProcessor, styleableAttr.attribute.get());
+ attrProcessor.appendNewLine();
}
- processor->appendComment("</table>");
+
+ std::stringstream doclavaName;
+ doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
+ attrProcessor.appendComment(doclavaName.str());
+ outClassDef->addIntMember(sortedAttributes[i].fieldName, &attrProcessor, i);
}
}
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 023d6d6..7e46f8c 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -19,7 +19,7 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
-
+#include "process/IResourceTableConsumer.h"
#include "util/StringPiece.h"
#include <ostream>
@@ -51,7 +51,8 @@
*/
class JavaClassGenerator {
public:
- JavaClassGenerator(ResourceTable* table, JavaClassGeneratorOptions options);
+ JavaClassGenerator(IAaptContext* context, ResourceTable* table,
+ const JavaClassGeneratorOptions& options);
/*
* Writes the R.java file to `out`. Only symbols belonging to `package` are written.
@@ -82,6 +83,7 @@
bool skipSymbol(SymbolState state);
+ IAaptContext* mContext;
ResourceTable* mTable;
JavaClassGeneratorOptions mOptions;
std::string mError;
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 63d38a8..4f041b8 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -15,11 +15,9 @@
*/
#include "java/JavaClassGenerator.h"
+#include "test/Test.h"
#include "util/Util.h"
-#include "test/Builders.h"
-
-#include <gtest/gtest.h>
#include <sstream>
#include <string>
@@ -31,7 +29,11 @@
.addSimple(u"@android:id/class", ResourceId(0x01020000))
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
EXPECT_FALSE(generator.generate(u"android", &out));
@@ -48,7 +50,11 @@
.build())
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
EXPECT_TRUE(generator.generate(u"android", &out));
@@ -72,7 +78,11 @@
.addSimple(u"@android:id/com.foo$two", ResourceId(0x01020001))
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", u"com.android.internal", &out));
@@ -90,7 +100,11 @@
.addSimple(u"@android:^attr-private/one", ResourceId(0x01010000))
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
@@ -110,10 +124,15 @@
.setSymbolState(u"@android:id/two", ResourceId(0x01020001), SymbolState::kPrivate)
.build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+
JavaClassGeneratorOptions options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
{
- JavaClassGenerator generator(table.get(), options);
+ JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
std::string output = out.str();
@@ -124,7 +143,7 @@
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
{
- JavaClassGenerator generator(table.get(), options);
+ JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
std::string output = out.str();
@@ -135,7 +154,7 @@
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
{
- JavaClassGenerator generator(table.get(), options);
+ JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
std::string output = out.str();
@@ -189,7 +208,11 @@
.build())
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
EXPECT_TRUE(generator.generate(u"android", &out));
@@ -207,8 +230,11 @@
test::getValue<Id>(table.get(), u"@android:id/foo")
->setComment(std::u16string(u"This is a comment\n@deprecated"));
- JavaClassGenerator generator(table.get(), {});
-
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
std::string actual = out.str();
@@ -241,10 +267,13 @@
std::unique_ptr<Styleable>(styleable.clone(nullptr)))
.build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
JavaClassGeneratorOptions options;
options.useFinal = false;
- JavaClassGenerator generator(table.get(), options);
-
+ JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
std::string actual = out.str();
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 418a802..b84074d 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -744,7 +744,7 @@
return false;
}
- JavaClassGenerator generator(table, javaOptions);
+ JavaClassGenerator generator(mContext, table, javaOptions);
if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
return false;
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index a8f9bfe..eaaf06f 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -51,6 +51,11 @@
// doesn't support unique_ptr.
std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
mCache.put(name, sharedSymbol);
+
+ if (sharedSymbol->id) {
+ // The symbol has an ID, so we can also cache this!
+ mIdCache.put(sharedSymbol->id.value(), sharedSymbol);
+ }
return sharedSymbol.get();
}
}
@@ -76,6 +81,25 @@
return nullptr;
}
+const SymbolTable::Symbol* SymbolTable::findByReference(const Reference& ref) {
+ // First try the ID. This is because when we lookup by ID, we only fill in the ID cache.
+ // Looking up by name fills in the name and ID cache. So a cache miss will cause a failed
+ // ID lookup, then a successfull name lookup. Subsequent look ups will hit immediately
+ // because the ID is cached too.
+ //
+ // If we looked up by name first, a cache miss would mean we failed to lookup by name, then
+ // succeeded to lookup by ID. Subsequent lookups will miss then hit.
+ const SymbolTable::Symbol* symbol = nullptr;
+ if (ref.id) {
+ symbol = findById(ref.id.value());
+ }
+
+ if (ref.name && !symbol) {
+ symbol = findByName(ref.name.value());
+ }
+ return symbol;
+}
+
std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::findByName(
const ResourceName& name) {
Maybe<ResourceTable::SearchResult> result = mTable->findResource(name);
@@ -102,7 +126,7 @@
if (configValue) {
// This resource has an Attribute.
if (Attribute* attr = valueCast<Attribute>(configValue->value.get())) {
- symbol->attribute = util::make_unique<Attribute>(*attr);
+ symbol->attribute = std::make_shared<Attribute>(*attr);
} else {
return {};
}
@@ -133,7 +157,7 @@
// Check to see if it is an attribute.
for (size_t i = 0; i < (size_t) count; i++) {
if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
- s->attribute = util::make_unique<Attribute>(false);
+ s->attribute = std::make_shared<Attribute>(false);
s->attribute->typeMask = entry[i].map.value.data;
break;
}
@@ -272,4 +296,15 @@
return {};
}
+std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByReference(
+ const Reference& ref) {
+ // AssetManager always prefers IDs.
+ if (ref.id) {
+ return findById(ref.id.value());
+ } else if (ref.name) {
+ return findByName(ref.name.value());
+ }
+ return {};
+}
+
} // namespace aapt
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 8ea1c75..0a6a4a5 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -52,7 +52,7 @@
public:
struct Symbol {
Maybe<ResourceId> id;
- std::unique_ptr<Attribute> attribute;
+ std::shared_ptr<Attribute> attribute;
bool isPublic;
};
@@ -69,6 +69,12 @@
const Symbol* findByName(const ResourceName& name);
const Symbol* findById(ResourceId id);
+ /**
+ * Let's the ISymbolSource decide whether looking up by name or ID is faster, if both
+ * are available.
+ */
+ const Symbol* findByReference(const Reference& ref);
+
private:
std::vector<std::unique_ptr<ISymbolSource>> mSources;
@@ -90,6 +96,18 @@
virtual std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) = 0;
virtual std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) = 0;
+
+ /**
+ * Default implementation tries the name if it exists, else the ID.
+ */
+ virtual std::unique_ptr<SymbolTable::Symbol> findByReference(const Reference& ref) {
+ if (ref.name) {
+ return findByName(ref.name.value());
+ } else if (ref.id) {
+ return findById(ref.id.value());
+ }
+ return {};
+ }
};
/**
@@ -122,6 +140,7 @@
std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override;
+ std::unique_ptr<SymbolTable::Symbol> findByReference(const Reference& ref) override;
private:
android::AssetManager mAssets;
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
new file mode 100755
index 0000000..fb2213c
--- /dev/null
+++ b/tools/fonts/fontchain_lint.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+
+import collections
+import glob
+from os import path
+import sys
+from xml.etree import ElementTree
+
+from fontTools import ttLib
+
+LANG_TO_SCRIPT = {
+ 'de': 'Latn',
+ 'en': 'Latn',
+ 'es': 'Latn',
+ 'eu': 'Latn',
+ 'ja': 'Jpan',
+ 'ko': 'Kore',
+ 'hu': 'Latn',
+ 'hy': 'Armn',
+ 'nb': 'Latn',
+ 'nn': 'Latn',
+ 'pt': 'Latn',
+}
+
+def lang_to_script(lang_code):
+ lang = lang_code.lower()
+ while lang not in LANG_TO_SCRIPT:
+ hyphen_idx = lang.rfind('-')
+ assert hyphen_idx != -1, (
+ 'We do not know what script the "%s" language is written in.'
+ % lang_code)
+ assumed_script = lang[hyphen_idx+1:]
+ if len(assumed_script) == 4 and assumed_script.isalpha():
+ # This is actually the script
+ return assumed_script.title()
+ lang = lang[:hyphen_idx]
+ return LANG_TO_SCRIPT[lang]
+
+
+def get_best_cmap(font):
+ font_file, index = font
+ font_path = path.join(_fonts_dir, font_file)
+ if index is not None:
+ ttfont = ttLib.TTFont(font_path, fontNumber=index)
+ else:
+ ttfont = ttLib.TTFont(font_path)
+ all_unicode_cmap = None
+ bmp_cmap = None
+ for cmap in ttfont['cmap'].tables:
+ specifier = (cmap.format, cmap.platformID, cmap.platEncID)
+ if specifier == (4, 3, 1):
+ assert bmp_cmap is None, 'More than one BMP cmap in %s' % (font, )
+ bmp_cmap = cmap
+ elif specifier == (12, 3, 10):
+ assert all_unicode_cmap is None, (
+ 'More than one UCS-4 cmap in %s' % (font, ))
+ all_unicode_cmap = cmap
+
+ return all_unicode_cmap.cmap if all_unicode_cmap else bmp_cmap.cmap
+
+
+def assert_font_supports_any_of_chars(font, chars):
+ best_cmap = get_best_cmap(font)
+ for char in chars:
+ if char in best_cmap:
+ return
+ sys.exit('None of characters in %s were found in %s' % (chars, font))
+
+
+def check_hyphens(hyphens_dir):
+ # Find all the scripts that need automatic hyphenation
+ scripts = set()
+ for hyb_file in glob.iglob(path.join(hyphens_dir, '*.hyb')):
+ hyb_file = path.basename(hyb_file)
+ assert hyb_file.startswith('hyph-'), (
+ 'Unknown hyphenation file %s' % hyb_file)
+ lang_code = hyb_file[hyb_file.index('-')+1:hyb_file.index('.')]
+ scripts.add(lang_to_script(lang_code))
+
+ HYPHENS = {0x002D, 0x2010}
+ for script in scripts:
+ fonts = _script_to_font_map[script]
+ assert fonts, 'No fonts found for the "%s" script' % script
+ for font in fonts:
+ assert_font_supports_any_of_chars(font, HYPHENS)
+
+
+def parse_fonts_xml(fonts_xml_path):
+ global _script_to_font_map, _fallback_chain
+ _script_to_font_map = collections.defaultdict(set)
+ _fallback_chain = []
+ tree = ElementTree.parse(fonts_xml_path)
+ for family in tree.findall('family'):
+ name = family.get('name')
+ variant = family.get('variant')
+ langs = family.get('lang')
+ if name:
+ assert variant is None, (
+ 'No variant expected for LGC font %s.' % name)
+ assert langs is None, (
+ 'No language expected for LGC fonts %s.' % name)
+ else:
+ assert variant in {None, 'elegant', 'compact'}, (
+ 'Unexpected value for variant: %s' % variant)
+
+ if langs:
+ langs = langs.split()
+ scripts = {lang_to_script(lang) for lang in langs}
+ else:
+ scripts = set()
+
+ for child in family:
+ assert child.tag == 'font', (
+ 'Unknown tag <%s>' % child.tag)
+ font_file = child.text
+ weight = int(child.get('weight'))
+ assert weight % 100 == 0, (
+ 'Font weight "%d" is not a multiple of 100.' % weight)
+
+ style = child.get('style')
+ assert style in {'normal', 'italic'}, (
+ 'Unknown style "%s"' % style)
+
+ index = child.get('index')
+ if index:
+ index = int(index)
+
+ _fallback_chain.append((
+ name,
+ frozenset(scripts),
+ variant,
+ weight,
+ style,
+ (font_file, index)))
+
+ if name: # non-empty names are used for default LGC fonts
+ map_scripts = {'Latn', 'Grek', 'Cyrl'}
+ else:
+ map_scripts = scripts
+ for script in map_scripts:
+ _script_to_font_map[script].add((font_file, index))
+
+
+def main():
+ target_out = sys.argv[1]
+ global _fonts_dir
+ _fonts_dir = path.join(target_out, 'fonts')
+
+ fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml')
+ parse_fonts_xml(fonts_xml_path)
+
+ hyphens_dir = path.join(target_out, 'usr', 'hyphen-data')
+ check_hyphens(hyphens_dir)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 97195e4..62f91f7 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -561,6 +561,10 @@
}
@Override
+ public void setDockedStackDividerTouchRegion(Rect touchableRegion) throws RemoteException {
+ }
+
+ @Override
public void requestAppKeyboardShortcuts(IResultReceiver receiver) throws RemoteException {
}