Merge "Fix for drag start event being incorrectly cached" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index e6a5b1a..e253ae1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9327,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;
diff --git a/api/system-current.txt b/api/system-current.txt
index e70b6f6..c9176ec 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9649,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;
@@ -9930,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);
diff --git a/api/test-current.txt b/api/test-current.txt
index 40e1156..a7b434c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -9336,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;
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/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/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/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/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index a747f16..d201ade 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -154,11 +154,11 @@
public static final int DENSITY_DEVICE_STABLE = getDeviceDensity();
/**
- * The absolute width of the display in pixels.
+ * The absolute width of the available display size in pixels.
*/
public int widthPixels;
/**
- * The absolute height of the display in pixels.
+ * The absolute height of the available display size in pixels.
*/
public int heightPixels;
/**
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/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/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/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 0ce4a12..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)) {
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 7a3c253..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: Use LocaleUtils#filterByLanguage() instead.
- if (locale != null && TextUtils.equals(locale.getLanguage(), 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/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/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/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index 20e9165..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,12 +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,
@@ -430,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<>();
@@ -778,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);
@@ -796,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 d354c1f..39ea205 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -715,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_enter.xml b/packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml
deleted file mode 100644
index 6c7e224..0000000
--- a/packages/DocumentsUI/res/animator-ldrtl/dir_enter.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="-1"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime"
- android:interpolator="@android:interpolator/decelerate_quad" />
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 7daf1c0..43c50bd 100644
--- a/packages/DocumentsUI/res/animator/dir_enter.xml
+++ b/packages/DocumentsUI/res/animator/dir_enter.xml
@@ -24,6 +24,7 @@
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"
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 de09638..7574655 100644
--- a/packages/DocumentsUI/res/animator/dir_leave.xml
+++ b/packages/DocumentsUI/res/animator/dir_leave.xml
@@ -24,6 +24,7 @@
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"
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 a26fb47..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;
- setY((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 95aa067..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.fade_out);
- break;
- case ANIM_LEAVE:
- ft.setCustomAnimations(R.animator.fade_in, 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/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/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 81ba23f..1fe218a 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -517,16 +517,35 @@
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (mCurrView == null) {
- break;
- }
- mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity());
- float velocity = getVelocity(mVelocityTracker);
+ if (mCurrView != null) {
+ float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
+ mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
+ float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
+ float velocity = getVelocity(mVelocityTracker);
+ float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
- if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) {
- if (isDismissGesture(ev)) {
+ float translation = getTranslation(mCurrView);
+ // Decide whether to dismiss the current view
+ boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
+ Math.abs(translation) > 0.4 * getSize(mCurrView);
+ boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
+ (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
+ (velocity > 0) == (translation > 0);
+ boolean falsingDetected = mCallback.isAntiFalsingNeeded();
+
+ if (mFalsingManager.isClassiferEnabled()) {
+ falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
+ } else {
+ falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
+ }
+
+ boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
+ && !falsingDetected && (childSwipedFastEnough || childSwipedFarEnough)
+ && ev.getActionMasked() == MotionEvent.ACTION_UP;
+
+ if (dismissChild) {
// flingadingy
- dismissChild(mCurrView, swipedFastEnough() ? velocity : 0f);
+ dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
} else {
// snappity
mCallback.onDragCancelled(mCurrView);
@@ -543,46 +562,6 @@
return (int) (mFalsingThreshold * factor);
}
- private float getMaxVelocity() {
- return MAX_DISMISS_VELOCITY * mDensityScale;
- }
-
- protected float getEscapeVelocity() {
- return SWIPE_ESCAPE_VELOCITY * mDensityScale;
- }
-
- protected boolean swipedFarEnough() {
- float translation = getTranslation(mCurrView);
- return DISMISS_IF_SWIPED_FAR_ENOUGH && Math.abs(translation) > 0.4 * getSize(mCurrView);
- }
-
- protected boolean isDismissGesture(MotionEvent ev) {
- boolean falsingDetected = mCallback.isAntiFalsingNeeded();
- if (mFalsingManager.isClassiferEnabled()) {
- falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
- } else {
- falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
- }
- return !falsingDetected && (swipedFastEnough() || swipedFarEnough())
- && ev.getActionMasked() == MotionEvent.ACTION_UP
- && mCallback.canChildBeDismissed(mCurrView);
- }
-
- protected boolean swipedFastEnough() {
- float velocity = getVelocity(mVelocityTracker);
- float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
- float translation = getTranslation(mCurrView);
- boolean ret = (Math.abs(velocity) > getEscapeVelocity()) &&
- (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
- (velocity > 0) == (translation > 0);
- return ret;
- }
-
- protected boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
- float translation) {
- return false;
- }
-
public interface Callback {
View getChildAtPosition(MotionEvent ev);
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/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 51553be..93cb952 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -241,18 +241,22 @@
}
private void updateLimits() {
- boolean customView = getPrivateLayout().getContractedChild().getId()
+ updateLimitsForView(mPrivateLayout);
+ updateLimitsForView(mPublicLayout);
+ }
+
+ private void updateLimitsForView(NotificationContentView layout) {
+ boolean customView = layout.getContractedChild().getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
mNotificationMinHeightLegacy : mNotificationMinHeight;
- boolean headsUpCustom = getPrivateLayout().getHeadsUpChild() != null &&
- getPrivateLayout().getHeadsUpChild().getId()
- != com.android.internal.R.id.status_bar_latest_event_content;
+ boolean headsUpCustom = layout.getHeadsUpChild() != null &&
+ layout.getHeadsUpChild().getId()
+ != com.android.internal.R.id.status_bar_latest_event_content;
int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
: mMaxHeadsUpHeight;
- mPrivateLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
- mPublicLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
+ layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
}
public StatusBarNotification getStatusBarNotification() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
index fcc48bf..375459f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
@@ -29,18 +29,11 @@
public class NotificationSettingsIconRow extends FrameLayout implements View.OnClickListener {
- private static final int GEAR_ALPHA_ANIM_DURATION = 200;
-
public interface SettingsIconRowListener {
/**
* Called when the gear behind a notification is touched.
*/
public void onGearTouched(ExpandableNotificationRow row, int x, int y);
-
- /**
- * Called when a notification is slid back over the gear.
- */
- public void onSettingsIconRowReset(NotificationSettingsIconRow row);
}
private ExpandableNotificationRow mParent;
@@ -52,8 +45,6 @@
private boolean mSettingsFadedIn = false;
private boolean mAnimating = false;
private boolean mOnLeft = true;
- private boolean mDismissing = false;
- private boolean mSnapping = false;
private int[] mGearLocation = new int[2];
private int[] mParentLocation = new int[2];
@@ -87,14 +78,8 @@
public void resetState() {
setGearAlpha(0f);
- mSettingsFadedIn = false;
mAnimating = false;
- mSnapping = false;
- mDismissing = false;
setIconLocation(true /* on left */);
- if (mListener != null) {
- mListener.onSettingsIconRowReset(this);
- }
}
public void setGearListener(SettingsIconRowListener listener) {
@@ -109,24 +94,20 @@
return mParent;
}
- public void setGearAlpha(float alpha) {
+ private void setGearAlpha(float alpha) {
if (alpha == 0) {
mSettingsFadedIn = false; // Can fade in again once it's gone.
setVisibility(View.INVISIBLE);
} else {
+ if (alpha == 1) {
+ mSettingsFadedIn = true;
+ }
setVisibility(View.VISIBLE);
}
mGearIcon.setAlpha(alpha);
}
/**
- * Returns whether the icon is on the left side of the view or not.
- */
- public boolean isIconOnLeft() {
- return mOnLeft;
- }
-
- /**
* Returns the horizontal space in pixels required to display the gear behind a notification.
*/
public float getSpaceForGear() {
@@ -138,7 +119,7 @@
* if entire view is visible.
*/
public boolean isVisible() {
- return mGearIcon.getAlpha() > 0;
+ return mSettingsFadedIn;
}
public void cancelFadeAnimator() {
@@ -148,18 +129,16 @@
}
public void updateSettingsIcons(final float transX, final float size) {
- if (mAnimating || !mSettingsFadedIn) {
- // Don't adjust when animating, or if the gear hasn't been shown yet.
+ if (mAnimating || (mGearIcon.getAlpha() == 0)) {
+ // Don't adjust when animating or settings aren't visible
return;
}
-
+ setIconLocation(transX > 0 /* fromLeft */);
final float fadeThreshold = size * 0.3f;
final float absTrans = Math.abs(transX);
float desiredAlpha = 0;
- if (absTrans == 0) {
- desiredAlpha = 0;
- } else if (absTrans <= fadeThreshold) {
+ if (absTrans <= fadeThreshold) {
desiredAlpha = 1;
} else {
desiredAlpha = 1 - ((absTrans - fadeThreshold) / (size - fadeThreshold));
@@ -169,12 +148,6 @@
public void fadeInSettings(final boolean fromLeft, final float transX,
final float notiThreshold) {
- if (mDismissing || mAnimating) {
- return;
- }
- if (isIconLocationChange(transX)) {
- setGearAlpha(0f);
- }
setIconLocation(transX > 0 /* fromLeft */);
mFadeAnimator = ValueAnimator.ofFloat(mGearIcon.getAlpha(), 1);
mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -191,53 +164,40 @@
});
mFadeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ mAnimating = false;
+ mSettingsFadedIn = false;
+ }
+
+ @Override
public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
mAnimating = true;
}
@Override
- public void onAnimationCancel(Animator animation) {
- // TODO should animate back to 0f from current alpha
- mGearIcon.setAlpha(0f);
- }
-
- @Override
public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
mAnimating = false;
- mSettingsFadedIn = mGearIcon.getAlpha() == 1;
+ mSettingsFadedIn = true;
}
});
mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
- mFadeAnimator.setDuration(GEAR_ALPHA_ANIM_DURATION);
+ mFadeAnimator.setDuration(200);
mFadeAnimator.start();
}
- public void setIconLocation(boolean onLeft) {
- if (onLeft == mOnLeft || mSnapping) {
+ private void setIconLocation(boolean onLeft) {
+ if (onLeft == mOnLeft) {
// Same side? Do nothing.
return;
}
+
setTranslationX(onLeft ? 0 : (mParent.getWidth() - mHorizSpaceForGear));
mOnLeft = onLeft;
}
- public boolean isIconLocationChange(float translation) {
- boolean onLeft = translation > mGearIcon.getPaddingStart();
- boolean onRight = translation < -mGearIcon.getPaddingStart();
- if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
- return true;
- }
- return false;
- }
-
- public void setDismissing() {
- mDismissing = true;
- }
-
- public void setSnapping(boolean snapping) {
- mSnapping = snapping;
- }
-
@Override
public void onClick(View v) {
if (v.getId() == R.id.gear_icon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 66f945e..f75f3574 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -188,6 +188,9 @@
@Override
public void setVisible(boolean visible) {
+ if (mViewTransformationAnimation != null) {
+ mViewTransformationAnimation.cancel();
+ }
for (Integer viewType : mTransformedViews.keySet()) {
TransformState ownState = getCurrentState(viewType);
if (ownState != null) {
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/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 4cb0dea..2da4787 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -372,11 +372,6 @@
}
@Override
- public void onSettingsIconRowReset(NotificationSettingsIconRow row) {
- mSwipeHelper.setSnappedToGear(false);
- }
-
- @Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, mBackgroundPaint);
if (DEBUG) {
@@ -722,15 +717,11 @@
mDragAnimPendingChildren.remove(animView);
}
- if (mCurrIconRow != null) {
- if (targetLeft == 0) {
- mCurrIconRow.resetState();
- mCurrIconRow = null;
- if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
- mGearExposedView = null;
- }
- } else {
- mSwipeHelper.setSnappedToGear(true);
+ if (targetLeft == 0 && mCurrIconRow != null) {
+ mCurrIconRow.resetState();
+ mCurrIconRow = null;
+ if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
+ mGearExposedView = null;
}
}
}
@@ -3388,11 +3379,15 @@
}
private class NotificationSwipeHelper extends SwipeHelper {
+ private static final int MOVE_STATE_LEFT = -1;
+ private static final int MOVE_STATE_UNDEFINED = 0;
+ private static final int MOVE_STATE_RIGHT = 1;
+
private static final long GEAR_SHOW_DELAY = 60;
+
private CheckForDrag mCheckForDrag;
private Handler mHandler;
- private boolean mGearSnappedTo;
- private boolean mGearSnappedOnLeft;
+ private int mMoveState = MOVE_STATE_UNDEFINED;
public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
super(swipeDirection, callback, context);
@@ -3405,10 +3400,6 @@
mTranslatingParentView = currView;
// Reset check for drag gesture
- cancelCheckForDrag();
- if (mCurrIconRow != null) {
- mCurrIconRow.setSnapping(false);
- }
mCheckForDrag = null;
mCurrIconRow = null;
@@ -3420,32 +3411,17 @@
mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow();
mCurrIconRow.setGearListener(NotificationStackScrollLayout.this);
}
+ mMoveState = MOVE_STATE_UNDEFINED;
}
@Override
public void onMoveUpdate(View view, float translation, float delta) {
- if (mCurrIconRow != null) {
- mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping.
-
- // If the gear is visible and the movement is towards it it's not a location change.
- boolean onLeft = mGearSnappedTo ? mGearSnappedOnLeft : mCurrIconRow.isIconOnLeft();
- boolean locationChange = isTowardsGear(translation, onLeft)
- ? false : mCurrIconRow.isIconLocationChange(translation);
- if (locationChange) {
- // Don't consider it "snapped" if location has changed.
- setSnappedToGear(false);
-
- // Changed directions, make sure we check to fade in icon again.
- if (!mHandler.hasCallbacks(mCheckForDrag)) {
- // No check scheduled, set null to schedule a new one.
- mCheckForDrag = null;
- } else {
- // Check scheduled, reset alpha and update location; check will fade it in
- mCurrIconRow.setGearAlpha(0f);
- mCurrIconRow.setIconLocation(translation > 0 /* onLeft */);
- }
- }
+ final int newMoveState = (delta < 0) ? MOVE_STATE_RIGHT : MOVE_STATE_LEFT;
+ if (mMoveState != MOVE_STATE_UNDEFINED && mMoveState != newMoveState) {
+ // Changed directions, make sure we check for drag again.
+ mCheckForDrag = null;
}
+ mMoveState = newMoveState;
final boolean gutsExposed = (view instanceof ExpandableNotificationRow)
&& ((ExpandableNotificationRow) view).areGutsExposed();
@@ -3458,99 +3434,35 @@
@Override
public void dismissChild(final View view, float velocity) {
- super.dismissChild(view, velocity);
cancelCheckForDrag();
- setSnappedToGear(false);
+ super.dismissChild(view, velocity);
}
@Override
public void snapChild(final View animView, final float targetLeft, float velocity) {
- super.snapChild(animView, targetLeft, velocity);
- if (targetLeft == 0) {
- cancelCheckForDrag();
- setSnappedToGear(false);
- }
- }
-
-
- @Override
- public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
- float translation) {
- if (mCurrIconRow == null) {
- cancelCheckForDrag();
- return false; // Let SwipeHelper handle it.
- }
-
- boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isIconOnLeft());
- boolean gestureFastEnough = Math.abs(velocity) > getEscapeVelocity();
-
- if (mGearSnappedTo && mCurrIconRow.isVisible()) {
- if (mGearSnappedOnLeft == mCurrIconRow.isIconOnLeft()) {
- boolean coveringGear =
- Math.abs(getTranslation(animView)) <= getSpaceForGear(animView) * 0.6f;
- if (gestureTowardsGear || coveringGear) {
- // Gesture is towards or covering the gear
- snapChild(animView, 0 /* leftTarget */, velocity);
- } else if (isDismissGesture(ev)) {
- // Gesture is a dismiss that's not towards the gear
- dismissChild(animView, swipedFastEnough() ? velocity : 0f);
- } else {
- // Didn't move enough to dismiss or cover, snap to the gear
- snapToGear(animView, velocity);
- }
- } else if ((!gestureFastEnough && swipedEnoughToShowGear(animView))
- || (gestureTowardsGear && !swipedFarEnough())) {
- // The gear has been snapped to previously, however, the gear is now on the
- // other side. If gesture is towards gear and not too far snap to the gear.
- snapToGear(animView, velocity);
- } else {
- dismissOrSnapBack(animView, velocity, ev);
- }
- } else if ((!gestureFastEnough && swipedEnoughToShowGear(animView))
- || gestureTowardsGear) {
- // Gear has not been snapped to previously and this is gear revealing gesture
- snapToGear(animView, velocity);
- } else {
- dismissOrSnapBack(animView, velocity, ev);
- }
- return true;
- }
-
- private void dismissOrSnapBack(View animView, float velocity, MotionEvent ev) {
- if (isDismissGesture(ev)) {
- dismissChild(animView, swipedFastEnough() ? velocity : 0f);
- } else {
- snapChild(animView, 0 /* leftTarget */, velocity);
- }
- }
-
- private void snapToGear(View animView, float velocity) {
- final float snapBackThreshold = getSpaceForGear(animView);
- final float target = mCurrIconRow.isIconOnLeft() ? snapBackThreshold
- : -snapBackThreshold;
- mGearExposedView = mTranslatingParentView;
- if (mGearDisplayedListener != null
- && (animView instanceof ExpandableNotificationRow)) {
- mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView);
- }
- if (mCurrIconRow != null) {
- mCurrIconRow.setSnapping(true);
- setSnappedToGear(true);
- }
- super.snapChild(animView, target, velocity);
- }
-
- private boolean swipedEnoughToShowGear(View animView) {
final float snapBackThreshold = getSpaceForGear(animView);
final float translation = getTranslation(animView);
final boolean fromLeft = translation > 0;
final float absTrans = Math.abs(translation);
final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
- // If the notification can't be dismissed then how far it can move is
- // restricted -- reduce the distance it needs to move in this case.
- final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f;
- return absTrans >= snapBackThreshold * 0.4f && absTrans <= notiThreshold;
+ boolean pastGear = (fromLeft && translation >= snapBackThreshold * 0.4f
+ && translation <= notiThreshold) ||
+ (!fromLeft && absTrans >= snapBackThreshold * 0.4f
+ && absTrans <= notiThreshold);
+
+ if (pastGear && !isPinnedHeadsUp(animView)
+ && (animView instanceof ExpandableNotificationRow)) {
+ // bouncity
+ final float target = fromLeft ? snapBackThreshold : -snapBackThreshold;
+ mGearExposedView = mTranslatingParentView;
+ if (mGearDisplayedListener != null) {
+ mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView);
+ }
+ super.snapChild(animView, target, velocity);
+ } else {
+ super.snapChild(animView, 0, velocity);
+ }
}
@Override
@@ -3587,25 +3499,6 @@
}
/**
- * Returns whether the gesture is towards the gear location or not.
- */
- private boolean isTowardsGear(float velocity, boolean onLeft) {
- if (mCurrIconRow == null) {
- return false;
- }
- return mCurrIconRow.isVisible()
- && ((onLeft && velocity <= 0) || (!onLeft && velocity >= 0));
- }
-
- /**
- * Indicates the the gear has been snapped to.
- */
- private void setSnappedToGear(boolean snapped) {
- mGearSnappedOnLeft = (mCurrIconRow != null) ? mCurrIconRow.isIconOnLeft() : false;
- mGearSnappedTo = snapped && mCurrIconRow != null;
- }
-
- /**
* Returns the horizontal space in pixels required to display the gear behind a
* notification.
*/
@@ -3617,7 +3510,7 @@
}
private void checkForDrag() {
- if (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag)) {
+ if (mCheckForDrag == null) {
mCheckForDrag = new CheckForDrag();
mHandler.postDelayed(mCheckForDrag, GEAR_SHOW_DELAY);
}
@@ -3628,6 +3521,7 @@
mCurrIconRow.cancelFadeAnimator();
}
mHandler.removeCallbacks(mCheckForDrag);
+ mCheckForDrag = null;
}
private final class CheckForDrag implements Runnable {
@@ -3637,13 +3531,14 @@
final float absTransX = Math.abs(translation);
final float bounceBackToGearWidth = getSpaceForGear(mTranslatingParentView);
final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
- if ((mCurrIconRow != null && (!mCurrIconRow.isVisible()
- || mCurrIconRow.isIconLocationChange(translation)))
- && absTransX >= bounceBackToGearWidth * 0.4
+ if (mCurrIconRow != null && absTransX >= bounceBackToGearWidth * 0.4
&& absTransX < notiThreshold) {
- // Fade in the gear
+ // Show icon
mCurrIconRow.fadeInSettings(translation > 0 /* fromLeft */, translation,
notiThreshold);
+ } else {
+ // Allow more to be posted if this wasn't a drag.
+ mCheckForDrag = null;
}
}
}
@@ -3656,7 +3551,7 @@
final View prevGearExposedView = mGearExposedView;
mGearExposedView = null;
- mGearSnappedTo = false;
+
Animator anim = getViewTranslationAnimator(prevGearExposedView,
0 /* leftTarget */, null /* updateListener */);
if (anim != null) {
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/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6bb2a60..d364d85 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2389,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/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/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/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()