diff options
299 files changed, 6419 insertions, 1771 deletions
diff --git a/Android.mk b/Android.mk index 769a555135b5..40da13478ec8 100644 --- a/Android.mk +++ b/Android.mk @@ -90,6 +90,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IWallpaperManager.aidl \ core/java/android/app/IWallpaperManagerCallback.aidl \ core/java/android/app/admin/IDevicePolicyManager.aidl \ + core/java/android/app/trust/IStrongAuthTracker.aidl \ core/java/android/app/trust/ITrustManager.aidl \ core/java/android/app/trust/ITrustListener.aidl \ core/java/android/app/backup/IBackupManager.aidl \ diff --git a/api/current.txt b/api/current.txt index acc9c4b11024..4157c6ffb3cf 100644 --- a/api/current.txt +++ b/api/current.txt @@ -240,8 +240,10 @@ package android { field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb + field public static final int activityHeight = 16844019; // 0x10104f3 field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8 field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9 + field public static final int activityWidth = 16844018; // 0x10104f2 field public static final int addPrintersActivity = 16843750; // 0x10103e6 field public static final int addStatesFromChildren = 16842992; // 0x10100f0 field public static final int adjustViewBounds = 16843038; // 0x101011e @@ -8881,6 +8883,7 @@ package android.content.pm { field public int configChanges; field public int documentLaunchMode; field public int flags; + field public android.content.pm.ActivityInfo.InitialLayout initialLayout; field public int launchMode; field public int maxRecents; field public java.lang.String parentActivityName; @@ -8894,6 +8897,15 @@ package android.content.pm { field public int uiOptions; } + public static final class ActivityInfo.InitialLayout { + ctor public ActivityInfo.InitialLayout(int, float, int, float, int); + field public final int gravity; + field public final int height; + field public final float heightFraction; + field public final int width; + field public final float widthFraction; + } + public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { ctor public ApplicationInfo(); ctor public ApplicationInfo(android.content.pm.ApplicationInfo); @@ -18136,7 +18148,7 @@ package android.mtp { method public final int getThumbPixWidth(); } - public class MtpObjectInfo.Builder { + public static class MtpObjectInfo.Builder { ctor public MtpObjectInfo.Builder(); ctor public MtpObjectInfo.Builder(android.mtp.MtpObjectInfo); method public android.mtp.MtpObjectInfo build(); @@ -32512,7 +32524,7 @@ package android.text { method public static int getLayoutDirectionFromLocale(java.util.Locale); method public static int getOffsetAfter(java.lang.CharSequence, int); method public static int getOffsetBefore(java.lang.CharSequence, int); - method public static java.lang.CharSequence getReverse(java.lang.CharSequence, int, int); + method public static deprecated java.lang.CharSequence getReverse(java.lang.CharSequence, int, int); method public static int getTrimmedLength(java.lang.CharSequence); method public static java.lang.String htmlEncode(java.lang.String); method public static int indexOf(java.lang.CharSequence, char); diff --git a/api/system-current.txt b/api/system-current.txt index 955fb07e20a7..33ce543c9ff8 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -333,8 +333,10 @@ package android { field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb + field public static final int activityHeight = 16844019; // 0x10104f3 field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8 field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9 + field public static final int activityWidth = 16844018; // 0x10104f2 field public static final int addPrintersActivity = 16843750; // 0x10103e6 field public static final int addStatesFromChildren = 16842992; // 0x10100f0 field public static final int adjustViewBounds = 16843038; // 0x101011e @@ -9139,6 +9141,7 @@ package android.content.pm { field public int configChanges; field public int documentLaunchMode; field public int flags; + field public android.content.pm.ActivityInfo.InitialLayout initialLayout; field public int launchMode; field public int maxRecents; field public java.lang.String parentActivityName; @@ -9152,6 +9155,15 @@ package android.content.pm { field public int uiOptions; } + public static final class ActivityInfo.InitialLayout { + ctor public ActivityInfo.InitialLayout(int, float, int, float, int); + field public final int gravity; + field public final int height; + field public final float heightFraction; + field public final int width; + field public final float widthFraction; + } + public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { ctor public ApplicationInfo(); ctor public ApplicationInfo(android.content.pm.ApplicationInfo); @@ -19649,7 +19661,7 @@ package android.mtp { method public final int getThumbPixWidth(); } - public class MtpObjectInfo.Builder { + public static class MtpObjectInfo.Builder { ctor public MtpObjectInfo.Builder(); ctor public MtpObjectInfo.Builder(android.mtp.MtpObjectInfo); method public android.mtp.MtpObjectInfo build(); @@ -34845,7 +34857,7 @@ package android.text { method public static int getLayoutDirectionFromLocale(java.util.Locale); method public static int getOffsetAfter(java.lang.CharSequence, int); method public static int getOffsetBefore(java.lang.CharSequence, int); - method public static java.lang.CharSequence getReverse(java.lang.CharSequence, int, int); + method public static deprecated java.lang.CharSequence getReverse(java.lang.CharSequence, int, int); method public static int getTrimmedLength(java.lang.CharSequence); method public static java.lang.String htmlEncode(java.lang.String); method public static int indexOf(java.lang.CharSequence, char); diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 2ee586f2c3ca..7c8842ca0231 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -27,6 +27,8 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= bootanimation +LOCAL_INIT_RC := bootanim.rc + ifdef TARGET_32_BIT_SURFACEFLINGER LOCAL_32_BIT_ONLY := true endif diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc new file mode 100644 index 000000000000..ee0d0b8c042f --- /dev/null +++ b/cmds/bootanimation/bootanim.rc @@ -0,0 +1,6 @@ +service bootanim /system/bin/bootanimation + class core + user graphics + group graphics audio + disabled + oneshot diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index f66a4c7748ee..68c11add8c0f 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -51,6 +51,7 @@ import android.content.pm.VerificationParams; import android.content.res.AssetManager; import android.content.res.Resources; import android.net.Uri; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.IUserManager; @@ -1094,6 +1095,7 @@ public final class Pm { } } + userId = translateUserId(userId, "runInstall"); if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; installFlags |= PackageManager.INSTALL_ALL_USERS; @@ -1164,6 +1166,16 @@ public final class Pm { } } + /** + * @param userId The user id to be translated. + * @param logContext Optional human readable text to provide some context in error log. + * @return Translated concrete user id. This will include USER_ALL. + */ + private int translateUserId(int userId, String logContext) { + return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, true, true, logContext, "pm command"); + } + private int runInstallCreate() throws RemoteException { int userId = UserHandle.USER_ALL; String installerPackageName = null; @@ -1220,6 +1232,7 @@ public final class Pm { } } + userId = translateUserId(userId, "runInstallCreate"); if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; params.installFlags |= PackageManager.INSTALL_ALL_USERS; @@ -1551,6 +1564,7 @@ public final class Pm { return 1; } + userId = translateUserId(userId, "runUninstall"); if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; flags |= PackageManager.DELETE_ALL_USERS; diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java index c551482f36bf..cb39e375d307 100644 --- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java +++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java @@ -61,7 +61,7 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge { IBinder token = new Binder(); try { ContentProviderHolder holder = activityManager.getContentProviderExternal( - providerName, UserHandle.USER_OWNER, token); + providerName, UserHandle.USER_SYSTEM, token); if (holder == null) { throw new IllegalStateException("Could not find provider: " + providerName); } diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java index 7a809361632c..30c11efd658e 100644 --- a/core/java/android/app/backup/WallpaperBackupHelper.java +++ b/core/java/android/app/backup/WallpaperBackupHelper.java @@ -56,11 +56,12 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu // This path must match what the WallpaperManagerService uses // TODO: Will need to change if backing up non-primary user's wallpaper + // http://b/22388012 public static final String WALLPAPER_IMAGE = - new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER), + new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM), "wallpaper").getAbsolutePath(); public static final String WALLPAPER_INFO = - new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER), + new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM), "wallpaper_info.xml").getAbsolutePath(); // Use old keys to keep legacy data compatibility and avoid writing two wallpapers public static final String WALLPAPER_IMAGE_KEY = @@ -71,8 +72,9 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu // will be saved to this file from the restore stream, then renamed to the proper // location if it's deemed suitable. // TODO: Will need to change if backing up non-primary user's wallpaper + // http://b/22388012 private static final String STAGE_FILE = - new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER), + new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM), "wallpaper-tmp").getAbsolutePath(); Context mContext; diff --git a/core/java/android/app/trust/IStrongAuthTracker.aidl b/core/java/android/app/trust/IStrongAuthTracker.aidl new file mode 100644 index 000000000000..36c71bff7682 --- /dev/null +++ b/core/java/android/app/trust/IStrongAuthTracker.aidl @@ -0,0 +1,26 @@ +/* +** +** Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package android.app.trust; + +/** + * Private API to be notified about strong auth changes + * + * {@hide} + */ +oneway interface IStrongAuthTracker { + void onStrongAuthRequiredChanged(int strongAuthRequired, int userId); +}
\ No newline at end of file diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl index 32951d997975..2dea5454054c 100644 --- a/core/java/android/app/trust/ITrustManager.aidl +++ b/core/java/android/app/trust/ITrustManager.aidl @@ -26,11 +26,9 @@ import android.app.trust.ITrustListener; interface ITrustManager { void reportUnlockAttempt(boolean successful, int userId); void reportEnabledTrustAgentsChanged(int userId); - void reportRequireCredentialEntry(int userId); void registerTrustListener(in ITrustListener trustListener); void unregisterTrustListener(in ITrustListener trustListener); void reportKeyguardShowingChanged(); boolean isDeviceLocked(int userId); boolean isDeviceSecure(int userId); - boolean hasUserAuthenticatedSinceBoot(int userId); } diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index 8cab565f8bad..aff69f07eef8 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -16,13 +16,19 @@ package android.app.trust; +import android.annotation.IntDef; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; +import android.util.SparseIntArray; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * See {@link com.android.server.trust.TrustManagerService} @@ -73,21 +79,6 @@ public class TrustManager { } /** - * Reports that trust is disabled until credentials have been entered for user {@param userId}. - * - * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. - * - * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL} - */ - public void reportRequireCredentialEntry(int userId) { - try { - mService.reportRequireCredentialEntry(userId); - } catch (RemoteException e) { - onError(e); - } - } - - /** * Reports that the visibility of the keyguard has changed. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. @@ -147,23 +138,6 @@ public class TrustManager { } } - /** - * Checks whether the specified user has been authenticated since the last boot. - * - * @param userId the user id of the user to check for - * @return true if the user has authenticated since boot, false otherwise - * - * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. - */ - public boolean hasUserAuthenticatedSinceBoot(int userId) { - try { - return mService.hasUserAuthenticatedSinceBoot(userId); - } catch (RemoteException e) { - onError(e); - return false; - } - } - private void onError(Exception e) { Log.e(TAG, "Error while calling TrustManagerService", e); } diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java index ab049ecbed95..0473475c1a77 100644 --- a/core/java/android/content/RestrictionEntry.java +++ b/core/java/android/content/RestrictionEntry.java @@ -456,7 +456,7 @@ public class RestrictionEntry implements Parcelable { if (o == this) return true; if (!(o instanceof RestrictionEntry)) return false; final RestrictionEntry other = (RestrictionEntry) o; - if (mType != other.mType || mKey.equals(other.mKey)) { + if (mType != other.mType || !mKey.equals(other.mKey)) { return false; } if (mCurrentValues == null && other.mCurrentValues == null diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java index 1fac06e23da1..a7744e7a1208 100644 --- a/core/java/android/content/RestrictionsManager.java +++ b/core/java/android/content/RestrictionsManager.java @@ -677,7 +677,8 @@ public class RestrictionsManager { * <table> * <tr><th>RestrictionEntry</th><th>Bundle</th></tr> * <tr><td>{@link RestrictionEntry#TYPE_BOOLEAN}</td><td>{@link Bundle#putBoolean}</td></tr> - * <tr><td>{@link RestrictionEntry#TYPE_CHOICE}, {@link RestrictionEntry#TYPE_CHOICE}</td> + * <tr><td>{@link RestrictionEntry#TYPE_CHOICE}, + * {@link RestrictionEntry#TYPE_MULTI_SELECT}</td> * <td>{@link Bundle#putStringArray}</td></tr> * <tr><td>{@link RestrictionEntry#TYPE_INTEGER}</td><td>{@link Bundle#putInt}</td></tr> * <tr><td>{@link RestrictionEntry#TYPE_STRING}</td><td>{@link Bundle#putString}</td></tr> diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 43cc63b4a3c8..83092a9ab034 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -698,6 +698,8 @@ public class ActivityInfo extends ComponentInfo */ public int lockTaskLaunchMode; + public InitialLayout initialLayout; + public ActivityInfo() { } @@ -717,6 +719,7 @@ public class ActivityInfo extends ComponentInfo maxRecents = orig.maxRecents; resizeable = orig.resizeable; lockTaskLaunchMode = orig.lockTaskLaunchMode; + initialLayout = orig.initialLayout; } /** @@ -763,9 +766,14 @@ public class ActivityInfo extends ComponentInfo } pw.println(prefix + "resizeable=" + resizeable + " lockTaskLaunchMode=" + lockTaskLaunchModeToString(lockTaskLaunchMode)); + if (initialLayout != null) { + pw.println(prefix + "initialLayout=" + initialLayout.width + "|" + + initialLayout.widthFraction + ", " + initialLayout.height + "|" + + initialLayout.heightFraction + ", " + initialLayout.gravity); + } super.dumpBack(pw, prefix); } - + public String toString() { return "ActivityInfo{" + Integer.toHexString(System.identityHashCode(this)) @@ -793,6 +801,16 @@ public class ActivityInfo extends ComponentInfo dest.writeInt(maxRecents); dest.writeInt(resizeable ? 1 : 0); dest.writeInt(lockTaskLaunchMode); + if (initialLayout != null) { + dest.writeInt(1); + dest.writeInt(initialLayout.width); + dest.writeFloat(initialLayout.widthFraction); + dest.writeInt(initialLayout.height); + dest.writeFloat(initialLayout.heightFraction); + dest.writeInt(initialLayout.gravity); + } else { + dest.writeInt(0); + } } public static final Parcelable.Creator<ActivityInfo> CREATOR @@ -822,5 +840,33 @@ public class ActivityInfo extends ComponentInfo maxRecents = source.readInt(); resizeable = (source.readInt() == 1); lockTaskLaunchMode = source.readInt(); + if (source.readInt() == 1) { + initialLayout = new InitialLayout(source); + } + } + + public static final class InitialLayout { + public InitialLayout(int width, float widthFraction, int height, float heightFraction, + int gravity) { + this.width = width; + this.widthFraction = widthFraction; + this.height = height; + this.heightFraction = heightFraction; + this.gravity = gravity; + } + + InitialLayout(Parcel source) { + width = source.readInt(); + widthFraction = source.readFloat(); + height = source.readInt(); + heightFraction = source.readFloat(); + gravity = source.readInt(); + } + + public final int width; + public final float widthFraction; + public final int height; + public final float heightFraction; + public final int gravity; } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 924df1b39cb8..502f73562418 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -50,6 +50,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.TypedValue; +import android.view.Gravity; import com.android.internal.R; import com.android.internal.util.ArrayUtils; @@ -3260,10 +3261,12 @@ public class PackageParser { owner.preferredActivityFilters.add(intent); } } else if (parser.getName().equals("meta-data")) { - if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData, + if ((a.metaData = parseMetaData(res, parser, attrs, a.metaData, outError)) == null) { return null; } + } else if (!receiver && parser.getName().equals("initial-layout")) { + parseInitialLayout(res, attrs, a); } else { if (!RIGID_PARSER) { Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); @@ -3296,6 +3299,43 @@ public class PackageParser { return a; } + private void parseInitialLayout(Resources res, AttributeSet attrs, Activity a) { + TypedArray sw = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestInitialLayout); + int width = -1; + float widthFraction = -1f; + int height = -1; + float heightFraction = -1f; + final int widthType = sw.getType( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activityWidth); + if (widthType == TypedValue.TYPE_FRACTION) { + widthFraction = sw.getFraction( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activityWidth, + 1, 1, -1); + } else if (widthType == TypedValue.TYPE_DIMENSION) { + width = sw.getDimensionPixelSize( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activityWidth, + -1); + } + final int heightType = sw.getType( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activityHeight); + if (heightType == TypedValue.TYPE_FRACTION) { + heightFraction = sw.getFraction( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activityHeight, + 1, 1, -1); + } else if (heightType == TypedValue.TYPE_DIMENSION) { + height = sw.getDimensionPixelSize( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activityHeight, + -1); + } + int gravity = sw.getInt( + com.android.internal.R.styleable.AndroidManifestInitialLayout_gravity, + Gravity.CENTER); + sw.recycle(); + a.info.initialLayout = new ActivityInfo.InitialLayout(width, widthFraction, + height, heightFraction, gravity); + } + private Activity parseActivityAlias(Package owner, Resources res, XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException, IOException { @@ -3375,6 +3415,7 @@ public class PackageParser { info.uiOptions = target.info.uiOptions; info.parentActivityName = target.info.parentActivityName; info.maxRecents = target.info.maxRecents; + info.initialLayout = target.info.initialLayout; Activity a = new Activity(mParseActivityAliasArgs, info); if (outError[0] != null) { diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index cbd2f7850657..38f971a20479 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -145,6 +145,18 @@ public class UserInfo implements Parcelable { return (!hideSystemUser || id != UserHandle.USER_SYSTEM) && supportsSwitchTo(); } + /* @hide */ + public boolean canHaveProfile() { + if (isManagedProfile() || isGuest() || isRestricted()) { + return false; + } + if (UserManager.isSplitSystemUser()) { + return id != UserHandle.USER_SYSTEM; + } else { + return id == UserHandle.USER_SYSTEM; + } + } + public UserInfo() { } diff --git a/core/java/android/hardware/ICameraServiceProxy.aidl b/core/java/android/hardware/ICameraServiceProxy.aidl index 0bb24bc2b04e..0e654d5083b6 100644 --- a/core/java/android/hardware/ICameraServiceProxy.aidl +++ b/core/java/android/hardware/ICameraServiceProxy.aidl @@ -19,6 +19,8 @@ package android.hardware; /** * Binder interface for the camera service proxy running in system_server. * + * Keep in sync with frameworks/av/include/camera/ICameraServiceProxy.h + * * @hide */ interface ICameraServiceProxy @@ -27,4 +29,9 @@ interface ICameraServiceProxy * Ping the service proxy to update the valid users for the camera service. */ oneway void pingForUserUpdate(); + + /** + * Update the status of a camera device + */ + oneway void notifyCameraState(String cameraId, int newCameraState); } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 558392069c51..a2ef078a6a71 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1003,13 +1003,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>The orientation of the camera relative to the sensor * coordinate system.</p> - * <p>The four coefficients that describe the quarternion + * <p>The four coefficients that describe the quaternion * rotation from the Android sensor coordinate system to a * camera-aligned coordinate system where the X-axis is * aligned with the long side of the image sensor, the Y-axis * is aligned with the short side of the image sensor, and * the Z-axis is aligned with the optical axis of the sensor.</p> - * <p>To convert from the quarternion coefficients <code>(x,y,z,w)</code> + * <p>To convert from the quaternion coefficients <code>(x,y,z,w)</code> * to the axis of rotation <code>(a_x, a_y, a_z)</code> and rotation * amount <code>theta</code>, the following formulas can be used:</p> * <pre><code> theta = 2 * acos(w) @@ -1018,7 +1018,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * a_z = z / sin(theta/2) * </code></pre> * <p>To create a 3x3 rotation matrix that applies the rotation - * defined by this quarternion, the following matrix can be + * defined by this quaternion, the following matrix can be * used:</p> * <pre><code>R = [ 1 - 2y^2 - 2z^2, 2xy - 2zw, 2xz + 2yw, * 2xy + 2zw, 1 - 2x^2 - 2z^2, 2yz - 2xw, @@ -1030,7 +1030,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>where <code>p</code> is in the device sensor coordinate system, and * <code>p'</code> is in the camera-oriented coordinate system.</p> * <p><b>Units</b>: - * Quarternion coefficients</p> + * Quaternion coefficients</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> */ @PublicKey @@ -1052,13 +1052,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * user's perspective) will report <code>(0.03, 0, 0)</code>.</p> * <p>To transform a pixel coordinates between two cameras * facing the same direction, first the source camera - * android.lens.radialDistortion must be corrected for. Then - * the source camera android.lens.intrinsicCalibration needs + * {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for. Then + * the source camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs * to be applied, followed by the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} * of the source camera, the translation of the source camera * relative to the destination camera, the * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination camera, and - * finally the inverse of android.lens.intrinsicCalibration + * finally the inverse of {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} * of the destination camera. This obtains a * radial-distortion-free coordinate in the destination * camera pixel coordinates.</p> @@ -1069,7 +1069,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Units</b>: Meters</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * + * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @see CameraCharacteristics#LENS_POSE_ROTATION + * @see CameraCharacteristics#LENS_RADIAL_DISTORTION */ @PublicKey public static final Key<float[]> LENS_POSE_TRANSLATION = @@ -1115,7 +1117,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * where <code>(0,0)</code> is the top-left of the * preCorrectionActiveArraySize rectangle. Once the pose and * intrinsic calibration transforms have been applied to a - * world point, then the android.lens.radialDistortion + * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} * transform needs to be applied, and the result adjusted to * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate * system (where <code>(0, 0)</code> is the top-left of the @@ -1130,6 +1132,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * * @see CameraCharacteristics#LENS_POSE_ROTATION * @see CameraCharacteristics#LENS_POSE_TRANSLATION + * @see CameraCharacteristics#LENS_RADIAL_DISTORTION * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE */ @@ -1156,7 +1159,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * </code></pre> * <p>The pixel coordinates are defined in a normalized * coordinate system related to the - * android.lens.intrinsicCalibration calibration fields. + * {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} calibration fields. * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code> have <code>(0,0)</code> at the * lens optical center <code>[c_x, c_y]</code>. The maximum magnitudes * of both x and y coordinates are normalized to be 1 at the @@ -1169,6 +1172,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Units</b>: * Unitless coefficients.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION */ @PublicKey public static final Key<float[]> LENS_RADIAL_DISTORTION = @@ -2262,7 +2267,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p> * <p>The currently supported fields that correct for geometric distortion are:</p> * <ol> - * <li>android.lens.radialDistortion.</li> + * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}.</li> * </ol> * <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same * as the post-distortion-corrected rectangle given in @@ -2275,6 +2280,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Units</b>: Pixel coordinates on the image sensor</p> * <p>This key is available on all devices.</p> * + * @see CameraCharacteristics#LENS_RADIAL_DISTORTION * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index c580083b94d9..35a1d960e22d 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -647,8 +647,8 @@ public abstract class CameraMetadata<TKey> { * {@link android.hardware.camera2.CaptureResult }:<ul> * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li> * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li> - * <li>android.lens.intrinsicCalibration</li> - * <li>android.lens.radialDistortion</li> + * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li> + * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li> * </ul> * </li> * <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li> @@ -667,8 +667,10 @@ public abstract class CameraMetadata<TKey> { * * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE * @see CameraCharacteristics#LENS_FACING + * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @see CameraCharacteristics#LENS_POSE_ROTATION * @see CameraCharacteristics#LENS_POSE_TRANSLATION + * @see CameraCharacteristics#LENS_RADIAL_DISTORTION * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index d5511c122c3d..46eddb3f6651 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2602,13 +2602,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>The orientation of the camera relative to the sensor * coordinate system.</p> - * <p>The four coefficients that describe the quarternion + * <p>The four coefficients that describe the quaternion * rotation from the Android sensor coordinate system to a * camera-aligned coordinate system where the X-axis is * aligned with the long side of the image sensor, the Y-axis * is aligned with the short side of the image sensor, and * the Z-axis is aligned with the optical axis of the sensor.</p> - * <p>To convert from the quarternion coefficients <code>(x,y,z,w)</code> + * <p>To convert from the quaternion coefficients <code>(x,y,z,w)</code> * to the axis of rotation <code>(a_x, a_y, a_z)</code> and rotation * amount <code>theta</code>, the following formulas can be used:</p> * <pre><code> theta = 2 * acos(w) @@ -2617,7 +2617,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * a_z = z / sin(theta/2) * </code></pre> * <p>To create a 3x3 rotation matrix that applies the rotation - * defined by this quarternion, the following matrix can be + * defined by this quaternion, the following matrix can be * used:</p> * <pre><code>R = [ 1 - 2y^2 - 2z^2, 2xy - 2zw, 2xz + 2yw, * 2xy + 2zw, 1 - 2x^2 - 2z^2, 2yz - 2xw, @@ -2629,7 +2629,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>where <code>p</code> is in the device sensor coordinate system, and * <code>p'</code> is in the camera-oriented coordinate system.</p> * <p><b>Units</b>: - * Quarternion coefficients</p> + * Quaternion coefficients</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> */ @PublicKey @@ -2651,13 +2651,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * user's perspective) will report <code>(0.03, 0, 0)</code>.</p> * <p>To transform a pixel coordinates between two cameras * facing the same direction, first the source camera - * android.lens.radialDistortion must be corrected for. Then - * the source camera android.lens.intrinsicCalibration needs + * {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for. Then + * the source camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs * to be applied, followed by the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} * of the source camera, the translation of the source camera * relative to the destination camera, the * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination camera, and - * finally the inverse of android.lens.intrinsicCalibration + * finally the inverse of {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} * of the destination camera. This obtains a * radial-distortion-free coordinate in the destination * camera pixel coordinates.</p> @@ -2668,7 +2668,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p><b>Units</b>: Meters</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * + * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @see CameraCharacteristics#LENS_POSE_ROTATION + * @see CameraCharacteristics#LENS_RADIAL_DISTORTION */ @PublicKey public static final Key<float[]> LENS_POSE_TRANSLATION = @@ -2714,7 +2716,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * where <code>(0,0)</code> is the top-left of the * preCorrectionActiveArraySize rectangle. Once the pose and * intrinsic calibration transforms have been applied to a - * world point, then the android.lens.radialDistortion + * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} * transform needs to be applied, and the result adjusted to * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate * system (where <code>(0, 0)</code> is the top-left of the @@ -2729,6 +2731,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * * @see CameraCharacteristics#LENS_POSE_ROTATION * @see CameraCharacteristics#LENS_POSE_TRANSLATION + * @see CameraCharacteristics#LENS_RADIAL_DISTORTION * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE */ @@ -2755,7 +2758,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * </code></pre> * <p>The pixel coordinates are defined in a normalized * coordinate system related to the - * android.lens.intrinsicCalibration calibration fields. + * {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} calibration fields. * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code> have <code>(0,0)</code> at the * lens optical center <code>[c_x, c_y]</code>. The maximum magnitudes * of both x and y coordinates are normalized to be 1 at the @@ -2768,6 +2771,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p><b>Units</b>: * Unitless coefficients.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION */ @PublicKey public static final Key<float[]> LENS_RADIAL_DISTORTION = diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index e786707c383d..fddfbde9844e 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -338,6 +338,8 @@ public class LegacyCameraDevice implements AutoCloseable { } else { sizedSurfaces.add(new Pair<>(output, s)); } + // Lock down the size before configuration + setSurfaceDimens(output, s.getWidth(), s.getHeight()); } catch (BufferQueueAbandonedException e) { Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e); return BAD_VALUE; diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index e71e49fcc5ed..b8d69601976e 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -1290,7 +1290,7 @@ public final class StreamConfigurationMap { for (StreamConfiguration config : configurations) { int fmt = config.getFormat(); if (fmt == format && config.isOutput() == output) { - if (output) { + if (output && mListHighResolution) { // Filter slow high-res output formats; include for // highRes, remove for !highRes long duration = 0; diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 061fad96ed25..7cff11b01383 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -395,7 +395,7 @@ public class FingerprintManager { * Request authentication of a crypto object. This call warms up the fingerprint hardware * and starts scanning for a fingerprint. It terminates when * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or - * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at + * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at * which point the object is no longer valid. The operation can be canceled by using the * provided cancel object. * diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 364c0eb4f326..aeb5d4517e6c 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -43,7 +43,7 @@ interface IUserManager { UserInfo getPrimaryUser(); List<UserInfo> getUsers(boolean excludeDying); List<UserInfo> getProfiles(int userHandle, boolean enabledOnly); - boolean canAddMoreManagedProfiles(); + boolean canAddMoreManagedProfiles(int userId); UserInfo getProfileParent(int userHandle); UserInfo getUserInfo(int userHandle); long getUserCreationTime(int userHandle); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index aea5123237f3..849f5de2a22c 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1024,9 +1024,9 @@ public class UserManager { * @return true if more managed profiles can be added, false if limit has been reached. * @hide */ - public boolean canAddMoreManagedProfiles() { + public boolean canAddMoreManagedProfiles(int userId) { try { - return mService.canAddMoreManagedProfiles(); + return mService.canAddMoreManagedProfiles(userId); } catch (RemoteException re) { Log.w(TAG, "Could not check if we can add more managed profiles", re); return false; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index db19f7a4ce27..631057013e50 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -118,7 +118,7 @@ public class ZenModeConfig implements Parcelable { public boolean allowEvents = DEFAULT_ALLOW_EVENTS; public int allowCallsFrom = DEFAULT_SOURCE; public int allowMessagesFrom = DEFAULT_SOURCE; - public int user = UserHandle.USER_OWNER; + public int user = UserHandle.USER_SYSTEM; public ZenRule manualRule; public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>(); diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 4d8a7d00ba89..49cb8e2c0e72 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -462,7 +462,7 @@ public class TextUtils { /** * Returns the length that the specified CharSequence would have if - * spaces and control characters were trimmed from the start and end, + * spaces and ASCII control characters were trimmed from the start and end, * as by {@link String#trim}. */ public static int getTrimmedLength(CharSequence s) { @@ -505,7 +505,13 @@ public class TextUtils { return false; } - // XXX currently this only reverses chars, not spans + /* + * @deprecated + * Do not use. This function only reverses individual {@code char}s and not their associated + * spans. It doesn't support surrogate pairs (that correspond to non-BMP code points), + * combining sequences or conjuncts either. + */ + @Deprecated public static CharSequence getReverse(CharSequence source, int start, int end) { return new Reverser(source, start, end); @@ -1470,8 +1476,9 @@ public class TextUtils { */ public static boolean isDigitsOnly(CharSequence str) { final int len = str.length(); - for (int i = 0; i < len; i++) { - if (!Character.isDigit(str.charAt(i))) { + for (int cp, i = 0; i < len; i += Character.charCount(cp)) { + cp = Character.codePointAt(str, i); + if (!Character.isDigit(cp)) { return false; } } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index a53da0955f0c..9f5dfa695174 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -98,7 +98,8 @@ public final class Formatter { /** {@hide} */ public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) { - float result = sizeBytes; + final boolean isNegative = (sizeBytes < 0); + float result = isNegative ? -sizeBytes : sizeBytes; int suffix = com.android.internal.R.string.byteShort; long mult = 1; if (result > 900) { @@ -154,9 +155,13 @@ public final class Formatter { roundFormat = "%.2f"; } } + + if (isNegative) { + result = -result; + } final String roundedString = String.format(roundFormat, result); - // Note this might overflow if result >= Long.MAX_VALUE / 100, but that's like 80PB so + // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like 80PB so // it's okay (for now)... final long roundedBytes = (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0 diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java index 948cec71d819..080ed9ae1841 100644 --- a/core/java/android/view/DisplayListCanvas.java +++ b/core/java/android/view/DisplayListCanvas.java @@ -20,12 +20,7 @@ import android.annotation.NonNull; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.CanvasProperty; -import android.graphics.NinePatch; import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Picture; -import android.graphics.Rect; -import android.graphics.RectF; import android.util.Pools.SynchronizedPool; /** @@ -206,16 +201,12 @@ public class DisplayListCanvas extends Canvas { * Draws the specified layer onto this canvas. * * @param layer The layer to composite on this canvas - * @param x The left coordinate of the layer - * @param y The top coordinate of the layer - * @param paint The paint used to draw the layer */ - void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) { - layer.setLayerPaint(paint); - nDrawLayer(mNativeCanvasWrapper, layer.getLayerHandle(), x, y); + void drawHardwareLayer(HardwareLayer layer) { + nDrawLayer(mNativeCanvasWrapper, layer.getLayerHandle()); } - private static native void nDrawLayer(long renderer, long layer, float x, float y); + private static native void nDrawLayer(long renderer, long layer); /////////////////////////////////////////////////////////////////////////// // Drawing diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java index 65ae8a649241..692ca7ba99cf 100644 --- a/core/java/android/view/HardwareLayer.java +++ b/core/java/android/view/HardwareLayer.java @@ -150,8 +150,4 @@ final class HardwareLayer { private static native void nSetSurfaceTexture(long layerUpdater, SurfaceTexture surface, boolean isAlreadyAttached); private static native void nUpdateSurfaceTexture(long layerUpdater); - private static native void nUpdateRenderLayer(long layerUpdater, long displayList, - int left, int top, int right, int bottom); - - private static native int nGetTexName(long layerUpdater); } diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index 197ea09e2d60..6b60be9c91b5 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -319,11 +319,25 @@ public class TextureView extends View { */ @Override public final void draw(Canvas canvas) { - // NOTE: Maintain this carefully (see View.java) + // NOTE: Maintain this carefully (see View#draw) mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; - applyUpdate(); - applyTransformMatrix(); + /* Simplify drawing to guarantee the layer is the only thing drawn - so e.g. no background, + scrolling, or fading edges. This guarantees all drawing is in the layer, so drawing + properties (alpha, layer paint) affect all of the content of a TextureView. */ + + if (canvas.isHardwareAccelerated()) { + DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas; + + HardwareLayer layer = getHardwareLayer(); + if (layer != null) { + applyUpdate(); + applyTransformMatrix(); + + mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date + displayListCanvas.drawHardwareLayer(layer); + } + } } /** @@ -359,12 +373,7 @@ public class TextureView extends View { invalidate(true); } - @Override HardwareLayer getHardwareLayer() { - // NOTE: Maintain these two lines very carefully (see View.java) - mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; - mPrivateFlags &= ~PFLAG_DIRTY_MASK; - if (mLayer == null) { if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { return null; @@ -402,9 +411,6 @@ public class TextureView extends View { mSurface.setDefaultBufferSize(getWidth(), getHeight()); } - applyUpdate(); - applyTransformMatrix(); - return mLayer; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a4925184f181..b17f88fc7497 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15002,16 +15002,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * If this View draws with a HardwareLayer, returns it. - * Otherwise returns null - * - * TODO: Only TextureView uses this, can we eliminate it? - */ - HardwareLayer getHardwareLayer() { - return null; - } - - /** * Destroys all hardware rendering resources. This method is invoked * when the system needs to reclaim resources. Upon execution of this * method, you should free any OpenGL resources created by the view. @@ -15161,10 +15151,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, canvas.setHighContrastText(mAttachInfo.mHighContrastText); try { - final HardwareLayer layer = getHardwareLayer(); - if (layer != null && layer.isValid()) { - canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint); - } else if (layerType == LAYER_TYPE_SOFTWARE) { + if (layerType == LAYER_TYPE_SOFTWARE) { buildDrawingCache(true); Bitmap cache = getDrawingCache(true); if (cache != null) { diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 20fe61d25f26..b53af0c7342a 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -72,6 +72,8 @@ import java.io.InputStream; */ @RemoteView public class ImageView extends View { + private static final String LOG_TAG = "ImageView"; + // settable by the client private Uri mUri; private int mResource = 0; @@ -87,7 +89,7 @@ public class ImageView extends View { private boolean mHasColorFilter = false; private Xfermode mXfermode; private int mAlpha = 255; - private int mViewAlphaScale = 256; + private final int mViewAlphaScale = 256; private boolean mColorMod = false; private Drawable mDrawable = null; @@ -105,8 +107,8 @@ public class ImageView extends View { private Matrix mDrawMatrix = null; // Avoid allocations... - private RectF mTempSrc = new RectF(); - private RectF mTempDst = new RectF(); + private final RectF mTempSrc = new RectF(); + private final RectF mTempDst = new RectF(); private boolean mCropToPadding; @@ -116,6 +118,9 @@ public class ImageView extends View { // AdjustViewBounds behavior will be in compatibility mode for older apps. private boolean mAdjustViewBoundsCompat = false; + /** Whether to pass Resources when creating the source from a stream. */ + private boolean mUseCorrectStreamDensity; + private static final ScaleType[] sScaleTypeArray = { ScaleType.MATRIX, ScaleType.FIT_XY, @@ -147,30 +152,21 @@ public class ImageView extends View { initImageView(); final TypedArray a = context.obtainStyledAttributes( - attrs, com.android.internal.R.styleable.ImageView, defStyleAttr, defStyleRes); + attrs, R.styleable.ImageView, defStyleAttr, defStyleRes); - Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src); + final Drawable d = a.getDrawable(R.styleable.ImageView_src); if (d != null) { setImageDrawable(d); } - mBaselineAlignBottom = a.getBoolean( - com.android.internal.R.styleable.ImageView_baselineAlignBottom, false); - - mBaseline = a.getDimensionPixelSize( - com.android.internal.R.styleable.ImageView_baseline, -1); + mBaselineAlignBottom = a.getBoolean(R.styleable.ImageView_baselineAlignBottom, false); + mBaseline = a.getDimensionPixelSize(R.styleable.ImageView_baseline, -1); - setAdjustViewBounds( - a.getBoolean(com.android.internal.R.styleable.ImageView_adjustViewBounds, - false)); + setAdjustViewBounds(a.getBoolean(R.styleable.ImageView_adjustViewBounds, false)); + setMaxWidth(a.getDimensionPixelSize(R.styleable.ImageView_maxWidth, Integer.MAX_VALUE)); + setMaxHeight(a.getDimensionPixelSize(R.styleable.ImageView_maxHeight, Integer.MAX_VALUE)); - setMaxWidth(a.getDimensionPixelSize( - com.android.internal.R.styleable.ImageView_maxWidth, Integer.MAX_VALUE)); - - setMaxHeight(a.getDimensionPixelSize( - com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE)); - - final int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1); + final int index = a.getInt(R.styleable.ImageView_scaleType, -1); if (index >= 0) { setScaleType(sScaleTypeArray[index]); } @@ -193,24 +189,26 @@ public class ImageView extends View { applyImageTint(); - final int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255); + final int alpha = a.getInt(R.styleable.ImageView_drawableAlpha, 255); if (alpha != 255) { - setAlpha(alpha); + setImageAlpha(alpha); } mCropToPadding = a.getBoolean( - com.android.internal.R.styleable.ImageView_cropToPadding, false); - + R.styleable.ImageView_cropToPadding, false); + a.recycle(); //need inflate syntax/reader for matrix } private void initImageView() { - mMatrix = new Matrix(); - mScaleType = ScaleType.FIT_CENTER; - mAdjustViewBoundsCompat = mContext.getApplicationInfo().targetSdkVersion <= - Build.VERSION_CODES.JELLY_BEAN_MR1; + mMatrix = new Matrix(); + mScaleType = ScaleType.FIT_CENTER; + + final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; + mAdjustViewBoundsCompat = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1; + mUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M; } @Override @@ -258,7 +256,8 @@ public class ImageView extends View { @Override public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) { super.onPopulateAccessibilityEventInternal(event); - CharSequence contentDescription = getContentDescription(); + + final CharSequence contentDescription = getContentDescription(); if (!TextUtils.isEmpty(contentDescription)) { event.getText().add(contentDescription); } @@ -269,7 +268,7 @@ public class ImageView extends View { * to preserve the aspect ratio of its drawable * * @return whether to adjust the bounds of this view - * to presrve the original aspect ratio of the drawable + * to preserve the original aspect ratio of the drawable * * @see #setAdjustViewBounds(boolean) * @@ -291,7 +290,7 @@ public class ImageView extends View { * * @param adjustViewBounds Whether to adjust the bounds of this view * to preserve the original aspect ratio of the drawable. - * + * * @see #getAdjustViewBounds() * * @attr ref android.R.styleable#ImageView_adjustViewBounds @@ -323,14 +322,14 @@ public class ImageView extends View { * of 100 x 100 while preserving the original aspect ratio, do the following: 1) set * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width * layout params to WRAP_CONTENT. - * + * * <p> * Note that this view could be still smaller than 100 x 100 using this approach if the original * image is small. To set an image to a fixed size, specify that size in the layout params and * then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit * the image within the bounds. * </p> - * + * * @param maxWidth maximum width for this view * * @see #getMaxWidth() @@ -361,14 +360,14 @@ public class ImageView extends View { * maximum of 100 x 100 while preserving the original aspect ratio, do the following: 1) set * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width * layout params to WRAP_CONTENT. - * + * * <p> * Note that this view could be still smaller than 100 x 100 using this approach if the original * image is small. To set an image to a fixed size, specify that size in the layout params and * then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit * the image within the bounds. * </p> - * + * * @param maxHeight maximum height for this view * * @see #getMaxHeight() @@ -436,9 +435,7 @@ public class ImageView extends View { */ @android.view.RemotableViewMethod public void setImageURI(@Nullable Uri uri) { - if (mResource != 0 || - (mUri != uri && - (uri == null || mUri == null || !uri.equals(mUri)))) { + if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) { updateDrawable(null); mResource = 0; mUri = uri; @@ -457,7 +454,7 @@ public class ImageView extends View { /** * Sets a drawable as the content of this ImageView. - * + * * @param drawable the Drawable to set, or {@code null} to clear the * content */ @@ -577,7 +574,7 @@ public class ImageView extends View { /** * Sets a Bitmap as the content of this ImageView. - * + * * @param bm The bitmap to set */ @android.view.RemotableViewMethod @@ -609,7 +606,7 @@ public class ImageView extends View { } /** - * Sets the image level, when it is constructed from a + * Sets the image level, when it is constructed from a * {@link android.graphics.drawable.LevelListDrawable}. * * @param level The new level for the image. @@ -675,7 +672,7 @@ public class ImageView extends View { * From XML, use this syntax: <code>android:scaleType="centerInside"</code>. */ CENTER_INSIDE (7); - + ScaleType(int ni) { nativeInt = ni; } @@ -685,9 +682,9 @@ public class ImageView extends View { /** * Controls how the image should be resized or moved to match the size * of this ImageView. - * + * * @param scaleType The desired scaling mode. - * + * * @attr ref android.R.styleable#ImageView_scaleType */ public void setScaleType(ScaleType scaleType) { @@ -698,13 +695,13 @@ public class ImageView extends View { if (mScaleType != scaleType) { mScaleType = scaleType; - setWillNotCacheDrawing(mScaleType == ScaleType.CENTER); + setWillNotCacheDrawing(mScaleType == ScaleType.CENTER); requestLayout(); invalidate(); } } - + /** * Return the current scale type in use by this ImageView. * @@ -734,7 +731,7 @@ public class ImageView extends View { * Adds a transformation {@link Matrix} that is applied * to the view's drawable when it is drawn. Allows custom scaling, * translation, and perspective distortion. - * + * * @param matrix the transformation parameters in matrix form */ public void setImageMatrix(Matrix matrix) { @@ -787,8 +784,8 @@ public class ImageView extends View { return; } - Resources rsrc = getResources(); - if (rsrc == null) { + final Resources res = getResources(); + if (res == null) { return; } @@ -798,12 +795,12 @@ public class ImageView extends View { try { d = mContext.getDrawable(mResource); } catch (Exception e) { - Log.w("ImageView", "Unable to find resource: " + mResource, e); + Log.w(LOG_TAG, "Unable to find resource: " + mResource, e); // Don't try again. mUri = null; } } else if (mUri != null) { - String scheme = mUri.getScheme(); + final String scheme = mUri.getScheme(); if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { try { // Load drawable through Resources, to get the source density information @@ -811,31 +808,32 @@ public class ImageView extends View { mContext.getContentResolver().getResourceId(mUri); d = r.r.getDrawable(r.id, mContext.getTheme()); } catch (Exception e) { - Log.w("ImageView", "Unable to open content: " + mUri, e); + Log.w(LOG_TAG, "Unable to open content: " + mUri, e); } } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_FILE.equals(scheme)) { InputStream stream = null; try { stream = mContext.getContentResolver().openInputStream(mUri); - d = Drawable.createFromStream(stream, null); + d = Drawable.createFromResourceStream( + mUseCorrectStreamDensity ? res : null, null, stream, null); } catch (Exception e) { - Log.w("ImageView", "Unable to open content: " + mUri, e); + Log.w(LOG_TAG, "Unable to open content: " + mUri, e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { - Log.w("ImageView", "Unable to close content: " + mUri, e); + Log.w(LOG_TAG, "Unable to close content: " + mUri, e); } } } - } else { + } else { d = Drawable.createFromPath(mUri.toString()); } - + if (d == null) { - System.out.println("resolveUri failed on bad bitmap uri: " + mUri); + Log.w(LOG_TAG, "resolveUri failed on bad bitmap uri: " + mUri); // Don't try again. mUri = null; } @@ -890,7 +888,7 @@ public class ImageView extends View { } private void resizeFromDrawable() { - Drawable d = mDrawable; + final Drawable d = mDrawable; if (d != null) { int w = d.getIntrinsicWidth(); if (w < 0) w = mDrawableWidth; @@ -923,23 +921,23 @@ public class ImageView extends View { private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) { // ScaleToFit enum to their corresponding Matrix.ScaleToFit values return sS2FArray[st.nativeInt - 1]; - } + } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { resolveUri(); int w; int h; - + // Desired aspect ratio of the view's contents (not including padding) float desiredAspect = 0.0f; - + // We are allowed to change the view's width boolean resizeWidth = false; - + // We are allowed to change the view's height boolean resizeHeight = false; - + final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); @@ -959,15 +957,15 @@ public class ImageView extends View { if (mAdjustViewBounds) { resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; - + desiredAspect = (float) w / (float) h; } } - - int pleft = mPaddingLeft; - int pright = mPaddingRight; - int ptop = mPaddingTop; - int pbottom = mPaddingBottom; + + final int pleft = mPaddingLeft; + final int pright = mPaddingRight; + final int ptop = mPaddingTop; + final int pbottom = mPaddingBottom; int widthSize; int heightSize; @@ -975,7 +973,7 @@ public class ImageView extends View { if (resizeWidth || resizeHeight) { /* If we get here, it means we want to resize to match the drawables aspect ratio, and we have the freedom to change at - least one dimension. + least one dimension. */ // Get the max possible width given our constraints @@ -986,13 +984,13 @@ public class ImageView extends View { if (desiredAspect != 0.0f) { // See what our actual aspect ratio is - float actualAspect = (float)(widthSize - pleft - pright) / + final float actualAspect = (float)(widthSize - pleft - pright) / (heightSize - ptop - pbottom); - + if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { - + boolean done = false; - + // Try adjusting width to be proportional to height if (resizeWidth) { int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + @@ -1006,9 +1004,9 @@ public class ImageView extends View { if (newWidth <= widthSize) { widthSize = newWidth; done = true; - } + } } - + // Try adjusting height to be proportional to width if (!done && resizeHeight) { int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) + @@ -1033,7 +1031,7 @@ public class ImageView extends View { */ w += pleft + pright; h += ptop + pbottom; - + w = Math.max(w, getSuggestedMinimumWidth()); h = Math.max(h, getSuggestedMinimumHeight()); @@ -1047,8 +1045,8 @@ public class ImageView extends View { private int resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec) { int result = desiredSize; - int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); + final int specMode = MeasureSpec.getMode(measureSpec); + final int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: /* Parent says we can be as big as we want. Just don't be larger @@ -1057,8 +1055,8 @@ public class ImageView extends View { result = Math.min(desiredSize, maxSize); break; case MeasureSpec.AT_MOST: - // Parent says we can be as big as we want, up to specSize. - // Don't be larger than specSize, and don't be larger than + // Parent says we can be as big as we want, up to specSize. + // Don't be larger than specSize, and don't be larger than // the max size imposed on ourselves. result = Math.min(Math.min(desiredSize, specSize), maxSize); break; @@ -1072,7 +1070,7 @@ public class ImageView extends View { @Override protected boolean setFrame(int l, int t, int r, int b) { - boolean changed = super.setFrame(l, t, r, b); + final boolean changed = super.setFrame(l, t, r, b); mHaveFrame = true; configureBounds(); return changed; @@ -1083,14 +1081,14 @@ public class ImageView extends View { return; } - int dwidth = mDrawableWidth; - int dheight = mDrawableHeight; + final int dwidth = mDrawableWidth; + final int dheight = mDrawableHeight; - int vwidth = getWidth() - mPaddingLeft - mPaddingRight; - int vheight = getHeight() - mPaddingTop - mPaddingBottom; + final int vwidth = getWidth() - mPaddingLeft - mPaddingRight; + final int vheight = getHeight() - mPaddingTop - mPaddingBottom; - boolean fits = (dwidth < 0 || vwidth == dwidth) && - (dheight < 0 || vheight == dheight); + final boolean fits = (dwidth < 0 || vwidth == dwidth) + && (dheight < 0 || vheight == dheight); if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) { /* If the drawable has no intrinsic size, or we're told to @@ -1125,7 +1123,7 @@ public class ImageView extends View { float dx = 0, dy = 0; if (dwidth * vheight > vwidth * dheight) { - scale = (float) vheight / (float) dheight; + scale = (float) vheight / (float) dheight; dx = (vwidth - dwidth * scale) * 0.5f; } else { scale = (float) vwidth / (float) dwidth; @@ -1139,14 +1137,14 @@ public class ImageView extends View { float scale; float dx; float dy; - + if (dwidth <= vwidth && dheight <= vheight) { scale = 1.0f; } else { scale = Math.min((float) vwidth / (float) dwidth, (float) vheight / (float) dheight); } - + dx = Math.round((vwidth - dwidth * scale) * 0.5f); dy = Math.round((vheight - dheight * scale) * 0.5f); @@ -1156,7 +1154,7 @@ public class ImageView extends View { // Generate the required transform. mTempSrc.set(0, 0, dwidth, dheight); mTempDst.set(0, 0, vwidth, vheight); - + mDrawMatrix = mMatrix; mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType)); } @@ -1166,7 +1164,8 @@ public class ImageView extends View { @Override protected void drawableStateChanged() { super.drawableStateChanged(); - Drawable d = mDrawable; + + final Drawable d = mDrawable; if (d != null && d.isStateful()) { d.setState(getDrawableState()); } @@ -1213,9 +1212,9 @@ public class ImageView extends View { if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) { mDrawable.draw(canvas); } else { - int saveCount = canvas.getSaveCount(); + final int saveCount = canvas.getSaveCount(); canvas.save(); - + if (mCropToPadding) { final int scrollX = mScrollX; final int scrollY = mScrollY; @@ -1223,7 +1222,7 @@ public class ImageView extends View { scrollX + mRight - mLeft - mPaddingRight, scrollY + mBottom - mTop - mPaddingBottom); } - + canvas.translate(mPaddingLeft, mPaddingTop); if (mDrawMatrix != null) { @@ -1258,7 +1257,7 @@ public class ImageView extends View { * * @param baseline The baseline to use, or -1 if none is to be provided. * - * @see #setBaseline(int) + * @see #setBaseline(int) * @attr ref android.R.styleable#ImageView_baseline */ public void setBaseline(int baseline) { @@ -1295,11 +1294,11 @@ public class ImageView extends View { /** * Set a tinting option for the image. - * + * * @param color Color tint to apply. * @param mode How to apply the color. The standard mode is * {@link PorterDuff.Mode#SRC_ATOP} - * + * * @attr ref android.R.styleable#ImageView_tint */ public final void setColorFilter(int color, PorterDuff.Mode mode) { diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 088adbb04803..ad2b4a714b82 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -816,7 +816,15 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { mSearchButton.setVisibility(visCollapsed); updateSubmitButton(hasText); mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE); - mCollapsedIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE); + + final int iconVisibility; + if (mCollapsedIcon.getDrawable() == null || mIconifiedByDefault) { + iconVisibility = GONE; + } else { + iconVisibility = VISIBLE; + } + mCollapsedIcon.setVisibility(iconVisibility); + updateCloseButton(); updateVoiceButton(!hasText); updateSubmitArea(); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index d9faece9485f..5fc8c7a12b26 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -220,7 +220,7 @@ public class ChooserActivity extends ResolverActivity { result.putExtras(replExtras); } } - if (aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER) + if (aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_PARENT) || aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE)) { result = Intent.createChooser(result, getIntent().getCharSequenceExtra(Intent.EXTRA_TITLE)); diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 39b66aa24a82..dbec7405fe49 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -45,8 +45,8 @@ public class IntentForwarderActivity extends Activity { public static String TAG = "IntentForwarderActivity"; - public static String FORWARD_INTENT_TO_USER_OWNER - = "com.android.internal.app.ForwardIntentToUserOwner"; + public static String FORWARD_INTENT_TO_PARENT + = "com.android.internal.app.ForwardIntentToParent"; public static String FORWARD_INTENT_TO_MANAGED_PROFILE = "com.android.internal.app.ForwardIntentToManagedProfile"; @@ -60,9 +60,9 @@ public class IntentForwarderActivity extends Activity { final int targetUserId; final int userMessageId; - if (className.equals(FORWARD_INTENT_TO_USER_OWNER)) { + if (className.equals(FORWARD_INTENT_TO_PARENT)) { userMessageId = com.android.internal.R.string.forward_intent_to_owner; - targetUserId = UserHandle.USER_OWNER; + targetUserId = getProfileParent(); } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { userMessageId = com.android.internal.R.string.forward_intent_to_work; targetUserId = getManagedProfile(); @@ -72,7 +72,7 @@ public class IntentForwarderActivity extends Activity { targetUserId = UserHandle.USER_NULL; } if (targetUserId == UserHandle.USER_NULL) { - // This covers the case where there is no managed profile. + // This covers the case where there is no parent / managed profile. finish(); return; } @@ -168,7 +168,7 @@ public class IntentForwarderActivity extends Activity { */ private int getManagedProfile() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); - List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.USER_OWNER); + List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.myUserId()); for (UserInfo userInfo : relatedUsers) { if (userInfo.isManagedProfile()) return userInfo.id; } @@ -176,4 +176,19 @@ public class IntentForwarderActivity extends Activity { + " has been called, but there is no managed profile"); return UserHandle.USER_NULL; } + + /** + * Returns the userId of the profile parent or UserHandle.USER_NULL if there is + * no parent. + */ + private int getProfileParent() { + UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); + UserInfo parent = userManager.getProfileParent(UserHandle.myUserId()); + if (parent == null) { + Slog.wtf(TAG, FORWARD_INTENT_TO_PARENT + + " has been called, but there is no parent"); + return UserHandle.USER_NULL; + } + return parent.id; + } } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index be26c2479d92..55e23b14b7d0 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.FIRST_DYNAMIC_STACK_ID; import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.HOME_STACK_ID; +import static android.app.ActivityManager.INVALID_STACK_ID; import static android.view.View.MeasureSpec.AT_MOST; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.getMode; @@ -5250,7 +5251,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @return Returns the workspace stack id which contains this window. **/ private int getWorkspaceId() { - int workspaceId = FULLSCREEN_WORKSPACE_STACK_ID; + int workspaceId = INVALID_STACK_ID; WindowControllerCallback callback = getWindowControllerCallback(); if (callback != null) { try { @@ -5259,6 +5260,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { Log.e(TAG, "Failed to get the workspace ID of a PhoneWindow."); } } + if (workspaceId == INVALID_STACK_ID) { + return FULLSCREEN_WORKSPACE_STACK_ID; + } return workspaceId; } diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index c1645c330445..1e41e6396f6a 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -46,6 +46,8 @@ oneway interface IStatusBar void cancelPreloadRecentApps(); void showScreenPinningRequest(); + void showKeyboardShortcutsMenu(); + /** * Notifies the status bar that an app transition is pending to delay applying some flags with * visual impact until {@link #appTransitionReady} is called. diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 7db2cc9f0f18..6c957becc5e5 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -68,6 +68,8 @@ interface IStatusBarService void preloadRecentApps(); void cancelPreloadRecentApps(); + void showKeyboardShortcutsMenu(); + /** * Notifies the status bar that an app transition is pending to delay applying some flags with * visual impact until {@link #appTransitionReady} is called. diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index dfb7c50ab503..4e4552d33288 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -16,6 +16,7 @@ package com.android.internal.widget; +import android.app.trust.IStrongAuthTracker; import com.android.internal.widget.VerifyCredentialResponse; /** {@hide} */ @@ -35,4 +36,7 @@ interface ILockSettings { boolean checkVoldPassword(int userId); boolean havePattern(int userId); boolean havePassword(int userId); + void registerStrongAuthTracker(in IStrongAuthTracker tracker); + void unregisterStrongAuthTracker(in IStrongAuthTracker tracker); + void requireStrongAuth(int strongAuthReason, int userId); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index ca184d1b3d13..199569402838 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -16,19 +16,19 @@ package com.android.internal.widget; -import android.Manifest; +import android.annotation.IntDef; import android.app.ActivityManager; -import android.app.ActivityManagerNative; import android.app.admin.DevicePolicyManager; +import android.app.trust.IStrongAuthTracker; import android.app.trust.TrustManager; -import android.bluetooth.BluetoothClass; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; -import android.content.pm.PackageManager; import android.os.AsyncTask; +import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -39,9 +39,12 @@ import android.os.storage.StorageManager; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import android.util.SparseIntArray; import com.google.android.collect.Lists; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -229,7 +232,7 @@ public class LockPatternUtils { public void reportFailedPasswordAttempt(int userId) { getDevicePolicyManager().reportFailedPasswordAttempt(userId); getTrustManager().reportUnlockAttempt(false /* authenticated */, userId); - getTrustManager().reportRequireCredentialEntry(userId); + requireCredentialEntry(userId); } public void reportSuccessfulPasswordAttempt(int userId) { @@ -1168,10 +1171,32 @@ public class LockPatternUtils { } /** - * @see android.app.trust.TrustManager#reportRequireCredentialEntry(int) + * Disable trust until credentials have been entered for user {@param userId}. + * + * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + * + * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL} */ public void requireCredentialEntry(int userId) { - getTrustManager().reportRequireCredentialEntry(userId); + requireStrongAuth(StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId); + } + + /** + * Requests strong authentication for user {@param userId}. + * + * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + * + * @param strongAuthReason a combination of {@link StrongAuthTracker.StrongAuthFlags} indicating + * the reason for and the strength of the requested authentication. + * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL} + */ + public void requireStrongAuth(@StrongAuthTracker.StrongAuthFlags int strongAuthReason, + int userId) { + try { + getLockSettings().requireStrongAuth(strongAuthReason, userId); + } catch (RemoteException e) { + Log.e(TAG, "Error while requesting strong auth: " + e); + } } private void onAfterChangingPassword(int userHandle) { @@ -1209,4 +1234,147 @@ public class LockPatternUtils { throw new IllegalStateException("should not be called from the main thread."); } } + + public void registerStrongAuthTracker(final StrongAuthTracker strongAuthTracker) { + try { + getLockSettings().registerStrongAuthTracker(strongAuthTracker.mStub); + } catch (RemoteException e) { + throw new RuntimeException("Could not register StrongAuthTracker"); + } + } + + public void unregisterStrongAuthTracker(final StrongAuthTracker strongAuthTracker) { + try { + getLockSettings().unregisterStrongAuthTracker(strongAuthTracker.mStub); + } catch (RemoteException e) { + Log.e(TAG, "Could not unregister StrongAuthTracker", e); + } + } + + /** + * Tracks the global strong authentication state. + */ + public static class StrongAuthTracker { + + @IntDef(flag = true, + value = { STRONG_AUTH_NOT_REQUIRED, + STRONG_AUTH_REQUIRED_AFTER_BOOT, + STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, + SOME_AUTH_REQUIRED_AFTER_USER_REQUEST}) + @Retention(RetentionPolicy.SOURCE) + public @interface StrongAuthFlags {} + + /** + * Strong authentication is not required. + */ + public static final int STRONG_AUTH_NOT_REQUIRED = 0x0; + + /** + * Strong authentication is required because the user has not authenticated since boot. + */ + public static final int STRONG_AUTH_REQUIRED_AFTER_BOOT = 0x1; + + /** + * Strong authentication is required because a device admin has requested it. + */ + public static final int STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW = 0x2; + + /** + * Some authentication is required because the user has temporarily disabled trust. + */ + public static final int SOME_AUTH_REQUIRED_AFTER_USER_REQUEST = 0x4; + + public static final int DEFAULT = STRONG_AUTH_REQUIRED_AFTER_BOOT; + private static final int ALLOWING_FINGERPRINT = SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; + + final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray(); + + private final H mHandler; + + public StrongAuthTracker() { + this(Looper.myLooper()); + } + + /** + * @param looper the looper on whose thread calls to {@link #onStrongAuthRequiredChanged} + * will be scheduled. + */ + public StrongAuthTracker(Looper looper) { + mHandler = new H(looper); + } + + /** + * Returns {@link #STRONG_AUTH_NOT_REQUIRED} if strong authentication is not required, + * otherwise returns a combination of {@link StrongAuthFlags} indicating why strong + * authentication is required. + * + * @param userId the user for whom the state is queried. + */ + public @StrongAuthFlags int getStrongAuthForUser(int userId) { + return mStrongAuthRequiredForUser.get(userId, DEFAULT); + } + + /** + * @return true if unlocking with trust alone is allowed for {@param userId} by the current + * strong authentication requirements. + */ + public boolean isTrustAllowedForUser(int userId) { + return getStrongAuthForUser(userId) == STRONG_AUTH_NOT_REQUIRED; + } + + /** + * @return true if unlocking with fingerprint alone is allowed for {@param userId} by the + * current strong authentication requirements. + */ + public boolean isFingerprintAllowedForUser(int userId) { + return (getStrongAuthForUser(userId) & ~ALLOWING_FINGERPRINT) == 0; + } + + /** + * Called when the strong authentication requirements for {@param userId} changed. + */ + public void onStrongAuthRequiredChanged(int userId) { + } + + void handleStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags, + int userId) { + + int oldValue = getStrongAuthForUser(userId); + if (strongAuthFlags != oldValue) { + if (strongAuthFlags == DEFAULT) { + mStrongAuthRequiredForUser.delete(userId); + } else { + mStrongAuthRequiredForUser.put(userId, strongAuthFlags); + } + onStrongAuthRequiredChanged(userId); + } + } + + + final IStrongAuthTracker.Stub mStub = new IStrongAuthTracker.Stub() { + @Override + public void onStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags, + int userId) { + mHandler.obtainMessage(H.MSG_ON_STRONG_AUTH_REQUIRED_CHANGED, + strongAuthFlags, userId).sendToTarget(); + } + }; + + private class H extends Handler { + static final int MSG_ON_STRONG_AUTH_REQUIRED_CHANGED = 1; + + public H(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_ON_STRONG_AUTH_REQUIRED_CHANGED: + handleStrongAuthRequiredChanged(msg.arg1, msg.arg2); + break; + } + } + }; + } } diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index c66cdfec7d1c..988d13aa7ea8 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -112,15 +112,15 @@ public: } static jlong init(JNIEnv* env, jobject clazz) { - SK_COMPILE_ASSERT(1 << 0 == SkPaint::kAntiAlias_Flag, paint_flags_mismatch); - SK_COMPILE_ASSERT(1 << 2 == SkPaint::kDither_Flag, paint_flags_mismatch); - SK_COMPILE_ASSERT(1 << 3 == SkPaint::kUnderlineText_Flag, paint_flags_mismatch); - SK_COMPILE_ASSERT(1 << 4 == SkPaint::kStrikeThruText_Flag, paint_flags_mismatch); - SK_COMPILE_ASSERT(1 << 5 == SkPaint::kFakeBoldText_Flag, paint_flags_mismatch); - SK_COMPILE_ASSERT(1 << 6 == SkPaint::kLinearText_Flag, paint_flags_mismatch); - SK_COMPILE_ASSERT(1 << 7 == SkPaint::kSubpixelText_Flag, paint_flags_mismatch); - SK_COMPILE_ASSERT(1 << 8 == SkPaint::kDevKernText_Flag, paint_flags_mismatch); - SK_COMPILE_ASSERT(1 << 10 == SkPaint::kEmbeddedBitmapText_Flag, paint_flags_mismatch); + static_assert(1 << 0 == SkPaint::kAntiAlias_Flag, "paint_flags_mismatch"); + static_assert(1 << 2 == SkPaint::kDither_Flag, "paint_flags_mismatch"); + static_assert(1 << 3 == SkPaint::kUnderlineText_Flag, "paint_flags_mismatch"); + static_assert(1 << 4 == SkPaint::kStrikeThruText_Flag, "paint_flags_mismatch"); + static_assert(1 << 5 == SkPaint::kFakeBoldText_Flag, "paint_flags_mismatch"); + static_assert(1 << 6 == SkPaint::kLinearText_Flag, "paint_flags_mismatch"); + static_assert(1 << 7 == SkPaint::kSubpixelText_Flag, "paint_flags_mismatch"); + static_assert(1 << 8 == SkPaint::kDevKernText_Flag, "paint_flags_mismatch"); + static_assert(1 << 10 == SkPaint::kEmbeddedBitmapText_Flag, "paint_flags_mismatch"); Paint* obj = new Paint(); defaultSettingsForAndroid(obj); diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp index f7b5dc24cea1..dbd7c89805ab 100644 --- a/core/jni/android/graphics/Path.cpp +++ b/core/jni/android/graphics/Path.cpp @@ -519,6 +519,9 @@ static JNINativeMethod methods[] = { int register_android_graphics_Path(JNIEnv* env) { return RegisterMethodsOrDie(env, "android/graphics/Path", methods, NELEM(methods)); + + static_assert(0 == SkPath::kCW_Direction, "direction_mismatch"); + static_assert(1 == SkPath::kCCW_Direction, "direction_mismatch"); } } diff --git a/core/jni/android/graphics/PorterDuff.cpp b/core/jni/android/graphics/PorterDuff.cpp index d65864df2363..fed90a5d2f83 100644 --- a/core/jni/android/graphics/PorterDuff.cpp +++ b/core/jni/android/graphics/PorterDuff.cpp @@ -33,29 +33,29 @@ public: static jlong CreateXfermode(JNIEnv* env, jobject, jint modeHandle) { // validate that the Java enum values match our expectations - SK_COMPILE_ASSERT(0 == SkXfermode::kClear_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(1 == SkXfermode::kSrc_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(2 == SkXfermode::kDst_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(3 == SkXfermode::kSrcOver_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(4 == SkXfermode::kDstOver_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(5 == SkXfermode::kSrcIn_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(6 == SkXfermode::kDstIn_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(7 == SkXfermode::kSrcOut_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(8 == SkXfermode::kDstOut_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(9 == SkXfermode::kSrcATop_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(10 == SkXfermode::kDstATop_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(11 == SkXfermode::kXor_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(16 == SkXfermode::kDarken_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(17 == SkXfermode::kLighten_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(13 == SkXfermode::kModulate_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(14 == SkXfermode::kScreen_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(12 == SkXfermode::kPlus_Mode, xfermode_mismatch); - SK_COMPILE_ASSERT(15 == SkXfermode::kOverlay_Mode, xfermode_mismatch); - + static_assert(0 == SkXfermode::kClear_Mode, "xfermode_mismatch"); + static_assert(1 == SkXfermode::kSrc_Mode, "xfermode_mismatch"); + static_assert(2 == SkXfermode::kDst_Mode, "xfermode_mismatch"); + static_assert(3 == SkXfermode::kSrcOver_Mode, "xfermode_mismatch"); + static_assert(4 == SkXfermode::kDstOver_Mode, "xfermode_mismatch"); + static_assert(5 == SkXfermode::kSrcIn_Mode, "xfermode_mismatch"); + static_assert(6 == SkXfermode::kDstIn_Mode, "xfermode_mismatch"); + static_assert(7 == SkXfermode::kSrcOut_Mode, "xfermode_mismatch"); + static_assert(8 == SkXfermode::kDstOut_Mode, "xfermode_mismatch"); + static_assert(9 == SkXfermode::kSrcATop_Mode, "xfermode_mismatch"); + static_assert(10 == SkXfermode::kDstATop_Mode, "xfermode_mismatch"); + static_assert(11 == SkXfermode::kXor_Mode, "xfermode_mismatch"); + static_assert(16 == SkXfermode::kDarken_Mode, "xfermode_mismatch"); + static_assert(17 == SkXfermode::kLighten_Mode, "xfermode_mismatch"); + static_assert(13 == SkXfermode::kModulate_Mode, "xfermode_mismatch"); + static_assert(14 == SkXfermode::kScreen_Mode, "xfermode_mismatch"); + static_assert(12 == SkXfermode::kPlus_Mode, "xfermode_mismatch"); + static_assert(15 == SkXfermode::kOverlay_Mode, "xfermode_mismatch"); + SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(modeHandle); return reinterpret_cast<jlong>(SkXfermode::Create(mode)); } - + }; static JNINativeMethod methods[] = { diff --git a/core/jni/android_media_AudioErrors.h b/core/jni/android_media_AudioErrors.h index 49078300e647..c17a020f74fc 100644 --- a/core/jni/android_media_AudioErrors.h +++ b/core/jni/android_media_AudioErrors.h @@ -32,6 +32,7 @@ enum { AUDIO_JAVA_PERMISSION_DENIED = -4, AUDIO_JAVA_NO_INIT = -5, AUDIO_JAVA_DEAD_OBJECT = -6, + AUDIO_JAVA_WOULD_BLOCK = -7, }; static inline jint nativeToJavaStatus(status_t status) { @@ -46,6 +47,8 @@ static inline jint nativeToJavaStatus(status_t status) { return AUDIO_JAVA_PERMISSION_DENIED; case NO_INIT: return AUDIO_JAVA_NO_INIT; + case WOULD_BLOCK: + return AUDIO_JAVA_WOULD_BLOCK; case DEAD_OBJECT: return AUDIO_JAVA_DEAD_OBJECT; default: diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp index 3833325631c7..460c1a1a2ab9 100644 --- a/core/jni/android_view_DisplayListCanvas.cpp +++ b/core/jni/android_view_DisplayListCanvas.cpp @@ -40,18 +40,14 @@ namespace android { using namespace uirenderer; -static struct { - jmethodID set; -} gRectClassInfo; - // ---------------------------------------------------------------------------- // Setup // ---------------------------------------------------------------------------- static void android_view_DisplayListCanvas_insertReorderBarrier(JNIEnv* env, jobject clazz, - jlong rendererPtr, jboolean reorderEnable) { - DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr); - renderer->insertReorderBarrier(reorderEnable); + jlong canvasPtr, jboolean reorderEnable) { + DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr); + canvas->insertReorderBarrier(reorderEnable); } // ---------------------------------------------------------------------------- @@ -59,10 +55,10 @@ static void android_view_DisplayListCanvas_insertReorderBarrier(JNIEnv* env, job // ---------------------------------------------------------------------------- static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz, - jlong rendererPtr, jlong functorPtr) { - DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr); + jlong canvasPtr, jlong functorPtr) { + DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr); Functor* functor = reinterpret_cast<Functor*>(functorPtr); - renderer->callDrawGLFunction(functor); + canvas->callDrawGLFunction(functor); } // ---------------------------------------------------------------------------- @@ -82,9 +78,9 @@ static jint android_view_DisplayListCanvas_getMaxTextureHeight(JNIEnv* env, jobj // ---------------------------------------------------------------------------- static void android_view_DisplayListCanvas_drawRoundRectProps(JNIEnv* env, jobject clazz, - jlong rendererPtr, jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr, + jlong canvasPtr, jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr, jlong bottomPropPtr, jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) { - DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr); + DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr); CanvasPropertyPrimitive* leftProp = reinterpret_cast<CanvasPropertyPrimitive*>(leftPropPtr); CanvasPropertyPrimitive* topProp = reinterpret_cast<CanvasPropertyPrimitive*>(topPropPtr); CanvasPropertyPrimitive* rightProp = reinterpret_cast<CanvasPropertyPrimitive*>(rightPropPtr); @@ -92,17 +88,17 @@ static void android_view_DisplayListCanvas_drawRoundRectProps(JNIEnv* env, jobje CanvasPropertyPrimitive* rxProp = reinterpret_cast<CanvasPropertyPrimitive*>(rxPropPtr); CanvasPropertyPrimitive* ryProp = reinterpret_cast<CanvasPropertyPrimitive*>(ryPropPtr); CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr); - renderer->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp); + canvas->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp); } static void android_view_DisplayListCanvas_drawCircleProps(JNIEnv* env, jobject clazz, - jlong rendererPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) { - DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr); + jlong canvasPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) { + DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr); CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr); CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr); CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr); CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr); - renderer->drawCircle(xProp, yProp, radiusProp, paintProp); + canvas->drawCircle(xProp, yProp, radiusProp, paintProp); } // ---------------------------------------------------------------------------- @@ -110,9 +106,9 @@ static void android_view_DisplayListCanvas_drawCircleProps(JNIEnv* env, jobject // ---------------------------------------------------------------------------- static jlong android_view_DisplayListCanvas_finishRecording(JNIEnv* env, - jobject clazz, jlong rendererPtr) { - DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr); - return reinterpret_cast<jlong>(renderer->finishRecording()); + jobject clazz, jlong canvasPtr) { + DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr); + return reinterpret_cast<jlong>(canvas->finishRecording()); } static jlong android_view_DisplayListCanvas_createDisplayListCanvas(JNIEnv* env, jobject clazz, @@ -121,17 +117,17 @@ static jlong android_view_DisplayListCanvas_createDisplayListCanvas(JNIEnv* env, } static void android_view_DisplayListCanvas_resetDisplayListCanvas(JNIEnv* env, jobject clazz, - jlong rendererPtr, jint width, jint height) { - DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr); - renderer->reset(width, height); + jlong canvasPtr, jint width, jint height) { + DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr); + canvas->reset(width, height); } static void android_view_DisplayListCanvas_drawRenderNode(JNIEnv* env, - jobject clazz, jlong rendererPtr, jlong renderNodePtr) { - DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr); + jobject clazz, jlong canvasPtr, jlong renderNodePtr) { + DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr); RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); - renderer->drawRenderNode(renderNode); + canvas->drawRenderNode(renderNode); } // ---------------------------------------------------------------------------- @@ -139,10 +135,10 @@ static void android_view_DisplayListCanvas_drawRenderNode(JNIEnv* env, // ---------------------------------------------------------------------------- static void android_view_DisplayListCanvas_drawLayer(JNIEnv* env, jobject clazz, - jlong rendererPtr, jlong layerPtr, jfloat x, jfloat y) { - DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr); + jlong canvasPtr, jlong layerPtr) { + DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr); DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); - renderer->drawLayer(layer, x, y); + canvas->drawLayer(layer); } // ---------------------------------------------------------------------------- @@ -192,7 +188,7 @@ static JNINativeMethod gMethods[] = { { "nCreateDisplayListCanvas", "(II)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas }, { "nResetDisplayListCanvas", "(JII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas }, - { "nDrawLayer", "(JJFF)V", (void*) android_view_DisplayListCanvas_drawLayer }, + { "nDrawLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawLayer }, { "nGetMaximumTextureWidth", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureWidth }, { "nGetMaximumTextureHeight", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureHeight }, @@ -204,9 +200,6 @@ static JNINativeMethod gActivityThreadMethods[] = { }; int register_android_view_DisplayListCanvas(JNIEnv* env) { - jclass clazz = FindClassOrDie(env, "android/graphics/Rect"); - gRectClassInfo.set = GetMethodIDOrDie(env, clazz, "set", "(IIII)V"); - return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp index 9e49afb45790..36ba892b51c4 100644 --- a/core/jni/android_view_HardwareLayer.cpp +++ b/core/jni/android_view_HardwareLayer.cpp @@ -79,12 +79,6 @@ static void android_view_HardwareLayer_updateSurfaceTexture(JNIEnv* env, jobject layer->updateTexImage(); } -static jint android_view_HardwareLayer_getTexName(JNIEnv* env, jobject clazz, - jlong layerUpdaterPtr) { - DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); - return layer->backingLayer()->getTextureId(); -} - // ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- @@ -98,8 +92,6 @@ static JNINativeMethod gMethods[] = { { "nSetSurfaceTexture", "(JLandroid/graphics/SurfaceTexture;Z)V", (void*) android_view_HardwareLayer_setSurfaceTexture }, { "nUpdateSurfaceTexture", "(J)V", (void*) android_view_HardwareLayer_updateSurfaceTexture }, - - { "nGetTexName", "(J)I", (void*) android_view_HardwareLayer_getTexName }, }; int register_android_view_HardwareLayer(JNIEnv* env) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d591f8b10f85..fefd5a7abba3 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -786,6 +786,46 @@ android:protectionLevel="normal" android:permissionFlags="hidden"/> + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.WRITE_SMS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.MANAGE_ACCOUNTS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.USE_CREDENTIALS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.SUBSCRIBED_FEEDS_READ" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + <!-- ====================================================================== --> <!-- INSTALL PERMISSIONS --> <!-- ====================================================================== --> @@ -2658,7 +2698,7 @@ android:exported="true" > </activity> - <activity-alias android:name="com.android.internal.app.ForwardIntentToUserOwner" + <activity-alias android:name="com.android.internal.app.ForwardIntentToParent" android:targetActivity="com.android.internal.app.IntentForwarderActivity" android:exported="true" android:label="@string/user_owner_label"> diff --git a/core/res/res/drawable/scrollbar_handle_material.xml b/core/res/res/drawable/scrollbar_handle_material.xml index 56fececcc2c2..33efbbac8690 100644 --- a/core/res/res/drawable/scrollbar_handle_material.xml +++ b/core/res/res/drawable/scrollbar_handle_material.xml @@ -17,6 +17,9 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:tint="?attr/colorControlNormal" android:shape="rectangle"> - <solid android:color="#84ffffff" /> - <size android:width="4dp" /> + <solid + android:color="#84ffffff" /> + <size + android:width="4dp" + android:height="4dp" /> </shape> diff --git a/core/res/res/values-mcc219-mnc02/config.xml b/core/res/res/values-mcc219-mnc02/config.xml new file mode 100644 index 000000000000..2ac6ba6ddf83 --- /dev/null +++ b/core/res/res/values-mcc219-mnc02/config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You my obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Don't use roaming icon for considered operators --> + <string-array translatable="false" name="config_operatorConsideredNonRoaming"> + <item>21901</item> + </string-array> +</resources> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index cfdd69ba7c09..d77dca1fe147 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -39,7 +39,7 @@ <string name="durationMinuteSecond" msgid="3989228718067466680">"<xliff:g id="MINUTES">%1$d</xliff:g> min <xliff:g id="SECONDS">%2$d</xliff:g> sec"</string> <string name="durationSeconds" msgid="8050088505238241405">"<xliff:g id="SECONDS">%1$d</xliff:g> seconden"</string> <string name="durationSecond" msgid="985669622276420331">"<xliff:g id="SECONDS">%1$d</xliff:g> seconde"</string> - <string name="untitled" msgid="4638956954852782576">"<Zonder titel>"</string> + <string name="untitled" msgid="4638956954852782576">"<Naamloos>"</string> <string name="emptyPhoneNumber" msgid="7694063042079676517">"(Geen telefoonnummer)"</string> <string name="unknownName" msgid="6867811765370350269">"Onbekend"</string> <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Voicemail"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 2cf1d1e41f3b..d215d4c3d7a7 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -50,7 +50,7 @@ <string name="serviceEnabledFor" msgid="6856228140453471041">"Serviciul a fost activat pentru:"</string> <string name="serviceDisabled" msgid="1937553226592516411">"Serviciul a fost dezactivat."</string> <string name="serviceRegistered" msgid="6275019082598102493">"Înregistrarea a reuşit."</string> - <string name="serviceErased" msgid="1288584695297200972">"Ștergerea a reuşit."</string> + <string name="serviceErased" msgid="1288584695297200972">"Ștergerea a reușit."</string> <string name="passwordIncorrect" msgid="7612208839450128715">"Parolă incorectă."</string> <string name="mmiComplete" msgid="8232527495411698359">"MMI finalizat."</string> <string name="badPin" msgid="9015277645546710014">"Codul PIN vechi introdus nu este corect."</string> @@ -59,7 +59,7 @@ <string name="invalidPin" msgid="3850018445187475377">"Introduceţi un cod PIN alcătuit din 4 până la 8 cifre."</string> <string name="invalidPuk" msgid="8761456210898036513">"Introduceţi un cod PUK care să aibă 8 cifre sau mai mult."</string> <string name="needPuk" msgid="919668385956251611">"Cardul SIM este blocat cu codul PUK. Introduceţi codul PUK pentru a-l debloca."</string> - <string name="needPuk2" msgid="4526033371987193070">"Introduceţi codul PUK2 pentru a debloca cardul SIM."</string> + <string name="needPuk2" msgid="4526033371987193070">"Introduceți codul PUK2 pentru a debloca cardul SIM."</string> <string name="enablePin" msgid="209412020907207950">"Operațiunea nu a reușit. Activați blocarea cardului SIM/RUIM."</string> <plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582"> <item quantity="few">V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări până la blocarea cardului SIM.</item> @@ -85,8 +85,8 @@ <string name="DndMmi" msgid="1265478932418334331">"Nu deranjaţi"</string> <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"ID-ul apelantului este restricţionat în mod prestabilit. Apelul următor: restricţionat"</string> <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"ID-ul apelantului este restricționat în mod prestabilit. Apelul următor: nerestricționat"</string> - <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"ID-ul apelantului este nerestricţionat în mod prestabilit. Apelul următor: Restricţionat."</string> - <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"ID-ul apelantului este nerestricţionat în mod prestabilit. Apelul următor: nerestricţionat"</string> + <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: Restricționat."</string> + <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: nerestricționat"</string> <string name="serviceNotProvisioned" msgid="8614830180508686666">"Nu se asigură accesul la acest serviciu."</string> <string name="CLIRPermanent" msgid="3377371145926835671">"Nu puteți să modificaţi setarea pentru ID-ul apelantului."</string> <string name="RestrictedChangedTitle" msgid="5592189398956187498">"Acces restricționat modificat"</string> @@ -181,7 +181,7 @@ <string name="power_dialog" product="tv" msgid="6153888706430556356">"Opțiuni TV"</string> <string name="power_dialog" product="default" msgid="1319919075463988638">"Opţiuni telefon"</string> <string name="silent_mode" msgid="7167703389802618663">"Mod Silențios"</string> - <string name="turn_on_radio" msgid="3912793092339962371">"Activați funcţia wireless"</string> + <string name="turn_on_radio" msgid="3912793092339962371">"Activați funcția wireless"</string> <string name="turn_off_radio" msgid="8198784949987062346">"Dezactivați funcția wireless"</string> <string name="screen_lock" msgid="799094655496098153">"Blocați ecranul"</string> <string name="power_off" msgid="4266614107412865048">"Opriți alimentarea"</string> @@ -266,9 +266,9 @@ <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"redirecţionează apelurile efectuate"</string> <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permite aplicației să vadă numărul format în timpul unui apel de ieșire, cu opțiunea de a redirecționa apelul către un alt număr sau de a întrerupe apelul."</string> <string name="permlab_receiveSms" msgid="8673471768947895082">"primeşte mesaje text (SMS)"</string> - <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite aplicației să primească și să proceseze mesaje SMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau şterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string> + <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite aplicației să primească și să proceseze mesaje SMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string> <string name="permlab_receiveMms" msgid="1821317344668257098">"primeşte mesaje text (MMS)"</string> - <string name="permdesc_receiveMms" msgid="533019437263212260">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau şterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string> + <string name="permdesc_receiveMms" msgid="533019437263212260">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string> <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"citeşte mesajele cu transmisie celulară"</string> <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situaţiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanţa sau funcţionarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă."</string> <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"citire feeduri abonat"</string> @@ -280,7 +280,7 @@ <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Permite aplicației să citească mesajele SMS stocate pe televizor sau pe cardul SIM. Cu această permisiune, aplicația poate citi toate mesajele SMS, indiferent de conținutul sau de gradul de confidențialitate al acestora."</string> <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Permite aplicației să citească mesajele SMS stocate pe telefon sau pe cardul SIM. În acest fel, aplicația poate citi toate mesajele SMS, indiferent de conţinutul sau de gradul de confidenţialitate al acestora."</string> <string name="permlab_receiveWapPush" msgid="5991398711936590410">"primeşte mesaje text (WAP)"</string> - <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permite aplicației să primească și să proceseze mesaje WAP. Această permisiune include capacitatea de a monitoriza sau şterge mesajele care v-au fost trimise fără a vi le arăta."</string> + <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permite aplicației să primească și să proceseze mesaje WAP. Această permisiune include capacitatea de a monitoriza sau șterge mesajele care v-au fost trimise fără a vi le arăta."</string> <string name="permlab_getTasks" msgid="6466095396623933906">"preluare aplicații care rulează"</string> <string name="permdesc_getTasks" msgid="7454215995847658102">"Permite aplicației să preia informațiile despre activităţile care rulează în prezent și care au rulat recent. În acest fel, aplicația poate descoperi informații despre aplicațiile care sunt utilizate pe dispozitiv."</string> <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Gestionează proprietarii de profiluri și proprietarul dispozitivului"</string> @@ -314,17 +314,17 @@ <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Permite aplicației să citească datele despre persoanele de contact salvate pe televizor, inclusiv frecvența cu care ați apelat, ați trimis e-mailuri sau ați comunicat în alte moduri cu anumite persoane. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău-intenționate pot permite accesul la datele de contact fără cunoștința dvs."</string> <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Permite aplicației să citească datele despre persoanele din agenda stocată pe telefon, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără ştirea dvs."</string> <string name="permlab_writeContacts" msgid="5107492086416793544">"modifică agenda"</string> - <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate şterge datele de contact."</string> + <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string> <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Permite aplicației să modifice datele despre persoanele de contact salvate pe televizor, inclusiv frecvența cu care ați apelat, ați trimis e-mailuri sau ați comunicat în alte moduri cu anumite persoane de contact. Cu această permisiune, aplicația poate șterge datele de contact."</string> - <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe telefon, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate şterge datele de contact."</string> + <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe telefon, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"citeşte jurnalul de apeluri"</string> <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Permite aplicației să citească jurnalul de apeluri al tabletei, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără ştirea dvs."</string> <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Permite aplicației să citească jurnalul de apeluri al televizorului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune, aplicațiile pot să salveze datele din jurnalul de apeluri, iar aplicațiile rău-intenționate pot permite accesul la datele din jurnalul de apeluri fără cunoștința dvs."</string> <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Permite aplicației să citească jurnalul de apeluri al telefonului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără ştirea dvs."</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"scrie jurnalul de apeluri"</string> - <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite aplicației să modifice jurnalul de apeluri al tabletei dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a şterge sau pentru a modifica jurnalul dvs. de apeluri."</string> + <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite aplicației să modifice jurnalul de apeluri al tabletei dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string> <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite aplicației să modifice jurnalul de apeluri al televizorului, inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău-intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul de apeluri."</string> - <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite aplicației să modifice jurnalul de apeluri al telefonului dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a şterge sau pentru a modifica jurnalul dvs. de apeluri."</string> + <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Permite aplicației să modifice jurnalul de apeluri al telefonului dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string> <string name="permlab_bodySensors" msgid="4871091374767171066">"senzori (ex.: senzori de ritm cardiac)"</string> <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Permite aplicației să acceseze date de la senzorii care vă monitorizează starea fizică, cum ar fi ritmul cardiac."</string> <string name="permlab_readCalendar" msgid="5972727560257612398">"citirea evenimentelor din calendar și a informaţiilor confidenţiale"</string> @@ -335,7 +335,7 @@ <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe tabletă, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără ştirea proprietarilor."</string> <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe televizor, inclusiv pe cele ale prietenilor sau ale colegilor. Cu această permisiune, aplicația poate să trimită mesaje care par că vin din partea proprietarilor calendarului sau să modifice evenimentele fără cunoștința acestora."</string> <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe telefon, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără ştirea proprietarilor."</string> - <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesare comenzi suplimentare ale furnizorului locaţiei"</string> + <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesare comenzi suplimentare ale furnizorului locației"</string> <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite aplicației să acceseze comenzi suplimentare pentru furnizorul locației. Aplicația ar putea să utilizeze această permisiune pentru a influența operațiile GPS sau ale altor surse de locații."</string> <string name="permlab_accessFineLocation" msgid="1191898061965273372">"locaţia exactă (bazată pe rețea și GPS)"</string> <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permite aplicației să obţină locaţia dvs. exactă utilizând sistemul GPS (Global Positioning System) sau surse de localizare prin rețele, cum ar fi cele prin turn de celule și Wi-Fi. Pentru a fi utilizate de aplicație, aceste servicii de localizare trebuie să fie activate și disponibile pe dispozitivul dvs. Aplicaţiile pot utiliza această permisiune pentru a determina locaţia dvs. și pot să consume mai multă energie a bateriei."</string> @@ -446,8 +446,8 @@ <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"citeşte conţinutul cardului SD"</string> <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Permite aplic. citirea conținutului stoc. USB."</string> <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permite aplicației citirea conținutul cardului SD."</string> - <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modifică sau şterge conţinutul stocării USB"</string> - <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifică sau şterge conţinutul cardului SD"</string> + <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modifică sau șterge conţinutul stocării USB"</string> + <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifică sau șterge conţinutul cardului SD"</string> <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permite scriere în stoc. USB."</string> <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite aplicației să scrie pe cardul SD."</string> <string name="permlab_use_sip" msgid="2052499390128979920">"efectuarea/primirea apelurilor SIP"</string> @@ -650,10 +650,10 @@ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Introduceţi codul PIN pentru a debloca"</string> <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Cod PIN incorect."</string> <string name="keyguard_label_text" msgid="861796461028298424">"Pentru a debloca, apăsaţi Meniu, apoi 0."</string> - <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Număr de urgenţă"</string> + <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Număr de urgență"</string> <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Fără serviciu."</string> <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ecranul este blocat."</string> - <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Apăsaţi Meniu pentru a debloca sau pentru a efectua apeluri de urgenţă."</string> + <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Apăsaţi Meniu pentru deblocare."</string> <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Desenaţi modelul pentru a debloca"</string> <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Urgență"</string> @@ -710,7 +710,7 @@ <string name="lockscreen_sound_on_label" msgid="9068877576513425970">"Sunet activat"</string> <string name="lockscreen_sound_off_label" msgid="996822825154319026">"Sunet dezactivat"</string> <string name="lockscreen_access_pattern_start" msgid="3941045502933142847">"Desenarea modelului a început"</string> - <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Modelul a fost şters"</string> + <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"Modelul a fost șters"</string> <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"Celulă adăugată"</string> <string name="lockscreen_access_pattern_cell_added_verbose" msgid="7264580781744026939">"Celula <xliff:g id="CELL_INDEX">%1$s</xliff:g> a fost adăugată"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"Modelul a fost desenat"</string> @@ -755,7 +755,7 @@ <string name="js_dialog_before_unload_positive_button" msgid="3112752010600484130">"Părăsiți această pagină"</string> <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Rămâneți în această pagină"</string> <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nSigur doriți să părăsiți această pagină?"</string> - <string name="save_password_label" msgid="6860261758665825069">"Confirmaţi"</string> + <string name="save_password_label" msgid="6860261758665825069">"Confirmați"</string> <string name="double_tap_toast" msgid="4595046515400268881">"Sfat: măriţi și micşoraţi prin dublă atingere."</string> <string name="autofill_this_form" msgid="4616758841157816676">"Automat"</string> <string name="setup_autofill" msgid="7103495070180590814">"Conf.Compl.auto."</string> @@ -778,9 +778,9 @@ <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"citeşte marcajele și istoricul web"</string> <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Permite aplicației să citească istoricul tuturor adreselor URL accesate de Browser și toate marcajele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string> <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"scrie în marcajele și în istoricul web"</string> - <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe tabletă. În acest fel, aplicația poate şterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string> + <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe tabletă. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string> <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Permite aplicației să modifice istoricul sau marcajele browserului stocate pe televizor. Cu această permisiune, aplicația poate șterge sau modifica datele din browser. Notă: această permisiune nu poate fi aplicată de browsere terță parte sau de alte aplicații cu capacități de navigare pe web."</string> - <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe telefon. În acest fel, aplicația poate şterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string> + <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe telefon. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string> <string name="permlab_setAlarm" msgid="1379294556362091814">"setează o alarmă"</string> <string name="permdesc_setAlarm" msgid="316392039157473848">"Permite aplicației să seteze o alarmă într-o aplicație de ceas cu alarmă instalată. Este posibil ca unele aplicații de ceas cu alarmă să nu implementeze această funcție."</string> <string name="permlab_addVoicemail" msgid="5525660026090959044">"adăugare mesagerie vocală"</string> @@ -795,7 +795,7 @@ <string name="text_copied" msgid="4985729524670131385">"Text copiat în clipboard."</string> <string name="more_item_label" msgid="4650918923083320495">"Mai multe"</string> <string name="prepend_shortcut_label" msgid="2572214461676015642">"Meniu+"</string> - <string name="menu_space_shortcut_label" msgid="2410328639272162537">"spaţiu"</string> + <string name="menu_space_shortcut_label" msgid="2410328639272162537">"spațiu"</string> <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string> <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"delete"</string> <string name="search_go" msgid="8298016669822141719">"Căutați"</string> @@ -1140,8 +1140,8 @@ <item quantity="one">Un rezultat</item> </plurals> <string name="action_mode_done" msgid="7217581640461922289">"Terminat"</string> - <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Se şterge stocarea USB..."</string> - <string name="progress_erasing" product="default" msgid="6596988875507043042">"Se şterge cardul SD..."</string> + <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Se șterge stocarea USB..."</string> + <string name="progress_erasing" product="default" msgid="6596988875507043042">"Se șterge cardul SD..."</string> <string name="share" msgid="1778686618230011964">"Distribuiţi"</string> <string name="find" msgid="4808270900322985960">"Găsiţi"</string> <string name="websearch" msgid="4337157977400211589">"Căutare pe web"</string> diff --git a/core/res/res/values-watch/strings.xml b/core/res/res/values-watch/strings.xml index 4ea2b52c5b4d..e5991fc111ea 100644 --- a/core/res/res/values-watch/strings.xml +++ b/core/res/res/values-watch/strings.xml @@ -23,4 +23,50 @@ <string name="android_upgrading_apk">App <xliff:g id="number" example="123">%1$d</xliff:g> of <xliff:g id="number" example="123">%2$d</xliff:g>.</string> + + <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. Override from base which says "Body Sensors". [CHAR_LIMIT=25] --> + <string name="permgrouplab_sensors">Sensors</string> + + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permgrouplab_contactswear">access your contacts</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permgrouplab_locationwear">access this watch\'s location</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permgrouplab_calendarwear">access your calendar</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permgrouplab_smswear">send and view SMS messages</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permgrouplab_storagewear">access photos, media, and files on your watch</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permgrouplab_microphonewear">record audio</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permgrouplab_camerawear">take pictures and record video</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permgrouplab_phonewear">make and manage phone calls</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permgrouplab_sensorswear">access sensor data about your vital signs</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_statusBarServicewear">be the status bar</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_bodySensorswear">access body sensors (like heart rate monitors)</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_accessFineLocationwear">access precise location (GPS and network-based)</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_accessCoarseLocationwear">access approximate location (network-based)</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_sim_communicationwear">send commands to the SIM</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_createNetworkSocketswear">have full network access</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_manageProfileAndDeviceOwnerswear">manage profile and device owners</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_changeWimaxStatewear">change WiMAX state</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_handoverStatuswear">receive Android Beam transfer status</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_route_media_outputwear">route media output</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_readInstallSessionswear">read install sessions</string> + <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] --> + <string name="permlab_requestInstallPackageswear">request install packages</string> </resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 1a45b3ac75b3..1a2600d47820 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2195,4 +2195,18 @@ <attr name="name" /> </declare-styleable> + <!-- <code>initial-layout</code> tag allows configuring the initial layout for the activity + within multi-window environment. --> + <declare-styleable name="AndroidManifestInitialLayout" parent="AndroidManifestActivity"> + <!-- Initial width of the activity. Can be either a fixed value or fraction, in which case + the width will be constructed as a fraction of the total available width. --> + <attr name="activityWidth" format="dimension|fraction" /> + <!-- Initial height of the activity. Can be either a fixed value or fraction, in which case + the height will be constructed as a fraction of the total available height. --> + <attr name="activityHeight" format="dimension|fraction" /> + <!-- Where to initially position the activity inside the available space. Uses constants + defined in {@link android.view.Gravity}. --> + <attr name="gravity" /> + </declare-styleable> + </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7acc25dbcd82..2c940ae7a68e 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -619,6 +619,17 @@ <!-- rotation: 270 (rotate CW) --> <item>-25</item> <item>65</item> </integer-array> + <!-- Indicate the name of the window orientation sensor type if present. A + window orientation sensor produces values to be used in lieu of the + typical, accelerometer based sensor. It must only produce integral + values between 0 and 3, inclusive, with each one corresponding to a + given rotation: + 0: 0 degrees of rotation (natural) + 1: 90 degrees of rotation (rotate CCW) + 2: 180 degrees of rotation (reverse) + 3: 270 degrees of rotation (rotate CW) --> + <string name="config_orientationSensorType" translatable="false">@null</string> + <!-- Lid switch behavior --> <!-- The number of degrees to rotate the display when the keyboard is open. diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index cabb56cb006d..d2089cdad73b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2679,5 +2679,7 @@ <public type="style" name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" /> <public type="id" name="accessibilityActionSetProgress" /> + <public type="attr" name="activityWidth" /> + <public type="attr" name="activityHeight" /> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 004407f40c06..a55258c4c2f3 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1545,6 +1545,7 @@ <java-symbol type="string" name="bugreport_title" /> <java-symbol type="string" name="bugreport_message" /> <java-symbol type="string" name="bugreport_status" /> + <java-symbol type="string" name="config_orientationSensorType" /> <java-symbol type="string" name="faceunlock_multiple_failures" /> <java-symbol type="string" name="global_action_power_off" /> <java-symbol type="string" name="global_actions_airplane_mode_off_status" /> diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java new file mode 100644 index 000000000000..03ae9dc3f510 --- /dev/null +++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java @@ -0,0 +1,494 @@ +/* +* Copyright (C) 2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package android.animation; + +import android.os.SystemClock; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.SmallTest; + +import static android.test.MoreAsserts.assertNotEqual; + +public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { + private ValueAnimator a1; + private ValueAnimator a2; + + // Tolerance of error in calculations related to duration, frame time, etc. due to frame delay. + private final static long TOLERANCE = 100; // ms + private final static long POLL_INTERVAL = 100; // ms + + private final static float A1_START_VALUE = 0f; + private final static float A1_END_VALUE = 1f; + private final static int A2_START_VALUE = 100; + private final static int A2_END_VALUE = 200; + + public ValueAnimatorTests() { + super(BasicAnimatorActivity.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + a1 = ValueAnimator.ofFloat(A1_START_VALUE, A1_END_VALUE).setDuration(300); + a2 = ValueAnimator.ofInt(A2_START_VALUE, A2_END_VALUE).setDuration(500); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + a1 = null; + a2 = null; + } + + @SmallTest + public void testStartDelay() throws Throwable { + final ValueAnimator a = ValueAnimator.ofFloat(5f, 20f); + assertEquals(a.getStartDelay(), 0); + final long delay = 200; + a.setStartDelay(delay); + assertEquals(a.getStartDelay(), delay); + + final MyUpdateListener listener = new MyUpdateListener(); + a.addUpdateListener(listener); + final long[] startTime = new long[1]; + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + // Test the time between isRunning() and isStarted() + assertFalse(a.isStarted()); + assertFalse(a.isRunning()); + a.start(); + startTime[0] = SystemClock.uptimeMillis(); + assertTrue(a.isStarted()); + assertFalse(a.isRunning()); + } + }); + + Thread.sleep(a.getTotalDuration()); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertTrue(listener.wasRunning); + assertTrue(listener.firstRunningFrameTime - startTime[0] >= delay); + } + }); + + Thread.sleep(a.getTotalDuration()); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertFalse(a.isStarted()); + } + }); + } + + @SmallTest + public void testListenerCallbacks() throws Throwable { + final MyListener l1 = new MyListener(); + final MyListener l2 = new MyListener(); + a1.addListener(l1); + a2.addListener(l2); + a2.setStartDelay(400); + + assertFalse(l1.startCalled); + assertFalse(l1.cancelCalled); + assertFalse(l1.endCalled); + assertFalse(l2.startCalled); + assertFalse(l2.cancelCalled); + assertFalse(l2.endCalled); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + a1.start(); + a2.start(); + } + }); + + long wait = 0; + Thread.sleep(POLL_INTERVAL); + wait += POLL_INTERVAL; + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertFalse(l1.cancelCalled); + a1.cancel(); + assertTrue(l1.cancelCalled); + assertTrue(l1.endCalled); + } + }); + + while (wait < a2.getStartDelay()) { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + // Make sure a2's start listener isn't called during start delay. + assertTrue(l1.startCalled); + assertFalse(l2.startCalled); + } + }); + Thread.sleep(POLL_INTERVAL); + wait += POLL_INTERVAL; + } + + long delay = Math.max(a1.getTotalDuration(), a2.getTotalDuration()) + TOLERANCE; + Thread.sleep(delay); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + // a1 is canceled. + assertTrue(l1.startCalled); + assertTrue(l1.cancelCalled); + assertTrue(l1.endCalled); + + // a2 is supposed to finish normally + assertTrue(l2.startCalled); + assertFalse(l2.cancelCalled); + assertTrue(l2.endCalled); + } + }); + } + + @SmallTest + public void testIsStarted() throws Throwable { + assertFalse(a1.isStarted()); + assertFalse(a2.isStarted()); + assertFalse(a1.isRunning()); + assertFalse(a2.isRunning()); + final long startDelay = 150; + a1.setStartDelay(startDelay); + final long[] startTime = new long[1]; + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + a1.start(); + a2.start(); + startTime[0] = SystemClock.uptimeMillis(); + assertTrue(a1.isStarted()); + assertTrue(a2.isStarted()); + } + }); + long delayMs = 0; + while (delayMs < startDelay) { + Thread.sleep(POLL_INTERVAL); + delayMs += POLL_INTERVAL; + runTestOnUiThread(new Runnable() { + @Override + public void run() { + if (SystemClock.uptimeMillis() - startTime[0] < startDelay) { + assertFalse(a1.isRunning()); + } + } + }); + } + + Thread.sleep(startDelay); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertTrue(a1.isRunning()); + assertTrue(a2.isRunning()); + } + }); + + long delay = Math.max(a1.getTotalDuration(), a2.getTotalDuration()) * 2; + Thread.sleep(delay); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertFalse(a1.isStarted()); + assertFalse(a1.isRunning()); + assertFalse(a2.isStarted()); + assertFalse(a2.isRunning()); + } + }); + } + + @SmallTest + public void testPause() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertFalse(a1.isPaused()); + assertFalse(a2.isPaused()); + + a1.start(); + a2.start(); + + assertFalse(a1.isPaused()); + assertFalse(a2.isPaused()); + assertTrue(a1.isStarted()); + assertTrue(a2.isStarted()); + } + }); + + Thread.sleep(POLL_INTERVAL); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertTrue(a1.isRunning()); + assertTrue(a2.isRunning()); + a1.pause(); + assertTrue(a1.isPaused()); + assertFalse(a2.isPaused()); + assertTrue(a1.isRunning()); + } + }); + + Thread.sleep(a2.getTotalDuration()); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + // By this time, a2 should have finished, and a1 is still paused + assertFalse(a2.isStarted()); + assertFalse(a2.isRunning()); + assertTrue(a1.isStarted()); + assertTrue(a1.isRunning()); + assertTrue(a1.isPaused()); + + a1.resume(); + } + }); + + Thread.sleep(POLL_INTERVAL); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertTrue(a1.isRunning()); + assertTrue(a1.isStarted()); + assertFalse(a1.isPaused()); + } + }); + + Thread.sleep(a1.getTotalDuration()); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + // a1 should finish by now. + assertFalse(a1.isRunning()); + assertFalse(a1.isStarted()); + assertFalse(a1.isPaused()); + } + }); + + } + + @SmallTest + public void testPauseListener() throws Throwable { + MyPauseListener l1 = new MyPauseListener(); + MyPauseListener l2 = new MyPauseListener(); + a1.addPauseListener(l1); + a2.addPauseListener(l2); + + assertFalse(l1.pauseCalled); + assertFalse(l1.resumeCalled); + assertFalse(l2.pauseCalled); + assertFalse(l2.resumeCalled); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + a1.start(); + a2.start(); + } + }); + + Thread.sleep(a1.getTotalDuration() / 2); + a1.pause(); + + Thread.sleep(a2.getTotalDuration()); + + // Only a1's pause listener should be called. + assertTrue(l1.pauseCalled); + assertFalse(l1.resumeCalled); + a1.resume(); + + Thread.sleep(a1.getTotalDuration()); + + assertTrue(l1.pauseCalled); + assertTrue(l1.resumeCalled); + assertFalse(l2.pauseCalled); + assertFalse(l2.resumeCalled); + } + + @SmallTest + public void testResume() throws Throwable { + final MyUpdateListener l1 = new MyUpdateListener(); + final long totalDuration = a1.getTotalDuration(); + a1.addUpdateListener(l1); + // Set a longer duration on a1 for this test + a1.setDuration(1000); + assertTrue(l1.firstRunningFrameTime < 0); + assertTrue(l1.lastUpdateTime < 0); + + final long[] lastUpdate = new long[1]; + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + a1.start(); + } + }); + + Thread.sleep(totalDuration / 2); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertTrue(l1.firstRunningFrameTime > 0); + assertTrue(l1.lastUpdateTime > l1.firstRunningFrameTime); + lastUpdate[0] = l1.lastUpdateTime; + a1.pause(); + } + }); + + Thread.sleep(totalDuration); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + // There should be no update after pause() + assertEquals(lastUpdate[0], l1.lastUpdateTime); + a1.resume(); + } + }); + + do { + Thread.sleep(POLL_INTERVAL); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertTrue(l1.lastUpdateTime > lastUpdate[0]); + lastUpdate[0] = l1.lastUpdateTime; + } + }); + } while (!a1.isStarted()); + + // Time between pause and resume: totalDuration + long entireSpan = totalDuration * 2; + long frameDelta = l1.lastUpdateTime - l1.firstRunningFrameTime; + assertTrue(Math.abs(entireSpan - frameDelta) < TOLERANCE); + } + + @SmallTest + public void testEndValue() throws Throwable { + final MyListener l1 = new MyListener(); + a1.addListener(l1); + + final MyListener l2 = new MyListener(); + a2.addListener(l2); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + a1.start(); + a2.start(); + } + }); + + Thread.sleep(POLL_INTERVAL); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + // Animation has started but not finished, check animated values against end values + assertFalse(l1.endCalled); + assertFalse(l2.endCalled); + assertNotEqual(A1_END_VALUE, a1.getAnimatedValue()); + assertNotEqual(A1_END_VALUE, a2.getAnimatedValue()); + + // Force a2 to end. + a2.end(); + } + }); + + Thread.sleep(a1.getTotalDuration()); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertFalse(l1.cancelCalled); + assertTrue(l1.endCalled); + assertFalse(l2.cancelCalled); + assertTrue(l2.endCalled); + + // By now a1 should have finished normally and a2 has skipped to the end, check + // their end values. + assertEquals(A1_END_VALUE, ((Float) (a1.getAnimatedValue())).floatValue()); + assertEquals(A2_END_VALUE, ((Integer) (a2.getAnimatedValue())).intValue()); + } + }); + } + + class MyUpdateListener implements ValueAnimator.AnimatorUpdateListener { + boolean wasRunning = false; + long firstRunningFrameTime = -1; + long lastUpdateTime = -1; + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + lastUpdateTime = SystemClock.uptimeMillis(); + if (animation.isRunning() && !wasRunning) { + // Delay has passed + firstRunningFrameTime = lastUpdateTime; + wasRunning = animation.isRunning(); + } + } + } + + class MyListener implements Animator.AnimatorListener { + boolean startCalled = false; + boolean cancelCalled = false; + boolean endCalled = false; + + @Override + public void onAnimationStart(Animator animation) { + startCalled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + endCalled = true; + } + + @Override + public void onAnimationCancel(Animator animation) { + cancelCalled = true; + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + } + + class MyPauseListener implements Animator.AnimatorPauseListener { + boolean pauseCalled = false; + boolean resumeCalled = false; + + @Override + public void onAnimationPause(Animator animation) { + pauseCalled = true; + } + + @Override + public void onAnimationResume(Animator animation) { + resumeCalled = true; + } + } +} diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java index d2e2131c2da0..be6e7ea5965e 100644 --- a/core/tests/coretests/src/android/text/format/FormatterTest.java +++ b/core/tests/coretests/src/android/text/format/FormatterTest.java @@ -56,14 +56,14 @@ public class FormatterTest extends AndroidTestCase { public void testFormatBytes() { setLocale(Locale.ENGLISH); - checkFormatBytes(0, true, "0.00", 0); - checkFormatBytes(0, false, "0.00", 0); + checkFormatBytes(0, true, "0", 0); + checkFormatBytes(0, false, "0", 0); - checkFormatBytes(1, true, "1.0", 1); - checkFormatBytes(1, false, "1.00", 1); + checkFormatBytes(1, true, "1", 1); + checkFormatBytes(1, false, "1", 1); checkFormatBytes(12, true, "12", 12); - checkFormatBytes(12, false, "12.00", 12); + checkFormatBytes(12, false, "12", 12); checkFormatBytes(123, true, "123", 123); checkFormatBytes(123, false, "123", 123); @@ -80,13 +80,15 @@ public class FormatterTest extends AndroidTestCase { checkFormatBytes(9123000, true, "8.7", 9122611); checkFormatBytes(9123000, false, "8.70", 9122611); - // The method doesn't really support negative values, but apparently people pass -1... - checkFormatBytes(-1, true, "-1.00", -1); - checkFormatBytes(-1, false, "-1.00", -1); + checkFormatBytes(-1, true, "-1", -1); + checkFormatBytes(-1, false, "-1", -1); + + checkFormatBytes(-912, true, "-0.89", -911); + checkFormatBytes(-912, false, "-0.89", -911); // Missing FLAG_CALCULATE_ROUNDED case. BytesResult r = Formatter.formatBytes(getContext().getResources(), 1, 0); - assertEquals("1.00", r.value); + assertEquals("1", r.value); assertEquals(0, r.roundedBytes); // Didn't pass FLAG_CALCULATE_ROUNDED // Make sure it works on different locales. diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index 0b94f8b2c57a..49d9115319e8 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -18,7 +18,7 @@ package android.widget; import android.app.Activity; import android.content.Intent; -import android.test.AndroidTestCase; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.text.GetChars; import android.text.Selection; @@ -27,11 +27,15 @@ import android.text.Spannable; /** * TextViewTest tests {@link TextView}. */ -public class TextViewTest extends AndroidTestCase { +public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewActivity> { + + public TextViewTest() { + super(TextViewActivity.class); + } @SmallTest public void testArray() throws Exception { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; @@ -59,25 +63,34 @@ public class TextViewTest extends AndroidTestCase { assertEquals('\0', c2[5]); } + @SmallTest public void testProcessTextActivityResultNonEditable() { - TextView tv = new TextView(mContext); + final TextView tv = new TextView(getActivity()); CharSequence originalText = "This is some text."; tv.setText(originalText, TextView.BufferType.SPANNABLE); assertEquals(originalText, tv.getText().toString()); tv.setTextIsSelectable(true); Selection.setSelection((Spannable) tv.getText(), 0, tv.getText().length()); - CharSequence newText = "Text is replaced."; - Intent data = new Intent(); - data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); - tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data); + // We need to run this in the UI thread, as it will create a Toast. + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + CharSequence newText = "Text is replaced."; + Intent data = new Intent(); + data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); + tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data); + } + }); + getInstrumentation().waitForIdleSync(); // This is a TextView, which can't be modified. Hence no change should have been made. assertEquals(originalText, tv.getText().toString()); } + @SmallTest public void testProcessTextActivityResultEditable() { - EditText tv = new EditText(mContext); + EditText tv = new EditText(getActivity()); CharSequence originalText = "This is some text."; tv.setText(originalText, TextView.BufferType.SPANNABLE); assertEquals(originalText, tv.getText().toString()); @@ -92,8 +105,9 @@ public class TextViewTest extends AndroidTestCase { assertEquals(newText, tv.getText().toString()); } + @SmallTest public void testProcessTextActivityResultCancel() { - EditText tv = new EditText(mContext); + EditText tv = new EditText(getActivity()); CharSequence originalText = "This is some text."; tv.setText(originalText, TextView.BufferType.SPANNABLE); assertEquals(originalText, tv.getText().toString()); @@ -108,8 +122,9 @@ public class TextViewTest extends AndroidTestCase { assertEquals(originalText, tv.getText().toString()); } + @SmallTest public void testProcessTextActivityNoData() { - EditText tv = new EditText(mContext); + EditText tv = new EditText(getActivity()); CharSequence originalText = "This is some text."; tv.setText(originalText, TextView.BufferType.SPANNABLE); assertEquals(originalText, tv.getText().toString()); diff --git a/docs/html/preview/features/runtime-permissions.jd b/docs/html/preview/features/runtime-permissions.jd index 8ab7619fd584..62e49b9ba0ae 100644 --- a/docs/html/preview/features/runtime-permissions.jd +++ b/docs/html/preview/features/runtime-permissions.jd @@ -916,8 +916,11 @@ $ adb pm revoke <package_name> <permission_name> </dt> <dd> - Returns <code>true</code> if the app has the specified permission, whether - or not the device is using the M Preview. + Returns {@link android.content.pm.PackageManager#PERMISSION_GRANTED + PERMISSION_GRANTED} if the app has the specified permission, whether + or not the device is using the M Preview. If the app does not have the + specified permission, returns {@link + android.content.pm.PackageManager#PERMISSION_DENIED PERMISSION_DENIED}. </dd> <dt> diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd index dbc0f35753c8..39822e51f358 100644 --- a/docs/html/sdk/index.jd +++ b/docs/html/sdk/index.jd @@ -5,28 +5,28 @@ page.image=images/cards/android-studio_2x.png header.hide=1 page.metaDescription=Download the official Android IDE and developer tools to build apps for Android phones, tablets, wearables, TVs, and more. -studio.version=1.3.1.0 +studio.version=1.3.2.0 -studio.linux_bundle_download=android-studio-ide-141.2135290-linux.zip -studio.linux_bundle_bytes=351992670 -studio.linux_bundle_checksum=51e5f5de2b82883d87f85ee38cf7b7b8b2e7debf +studio.linux_bundle_download=android-studio-ide-141.2178183-linux.zip +studio.linux_bundle_bytes=352010593 +studio.linux_bundle_checksum=cf780413f8c8223eb348bd27c19a9c04b75eaeb2 -studio.mac_bundle_download=android-studio-ide-141.2135290-mac.dmg -studio.mac_bundle_bytes=368321249 -studio.mac_bundle_checksum=9fc12b5657ff52c761b7e7c115feade2a9728386 +studio.mac_bundle_download=android-studio-ide-141.2178183-mac.dmg +studio.mac_bundle_bytes=368335367 +studio.mac_bundle_checksum=75b67eb15a34a152a40e7189484ab0ebc375b877 -studio.win_bundle_download=android-studio-ide-141.2135290-windows.zip -studio.win_bundle_bytes=344406793 -studio.win_bundle_checksum=3b4c4924cb9495e56db61ca0e8c8d2bf588c4b97 +studio.win_bundle_download=android-studio-ide-141.2178183-windows.zip +studio.win_bundle_bytes=344424713 +studio.win_bundle_checksum=3134f226b5f3c3f74d4fc2d9cff03a4458f01d69 -studio.win_bundle_exe_download=android-studio-bundle-141.2135290-windows.exe -studio.win_bundle_exe_bytes=1008506096 -studio.win_bundle_exe_checksum=8cff590f2e08e339f8c2491b287a840ae87c7383 +studio.win_bundle_exe_download=android-studio-bundle-141.2178183-windows.exe +studio.win_bundle_exe_bytes=1136982712 +studio.win_bundle_exe_checksum=c7d39c529dd434489da9d086ff689d34dc791526 -studio.win_notools_exe_download=android-studio-ide-141.2135290-windows.exe -studio.win_notools_exe_bytes=321791312 -studio.win_notools_exe_checksum=d70fb49d03db9dded19c891a92452601e39272f4 +studio.win_notools_exe_download=android-studio-ide-141.2178183-windows.exe +studio.win_notools_exe_bytes=321810248 +studio.win_notools_exe_checksum=b5d1aaa000729c03a3cf980add79d1b93121c56d @@ -289,7 +289,7 @@ width: 400px;text-align: center;">The official Android IDE</p> </ul> <a class="online landing-button green download-bundle-button" -href="#Other" >Download Android Studio</a> +href="#Other" >Download Android Studio<br/><span class='small'></span></a> <!-- this appears when viewing the offline docs --> <p class="offline"> diff --git a/docs/html/tools/revisions/studio.jd b/docs/html/tools/revisions/studio.jd index c922b288411e..298b173559a7 100644 --- a/docs/html/tools/revisions/studio.jd +++ b/docs/html/tools/revisions/studio.jd @@ -43,6 +43,29 @@ Android Studio, as denoted by revision number. </p> <div class="toggle-content opened"> <p><a href="#" onclick="return toggleContent(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" + alt=""/>Android Studio v1.3.2</a> <em>(August 2015)</em> + </p> + <div class="toggle-content-toggleme"> + <p>Fixes and enhancements:</p> + <ul> + <li>Added support for Android 6.0 (API level 23), including new icons and AVD Manager + support for creating devices with new screen densities.</li> + <li>Fixed an exception that was occuring during update checks. + <a href="http://b.android.com/183068">Issue: 183068</a></li> + <li>Fixed problem where unresolved view coordinates could cause the layout editor to crash. + <a href="http://b.android.com/178690">Issue: 178690</a></li> + <li>Fixed issue with invalid resource type warnings. + <a href="http://b.android.com/182433">Issue: 182433</a></li> + <li>Fixed lint check that was incorrectly flagging resources as private. + <a href="http://b.android.com/183120">Issue: 183120</a></li> + </ul> + </div> +</div> + + +<div class="toggle-content closed"> + <p><a href="#" onclick="return toggleContent(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""/>Android Studio v1.3.1</a> <em>(August 2015)</em> </p> <div class="toggle-content-toggleme"> @@ -60,8 +83,6 @@ Android Studio, as denoted by revision number. </p> </div> - - <div class="toggle-content closed"> <p><a href="#" onclick="return toggleContent(this)"> <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" @@ -100,7 +121,7 @@ Android Studio, as denoted by revision number. </p> <li>Added <a href="{@docRoot}tools/data-binding/guide.html">data binding</a> support to create declarative layouts that bind your application logic to layout elements. </li> <li>Added support for a separate - <a href="{@docRoot}tools/studio/studio-features.html#test-module">test APK module</a> + <a href="{@docRoot}tools/studio/studio-features.html#test-module">test APK module</a> to build test APKs in Android Studio. </li> <li>Updated the <a href="{@docRoot}tools/help/avd-manager.html">AVD Manager</a> with HAXM optimizations and improved notifications. </li> diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd index 08634daa39da..3c12a640e989 100644 --- a/docs/html/tools/sdk/eclipse-adt.jd +++ b/docs/html/tools/sdk/eclipse-adt.jd @@ -53,6 +53,39 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues <div class="toggle-content opened"> <p><a href="#" onclick="return toggleContent(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" + alt=""/>ADT 23.0.7</a> <em>(August 2015)</em> + </p> + + <div class="toggle-content-toggleme"> +<dl> + <dt>Dependencies:</dt> + + <dd> + <ul> + <li>Java 7 or higher is required if you are targeting Android 5.0 and higher.</li> + <li>Java 1.6 or higher is required if you are targeting other releases.</li> + <li>Eclipse Indigo (Version 3.7.2) or higher is required.</li> + <li>This version of ADT is designed for use with + <a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools r24.1.2</a>. + If you haven't already installed SDK Tools r24.1.2 into your SDK, use the + Android SDK Manager to do so.</li> + </ul> + </dd> + + <dt>General Notes:</dt> + <dd> + <ul> + <li>Fixed issues with the rendering library for the visual layout editor.</li> + </ul> + </dd> +</dl> +</div> +</div> + + +<div class="toggle-content closed"> + <p><a href="#" onclick="return toggleContent(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""/>ADT 23.0.6</a> <em>(March 2015)</em> </p> @@ -75,7 +108,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues <dt>General Notes:</dt> <dd> <ul> - <li>Fixed issues with the rendering library.</li> + <li>Fixed issues with the rendering library for the visual layout editor.</li> </ul> </dd> </dl> diff --git a/docs/html/tools/support-library/features.jd b/docs/html/tools/support-library/features.jd index b3af7a27d9cb..19f93e9ce0e3 100644 --- a/docs/html/tools/support-library/features.jd +++ b/docs/html/tools/support-library/features.jd @@ -307,8 +307,8 @@ libraries in your build path when setting up your project. For more information on how to set up your project, follow the instructions in <a href="{@docRoot}tools/support-library/setup.html#libs-with-res">Adding libraries with resources</a>. If you are developing in Eclipse/ADT, make sure to include -both the <code>android-support-v7-mediarouter.jar</code> and -<code>android-support-v7-appcompat.jar</code> files.</p> +the <code>android-support-v7-mediarouter.jar</code>, <code>android-support-v7-appcompat.jar</code>, +and <code>android-support-v7-palette.jar</code> files.</p> <p>If you are using Android Studio, all you need to do is specify the Gradle build script dependency identifier <code>com.android.support:support-v7-mediarouter:<revision></code>, diff --git a/docs/html/training/wearables/watch-faces/index.jd b/docs/html/training/wearables/watch-faces/index.jd index b54463684033..a329fda071bc 100644 --- a/docs/html/training/wearables/watch-faces/index.jd +++ b/docs/html/training/wearables/watch-faces/index.jd @@ -66,7 +66,7 @@ Drawing Watch Faces</a></dt> Showing Information in Watch Faces</a></dt> <dd>Learn how to incorporate contextual information into your watch face.</dd> <dt><a href="{@docRoot}training/wearables/watch-faces/interacting.html"> -Making Your Watch Face Interactive </a></dt> +Creating Interactive Watch Faces</a></dt> <dd>Learn how to enable the user to interact with your watch face.</dd> <dt><a href="{@docRoot}training/wearables/watch-faces/configuration.html"> Providing Configuration Activities</a></dt> diff --git a/docs/html/training/wearables/watch-faces/interacting.jd b/docs/html/training/wearables/watch-faces/interacting.jd index 4f2486cf416f..5a44fdeeb300 100644 --- a/docs/html/training/wearables/watch-faces/interacting.jd +++ b/docs/html/training/wearables/watch-faces/interacting.jd @@ -62,10 +62,10 @@ implementation.</p> reserves gestures such as drag and long-press for system UI elements. Therefore, the system does not send raw touch events to the watch face. Instead, the system forwards specific commands to the <a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onTapCommand(int, int, int, long)"> -method. +onTapCommand()</a> method. <p>The system sends the first command, -<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.html#TAP_TYPE_TOUCH"</a> +<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.html#TAP_TYPE_TOUCH"> {@code TAP_TYPE_TOUCH}</a>, when the user initially touches the screen. This event lets you provide visual feedback to the user on touch. Your app should not launch a UI when this event triggers. Launching a UI prevents drag events from opening the app diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java index 0e9823d1661f..91e704a901cb 100644 --- a/graphics/java/android/graphics/Path.java +++ b/graphics/java/android/graphics/Path.java @@ -486,9 +486,9 @@ public class Path { */ public enum Direction { /** clockwise */ - CW (1), // must match enum in SkPath.h + CW (0), // must match enum in SkPath.h /** counter-clockwise */ - CCW (2); // must match enum in SkPath.h + CCW (1); // must match enum in SkPath.h Direction(int ni) { nativeInt = ni; diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java index 1c14e2f9205c..d960c8fee9ed 100644 --- a/graphics/java/android/graphics/drawable/RippleBackground.java +++ b/graphics/java/android/graphics/drawable/RippleBackground.java @@ -131,15 +131,7 @@ class RippleBackground extends RippleComponent { mPropX = CanvasProperty.createFloat(0); mPropY = CanvasProperty.createFloat(0); - // Linear "fast" enter based on current opacity. final int fastEnterDuration = (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST); - if (fastEnterDuration > 0) { - final RenderNodeAnimator enter = new RenderNodeAnimator( - mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha); - enter.setInterpolator(LINEAR_INTERPOLATOR); - enter.setDuration(fastEnterDuration); - set.add(enter); - } // Linear exit after enter is completed. final RenderNodeAnimator exit = new RenderNodeAnimator( @@ -149,6 +141,15 @@ class RippleBackground extends RippleComponent { exit.setStartDelay(fastEnterDuration); set.add(exit); + // Linear "fast" enter based on current opacity. + if (fastEnterDuration > 0) { + final RenderNodeAnimator enter = new RenderNodeAnimator( + mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha); + enter.setInterpolator(LINEAR_INTERPOLATOR); + enter.setDuration(fastEnterDuration); + set.add(enter); + } + return set; } diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 32f6a895f35d..464f3de5d29b 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -855,7 +855,8 @@ public class RippleDrawable extends LayerDrawable { // Position the shader to account for canvas translation. if (mMaskShader != null) { - mMaskMatrix.setTranslate(-x, -y); + final Rect bounds = getBounds(); + mMaskMatrix.setTranslate(bounds.left - x, bounds.top - y); mMaskShader.setLocalMatrix(mMaskMatrix); } diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 512e0e24aa93..5ca2a2fa37ab 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -36,8 +36,8 @@ BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue) , mFinalValue(finalValue) , mDeltaValue(0) , mFromValue(0) - , mStagingPlayState(NOT_STARTED) - , mPlayState(NOT_STARTED) + , mStagingPlayState(PlayState::NotStarted) + , mPlayState(PlayState::NotStarted) , mHasStartValue(false) , mStartTime(0) , mDuration(300) @@ -50,7 +50,7 @@ BaseRenderNodeAnimator::~BaseRenderNodeAnimator() { void BaseRenderNodeAnimator::checkMutable() { // Should be impossible to hit as the Java-side also has guards for this - LOG_ALWAYS_FATAL_IF(mStagingPlayState != NOT_STARTED, + LOG_ALWAYS_FATAL_IF(mStagingPlayState != PlayState::NotStarted, "Animator has already been started!"); } @@ -92,9 +92,9 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) { if (mStagingPlayState > mPlayState) { mPlayState = mStagingPlayState; // Oh boy, we're starting! Man the battle stations! - if (mPlayState == RUNNING) { + if (mPlayState == PlayState::Running) { transitionToRunning(context); - } else if (mPlayState == FINISHED) { + } else if (mPlayState == PlayState::Finished) { callOnFinishedListener(context); } } @@ -124,10 +124,10 @@ void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) { } bool BaseRenderNodeAnimator::animate(AnimationContext& context) { - if (mPlayState < RUNNING) { + if (mPlayState < PlayState::Running) { return false; } - if (mPlayState == FINISHED) { + if (mPlayState == PlayState::Finished) { return true; } @@ -141,18 +141,18 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) { } float fraction = 1.0f; - if (mPlayState == RUNNING && mDuration > 0) { + if (mPlayState == PlayState::Running && mDuration > 0) { fraction = (float)(context.frameTimeMs() - mStartTime) / mDuration; } if (fraction >= 1.0f) { fraction = 1.0f; - mPlayState = FINISHED; + mPlayState = PlayState::Finished; } fraction = mInterpolator->interpolate(fraction); setValue(mTarget, mFromValue + (mDeltaValue * fraction)); - if (mPlayState == FINISHED) { + if (mPlayState == PlayState::Finished) { callOnFinishedListener(context); return true; } @@ -161,8 +161,8 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) { } void BaseRenderNodeAnimator::forceEndNow(AnimationContext& context) { - if (mPlayState < FINISHED) { - mPlayState = FINISHED; + if (mPlayState < PlayState::Finished) { + mPlayState = PlayState::Finished; callOnFinishedListener(context); } } @@ -212,9 +212,9 @@ void RenderPropertyAnimator::onAttached() { } void RenderPropertyAnimator::onStagingPlayStateChanged() { - if (mStagingPlayState == RUNNING) { + if (mStagingPlayState == PlayState::Running) { (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); - } else if (mStagingPlayState == FINISHED) { + } else if (mStagingPlayState == PlayState::Finished) { // We're being canceled, so make sure that whatever values the UI thread // is observing for us is pushed over mTarget->setPropertyFieldsDirty(dirtyMask()); diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 1b3d8e7f4842..aea95bfc1c0e 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -59,8 +59,8 @@ public: mMayRunAsync = mayRunAsync; } bool mayRunAsync() { return mMayRunAsync; } - ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); } - ANDROID_API void end() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); } + ANDROID_API void start() { mStagingPlayState = PlayState::Running; onStagingPlayStateChanged(); } + ANDROID_API void end() { mStagingPlayState = PlayState::Finished; onStagingPlayStateChanged(); } void attach(RenderNode* target); virtual void onAttached() {} @@ -68,8 +68,8 @@ public: void pushStaging(AnimationContext& context); bool animate(AnimationContext& context); - bool isRunning() { return mPlayState == RUNNING; } - bool isFinished() { return mPlayState == FINISHED; } + bool isRunning() { return mPlayState == PlayState::Running; } + bool isFinished() { return mPlayState == PlayState::Finished; } float finalValue() { return mFinalValue; } ANDROID_API virtual uint32_t dirtyMask() = 0; @@ -77,6 +77,12 @@ public: void forceEndNow(AnimationContext& context); protected: + enum class PlayState { + NotStarted, + Running, + Finished, + }; + BaseRenderNodeAnimator(float finalValue); virtual ~BaseRenderNodeAnimator(); @@ -88,12 +94,6 @@ protected: virtual void onStagingPlayStateChanged() {} - enum PlayState { - NOT_STARTED, - RUNNING, - FINISHED, - }; - RenderNode* mTarget; float mFinalValue; diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index f663f07269d7..ff713133e389 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -245,7 +245,7 @@ void Caches::flush(FlushMode mode) { FLUSH_LOGD("Flushing caches (mode %d)", mode); switch (mode) { - case kFlushMode_Full: + case FlushMode::Full: textureCache.clear(); patchCache.clear(); dropShadowCache.clear(); @@ -254,13 +254,13 @@ void Caches::flush(FlushMode mode) { fboCache.clear(); dither.clear(); // fall through - case kFlushMode_Moderate: + case FlushMode::Moderate: fontRenderer->flush(); textureCache.flush(); pathCache.clear(); tessellationCache.clear(); // fall through - case kFlushMode_Layers: + case FlushMode::Layers: layerCache.clear(); renderBufferCache.clear(); break; diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index a02e15de7fb0..929db17a22a8 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -83,10 +83,10 @@ private: static Caches* sInstance; public: - enum FlushMode { - kFlushMode_Layers = 0, - kFlushMode_Moderate, - kFlushMode_Full + enum class FlushMode { + Layers = 0, + Moderate, + Full }; /** @@ -103,7 +103,7 @@ public: /** * Destroys all resources associated with this cache. This should - * be called after a flush(kFlushMode_Full). + * be called after a flush(FlushMode::Full). */ void terminate(); diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp index 54fb5f233fce..e307ad91885f 100644 --- a/libs/hwui/CanvasState.cpp +++ b/libs/hwui/CanvasState.cpp @@ -155,17 +155,20 @@ void CanvasState::concatMatrix(const Matrix4& matrix) { /////////////////////////////////////////////////////////////////////////////// bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { - mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op); + mSnapshot->clip(left, top, right, bottom, op); + mDirtyClip = true; return !mSnapshot->clipIsEmpty(); } bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) { - mDirtyClip |= mSnapshot->clipPath(*path, op); + mSnapshot->clipPath(*path, op); + mDirtyClip = true; return !mSnapshot->clipIsEmpty(); } bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) { - mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op); + mSnapshot->clipRegionTransformed(*region, op); + mDirtyClip = true; return !mSnapshot->clipIsEmpty(); } diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp index b1a68447fc21..8e7efb4e35d6 100644 --- a/libs/hwui/ClipArea.cpp +++ b/libs/hwui/ClipArea.cpp @@ -32,9 +32,7 @@ static bool intersect(Rect& r, const Rect& r2) { } static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) { - Vertex v; - v.x = x; - v.y = y; + Vertex v = {x, y}; transform.mapPoint(v.x, v.y); transformedBounds.expandToCoverVertex(v.x, v.y); } @@ -187,7 +185,7 @@ SkRegion RectangleList::convertToRegion(const SkRegion& clip) const { */ ClipArea::ClipArea() - : mMode(kModeRectangle) { + : mMode(Mode::Rectangle) { } /* @@ -200,45 +198,46 @@ void ClipArea::setViewportDimensions(int width, int height) { } void ClipArea::setEmpty() { - mMode = kModeRectangle; + mMode = Mode::Rectangle; mClipRect.setEmpty(); mClipRegion.setEmpty(); mRectangleList.setEmpty(); } void ClipArea::setClip(float left, float top, float right, float bottom) { - mMode = kModeRectangle; + mMode = Mode::Rectangle; mClipRect.set(left, top, right, bottom); mClipRegion.setEmpty(); } -bool ClipArea::clipRectWithTransform(float left, float top, float right, +void ClipArea::clipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op) { Rect r(left, top, right, bottom); - return clipRectWithTransform(r, transform, op); + clipRectWithTransform(r, transform, op); } -bool ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, +void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { switch (mMode) { - case kModeRectangle: - return rectangleModeClipRectWithTransform(r, transform, op); - case kModeRectangleList: - return rectangleListModeClipRectWithTransform(r, transform, op); - case kModeRegion: - return regionModeClipRectWithTransform(r, transform, op); + case Mode::Rectangle: + rectangleModeClipRectWithTransform(r, transform, op); + break; + case Mode::RectangleList: + rectangleListModeClipRectWithTransform(r, transform, op); + break; + case Mode::Region: + regionModeClipRectWithTransform(r, transform, op); + break; } - return false; } -bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { +void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { enterRegionMode(); mClipRegion.op(region, op); onClipRegionUpdated(); - return true; } -bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, +void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) { SkMatrix skTransform; transform->copyTo(skTransform); @@ -246,7 +245,7 @@ bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, path.transform(skTransform, &transformed); SkRegion region; regionFromPath(transformed, region); - return clipRegion(region, op); + clipRegion(region, op); } /* @@ -257,19 +256,20 @@ void ClipArea::enterRectangleMode() { // Entering rectangle mode discards any // existing clipping information from the other modes. // The only way this occurs is by a clip setting operation. - mMode = kModeRectangle; + mMode = Mode::Rectangle; } -bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r, +void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { if (op == SkRegion::kReplace_Op && transform->rectToRect()) { mClipRect = r; transform->mapRect(mClipRect); - return true; + return; } else if (op != SkRegion::kIntersect_Op) { enterRegionMode(); - return regionModeClipRectWithTransform(r, transform, op); + regionModeClipRectWithTransform(r, transform, op); + return; } if (transform->rectToRect()) { @@ -279,19 +279,18 @@ bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r, if (!hasIntersection) { mClipRect.setEmpty(); } - return true; + return; } enterRectangleListMode(); - return rectangleListModeClipRectWithTransform(r, transform, op); + rectangleListModeClipRectWithTransform(r, transform, op); } -bool ClipArea::rectangleModeClipRectWithTransform(float left, float top, +void ClipArea::rectangleModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op) { Rect r(left, top, right, bottom); - bool result = rectangleModeClipRectWithTransform(r, transform, op); + rectangleModeClipRectWithTransform(r, transform, op); mClipRect = mRectangleList.calculateBounds(); - return result; } /* @@ -302,25 +301,24 @@ void ClipArea::enterRectangleListMode() { // Is is only legal to enter rectangle list mode from // rectangle mode, since rectangle list mode cannot represent // all clip areas that can be represented by a region. - ALOG_ASSERT(mMode == kModeRectangle); - mMode = kModeRectangleList; + ALOG_ASSERT(mMode == Mode::Rectangle); + mMode = Mode::RectangleList; mRectangleList.set(mClipRect, Matrix4::identity()); } -bool ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, +void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) { enterRegionMode(); - return regionModeClipRectWithTransform(r, transform, op); + regionModeClipRectWithTransform(r, transform, op); } - return true; } -bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top, +void ClipArea::rectangleListModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op) { Rect r(left, top, right, bottom); - return rectangleListModeClipRectWithTransform(r, transform, op); + rectangleListModeClipRectWithTransform(r, transform, op); } /* @@ -329,9 +327,9 @@ bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top, void ClipArea::enterRegionMode() { Mode oldMode = mMode; - mMode = kModeRegion; - if (oldMode != kModeRegion) { - if (oldMode == kModeRectangle) { + mMode = Mode::Region; + if (oldMode != Mode::Region) { + if (oldMode == Mode::Rectangle) { mClipRegion.setRect(mClipRect.left, mClipRect.top, mClipRect.right, mClipRect.bottom); } else { @@ -341,20 +339,18 @@ void ClipArea::enterRegionMode() { } } -bool ClipArea::regionModeClipRectWithTransform(const Rect& r, +void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { SkPath transformedRect = pathFromTransformedRectangle(r, *transform); SkRegion transformedRectRegion; regionFromPath(transformedRect, transformedRectRegion); mClipRegion.op(transformedRectRegion, op); onClipRegionUpdated(); - return true; } -bool ClipArea::regionModeClipRectWithTransform(float left, float top, +void ClipArea::regionModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op) { - return regionModeClipRectWithTransform(Rect(left, top, right, bottom), - transform, op); + regionModeClipRectWithTransform(Rect(left, top, right, bottom), transform, op); } void ClipArea::onClipRegionUpdated() { diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h index 51ef27b4e9cc..38fefe5ab097 100644 --- a/libs/hwui/ClipArea.h +++ b/libs/hwui/ClipArea.h @@ -80,6 +80,13 @@ private: }; class ClipArea { +private: + enum class Mode { + Rectangle, + Region, + RectangleList + }; + public: ClipArea(); @@ -91,12 +98,12 @@ public: void setEmpty(); void setClip(float left, float top, float right, float bottom); - bool clipRectWithTransform(float left, float top, float right, float bottom, - const mat4* transform, SkRegion::Op op = SkRegion::kIntersect_Op); - bool clipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op = SkRegion::kIntersect_Op); - bool clipRegion(const SkRegion& region, SkRegion::Op op = SkRegion::kIntersect_Op); - bool clipPathWithTransform(const SkPath& path, const mat4* transform, + void clipRectWithTransform(float left, float top, float right, float bottom, + const mat4* transform, SkRegion::Op op); + void clipRectWithTransform(const Rect& r, const mat4* transform, + SkRegion::Op op); + void clipRegion(const SkRegion& region, SkRegion::Op op); + void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op); const Rect& getClipRect() const { @@ -112,41 +119,39 @@ public: } bool isRegion() const { - return kModeRegion == mMode; + return Mode::Region == mMode; } bool isSimple() const { - return mMode == kModeRectangle; + return mMode == Mode::Rectangle; } bool isRectangleList() const { - return mMode == kModeRectangleList; + return mMode == Mode::RectangleList; } private: void enterRectangleMode(); - bool rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - bool rectangleModeClipRectWithTransform(float left, float top, float right, + void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); + void rectangleModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op); void enterRectangleListMode(); - bool rectangleListModeClipRectWithTransform(float left, float top, + void rectangleListModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op); - bool rectangleListModeClipRectWithTransform(const Rect& r, + void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); void enterRegionModeFromRectangleMode(); void enterRegionModeFromRectangleListMode(); void enterRegionMode(); - bool regionModeClipRectWithTransform(const Rect& r, const mat4* transform, + void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - bool regionModeClipRectWithTransform(float left, float top, float right, + void regionModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op); void ensureClipRegion(); void onClipRegionUpdated(); - bool clipRegionOp(float left, float top, float right, float bottom, - SkRegion::Op op); SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); @@ -158,12 +163,6 @@ private: pathAsRegion.setPath(path, createViewportRegion()); } - enum Mode { - kModeRectangle, - kModeRegion, - kModeRectangleList - }; - Mode mMode; Rect mViewportBounds; Rect mClipRect; diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp index bb149fe09d69..506bfad08c1b 100644 --- a/libs/hwui/DisplayListCanvas.cpp +++ b/libs/hwui/DisplayListCanvas.cpp @@ -216,11 +216,11 @@ void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) { addRenderNodeOp(op); } -void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) { +void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { // We ref the DeferredLayerUpdater due to its thread-safe ref-counting // semantics. mDisplayListData->ref(layerHandle); - addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y)); + addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer())); } void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h index f29e835c4858..392bb3e5d85f 100644 --- a/libs/hwui/DisplayListCanvas.h +++ b/libs/hwui/DisplayListCanvas.h @@ -91,7 +91,7 @@ public: // ---------------------------------------------------------------------------- // HWUI Canvas draw operations - special // ---------------------------------------------------------------------------- - void drawLayer(DeferredLayerUpdater* layerHandle, float x, float y); + void drawLayer(DeferredLayerUpdater* layerHandle); void drawRenderNode(RenderNode* renderNode); // TODO: rename for consistency diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 8bb892f8e9d4..14126a9e31a7 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -1524,23 +1524,21 @@ private: class DrawLayerOp : public DrawOp { public: - DrawLayerOp(Layer* layer, float x, float y) - : DrawOp(nullptr), mLayer(layer), mX(x), mY(y) {} + DrawLayerOp(Layer* layer) + : DrawOp(nullptr), mLayer(layer) {} virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawLayer(mLayer, mX, mY); + renderer.drawLayer(mLayer); } virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY); + OP_LOG("Draw Layer %p", mLayer); } virtual const char* name() override { return "DrawLayer"; } private: Layer* mLayer; - float mX; - float mY; }; }; // namespace uirenderer diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index c9e388021558..e27b26b1d091 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -274,7 +274,7 @@ void GlopBuilder::setFill(int color, float alphaScale, SkXfermode::Mode mode; SkScalar srcColorMatrix[20]; if (colorFilter->asColorMode(&color, &mode)) { - mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorBlend; + mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend; mDescription.colorMode = mode; const float alpha = SkColorGetA(color) / 255.0f; @@ -286,7 +286,7 @@ void GlopBuilder::setFill(int color, float alphaScale, alpha, }; } else if (colorFilter->asColorMatrix(srcColorMatrix)) { - mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorMatrix; + mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix; float* colorMatrix = mOutGlop->fill.filter.matrix.matrix; memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float)); @@ -305,7 +305,7 @@ void GlopBuilder::setFill(int color, float alphaScale, LOG_ALWAYS_FATAL("unsupported ColorFilter"); } } else { - mOutGlop->fill.filterMode = ProgramDescription::kColorNone; + mOutGlop->fill.filterMode = ProgramDescription::ColorFilterMode::None; } } diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 928f91bdd4d9..e7482211b399 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -36,7 +36,7 @@ namespace android { namespace uirenderer { Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight) - : state(kState_Uncached) + : state(State::Uncached) , caches(Caches::getInstance()) , renderState(renderState) , texture(caches) diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index b670870ca55f..e90f055b667b 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -56,19 +56,19 @@ struct DeferStateStruct; */ class Layer : public VirtualLightRefBase { public: - enum Type { - kType_Texture, - kType_DisplayList, + enum class Type { + Texture, + DisplayList, }; // layer lifecycle, controlled from outside - enum State { - kState_Uncached = 0, - kState_InCache = 1, - kState_FailedToCache = 2, - kState_RemovedFromCache = 3, - kState_DeletedFromCache = 4, - kState_InGarbageList = 5, + enum class State { + Uncached = 0, + InCache = 1, + FailedToCache = 2, + RemovedFromCache = 3, + DeletedFromCache = 4, + InGarbageList = 5, }; State state; // public for logging/debugging purposes @@ -241,7 +241,7 @@ public: } inline bool isTextureLayer() const { - return type == kType_Texture; + return type == Type::Texture; } inline SkColorFilter* getColorFilter() const { diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 33f40b006092..39cadd198c83 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -81,7 +81,7 @@ void LayerCache::deleteLayer(Layer* layer) { LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(), layer->getFbo()); mSize -= layer->getWidth() * layer->getHeight() * 4; - layer->state = Layer::kState_DeletedFromCache; + layer->state = Layer::State::DeletedFromCache; layer->decStrong(nullptr); } } @@ -104,14 +104,14 @@ Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uin mCache.erase(iter); layer = entry.mLayer; - layer->state = Layer::kState_RemovedFromCache; + layer->state = Layer::State::RemovedFromCache; mSize -= layer->getWidth() * layer->getHeight() * 4; LAYER_LOGD("Reusing layer %dx%d", layer->getWidth(), layer->getHeight()); } else { LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight); - layer = new Layer(Layer::kType_DisplayList, renderState, entry.mWidth, entry.mHeight); + layer = new Layer(Layer::Type::DisplayList, renderState, entry.mWidth, entry.mHeight); layer->setBlend(true); layer->generateTexture(); layer->bindTexture(); @@ -156,11 +156,11 @@ bool LayerCache::put(Layer* layer) { mCache.insert(entry); mSize += size; - layer->state = Layer::kState_InCache; + layer->state = Layer::State::InCache; return true; } - layer->state = Layer::kState_FailedToCache; + layer->state = Layer::State::FailedToCache; return false; } diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 00add2903371..d8e6392bd07e 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -272,7 +272,7 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { Layer* LayerRenderer::createTextureLayer(RenderState& renderState) { LAYER_RENDERER_LOGD("Creating new texture layer"); - Layer* layer = new Layer(Layer::kType_Texture, renderState, 0, 0); + Layer* layer = new Layer(Layer::Type::Texture, renderState, 0, 0); layer->setCacheable(false); layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f); layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index b35c0179193a..5692d7e120ff 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2268,7 +2268,7 @@ void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) { mDirty = true; } -void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { +void OpenGLRenderer::drawLayer(Layer* layer) { if (!layer) { return; } @@ -2284,7 +2284,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { bool clipRequired = false; const bool rejected = mState.calculateQuickRejectForScissor( - x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), + 0, 0, layer->layer.getWidth(), layer->layer.getHeight(), &clipRequired, nullptr, false); if (rejected) { @@ -2313,7 +2313,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount) .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap) .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight())) + .setModelViewOffsetRectSnap(0, 0, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight())) .build(); DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop)); #if DEBUG_LAYERS_AS_REGIONS @@ -2326,7 +2326,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { SkPaint paint; paint.setColor(0x7f00ff00); - drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), &paint); + drawColorRect(0, 0, layer->layer.getWidth(), layer->layer.getHeight(), &paint); } } layer->hasDrawnSinceUpdate = true; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 45662a77cfdd..af85e8c72619 100755 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -35,6 +35,7 @@ #include <SkBitmap.h> #include <SkCanvas.h> #include <SkColorFilter.h> +#include <SkDrawLooper.h> #include <SkMatrix.h> #include <SkPaint.h> #include <SkRegion.h> @@ -187,7 +188,7 @@ public: const SkPaint* paint, int flags); void drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); - void drawLayer(Layer* layer, float x, float y); + void drawLayer(Layer* layer); void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); void drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint); diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h index 8d4d4f085f98..4bd4ae1d48c9 100644 --- a/libs/hwui/Outline.h +++ b/libs/hwui/Outline.h @@ -28,13 +28,13 @@ class Outline { public: Outline() : mShouldClip(false) - , mType(kOutlineType_None) + , mType(Type::None) , mRadius(0) , mAlpha(0.0f) {} void setRoundRect(int left, int top, int right, int bottom, float radius, float alpha) { mAlpha = alpha; - if (mType == kOutlineType_RoundRect + if (mType == Type::RoundRect && left == mBounds.left && right == mBounds.right && top == mBounds.top @@ -44,7 +44,7 @@ public: return; } - mType = kOutlineType_RoundRect; + mType = Type::RoundRect; mBounds.set(left, top, right, bottom); mRadius = radius; @@ -63,26 +63,26 @@ public: setEmpty(); return; } - mType = kOutlineType_ConvexPath; + mType = Type::ConvexPath; mPath = *outline; mBounds.set(outline->getBounds()); mAlpha = alpha; } void setEmpty() { - mType = kOutlineType_Empty; + mType = Type::Empty; mPath.reset(); mAlpha = 0.0f; } void setNone() { - mType = kOutlineType_None; + mType = Type::None; mPath.reset(); mAlpha = 0.0f; } bool isEmpty() const { - return mType == kOutlineType_Empty; + return mType == Type::Empty; } float getAlpha() const { @@ -99,7 +99,7 @@ public: bool willClip() const { // only round rect outlines can be used for clipping - return mShouldClip && (mType == kOutlineType_RoundRect); + return mShouldClip && (mType == Type::RoundRect); } bool willRoundRectClip() const { @@ -108,7 +108,7 @@ public: } bool getAsRoundRect(Rect* outRect, float* outRadius) const { - if (mType == kOutlineType_RoundRect) { + if (mType == Type::RoundRect) { outRect->set(mBounds); *outRadius = mRadius; return true; @@ -117,21 +117,21 @@ public: } const SkPath* getPath() const { - if (mType == kOutlineType_None || mType == kOutlineType_Empty) return nullptr; + if (mType == Type::None || mType == Type::Empty) return nullptr; return &mPath; } private: - enum OutlineType { - kOutlineType_None = 0, - kOutlineType_Empty = 1, - kOutlineType_ConvexPath = 2, - kOutlineType_RoundRect = 3 + enum class Type { + None = 0, + Empty = 1, + ConvexPath = 2, + RoundRect = 3 }; bool mShouldClip; - OutlineType mType; + Type mType; Rect mBounds; float mRadius; float mAlpha; diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index af1e4a74d46e..b09c207f6e45 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -103,10 +103,10 @@ typedef uint64_t programid; * A ProgramDescription must be used in conjunction with a ProgramCache. */ struct ProgramDescription { - enum ColorFilterMode { - kColorNone = 0, - kColorMatrix, - kColorBlend + enum class ColorFilterMode { + None = 0, + Matrix, + Blend }; enum Gradient { @@ -193,7 +193,7 @@ struct ProgramDescription { bitmapWrapS = GL_CLAMP_TO_EDGE; bitmapWrapT = GL_CLAMP_TO_EDGE; - colorOp = kColorNone; + colorOp = ColorFilterMode::None; colorMode = SkXfermode::kClear_Mode; framebufferMode = SkXfermode::kClear_Mode; @@ -249,14 +249,14 @@ struct ProgramDescription { key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT; } switch (colorOp) { - case kColorMatrix: + case ColorFilterMode::Matrix: key |= PROGRAM_KEY_COLOR_MATRIX; break; - case kColorBlend: + case ColorFilterMode::Blend: key |= PROGRAM_KEY_COLOR_BLEND; key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT; break; - case kColorNone: + case ColorFilterMode::None: break; } key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 7f16deb41813..b25a4ac9558d 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -38,7 +38,8 @@ namespace uirenderer { // Vertex shaders snippets /////////////////////////////////////////////////////////////////////////////// -const char* gVS_Header_Attributes = +const char* gVS_Header_Start = + "#version 100\n" "attribute vec4 position;\n"; const char* gVS_Header_Attributes_TexCoords = "attribute vec2 texCoords;\n"; @@ -132,6 +133,8 @@ const char* gVS_Footer = // Fragment shaders snippets /////////////////////////////////////////////////////////////////////////////// +const char* gFS_Header_Start = + "#version 100\n"; const char* gFS_Header_Extension_FramebufferFetch = "#extension GL_NV_shader_framebuffer_fetch : enable\n\n"; const char* gFS_Header_Extension_ExternalTexture = @@ -457,7 +460,7 @@ static inline size_t gradientIndex(const ProgramDescription& description) { String8 ProgramCache::generateVertexShader(const ProgramDescription& description) { // Add attributes - String8 shader(gVS_Header_Attributes); + String8 shader(gVS_Header_Start); if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Attributes_TexCoords); } @@ -543,7 +546,7 @@ static bool shaderOp(const ProgramDescription& description, String8& shader, } String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) { - String8 shader; + String8 shader(gFS_Header_Start); const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode; if (blendFramebuffer) { @@ -604,7 +607,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (!description.hasVertexAlpha && !blendFramebuffer && !description.hasColors - && description.colorOp == ProgramDescription::kColorNone + && description.colorOp == ProgramDescription::ColorFilterMode::None && !description.hasDebugHighlight && !description.hasRoundRectClip) { bool fast = false; @@ -668,13 +671,13 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasBitmap) { shader.append(gFS_Uniforms_BitmapSampler); } - shader.append(gFS_Uniforms_ColorOp[description.colorOp]); + shader.append(gFS_Uniforms_ColorOp[static_cast<int>(description.colorOp)]); // Generate required functions if (description.hasGradient && description.hasBitmap) { generateBlend(shader, "blendShaders", description.shadersMode); } - if (description.colorOp == ProgramDescription::kColorBlend) { + if (description.colorOp == ProgramDescription::ColorFilterMode::Blend) { generateBlend(shader, "blendColors", description.colorMode); } if (blendFramebuffer) { @@ -737,7 +740,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } // Apply the color op if needed - shader.append(gFS_Main_ApplyColorOp[description.colorOp]); + shader.append(gFS_Main_ApplyColorOp[static_cast<int>(description.colorOp)]); if (description.hasVertexAlpha) { if (description.useShadowAlphaInterp) { diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 73c01075e842..7d09c0beaf2a 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -710,8 +710,8 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) { const int size = zTranslatedNodes.size(); if (size == 0 - || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f) - || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) { + || (mode == ChildrenSelectMode::NegativeZChildren && zTranslatedNodes[0].key > 0.0f) + || (mode == ChildrenSelectMode::PositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) { // no 3d children to draw return; } @@ -730,7 +730,7 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, */ const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes); size_t drawIndex, shadowIndex, endIndex; - if (mode == kNegativeZChildren) { + if (mode == ChildrenSelectMode::NegativeZChildren) { drawIndex = 0; endIndex = nonNegativeIndex; shadowIndex = endIndex; // draw no shadows @@ -875,7 +875,7 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { renderer.setBaseTransform(initialTransform); if (drawLayer) { - handler(new (alloc) DrawLayerOp(mLayer, 0, 0), + handler(new (alloc) DrawLayerOp(mLayer), renderer.getSaveCount() - 1, properties().getClipToBounds()); } else { const int saveCountOffset = renderer.getSaveCount() - 1; @@ -886,7 +886,7 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { std::vector<ZDrawRenderNodeOpPair> zTranslatedNodes; buildZSortedChildList(chunk, zTranslatedNodes); - issueOperationsOf3dChildren(kNegativeZChildren, + issueOperationsOf3dChildren(ChildrenSelectMode::NegativeZChildren, initialTransform, zTranslatedNodes, renderer, handler); @@ -903,7 +903,7 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { } } - issueOperationsOf3dChildren(kPositiveZChildren, + issueOperationsOf3dChildren(ChildrenSelectMode::PositiveZChildren, initialTransform, zTranslatedNodes, renderer, handler); } } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 6d1a2b7be1f9..3bff2b393f74 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -184,9 +184,9 @@ private: return nodes.size(); } - enum ChildrenSelectMode { - kNegativeZChildren, - kPositiveZChildren + enum class ChildrenSelectMode { + NegativeZChildren, + PositiveZChildren }; void computeOrderingImpl(DrawRenderNodeOp* opState, diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 0c5b4b7c8e24..36633b5205a1 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -701,7 +701,7 @@ void SkiaCanvas::drawText(const uint16_t* text, const float* positions, int coun SkPaint paintCopy(paint); paintCopy.setTextAlign(SkPaint::kLeft_Align); - SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy); } diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp index 41152dcb879d..c3f5eb27e236 100644 --- a/libs/hwui/SkiaCanvasProxy.cpp +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -45,7 +45,7 @@ void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPo } // convert the SkPoints into floats - SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); const size_t floatCount = count << 1; const float* floatArray = &pts[0].fX; @@ -154,7 +154,7 @@ void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkP return; } // convert the SkPoints into floats - SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); const int floatCount = vertexCount << 1; const float* vArray = &vertices[0].fX; const float* tArray = (texs) ? &texs[0].fX : NULL; @@ -284,7 +284,7 @@ void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x } } - SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0); } @@ -320,7 +320,7 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds); bounds.offset(x, y); - SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0); } diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 1f959469fb56..6c105cfc7b00 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -32,12 +32,19 @@ namespace uirenderer { // Support /////////////////////////////////////////////////////////////////////////////// -static const GLenum gTileModes[] = { +static constexpr GLenum gTileModes[] = { GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode GL_REPEAT, // == SkShader::kRepeat_Mode GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode }; +static_assert(gTileModes[SkShader::kClamp_TileMode] == GL_CLAMP_TO_EDGE, + "SkShader TileModes have changed"); +static_assert(gTileModes[SkShader::kRepeat_TileMode] == GL_REPEAT, + "SkShader TileModes have changed"); +static_assert(gTileModes[SkShader::kMirror_TileMode] == GL_MIRRORED_REPEAT, + "SkShader TileModes have changed"); + /** * This function does not work for n == 0. */ diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index ca19a423e5c8..4d60b8dd0e7c 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -83,24 +83,24 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) // Clipping /////////////////////////////////////////////////////////////////////////////// -bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) { +void Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) { flags |= Snapshot::kFlagClipSet; - return mClipArea->clipRegion(region, op); + mClipArea->clipRegion(region, op); } -bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) { +void Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) { flags |= Snapshot::kFlagClipSet; - return mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op); + mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op); } -bool Snapshot::clipPath(const SkPath& path, SkRegion::Op op) { +void Snapshot::clipPath(const SkPath& path, SkRegion::Op op) { flags |= Snapshot::kFlagClipSet; - return mClipArea->clipPathWithTransform(path, transform, op); + mClipArea->clipPathWithTransform(path, transform, op); } void Snapshot::setClip(float left, float top, float right, float bottom) { - mClipArea->setClip(left, top, right, bottom); flags |= Snapshot::kFlagClipSet; + mClipArea->setClip(left, top, right, bottom); } bool Snapshot::hasPerspectiveTransform() const { diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index af6ad727da85..cf8f11c80058 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -124,26 +124,25 @@ public: * the specified operation. The specified rectangle is transformed * by this snapshot's trasnformation. */ - bool clip(float left, float top, float right, float bottom, - SkRegion::Op op = SkRegion::kIntersect_Op); + void clip(float left, float top, float right, float bottom, SkRegion::Op op); /** * Modifies the current clip with the new clip rectangle and * the specified operation. The specified rectangle is considered * already transformed. */ - bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op); + void clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op); /** * Modifies the current clip with the specified region and operation. * The specified region is considered already transformed. */ - bool clipRegionTransformed(const SkRegion& region, SkRegion::Op op); + void clipRegionTransformed(const SkRegion& region, SkRegion::Op op); /** * Modifies the current clip with the specified path and operation. */ - bool clipPath(const SkPath& path, SkRegion::Op op); + void clipPath(const SkPath& path, SkRegion::Op op); /** * Sets the current clip. diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp index 29927ed8667b..b21e15e8fad9 100644 --- a/libs/hwui/renderstate/Blend.cpp +++ b/libs/hwui/renderstate/Blend.cpp @@ -30,6 +30,26 @@ struct Blender { GLenum dst; }; +// assumptions made by lookup tables in either this file or ProgramCache +static_assert(0 == SkXfermode::kClear_Mode, "SkXfermode enums have changed"); +static_assert(1 == SkXfermode::kSrc_Mode, "SkXfermode enums have changed"); +static_assert(2 == SkXfermode::kDst_Mode, "SkXfermode enums have changed"); +static_assert(3 == SkXfermode::kSrcOver_Mode, "SkXfermode enums have changed"); +static_assert(4 == SkXfermode::kDstOver_Mode, "SkXfermode enums have changed"); +static_assert(5 == SkXfermode::kSrcIn_Mode, "SkXfermode enums have changed"); +static_assert(6 == SkXfermode::kDstIn_Mode, "SkXfermode enums have changed"); +static_assert(7 == SkXfermode::kSrcOut_Mode, "SkXfermode enums have changed"); +static_assert(8 == SkXfermode::kDstOut_Mode, "SkXfermode enums have changed"); +static_assert(9 == SkXfermode::kSrcATop_Mode, "SkXfermode enums have changed"); +static_assert(10 == SkXfermode::kDstATop_Mode, "SkXfermode enums have changed"); +static_assert(11 == SkXfermode::kXor_Mode, "SkXfermode enums have changed"); +static_assert(12 == SkXfermode::kPlus_Mode, "SkXfermode enums have changed"); +static_assert(13 == SkXfermode::kModulate_Mode, "SkXfermode enums have changed"); +static_assert(14 == SkXfermode::kScreen_Mode, "SkXfermode enums have changed"); +static_assert(15 == SkXfermode::kOverlay_Mode, "SkXfermode enums have changed"); +static_assert(16 == SkXfermode::kDarken_Mode, "SkXfermode enums have changed"); +static_assert(17 == SkXfermode::kLighten_Mode, "SkXfermode enums have changed"); + // In this array, the index of each Blender equals the value of the first // entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode] const Blender kBlends[] = { diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index b5ed9e6558a7..c5126def683c 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -229,11 +229,11 @@ void RenderState::render(const Glop& glop) { glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor); // Color filter uniforms - if (fill.filterMode == ProgramDescription::kColorBlend) { + if (fill.filterMode == ProgramDescription::ColorFilterMode::Blend) { const FloatColor& color = fill.filter.color; glUniform4f(mCaches->program().getUniform("colorBlend"), color.r, color.g, color.b, color.a); - } else if (fill.filterMode == ProgramDescription::kColorMatrix) { + } else if (fill.filterMode == ProgramDescription::ColorFilterMode::Matrix) { glUniformMatrix4fv(mCaches->program().getUniform("colorMatrix"), 1, GL_FALSE, fill.filter.matrix.matrix); glUniform4fv(mCaches->program().getUniform("colorMatrixVector"), 1, diff --git a/libs/hwui/renderstate/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp index 319cfe4ba0d0..d25ad514e892 100644 --- a/libs/hwui/renderstate/Stencil.cpp +++ b/libs/hwui/renderstate/Stencil.cpp @@ -34,10 +34,6 @@ namespace uirenderer { #define STENCIL_MASK_VALUE 0x1 #endif -Stencil::Stencil() - : mState(kDisabled) { -} - uint8_t Stencil::getStencilSize() { return STENCIL_BUFFER_SIZE; } @@ -64,14 +60,14 @@ void Stencil::clear() { glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); - if (mState == kTest) { + if (mState == StencilState::Test) { // reset to test state, with immutable stencil glStencilMask(0); } } void Stencil::enableTest(int incrementThreshold) { - if (mState != kTest) { + if (mState != StencilState::Test) { enable(); if (incrementThreshold > 0) { glStencilFunc(GL_EQUAL, incrementThreshold, 0xff); @@ -82,12 +78,12 @@ void Stencil::enableTest(int incrementThreshold) { glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilMask(0); - mState = kTest; + mState = StencilState::Test; } } void Stencil::enableWrite(int incrementThreshold) { - if (mState != kWrite) { + if (mState != StencilState::Write) { enable(); if (incrementThreshold > 0) { glStencilFunc(GL_ALWAYS, 1, 0xff); @@ -100,7 +96,7 @@ void Stencil::enableWrite(int incrementThreshold) { } glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glStencilMask(0xff); - mState = kWrite; + mState = StencilState::Write; } } @@ -109,7 +105,7 @@ void Stencil::enableDebugTest(GLint value, bool greater) { glStencilFunc(greater ? GL_LESS : GL_EQUAL, value, 0xffffffff); // We only want to test, let's keep everything glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - mState = kTest; + mState = StencilState::Test; glStencilMask(0); } @@ -119,20 +115,20 @@ void Stencil::enableDebugWrite() { // The test always passes so the first two values are meaningless glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - mState = kWrite; + mState = StencilState::Write; glStencilMask(0xff); } void Stencil::enable() { - if (mState == kDisabled) { + if (mState == StencilState::Disabled) { glEnable(GL_STENCIL_TEST); } } void Stencil::disable() { - if (mState != kDisabled) { + if (mState != StencilState::Disabled) { glDisable(GL_STENCIL_TEST); - mState = kDisabled; + mState = StencilState::Disabled; } } diff --git a/libs/hwui/renderstate/Stencil.h b/libs/hwui/renderstate/Stencil.h index 32618230bbf6..5f7d4056b51d 100644 --- a/libs/hwui/renderstate/Stencil.h +++ b/libs/hwui/renderstate/Stencil.h @@ -30,8 +30,6 @@ namespace uirenderer { class ANDROID_API Stencil { public: - Stencil(); - /** * Returns the desired size for the stencil buffer. If the returned value * is 0, then no stencil buffer is required. @@ -81,32 +79,31 @@ public: * Indicates whether either test or write is enabled. */ bool isEnabled() { - return mState != kDisabled; + return mState != StencilState::Disabled; } /** * Indicates whether testing only is enabled. */ bool isTestEnabled() { - return mState == kTest; + return mState == StencilState::Test; } bool isWriteEnabled() { - return mState == kWrite; + return mState == StencilState::Write; } void dump(); private: - void enable(); - - enum StencilState { - kDisabled, - kTest, - kWrite + enum class StencilState { + Disabled, + Test, + Write }; - StencilState mState; + void enable(); + StencilState mState = StencilState::Disabled; }; // class Stencil diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 67c42f3c9977..7cb773805c21 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -421,7 +421,7 @@ void CanvasContext::destroyHardwareResources() { // Make sure to release all the textures we were owning as there won't // be another draw caches.textureCache.resetMarkInUse(this); - caches.flush(Caches::kFlushMode_Layers); + caches.flush(Caches::FlushMode::Layers); } } @@ -431,10 +431,10 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { ATRACE_CALL(); if (level >= TRIM_MEMORY_COMPLETE) { - Caches::getInstance().flush(Caches::kFlushMode_Full); + Caches::getInstance().flush(Caches::FlushMode::Full); thread.eglManager().destroy(); } else if (level >= TRIM_MEMORY_UI_HIDDEN) { - Caches::getInstance().flush(Caches::kFlushMode_Moderate); + Caches::getInstance().flush(Caches::FlushMode::Moderate); } } diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 0f1be6b76d87..e92f29484dc5 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -671,15 +671,15 @@ public final class AudioAttributes implements Parcelable { case USAGE_VOICE_COMMUNICATION: return new String("USAGE_VOICE_COMMUNICATION"); case USAGE_VOICE_COMMUNICATION_SIGNALLING: - return new String("USAGE_VOICE_COMMUNICATION"); + return new String("USAGE_VOICE_COMMUNICATION_SIGNALLING"); case USAGE_ALARM: return new String("USAGE_ALARM"); case USAGE_NOTIFICATION: return new String("USAGE_NOTIFICATION"); case USAGE_NOTIFICATION_RINGTONE: - return new String("USAGE_NOTIFICATION"); + return new String("USAGE_NOTIFICATION_RINGTONE"); case USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - return new String("USAGE_NOTIFICATION"); + return new String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST"); case USAGE_NOTIFICATION_COMMUNICATION_INSTANT: return new String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT"); case USAGE_NOTIFICATION_COMMUNICATION_DELAYED: diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index e99a37a41d30..c59d1c7b9999 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -279,6 +279,7 @@ public class AudioSystem public static final int PERMISSION_DENIED = -4; public static final int NO_INIT = -5; public static final int DEAD_OBJECT = -6; + public static final int WOULD_BLOCK = -7; /* * AudioPolicyService methods diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 8880dad99270..bb4f7d9b7ad9 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -158,6 +158,18 @@ public class AudioTrack * Denotes a failure due to the improper use of a method. */ public static final int ERROR_INVALID_OPERATION = AudioSystem.INVALID_OPERATION; + /** + * An error code indicating that the object reporting it is no longer valid and needs to + * be recreated. + * @hide + */ + public static final int ERROR_DEAD_OBJECT = AudioSystem.DEAD_OBJECT; + /** + * {@link #getTimestampWithStatus(AudioTimestamp)} is called in STOPPED or FLUSHED state, + * or immediately after start/ACTIVE. + * @hide + */ + public static final int ERROR_WOULD_BLOCK = AudioSystem.WOULD_BLOCK; // Error codes: // to keep in sync with frameworks/base/core/jni/android_media_AudioTrack.cpp @@ -1225,6 +1237,44 @@ public class AudioTrack return true; } + /** + * Poll for a timestamp on demand. + * <p> + * Same as {@link #getTimestamp(AudioTimestamp)} but with a more useful return code. + * + * @param timestamp a reference to a non-null AudioTimestamp instance allocated + * and owned by caller. + * @return {@link #SUCCESS} if a timestamp is available + * {@link #ERROR_WOULD_BLOCK} if called in STOPPED or FLUSHED state, or if called + * immediately after start/ACTIVE, when the number of frames consumed is less than the + * overall hardware latency to physical output. In WOULD_BLOCK cases, one might poll + * again, or use {@link #getPlaybackHeadPosition}, or use 0 position and current time + * for the timestamp. + * {@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and + * needs to be recreated. + * {@link #ERROR_INVALID_OPERATION} if current route does not support + * timestamps. In this case, the approximate frame position can be obtained + * using {@link #getPlaybackHeadPosition}. + * + * The AudioTimestamp instance is filled in with a position in frame units, together + * with the estimated time when that frame was presented or is committed to + * be presented. + * @hide + */ + // Add this text when the "on new timestamp" API is added: + // Use if you need to get the most recent timestamp outside of the event callback handler. + public int getTimestampWithStatus(AudioTimestamp timestamp) + { + if (timestamp == null) { + throw new IllegalArgumentException(); + } + // It's unfortunate, but we have to either create garbage every time or use synchronized + long[] longArray = new long[2]; + int ret = native_get_timestamp(longArray); + timestamp.framePosition = longArray[0]; + timestamp.nanoTime = longArray[1]; + return ret; + } //-------------------------------------------------------------------------- // Initialization / configuration diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html index 7baa5bc42bd2..45fb5791dfa7 100644 --- a/media/java/android/media/midi/package.html +++ b/media/java/android/media/midi/package.html @@ -202,11 +202,12 @@ MidiInputPort inputPort = device.openInputPort(index); <p>MIDI messages are sent as byte arrays. Here we encode a NoteOn message.</p> <pre class=prettyprint> -byte[] buffer = new buffer[64]; +byte[] buffer = new byte[32]; int numBytes = 0; -buffer[numBytes++] = 0x90 + channel; // note on -buffer[numBytes++] = pitch; -buffer[numBytes++] = velocity; +int channel = 3; // MIDI channels 1-16 are encoded as 0-15. +buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on +buffer[numBytes++] = (byte)60; // pitch is middle C +buffer[numBytes++] = (byte)127; // max velocity int offset = 0; // post is non-blocking inputPort.send(buffer, offset, numBytes); diff --git a/media/java/android/mtp/MtpObjectInfo.java b/media/java/android/mtp/MtpObjectInfo.java index d2824b556315..f79d52ee8c56 100644 --- a/media/java/android/mtp/MtpObjectInfo.java +++ b/media/java/android/mtp/MtpObjectInfo.java @@ -256,7 +256,7 @@ public final class MtpObjectInfo { /** * Builds a new object info instance. */ - public class Builder { + public static class Builder { private MtpObjectInfo mObjectInfo; public Builder() { diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index ad804f32fd9f..9dd3861177e9 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -430,12 +430,14 @@ static jobject android_mtp_MtpDevice_send_object_info(JNIEnv *env, jobject thiz, jobject info) { MtpDevice* device = get_device_from_object(env, thiz); - if (!device) + if (!device) { return JNI_FALSE; + } // Updating existing objects is not supported. - if (env->GetIntField(info, field_objectInfo_handle) != -1) + if (env->GetIntField(info, field_objectInfo_handle) != -1) { return JNI_FALSE; + } MtpObjectInfo* object_info = new MtpObjectInfo(-1); object_info->mStorageID = env->GetIntField(info, field_objectInfo_storageId); @@ -456,17 +458,21 @@ android_mtp_MtpDevice_send_object_info(JNIEnv *env, jobject thiz, jobject info) object_info->mSequenceNumber = env->GetIntField(info, field_objectInfo_sequenceNumber); jstring name_jstring = (jstring) env->GetObjectField(info, field_objectInfo_name); - const char* name_string = env->GetStringUTFChars(name_jstring, NULL); - object_info->mName = strdup(name_string); - env->ReleaseStringUTFChars(name_jstring, name_string); + if (name_jstring != NULL) { + const char* name_string = env->GetStringUTFChars(name_jstring, NULL); + object_info->mName = strdup(name_string); + env->ReleaseStringUTFChars(name_jstring, name_string); + } object_info->mDateCreated = env->GetLongField(info, field_objectInfo_dateCreated) / 1000LL; object_info->mDateModified = env->GetLongField(info, field_objectInfo_dateModified) / 1000LL; jstring keywords_jstring = (jstring) env->GetObjectField(info, field_objectInfo_keywords); - const char* keywords_string = env->GetStringUTFChars(keywords_jstring, NULL); - object_info->mKeywords = strdup(keywords_string); - env->ReleaseStringUTFChars(keywords_jstring, keywords_string); + if (keywords_jstring != NULL) { + const char* keywords_string = env->GetStringUTFChars(keywords_jstring, NULL); + object_info->mKeywords = strdup(keywords_string); + env->ReleaseStringUTFChars(keywords_jstring, keywords_string); + } int object_handle = device->sendObjectInfo(object_info); if (object_handle == -1) { diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index a0e3868c1c9d..d57876922a07 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -48,8 +48,8 @@ </activity> <activity - android:name=".StandaloneActivity" - android:theme="@style/StandaloneTheme" + android:name=".FilesActivity" + android:theme="@style/FilesTheme" android:icon="@drawable/ic_files_app" android:label="@string/files_label" android:enabled="@bool/productivity_device"> diff --git a/packages/DocumentsUI/res/layout/activity.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml index 32431e3a3f78..32431e3a3f78 100644 --- a/packages/DocumentsUI/res/layout/activity.xml +++ b/packages/DocumentsUI/res/layout/drawer_layout.xml diff --git a/packages/DocumentsUI/res/layout-sw720dp/activity.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml index 221de1300edf..221de1300edf 100644 --- a/packages/DocumentsUI/res/layout-sw720dp/activity.xml +++ b/packages/DocumentsUI/res/layout/fixed_layout.xml diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml index 1001e105bcd0..71e618bf4314 100644 --- a/packages/DocumentsUI/res/layout/item_doc_grid.xml +++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/grid_item_height" android:layout_margin="@dimen/grid_item_margin" @@ -140,4 +140,4 @@ android:contentDescription="@null" android:duplicateParentState="true" /> -</FrameLayout> +</com.android.documentsui.GridItem> diff --git a/packages/DocumentsUI/res/layout/item_loading_grid.xml b/packages/DocumentsUI/res/layout/item_loading_grid.xml index 005a111beabd..147dfd449724 100644 --- a/packages/DocumentsUI/res/layout/item_loading_grid.xml +++ b/packages/DocumentsUI/res/layout/item_loading_grid.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/grid_height" android:orientation="horizontal"> @@ -26,4 +26,4 @@ android:indeterminate="true" style="?android:attr/progressBarStyle" /> -</FrameLayout> +</com.android.documentsui.GridItem> diff --git a/packages/DocumentsUI/res/layout/item_message_grid.xml b/packages/DocumentsUI/res/layout/item_message_grid.xml index 385563df64f6..45d61a506bb5 100644 --- a/packages/DocumentsUI/res/layout/item_message_grid.xml +++ b/packages/DocumentsUI/res/layout/item_message_grid.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/grid_height" android:paddingStart="?android:attr/listPreferredItemPaddingStart" @@ -48,4 +48,4 @@ </LinearLayout> -</FrameLayout> +</com.android.documentsui.GridItem> diff --git a/packages/DocumentsUI/res/values-sw720dp-land/layouts.xml b/packages/DocumentsUI/res/values-sw720dp-land/layouts.xml new file mode 100644 index 000000000000..0e1807ce165b --- /dev/null +++ b/packages/DocumentsUI/res/values-sw720dp-land/layouts.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <item name="files_activity" type="layout">@layout/fixed_layout</item> +</resources> diff --git a/packages/DocumentsUI/res/values-sw720dp/layouts.xml b/packages/DocumentsUI/res/values-sw720dp/layouts.xml new file mode 100644 index 000000000000..7d28f9cbc2ac --- /dev/null +++ b/packages/DocumentsUI/res/values-sw720dp/layouts.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <item name="docs_activity" type="layout">@layout/fixed_layout</item> +</resources> diff --git a/packages/DocumentsUI/res/values/layouts.xml b/packages/DocumentsUI/res/values/layouts.xml new file mode 100644 index 000000000000..c73a1cbf2538 --- /dev/null +++ b/packages/DocumentsUI/res/values/layouts.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <item name="docs_activity" type="layout">@layout/drawer_layout</item> + <item name="files_activity" type="layout">@layout/drawer_layout</item> +</resources> diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml index 95c1d9cb56df..fa94ff1bd353 100644 --- a/packages/DocumentsUI/res/values/styles.xml +++ b/packages/DocumentsUI/res/values/styles.xml @@ -63,7 +63,7 @@ <item name="android:colorAccent">@color/material_blue_700</item> </style> - <style name="StandaloneTheme" parent="@android:style/Theme.Material.DayNight.DarkActionBar"> + <style name="FilesTheme" parent="@android:style/Theme.Material.DayNight.DarkActionBar"> <item name="android:actionBarWidgetTheme">@null</item> <item name="android:colorPrimaryDark">@color/status_bar_background</item> diff --git a/packages/DocumentsUI/src/com/android/documentsui/BandSelectMatrix.java b/packages/DocumentsUI/src/com/android/documentsui/BandSelectMatrix.java new file mode 100644 index 000000000000..f2590592c52a --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/BandSelectMatrix.java @@ -0,0 +1,645 @@ +/* + * 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 java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.base.Preconditions; + +import android.graphics.Point; +import android.graphics.Rect; +import android.support.annotation.Nullable; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.LayoutManager; +import android.support.v7.widget.RecyclerView.OnScrollListener; +import android.util.SparseBooleanArray; +import android.view.View; + +/** + * Provides a band selection item model for views within a RecyclerView. This class queries the + * RecyclerView to determine where its items are placed; then, once band selection is underway, it + * alerts listeners of which items are covered by the selections. + */ +public final class BandSelectMatrix extends RecyclerView.OnScrollListener { + + private final RecyclerViewHelper mHelper; + private final List<OnSelectionChangedListener> mOnSelectionChangedListeners = new ArrayList<>(); + + // Map from the x-value of the left side of an item to an ordered list of metadata of all items + // whose x-values are the same. The list is ordered by the y-values of the items in the column. + // For example, if the first column of the view starts at an x-value of 5, mColumns.get(5) would + // return a list of all items in that column, with the top-most item first in the list and the + // bottom-most item last in the list. + private final Map<Integer, List<ItemData>> mColumns = new HashMap<>(); + + // List of limits along the x-axis. For example, if the view has two columns, this list will + // have two elements, each of which lists the lower- and upper-limits of the x-values of the + // view items. This list is sorted from furthest left to furthest right. + private final List<Limits> mXLimitsList = new ArrayList<>(); + + // Like mXLimitsList, but for y-coordinates. Note that this list only contains items which have + // been in the viewport. Thus, limits which exist in an area of the view to which the view has + // not scrolled are not present in the list. + private final List<Limits> mYLimitsList = new ArrayList<>(); + + // The adapter positions which have been recorded so far. + private final SparseBooleanArray mRecordedPositions = new SparseBooleanArray(); + + // Array passed to registered OnSelectionChangedListeners. One array is created and reused + // throughout the lifetime of the object. + private final SparseBooleanArray mSelectionForListeners = new SparseBooleanArray(); + + // The current pointer (in absolute positioning from the top of the view). + private Point mPointer = null; + + // The bounds of the band selection. + private RelativePoint mRelativeOrigin; + private RelativePoint mRelativePointer; + + BandSelectMatrix(RecyclerViewHelper helper) { + mHelper = helper; + mHelper.addOnScrollListener(this); + } + + BandSelectMatrix(RecyclerView rv) { + this(new RuntimeRecyclerViewHelper(rv)); + } + + /** + * Stops listening to the view's scrolls. Call this function before discarding a + * BandSelecMatrix object to prevent memory leaks. + */ + void stopListening() { + mHelper.removeOnScrollListener(this); + } + + /** + * Start a band select operation at the given point. + * @param relativeOrigin The origin of the band select operation, relative to the viewport. + * For example, if the view is scrolled to the bottom, the top-left of the viewport would + * have a relative origin of (0, 0), even though its absolute point has a higher y-value. + */ + void startSelection(Point relativeOrigin) { + Point absoluteOrigin = mHelper.createAbsolutePoint(relativeOrigin); + mPointer = new Point(absoluteOrigin.x, absoluteOrigin.y); + + processVisibleChildren(); + mRelativeOrigin = new RelativePoint(absoluteOrigin); + mRelativePointer = new RelativePoint(mPointer); + computeCurrentSelection(); + notifyListeners(); + } + + /** + * Resizes the selection by adjusting the pointer (i.e., the corner of the selection opposite + * the origin. + * @param relativePointer The pointer (opposite of the origin) of the band select operation, + * relative to the viewport. For example, if the view is scrolled to the bottom, the + * top-left of the viewport would have a relative origin of (0, 0), even though its absolute + * point has a higher y-value. + */ + void resizeSelection(Point relativePointer) { + mPointer = mHelper.createAbsolutePoint(relativePointer); + handlePointerMoved(); + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (mPointer == null) { + return; + } + + mPointer.x += dx; + mPointer.y += dy; + processVisibleChildren(); + handlePointerMoved(); + } + + /** + * Queries the view for all children and records their location metadata. + */ + private void processVisibleChildren() { + for (int i = 0; i < mHelper.getVisibleChildCount(); i++) { + int adapterPosition = mHelper.getAdapterPositionAt(i); + if (!mRecordedPositions.get(adapterPosition)) { + mRecordedPositions.put(adapterPosition, true); + captureItemLayoutData(mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition); + } + } + } + + /** + * Updates the limits lists and column map with the given item metadata. + * @param absoluteChildRect The absolute rectangle for the child view being processed. + * @param adapterPosition The position of the child view being processed. + */ + private void captureItemLayoutData(Rect absoluteChildRect, int adapterPosition) { + if (mXLimitsList.size() != mHelper.getNumColumns()) { + // If not all x-limits have been recorded, record this one. + recordLimits( + mXLimitsList, new Limits(absoluteChildRect.left, absoluteChildRect.right)); + } + + if (mYLimitsList.size() != mHelper.getNumRows()) { + // If not all y-limits have been recorded, record this one. + recordLimits( + mYLimitsList, new Limits(absoluteChildRect.top, absoluteChildRect.bottom)); + } + + List<ItemData> columnList = mColumns.get(absoluteChildRect.left); + if (columnList == null) { + columnList = new ArrayList<ItemData>(); + mColumns.put(absoluteChildRect.left, columnList); + } + recordItemData( + columnList, new ItemData(adapterPosition, absoluteChildRect.top)); + } + + /** + * Ensures limits exists within the sorted list limitsList, and adds it to the list if it does + * not exist. + */ + private static void recordLimits(List<Limits> limitsList, Limits limits) { + int index = Collections.binarySearch(limitsList, limits); + if (index < 0) { + limitsList.add(~index, limits); + } + } + + /** + * Ensures itemData exists within the sorted list itemDataList, and adds it to the list if it + * does not exist. + */ + private static void recordItemData(List<ItemData> itemDataList, ItemData itemData) { + int index = Collections.binarySearch(itemDataList, itemData); + if (index < 0) { + itemDataList.add(~index, itemData); + } + } + + /** + * Handles a moved pointer; this function determines whether the pointer movement resulted in a + * selection change and, if it has, notifies listeners of this change. + */ + private void handlePointerMoved() { + RelativePoint old = mRelativePointer; + mRelativePointer = new RelativePoint(mPointer); + if (old != null && mRelativePointer.equals(old)) { + return; + } + + computeCurrentSelection(); + notifyListeners(); + } + + /** + * Computes the currently-selected items. + */ + private void computeCurrentSelection() { + Rect selectionRect = mRelativePointer.computeBounds(mRelativeOrigin); + computePositionsCoveredByRect(selectionRect); + } + + /** + * Notifies all listeners of a selection change. Note that this function simply passes + * mSelectionForListeners, so computeCurrentSelection() should be called before this function. + */ + private void notifyListeners() { + for (OnSelectionChangedListener listener : mOnSelectionChangedListeners) { + listener.onSelectionChanged(mSelectionForListeners); + } + } + + /** + * @param rect Rectangle including all covered items. + */ + private void computePositionsCoveredByRect(@Nullable Rect rect) { + mSelectionForListeners.clear(); + if (rect == null) { + // If there is no bounding rectangle, there are no items selected, so just return early. + return; + } + + int columnIndex = Collections.binarySearch(mXLimitsList, new Limits(rect.left, rect.left)); + Preconditions.checkState(columnIndex >= 0); + + for (; columnIndex < mXLimitsList.size() && + mXLimitsList.get(columnIndex).lowerLimit <= rect.right; columnIndex++) { + List<ItemData> positions = + mColumns.get(mXLimitsList.get(columnIndex).lowerLimit); + int rowIndex = Collections.binarySearch(positions, new ItemData(0, rect.top)); + if (rowIndex < 0) { + // If band select occurs after the last item in a row with fewer items than columns, + // go to the next column. This situation occurs in the last row of the grid when the + // total number of items is not a multiple of the number of columns (e.g., when 10 + // items exist in a grid with 4 columns). + continue; + } + + for (; rowIndex < positions.size() && + positions.get(rowIndex).offset <= rect.bottom; rowIndex++) { + mSelectionForListeners.append(positions.get(rowIndex).position, true); + } + } + } + + /** + * Provides functionality for interfacing with the view. In practice, RecyclerViewMatrixHelper + * should be used; this interface exists solely for the purpose of decoupling the view from + * this class so that the view can be mocked out for tests. + */ + interface RecyclerViewHelper { + public void addOnScrollListener(RecyclerView.OnScrollListener listener); + public void removeOnScrollListener(RecyclerView.OnScrollListener listener); + public Point createAbsolutePoint(Point relativePoint); + public int getVisibleChildCount(); + public int getTotalChildCount(); + public int getNumColumns(); + public int getNumRows(); + public int getAdapterPositionAt(int index); + public Rect getAbsoluteRectForChildViewAt(int index); + } + + /** + * Concrete MatrixHelper implementation for use within the Files app. + */ + static class RuntimeRecyclerViewHelper implements RecyclerViewHelper { + private final RecyclerView mRecyclerView; + + RuntimeRecyclerViewHelper(RecyclerView rv) { + mRecyclerView = rv; + } + + @Override + public int getAdapterPositionAt(int index) { + View child = mRecyclerView.getChildAt(index); + return mRecyclerView.getChildViewHolder(child).getAdapterPosition(); + } + + @Override + public void addOnScrollListener(OnScrollListener listener) { + mRecyclerView.addOnScrollListener(listener); + } + + @Override + public void removeOnScrollListener(OnScrollListener listener) { + mRecyclerView.removeOnScrollListener(listener); + } + + @Override + public Point createAbsolutePoint(Point relativePoint) { + return new Point(relativePoint.x + mRecyclerView.computeHorizontalScrollOffset(), + relativePoint.y + mRecyclerView.computeVerticalScrollOffset()); + } + + @Override + public Rect getAbsoluteRectForChildViewAt(int index) { + final View child = mRecyclerView.getChildAt(index); + final Rect childRect = new Rect(); + child.getHitRect(childRect); + childRect.left += mRecyclerView.computeHorizontalScrollOffset(); + childRect.right += mRecyclerView.computeHorizontalScrollOffset(); + childRect.top += mRecyclerView.computeVerticalScrollOffset(); + childRect.bottom += mRecyclerView.computeVerticalScrollOffset(); + return childRect; + } + + @Override + public int getVisibleChildCount() { + return mRecyclerView.getChildCount(); + } + + @Override + public int getTotalChildCount() { + return mRecyclerView.getAdapter().getItemCount(); + } + + @Override + public int getNumColumns() { + LayoutManager layoutManager = mRecyclerView.getLayoutManager(); + if (layoutManager instanceof GridLayoutManager) { + return ((GridLayoutManager) layoutManager).getSpanCount(); + } + + // Otherwise, it is a list with 1 column. + return 1; + } + + @Override + public int getNumRows() { + int numFullColumns = getTotalChildCount() / getNumColumns(); + boolean hasPartiallyFullColumn = getTotalChildCount() % getNumColumns() != 0; + return numFullColumns + (hasPartiallyFullColumn ? 1 : 0); + } + } + + /** + * Listener for changes in which items have been band selected. + */ + interface OnSelectionChangedListener { + public void onSelectionChanged(SparseBooleanArray updatedSelection); + } + + void addOnSelectionChangedListener(OnSelectionChangedListener listener) { + mOnSelectionChangedListeners.add(listener); + } + + void removeOnSelectionChangedListener(OnSelectionChangedListener listener) { + mOnSelectionChangedListeners.remove(listener); + } + + /** + * Metadata for an item in the view, consisting of the adapter position and the offset from the + * top of the view (in pixels). Stored in the mColumns map to model the item grid. + */ + private static class ItemData implements Comparable<ItemData> { + int position; + int offset; + + ItemData(int position, int offset) { + this.position = position; + this.offset = offset; + } + + @Override + public int compareTo(ItemData other) { + // The list of columns is sorted via the offset from the top, so PositionMetadata + // objects with lower y-values are befor those with higher y-values. + return offset - other.offset; + } + } + + /** + * Limits of a view item. For example, if an item's left side is at x-value 5 and its right side + * is at x-value 10, the limits would be from 5 to 10. Used to record the left- and right sides + * of item columns and the top- and bottom sides of item rows so that it can be determined + * whether the pointer is located within the bounds of an item. + */ + private static class Limits implements Comparable<Limits> { + int lowerLimit; + int upperLimit; + + Limits(int lowerLimit, int upperLimit) { + this.lowerLimit = lowerLimit; + this.upperLimit = upperLimit; + } + + @Override + public int compareTo(Limits other) { + return lowerLimit - other.lowerLimit; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Limits)) { + return false; + } + + return ((Limits) other).lowerLimit == lowerLimit && + ((Limits) other).upperLimit == upperLimit; + } + } + + /** + * The location of a coordinate relative to items. This class represents a general area of the + * view as it relates to band selection rather than an explicit point. For example, two + * different points within an item are considered to have the same "location" because band + * selection originating within the item would select the same items no matter which point + * was used. Same goes for points between items as well as those at the very beginning or end + * of the view. + * + * Tracking a coordinate (e.g., an x-value) as a CoordinateLocation instead of as an int has the + * advantage of tying the value to the Limits of items along that axis. This allows easy + * selection of items within those Limits as opposed to a search through every item to see if a + * given coordinate value falls within those Limits. + */ + private static class RelativeCoordinate + implements Comparable<RelativeCoordinate> { + /** + * Location describing points after the last known item. + */ + static final int AFTER_LAST_ITEM = 0; + + /** + * Location describing points before the first known item. + */ + static final int BEFORE_FIRST_ITEM = 1; + + /** + * Location describing points between two items. + */ + static final int BETWEEN_TWO_ITEMS = 2; + + /** + * Location describing points within the limits of one item. + */ + static final int WITHIN_LIMITS = 3; + + /** + * The type of this coordinate, which is one of AFTER_LAST_ITEM, BEFORE_FIRST_ITEM, + * BETWEEN_TWO_ITEMS, or WITHIN_LIMITS. + */ + final int type; + + /** + * The limits before the coordinate; only populated when type == WITHIN_LIMITS or type == + * BETWEEN_TWO_ITEMS. + */ + Limits limitsBeforeCoordinate; + + /** + * The limits after the coordinate; only populated when type == BETWEEN_TWO_ITEMS. + */ + Limits limitsAfterCoordinate; + + // Limits of the first known item; only populated when type == BEFORE_FIRST_ITEM. + Limits mFirstKnownItem; + // Limits of the last known item; only populated when type == AFTER_LAST_ITEM. + Limits mLastKnownItem; + + /** + * @param limitsList The sorted limits list for the coordinate type. If this + * CoordinateLocation is an x-value, mXLimitsList should be passed; otherwise, + * mYLimitsList should be pased. + * @param value The coordinate value. + */ + RelativeCoordinate(List<Limits> limitsList, int value) { + Limits dummyLimits = new Limits(value, value); + int index = Collections.binarySearch(limitsList, dummyLimits); + + if (index >= 0) { + this.type = WITHIN_LIMITS; + this.limitsBeforeCoordinate = limitsList.get(index); + } else if (~index == 0) { + this.type = BEFORE_FIRST_ITEM; + this.mFirstKnownItem = limitsList.get(0); + } else if (~index == limitsList.size()) { + Limits lastLimits = limitsList.get(limitsList.size() - 1); + if (lastLimits.lowerLimit <= value && value <= lastLimits.upperLimit) { + this.type = WITHIN_LIMITS; + this.limitsBeforeCoordinate = lastLimits; + } else { + this.type = AFTER_LAST_ITEM; + this.mLastKnownItem = lastLimits; + } + } else { + Limits limitsBeforeIndex = limitsList.get(~index - 1); + if (limitsBeforeIndex.lowerLimit <= value && value <= limitsBeforeIndex.upperLimit) { + this.type = WITHIN_LIMITS; + this.limitsBeforeCoordinate = limitsList.get(~index - 1); + } else { + this.type = BETWEEN_TWO_ITEMS; + this.limitsBeforeCoordinate = limitsList.get(~index - 1); + this.limitsAfterCoordinate = limitsList.get(~index); + } + } + } + + int toComparisonValue() { + if (type == BEFORE_FIRST_ITEM) { + return mFirstKnownItem.lowerLimit - 1; + } else if (type == AFTER_LAST_ITEM) { + return mLastKnownItem.upperLimit + 1; + } else if (type == BETWEEN_TWO_ITEMS) { + return limitsBeforeCoordinate.upperLimit + 1; + } else { + return limitsBeforeCoordinate.lowerLimit; + } + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof RelativeCoordinate)) { + return false; + } + + RelativeCoordinate otherCoordinate = (RelativeCoordinate) other; + return toComparisonValue() == otherCoordinate.toComparisonValue(); + } + + @Override + public int compareTo(RelativeCoordinate other) { + return toComparisonValue() - other.toComparisonValue(); + } + } + + /** + * The location of a point relative to the Limits of nearby items; consists of both an x- and + * y-RelativeCoordinateLocation. + */ + private class RelativePoint { + final RelativeCoordinate xLocation; + final RelativeCoordinate yLocation; + + RelativePoint(Point point) { + this.xLocation = new RelativeCoordinate(mXLimitsList, point.x); + this.yLocation = new RelativeCoordinate(mYLimitsList, point.y); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof RelativePoint)) { + return false; + } + + RelativePoint otherPoint = (RelativePoint) other; + return xLocation.equals(otherPoint.xLocation) && yLocation.equals(otherPoint.yLocation); + } + + /** + * Generates a rectangle which contains the items selected by the two points. + * @param other The other PointLocation. A rectangle will be formed between "this" and + * "other". + * @return The rectangle, or null if no items were selected. + */ + Rect computeBounds(RelativePoint other) { + if (!areItemsCoveredBySelection(mRelativePointer, mRelativeOrigin)) { + return null; + } + + RelativeCoordinate minXLocation = + xLocation.compareTo(other.xLocation) < 0 ? xLocation : other.xLocation; + RelativeCoordinate maxXLocation = + minXLocation == xLocation ? other.xLocation : xLocation; + RelativeCoordinate minYLocation = + yLocation.compareTo(other.yLocation) < 0 ? yLocation : other.yLocation; + RelativeCoordinate maxYLocation = + minYLocation == yLocation ? other.yLocation : yLocation; + + Rect rect = new Rect(); + rect.left = getCoordinateValue(minXLocation, mXLimitsList, true); + rect.right = getCoordinateValue(maxXLocation, mXLimitsList, false); + rect.top = getCoordinateValue(minYLocation, mYLimitsList, true); + rect.bottom = getCoordinateValue(maxYLocation, mYLimitsList, false); + return rect; + } + + int getCoordinateValue(RelativeCoordinate coordinate, + List<Limits> limitsList, boolean isStartOfRange) { + switch (coordinate.type) { + case RelativeCoordinate.BEFORE_FIRST_ITEM: + return limitsList.get(0).lowerLimit; + case RelativeCoordinate.AFTER_LAST_ITEM: + return limitsList.get(limitsList.size() - 1).upperLimit; + case RelativeCoordinate.BETWEEN_TWO_ITEMS: + if (isStartOfRange) { + return coordinate.limitsAfterCoordinate.lowerLimit; + } else { + return coordinate.limitsBeforeCoordinate.upperLimit; + } + case RelativeCoordinate.WITHIN_LIMITS: + return coordinate.limitsBeforeCoordinate.lowerLimit; + } + + throw new RuntimeException("Invalid coordinate value."); + } + } + + private static boolean areItemsCoveredBySelection( + RelativePoint first, RelativePoint second) { + return doesCoordinateLocationCoverItems(first.xLocation, second.xLocation) && + doesCoordinateLocationCoverItems(first.yLocation, second.yLocation); + } + + private static boolean doesCoordinateLocationCoverItems( + RelativeCoordinate pointerCoordinate, + RelativeCoordinate originCoordinate) { + if (pointerCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM && + originCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM) { + return false; + } + + if (pointerCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM && + originCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM) { + return false; + } + + if (pointerCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS && + originCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS && + pointerCoordinate.limitsBeforeCoordinate.equals(originCoordinate) && + pointerCoordinate.limitsAfterCoordinate.equals(originCoordinate)) { + return false; + } + + return true; + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index 9b8d84723c03..2835106b9a87 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -22,7 +22,6 @@ import static com.android.documentsui.DirectoryFragment.ANIM_SIDE; import static com.android.documentsui.DirectoryFragment.ANIM_UP; import static com.android.internal.util.Preconditions.checkArgument; -import android.annotation.Nullable; import android.app.Activity; import android.app.Fragment; import android.content.Intent; @@ -38,6 +37,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Root; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; import android.util.Log; import android.util.SparseArray; import android.view.LayoutInflater; @@ -46,6 +47,7 @@ import android.view.MenuItem; import android.view.MenuItem.OnActionExpandListener; import android.view.View; import android.view.ViewGroup; +import android.view.ViewStub; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.BaseAdapter; @@ -60,8 +62,6 @@ import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; -import com.google.common.collect.Maps; - import libcore.io.IoUtils; import java.io.FileNotFoundException; @@ -76,9 +76,13 @@ abstract class BaseActivity extends Activity { static final String EXTRA_STATE = "state"; + State mState; RootsCache mRoots; SearchManager mSearchManager; + DrawerController mDrawer; + @LayoutRes + private int mLayoutId; private final String mTag; public abstract State getDisplayState(); @@ -89,16 +93,28 @@ abstract class BaseActivity extends Activity { abstract void onDirectoryChanged(int anim); abstract void updateActionBar(); abstract void saveStackBlocking(); + abstract State buildDefaultState(); - public BaseActivity(String tag) { + public BaseActivity(@LayoutRes int layoutId, String tag) { + mLayoutId = layoutId; mTag = tag; } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); + + mState = (icicle != null) + ? icicle.<State>getParcelable(EXTRA_STATE) + : buildDefaultState(); + + setContentView(mLayoutId); + mRoots = DocumentsApplication.getRootsCache(this); mSearchManager = new SearchManager(); + + // Base classes must update result in their onCreate. + setResult(Activity.RESULT_CANCELED); } @Override @@ -371,7 +387,7 @@ abstract class BaseActivity extends Activity { public String currentSearch; /** Instance state for every shown directory */ - public HashMap<String, SparseArray<Parcelable>> dirState = Maps.newHashMap(); + public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>(); /** Currently copying file */ public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<DocumentInfo>(); @@ -527,6 +543,30 @@ abstract class BaseActivity extends Activity { } } + @Override + public void onBackPressed() { + // While action bar is expanded, the state stack UI is hidden. + if (mSearchManager.cancelSearch()) { + return; + } + + if (!mState.stackTouched) { + super.onBackPressed(); + return; + } + + final int size = mState.stack.size(); + + if (mDrawer.isOpen()) { + mDrawer.setOpen(false); + } else if (size > 1) { + mState.stack.pop(); + onCurrentDirectoryChanged(ANIM_UP); + } else { + super.onBackPressed(); + } + } + public void onStackPicked(DocumentStack stack) { try { // Update the restored stack to ensure we have freshest data diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java index 14155d506711..8b9233100827 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java @@ -167,7 +167,7 @@ public class CopyService extends IntentService { if (mFailedFiles.size() > 0) { final Context context = getApplicationContext(); - final Intent navigateIntent = new Intent(context, StandaloneActivity.class); + final Intent navigateIntent = new Intent(context, FilesActivity.class); navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack); navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY); navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, mFailedFiles); @@ -213,7 +213,7 @@ public class CopyService extends IntentService { mIsCancelled = false; final Context context = getApplicationContext(); - final Intent navigateIntent = new Intent(context, StandaloneActivity.class); + final Intent navigateIntent = new Intent(context, FilesActivity.class); navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack); final String contentTitle = getString(copying ? R.string.copy_notification_title diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java index f9275951b9df..e408e6eb821d 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java @@ -16,7 +16,7 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import android.app.AlertDialog; import android.app.Dialog; @@ -36,7 +36,6 @@ import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; -import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 5223d760e96d..240cdda8dc8d 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -24,14 +24,13 @@ import static com.android.documentsui.BaseActivity.State.MODE_GRID; import static com.android.documentsui.BaseActivity.State.MODE_LIST; import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import static com.android.documentsui.model.DocumentInfo.getCursorInt; import static com.android.documentsui.model.DocumentInfo.getCursorLong; import static com.android.documentsui.model.DocumentInfo.getCursorString; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkState; -import android.annotation.NonNull; import android.app.Activity; import android.app.ActivityManager; import android.app.Fragment; @@ -98,7 +97,7 @@ import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; -import com.google.android.collect.Lists; +import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Collections; @@ -235,8 +234,7 @@ public class DirectoryFragment extends Fragment { public void onLayoutChange( View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - int thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width); - mColumnCount = pickColumnCount(thumbSize); + mColumnCount = calculateColumnCount(); if (mGridLayout != null) { mGridLayout.setSpanCount(mColumnCount); } @@ -535,8 +533,6 @@ public class DirectoryFragment extends Fragment { updateLayout(state.derivedMode); - final int thumbSize = getResources().getDimensionPixelSize(R.dimen.icon_size); - mThumbSize = new Point(thumbSize, thumbSize); mRecView.setAdapter(mAdapter); } @@ -573,13 +569,15 @@ public class DirectoryFragment extends Fragment { mThumbSize = new Point(thumbSize, thumbSize); } - private int pickColumnCount(final int thumbSize) { - int itemPadding = - getResources().getDimensionPixelSize(R.dimen.grid_item_margin); + private int calculateColumnCount() { + int cellWidth = getResources().getDimensionPixelSize(R.dimen.grid_width); + int cellMargin = 2 * getResources().getDimensionPixelSize(R.dimen.grid_item_margin); int viewPadding = mRecView.getPaddingLeft() + mRecView.getPaddingRight(); + checkState(mRecView.getWidth() > 0); int columnCount = Math.max(1, - (mRecView.getWidth() - viewPadding) / (thumbSize + itemPadding)); + (mRecView.getWidth() - viewPadding) / (cellWidth + cellMargin)); + return columnCount; } @@ -740,7 +738,7 @@ public class DirectoryFragment extends Fragment { new GetDocumentsTask() { @Override void onDocumentsReady(List<DocumentInfo> docs) { - // TODO: Implement support in standalone for opening multiple docs. + // TODO: Implement support in Files activity for opening multiple docs. BaseActivity.get(DirectoryFragment.this).onDocumentsPicked(docs); } }.execute(selected); @@ -753,7 +751,7 @@ public class DirectoryFragment extends Fragment { Intent intent; // Filter out directories - those can't be shared. - List<DocumentInfo> docsForSend = Lists.newArrayList(); + List<DocumentInfo> docsForSend = new ArrayList<>(); for (DocumentInfo doc: docs) { if (!Document.MIME_TYPE_DIR.equals(doc.mimeType)) { docsForSend.add(doc); @@ -774,8 +772,8 @@ public class DirectoryFragment extends Fragment { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addCategory(Intent.CATEGORY_DEFAULT); - final ArrayList<String> mimeTypes = Lists.newArrayList(); - final ArrayList<Uri> uris = Lists.newArrayList(); + final ArrayList<String> mimeTypes = new ArrayList<>(); + final ArrayList<Uri> uris = new ArrayList<>(); for (DocumentInfo doc : docsForSend) { mimeTypes.add(doc.mimeType); uris.add(doc.derivedUri); @@ -956,7 +954,7 @@ public class DirectoryFragment extends Fragment { private final Context mContext; private final LayoutInflater mInflater; // TODO: Bring back support for footers. - private final List<Footer> mFooters = Lists.newArrayList(); + private final List<Footer> mFooters = new ArrayList<>(); private Cursor mCursor; private int mCursorCount; @@ -1330,7 +1328,7 @@ public class DirectoryFragment extends Fragment { return MimePredicate.mimeMatches(state.acceptMimes, docMimeType); } - private @NonNull List<DocumentInfo> getSelectedDocuments() { + private List<DocumentInfo> getSelectedDocuments() { Selection sel = mSelectionManager.getSelection(new Selection()); return getItemsAsDocuments(sel); } @@ -1570,6 +1568,7 @@ public class DirectoryFragment extends Fragment { final Cursor cursor = mAdapter.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); + return Lists.newArrayList(doc); } @@ -1685,7 +1684,7 @@ public class DirectoryFragment extends Fragment { private FragmentTuner pickFragmentTuner(final State state) { return state.action == ACTION_BROWSE_ALL - ? new StandaloneTuner() + ? new FilesTuner() : new DefaultTuner(state); } @@ -1750,7 +1749,7 @@ public class DirectoryFragment extends Fragment { copyTo.setVisible(manageOrBrowse && dirType != TYPE_RECENT_OPEN); moveTo.setVisible(SystemProperties.getBoolean("debug.documentsui.enable_move", false)); - // Only shown in standalone mode. + // Only shown in files mode. copyToClipboard.setVisible(false); } @@ -1759,9 +1758,9 @@ public class DirectoryFragment extends Fragment { } /** - * Provides support for Standalone specific specializations of DirectoryFragment. + * Provides support for Files activity specific specializations of DirectoryFragment. */ - private static final class StandaloneTuner implements FragmentTuner { + private static final class FilesTuner implements FragmentTuner { @Override public void updateActionMenu(Menu menu, int dirType, boolean canDelete) { menu.findItem(R.id.menu_share).setVisible(true); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java index a8a61d28c222..0edb2413b0ca 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java @@ -16,12 +16,12 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN; +import static com.android.documentsui.Shared.TAG; import static com.android.documentsui.model.DocumentInfo.getCursorInt; import android.content.AsyncTaskLoader; @@ -31,8 +31,6 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.CancellationSignal; -import android.os.Handler; -import android.os.Looper; import android.os.OperationCanceledException; import android.os.RemoteException; import android.provider.DocumentsContract; diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java index 237057570d49..6ba07fbbaf6a 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java @@ -16,8 +16,6 @@ package com.android.documentsui; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ContentProviderClient; @@ -26,15 +24,15 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.provider.DocumentsContract; +import android.support.annotation.Nullable; import android.util.Log; import com.android.documentsui.model.DocumentInfo; import com.android.internal.util.Preconditions; -import com.google.android.collect.Lists; - import libcore.io.IoUtils; +import java.util.ArrayList; import java.util.List; /** @@ -78,7 +76,7 @@ final class DocumentClipper { * Returns a list of Documents as decoded from Clipboard primary clipdata. * This should be run from inside an AsyncTask. */ - public @NonNull List<DocumentInfo> getClippedDocuments() { + public List<DocumentInfo> getClippedDocuments() { return getDocumentsFromClipData(mClipboard.getPrimaryClip()); } @@ -86,9 +84,9 @@ final class DocumentClipper { * Returns a list of Documents as decoded in clipData. * This should be run from inside an AsyncTask. */ - public @NonNull List<DocumentInfo> getDocumentsFromClipData(ClipData clipData) { + public List<DocumentInfo> getDocumentsFromClipData(ClipData clipData) { Preconditions.checkNotNull(clipData); - final List<DocumentInfo> srcDocs = Lists.newArrayList(); + final List<DocumentInfo> srcDocs = new ArrayList<>(); int count = clipData.getItemCount(); if (count == 0) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index 2de7fc474cd4..fdc4bb06ffff 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -25,8 +25,6 @@ import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_COPY_DESTIN import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE; import static com.android.documentsui.DirectoryFragment.ANIM_DOWN; import static com.android.documentsui.DirectoryFragment.ANIM_NONE; -import static com.android.documentsui.DirectoryFragment.ANIM_UP; -import static com.android.internal.util.Preconditions.checkArgument; import android.app.Activity; import android.app.Fragment; @@ -48,9 +46,6 @@ import android.os.Bundle; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Root; -import android.support.v4.app.ActionBarDrawerToggle; -import android.support.v4.widget.DrawerLayout; -import android.support.v4.widget.DrawerLayout.DrawerListener; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -81,42 +76,32 @@ public class DocumentsActivity extends BaseActivity { private Toolbar mRootsToolbar; - private DrawerLayout mDrawerLayout; - private ActionBarDrawerToggle mDrawerToggle; - private View mRootsDrawer; - private DirectoryContainerView mDirectoryContainer; - private State mState; - private ItemSelectedListener mStackListener; private BaseAdapter mStackAdapter; public DocumentsActivity() { - super(TAG); + super(R.layout.docs_activity, TAG); } @Override public void onCreate(Bundle icicle) { - mState = (icicle != null) - ? icicle.<State>getParcelable(EXTRA_STATE) - : buildDefaultState(); + super.onCreate(icicle); final Resources res = getResources(); mShowAsDialog = res.getBoolean(R.bool.show_as_dialog) && mState.action != ACTION_MANAGE && mState.action != ACTION_BROWSE; + if (!mShowAsDialog) { setTheme(R.style.DocumentsNonDialogTheme); } - super.onCreate(icicle); - - setResult(Activity.RESULT_CANCELED); - setContentView(R.layout.activity); - final Context context = this; if (mShowAsDialog) { + mDrawer = DrawerController.createDummy(); + // Strongly define our horizontal dimension; we leave vertical as // WRAP_CONTENT so that system resizes us when IME is showing. final WindowManager.LayoutParams a = getWindow().getAttributes(); @@ -128,17 +113,7 @@ public class DocumentsActivity extends BaseActivity { getWindow().setAttributes(a); } else { - // Non-dialog means we have a drawer - mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - - if (mDrawerLayout != null) { - mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, - R.drawable.ic_hamburger, R.string.drawer_open, R.string.drawer_close); - - mDrawerLayout.setDrawerListener(mDrawerListener); - - mRootsDrawer = findViewById(R.id.drawer_roots); - } + mDrawer = DrawerController.create(this); } mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory); @@ -162,10 +137,9 @@ public class DocumentsActivity extends BaseActivity { // Hide roots when we're managing a specific root if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) { - if (mShowAsDialog || mDrawerLayout == null) { + mDrawer.lockClosed(); + if (mShowAsDialog) { findViewById(R.id.container_roots).setVisibility(View.GONE); - } else { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); } } @@ -205,7 +179,8 @@ public class DocumentsActivity extends BaseActivity { } } - private State buildDefaultState() { + @Override + State buildDefaultState() { State state = new State(); final Intent intent = getIntent(); @@ -341,53 +316,15 @@ public class DocumentsActivity extends BaseActivity { } } - private DrawerListener mDrawerListener = new DrawerListener() { - @Override - public void onDrawerSlide(View drawerView, float slideOffset) { - mDrawerToggle.onDrawerSlide(drawerView, slideOffset); - } - - @Override - public void onDrawerOpened(View drawerView) { - mDrawerToggle.onDrawerOpened(drawerView); - } - - @Override - public void onDrawerClosed(View drawerView) { - mDrawerToggle.onDrawerClosed(drawerView); - } - - @Override - public void onDrawerStateChanged(int newState) { - mDrawerToggle.onDrawerStateChanged(newState); - } - }; - @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - if (mDrawerToggle != null) { - mDrawerToggle.syncState(); - } + mDrawer.syncState(); updateActionBar(); } public void setRootsDrawerOpen(boolean open) { - if (!mShowAsDialog && mDrawerLayout != null) { - if (open) { - mDrawerLayout.openDrawer(mRootsDrawer); - } else { - mDrawerLayout.closeDrawer(mRootsDrawer); - } - } - } - - private boolean isRootsDrawerOpen() { - if (mShowAsDialog || mDrawerLayout == null) { - return false; - } else { - return mDrawerLayout.isDrawerOpen(mRootsDrawer); - } + mDrawer.setOpen(open); } @Override @@ -408,8 +345,7 @@ public class DocumentsActivity extends BaseActivity { } } - if (!mShowAsDialog && mDrawerLayout != null && - mDrawerLayout.getDrawerLockMode(mRootsDrawer) == DrawerLayout.LOCK_MODE_UNLOCKED) { + if (!mShowAsDialog && mDrawer.isUnlocked()) { mToolbar.setNavigationIcon(R.drawable.ic_hamburger); mToolbar.setNavigationContentDescription(R.string.drawer_open); mToolbar.setNavigationOnClickListener(new View.OnClickListener() { @@ -504,34 +440,7 @@ public class DocumentsActivity extends BaseActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { - if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) { - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - public void onBackPressed() { - // While action bar is expanded, the state stack UI is hidden. - if (mSearchManager.cancelSearch()) { - return; - } - - if (!mState.stackTouched) { - super.onBackPressed(); - return; - } - - final int size = mState.stack.size(); - if (size > 1) { - mState.stack.pop(); - onCurrentDirectoryChanged(ANIM_UP); - } else if (size == 1 && !isRootsDrawerOpen()) { - // TODO: open root drawer once we can capture back key - super.onBackPressed(); - } else { - super.onBackPressed(); - } + return mDrawer.onOptionsItemSelected(item) || super.onOptionsItemSelected(item); } @Override diff --git a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java new file mode 100644 index 000000000000..df3ac1b0e7ef --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import static com.android.internal.util.Preconditions.checkArgument; + +import android.app.Activity; +import android.support.v4.app.ActionBarDrawerToggle; +import android.support.v4.widget.DrawerLayout; +import android.support.v4.widget.DrawerLayout.DrawerListener; +import android.view.MenuItem; +import android.view.View; + +/** + * A facade over the various pieces comprising "roots fragment in a Drawer". + * + * @see DrawerController#create(DrawerLayout) + */ +abstract class DrawerController implements DrawerListener { + + abstract void setOpen(boolean open); + abstract void lockOpen(); + abstract void lockClosed(); + abstract boolean isPresent(); + abstract boolean isOpen(); + abstract boolean isUnlocked(); + abstract void syncState(); + abstract boolean onOptionsItemSelected(MenuItem item); + + /** + * Returns a controller suitable for {@code Layout}. + */ + static DrawerController create(Activity activity) { + + DrawerLayout layout = (DrawerLayout) activity.findViewById(R.id.drawer_layout); + + if (layout == null) { + return new DummyDrawerController(); + } + + View drawer = activity.findViewById(R.id.drawer_roots); + ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( + activity, + layout, + R.drawable.ic_hamburger, + R.string.drawer_open, + R.string.drawer_close); + + return new RuntimeDrawerController(layout, drawer, toggle); + } + + /** + * Returns a controller suitable for {@code Layout}. + */ + static DrawerController createDummy() { + return new DummyDrawerController(); + } + + /** + * Runtime controller that manages a real drawer. + */ + private static final class RuntimeDrawerController extends DrawerController { + + private final ActionBarDrawerToggle mToggle; + private DrawerLayout mLayout; + private View mDrawer; + + public RuntimeDrawerController( + DrawerLayout layout, View drawer, ActionBarDrawerToggle toggle) { + checkArgument(layout != null); + + mLayout = layout; + mDrawer = drawer; + mToggle = toggle; + + mLayout.setDrawerListener(this); + } + + @Override + void setOpen(boolean open) { + if (open) { + mLayout.openDrawer(mDrawer); + } else { + mLayout.closeDrawer(mDrawer); + } + } + + @Override + boolean isOpen() { + return mLayout.isDrawerOpen(mDrawer); + } + + @Override + boolean isPresent() { + return true; + } + + @Override + void syncState() { + mToggle.syncState(); + } + + @Override + boolean isUnlocked() { + return mLayout.getDrawerLockMode(mDrawer) == DrawerLayout.LOCK_MODE_UNLOCKED; + } + + @Override + void lockOpen() { + mLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN); + } + + @Override + void lockClosed() { + mLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + } + + @Override + boolean onOptionsItemSelected(MenuItem item) { + return false; + } + + @Override + public void onDrawerSlide(View drawerView, float slideOffset) { + mToggle.onDrawerSlide(drawerView, slideOffset); + } + + @Override + public void onDrawerOpened(View drawerView) { + mToggle.onDrawerOpened(drawerView); + } + + @Override + public void onDrawerClosed(View drawerView) { + mToggle.onDrawerClosed(drawerView); + } + + @Override + public void onDrawerStateChanged(int newState) { + mToggle.onDrawerStateChanged(newState); + } + } + + /* + * Dummy controller useful with clients that don't host a real drawer. + */ + private static final class DummyDrawerController extends DrawerController { + + @Override + void setOpen(boolean open) {} + + @Override + void syncState() {} + + @Override + void lockOpen() {} + + @Override + void lockClosed() {} + + @Override + boolean isOpen() { + return false; + } + + @Override + boolean isUnlocked() { + return true; + } + + @Override + boolean isPresent() { + return false; + } + + @Override + boolean onOptionsItemSelected(MenuItem item) { + return false; + } + + @Override + public void onDrawerSlide(View drawerView, float slideOffset) {} + + @Override + public void onDrawerOpened(View drawerView) {} + + @Override + public void onDrawerClosed(View drawerView) {} + + @Override + public void onDrawerStateChanged(int newState) {} + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java index 1ca277dd3b1f..7c445bf79ee1 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java @@ -18,9 +18,7 @@ package com.android.documentsui; import static com.android.documentsui.DirectoryFragment.ANIM_DOWN; import static com.android.documentsui.DirectoryFragment.ANIM_NONE; -import static com.android.documentsui.DirectoryFragment.ANIM_UP; -import android.annotation.Nullable; import android.app.Activity; import android.app.FragmentManager; import android.content.ActivityNotFoundException; @@ -33,6 +31,7 @@ import android.net.Uri; import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Root; +import android.support.annotation.Nullable; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; @@ -57,7 +56,7 @@ import java.util.List; /** * Standalone file management activity. */ -public class StandaloneActivity extends BaseActivity { +public class FilesActivity extends BaseActivity { public static final String TAG = "StandaloneFileManagement"; static final boolean DEBUG = false; @@ -66,30 +65,22 @@ public class StandaloneActivity extends BaseActivity { private Spinner mToolbarStack; private Toolbar mRootsToolbar; private DirectoryContainerView mDirectoryContainer; - private State mState; private ItemSelectedListener mStackListener; private BaseAdapter mStackAdapter; private DocumentClipper mClipper; - public StandaloneActivity() { - super(TAG); + public FilesActivity() { + super(R.layout.files_activity, TAG); } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - setResult(Activity.RESULT_CANCELED); - setContentView(R.layout.activity); - final Context context = this; mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory); - mState = (icicle != null) - ? icicle.<State> getParcelable(EXTRA_STATE) - : buildDefaultState(); - mToolbar = (Toolbar) findViewById(R.id.toolbar); mToolbar.setTitleTextAppearance(context, android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title); @@ -108,6 +99,11 @@ public class StandaloneActivity extends BaseActivity { setActionBar(mToolbar); mClipper = new DocumentClipper(this); + mDrawer = DrawerController.create(this); + if (mDrawer.isPresent()) { + setTheme(R.style.DocumentsNonDialogTheme); + } + RootsFragment.show(getFragmentManager(), null); if (!mState.restored) { @@ -130,7 +126,8 @@ public class StandaloneActivity extends BaseActivity { } } - private State buildDefaultState() { + @Override + State buildDefaultState() { State state = new State(); final Intent intent = getIntent(); @@ -164,10 +161,23 @@ public class StandaloneActivity extends BaseActivity { @Override public void updateActionBar() { final RootInfo root = getCurrentRoot(); - mToolbar.setNavigationIcon( - root != null ? root.loadToolbarIcon(mToolbar.getContext()) : null); - mToolbar.setNavigationContentDescription(R.string.drawer_open); - mToolbar.setNavigationOnClickListener(null); + + if (mDrawer.isPresent()) { + mToolbar.setNavigationIcon(R.drawable.ic_hamburger); + mToolbar.setNavigationContentDescription(R.string.drawer_open); + mToolbar.setNavigationOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + mDrawer.setOpen(true); + } + }); + } else { + mToolbar.setNavigationIcon( + root != null ? root.loadToolbarIcon(mToolbar.getContext()) : null); + mToolbar.setNavigationContentDescription(R.string.drawer_open); + mToolbar.setNavigationOnClickListener(null); + } if (mSearchManager.isExpanded()) { mToolbar.setTitle(null); @@ -236,22 +246,6 @@ public class StandaloneActivity extends BaseActivity { } @Override - public void onBackPressed() { - if (!mState.stackTouched) { - super.onBackPressed(); - return; - } - - final int size = mState.stack.size(); - if (size > 1) { - mState.stack.pop(); - onCurrentDirectoryChanged(ANIM_UP); - } else { - super.onBackPressed(); - } - } - - @Override public State getDisplayState() { return mState; } @@ -284,6 +278,12 @@ public class StandaloneActivity extends BaseActivity { } @Override + void onRootPicked(RootInfo root) { + super.onRootPicked(root); + mDrawer.setOpen(false); + } + + @Override public void onDocumentsPicked(List<DocumentInfo> docs) { throw new UnsupportedOperationException(); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java index 1cbc221e1057..17a1161f7c25 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java @@ -16,7 +16,7 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import static com.android.documentsui.model.DocumentInfo.getCursorLong; import static com.android.documentsui.model.DocumentInfo.getCursorString; diff --git a/packages/DocumentsUI/src/com/android/documentsui/GridItem.java b/packages/DocumentsUI/src/com/android/documentsui/GridItem.java new file mode 100644 index 000000000000..990dca70f9c6 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/GridItem.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +/** + * A FrameLayout subclass used by DirectoryFragment. Ensures that the resulting grid item is always + * square. + */ +public class GridItem extends FrameLayout { + public GridItem(Context context) { + super(context); + } + + public GridItem(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public GridItem(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Grid layout uses item width to figure out the number of columns, then dynamically fits + // rows into the view. The upshot of this is that changing the item width will mess up the + // grid layout - so to make the items square, throw out the height and use the width for + // both dimensions. The grid layout will correctly adjust the row height. + // + // Note that this code will need to be changed if the layout manager's orientation is + // changed from VERTICAL to HORIZONTAL. + super.onMeasure(widthMeasureSpec, widthMeasureSpec); + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java index b43fedf3d1fb..99592659a7fb 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java +++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java @@ -23,13 +23,11 @@ import android.graphics.drawable.Drawable; import android.provider.DocumentsContract.Document; import android.util.TypedValue; -import com.google.android.collect.Maps; - import java.util.HashMap; public class IconUtils { - private static HashMap<String, Integer> sMimeIcons = Maps.newHashMap(); + private static HashMap<String, Integer> sMimeIcons = new HashMap<>(); private static void add(String mimeType, int resId) { if (sMimeIcons.put(mimeType, resId) != null) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java index 02edd0c87448..f87fe4ccfa46 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java +++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java @@ -31,7 +31,7 @@ import android.view.GestureDetector.OnGestureListener; import android.view.MotionEvent; import android.view.View; -import com.google.common.annotations.VisibleForTesting; +import android.support.annotation.VisibleForTesting; import java.util.ArrayList; import java.util.List; diff --git a/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java index f94aebd50d61..b0e332faa3fc 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java +++ b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java @@ -20,8 +20,6 @@ import android.os.AsyncTask; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; -import com.google.android.collect.Lists; -import com.google.android.collect.Maps; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -32,7 +30,7 @@ import java.util.concurrent.LinkedBlockingQueue; public class ProviderExecutor extends Thread implements Executor { @GuardedBy("sExecutors") - private static HashMap<String, ProviderExecutor> sExecutors = Maps.newHashMap(); + private static HashMap<String, ProviderExecutor> sExecutors = new HashMap<>(); public static ProviderExecutor forAuthority(String authority) { synchronized (sExecutors) { @@ -53,7 +51,7 @@ public class ProviderExecutor extends Thread implements Executor { private final LinkedBlockingQueue<Runnable> mQueue = new LinkedBlockingQueue<Runnable>(); - private final ArrayList<WeakReference<Preemptable>> mPreemptable = Lists.newArrayList(); + private final ArrayList<WeakReference<Preemptable>> mPreemptable = new ArrayList<>(); private void preempt() { synchronized (mPreemptable) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java index 878c4c217588..4685c41fc2b6 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java +++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java @@ -18,7 +18,6 @@ package com.android.documentsui; import static com.android.documentsui.model.DocumentInfo.getCursorString; -import android.annotation.Nullable; import android.content.ClipData; import android.content.ClipDescription; import android.content.ComponentName; @@ -28,6 +27,7 @@ import android.database.Cursor; import android.net.Uri; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; +import android.support.annotation.Nullable; import android.util.Log; import com.android.documentsui.BaseActivity.DocumentContext; diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java index f5908c55cfa2..1a7095a054c3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java @@ -16,8 +16,8 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED; +import static com.android.documentsui.Shared.TAG; import android.app.ActivityManager; import android.content.AsyncTaskLoader; @@ -36,14 +36,14 @@ import android.util.Log; import com.android.documentsui.BaseActivity.State; import com.android.documentsui.model.RootInfo; -import com.google.android.collect.Maps; -import com.google.common.collect.Lists; + import com.google.common.util.concurrent.AbstractFuture; import libcore.io.IoUtils; import java.io.Closeable; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -53,7 +53,7 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { - private static final boolean LOGD = true; + private static final boolean DEBUG = false; // TODO: clean up cursor ownership so background thread doesn't traverse // previously returned cursors for filtering/sorting; this currently races @@ -81,7 +81,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { private final RootsCache mRoots; private final State mState; - private final HashMap<RootInfo, RecentTask> mTasks = Maps.newHashMap(); + private final HashMap<RootInfo, RecentTask> mTasks = new HashMap<>(); private final int mSortOrder = State.SORT_ORDER_LAST_MODIFIED; @@ -196,7 +196,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { // Collect all finished tasks boolean allDone = true; - List<Cursor> cursors = Lists.newArrayList(); + List<Cursor> cursors = new ArrayList<>(); for (RecentTask task : mTasks.values()) { if (task.isDone()) { try { @@ -221,7 +221,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { } } - if (LOGD) { + if (DEBUG) { Log.d(TAG, "Found " + cursors.size() + " of " + mTasks.size() + " recent queries done"); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java index e11d7d967961..662822ee35d3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java @@ -16,7 +16,7 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import android.app.Fragment; import android.app.FragmentManager; @@ -50,7 +50,6 @@ import com.android.documentsui.RecentsProvider.RecentColumns; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; -import com.google.android.collect.Lists; import libcore.io.IoUtils; @@ -157,7 +156,7 @@ public class RecentsCreateFragment extends Fragment { @Override public List<DocumentStack> loadInBackground(Uri uri, CancellationSignal signal) { final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState); - final ArrayList<DocumentStack> result = Lists.newArrayList(); + final ArrayList<DocumentStack> result = new ArrayList<>(); final ContentResolver resolver = getContext().getContentResolver(); final Cursor cursor = resolver.query( diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java index fbcb938692af..05f7d8dd11e3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java @@ -16,7 +16,7 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import android.content.ContentProviderClient; import android.content.ContentResolver; @@ -39,14 +39,14 @@ import android.util.Log; import com.android.documentsui.BaseActivity.State; import com.android.documentsui.model.RootInfo; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.google.android.collect.Lists; -import com.google.android.collect.Sets; +import android.support.annotation.VisibleForTesting; + import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import libcore.io.IoUtils; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -74,10 +74,10 @@ public class RootsCache { @GuardedBy("mLock") private Multimap<String, RootInfo> mRoots = ArrayListMultimap.create(); @GuardedBy("mLock") - private HashSet<String> mStoppedAuthorities = Sets.newHashSet(); + private HashSet<String> mStoppedAuthorities = new HashSet<>(); @GuardedBy("mObservedAuthorities") - private final HashSet<String> mObservedAuthorities = Sets.newHashSet(); + private final HashSet<String> mObservedAuthorities = new HashSet<>(); public RootsCache(Context context) { mContext = context; @@ -159,7 +159,7 @@ public class RootsCache { private final String mFilterPackage; private final Multimap<String, RootInfo> mTaskRoots = ArrayListMultimap.create(); - private final HashSet<String> mTaskStoppedAuthorities = Sets.newHashSet(); + private final HashSet<String> mTaskStoppedAuthorities = new HashSet<>(); /** * Update all roots. @@ -251,7 +251,7 @@ public class RootsCache { } } - final List<RootInfo> roots = Lists.newArrayList(); + final List<RootInfo> roots = new ArrayList<>(); final Uri rootsUri = DocumentsContract.buildRootsUri(authority); ContentProviderClient client = null; @@ -350,7 +350,7 @@ public class RootsCache { @VisibleForTesting static List<RootInfo> getMatchingRoots(Collection<RootInfo> roots, State state) { - final List<RootInfo> matching = Lists.newArrayList(); + final List<RootInfo> matching = new ArrayList<>(); for (RootInfo root : roots) { final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0; final boolean supportsIsChild = (root.flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0; diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java index fd67a77254a6..c02184b72b2f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java @@ -44,8 +44,8 @@ import android.widget.TextView; import com.android.documentsui.BaseActivity.State; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.RootInfo; -import com.google.common.collect.Lists; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -293,8 +293,8 @@ public class RootsFragment extends Fragment { RootItem audio = null; RootItem downloads = null; - final List<RootInfo> clouds = Lists.newArrayList(); - final List<RootInfo> locals = Lists.newArrayList(); + final List<RootInfo> clouds = new ArrayList<>(); + final List<RootInfo> locals = new ArrayList<>(); for (RootInfo root : roots) { if (root.isRecents()) { @@ -338,7 +338,7 @@ public class RootsFragment extends Fragment { final List<ResolveInfo> infos = pm.queryIntentActivities( includeApps, PackageManager.MATCH_DEFAULT_ONLY); - final List<AppItem> apps = Lists.newArrayList(); + final List<AppItem> apps = new ArrayList<>(); // Omit ourselves from the list for (ResolveInfo info : infos) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java index 088e3fa70d43..ae959f91dec0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java +++ b/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java @@ -22,8 +22,6 @@ import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListAdapter; -import com.google.android.collect.Lists; - import java.util.ArrayList; /** @@ -31,7 +29,7 @@ import java.util.ArrayList; * provide a header, and correctly handling item types across child adapters. */ public class SectionedListAdapter extends BaseAdapter { - private ArrayList<SectionAdapter> mSections = Lists.newArrayList(); + private ArrayList<SectionAdapter> mSections = new ArrayList<>(); public interface SectionAdapter extends ListAdapter { public View getHeaderView(View convertView, ViewGroup parent); diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java new file mode 100644 index 000000000000..b414ee3269d8 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +/** + * @hide + */ +public final class Shared { + public static final String TAG = "Documents"; +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/UriDerivativeLoader.java b/packages/DocumentsUI/src/com/android/documentsui/UriDerivativeLoader.java index 1a5bb0c099a8..7bb662c72cf5 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/UriDerivativeLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/UriDerivativeLoader.java @@ -27,16 +27,16 @@ import android.os.OperationCanceledException; * changes while started, manages {@link CancellationSignal}, and caches * returned results. */ -public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { +public abstract class UriDerivativeLoader<Param, Res> extends AsyncTaskLoader<Res> { final ForceLoadContentObserver mObserver; - private final P mParam; + private final Param mParam; - private R mResult; + private Res mResult; private CancellationSignal mCancellationSignal; @Override - public final R loadInBackground() { + public final Res loadInBackground() { synchronized (this) { if (isLoadInBackgroundCanceled()) { throw new OperationCanceledException(); @@ -52,7 +52,7 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { } } - public abstract R loadInBackground(P param, CancellationSignal signal); + public abstract Res loadInBackground(Param param, CancellationSignal signal); @Override public void cancelLoadInBackground() { @@ -66,12 +66,12 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { } @Override - public void deliverResult(R result) { + public void deliverResult(Res result) { if (isReset()) { closeQuietly(result); return; } - R oldResult = mResult; + Res oldResult = mResult; mResult = result; if (isStarted()) { @@ -83,7 +83,7 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { } } - public UriDerivativeLoader(Context context, P param) { + public UriDerivativeLoader(Context context, Param param) { super(context); mObserver = new ForceLoadContentObserver(); mParam = param; @@ -105,7 +105,7 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { } @Override - public void onCanceled(R result) { + public void onCanceled(Res result) { closeQuietly(result); } @@ -122,7 +122,7 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { getContext().getContentResolver().unregisterContentObserver(mObserver); } - private void closeQuietly(R result) { + private void closeQuietly(Res result) { if (result instanceof AutoCloseable) { try { ((AutoCloseable) result).close(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java index 5d5f2ebabf2e..cc981e1eaaad 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java @@ -24,7 +24,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; -import android.provider.DocumentsContract.Root; import android.provider.DocumentsProvider; import android.text.TextUtils; diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java b/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java index 2a29cbc23d0c..e21dd4930678 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java @@ -16,7 +16,7 @@ package com.android.documentsui.model; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import android.os.BadParcelableException; import android.os.Parcel; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectMatrixTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectMatrixTest.java new file mode 100644 index 000000000000..f15a6430388c --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectMatrixTest.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import static org.junit.Assert.*; + +import com.android.documentsui.BandSelectMatrix; + +import android.graphics.Point; +import android.graphics.Rect; +import android.support.v7.widget.RecyclerView.OnScrollListener; +import android.util.SparseBooleanArray; + +import org.junit.After; +import org.junit.Test; + +public class BandSelectMatrixTest { + + private static final int VIEW_PADDING_PX = 5; + private static final int CHILD_VIEW_EDGE_PX = 100; + private static final int VIEWPORT_HEIGHT = 500; + + private static BandSelectMatrix matrix; + private static TestHelper helper; + private static SparseBooleanArray lastSelection; + private static int viewWidth; + + private static void setUp(int numChildren, int numColumns) { + helper = new TestHelper(numChildren, numColumns); + viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX); + matrix = new BandSelectMatrix(helper); + matrix.addOnSelectionChangedListener(new BandSelectMatrix.OnSelectionChangedListener() { + + @Override + public void onSelectionChanged(SparseBooleanArray updatedSelection) { + lastSelection = updatedSelection; + } + }); + } + + @After + public void tearDown() { + matrix = null; + helper = null; + lastSelection = null; + } + + @Test + public void testSelectionLeftOfItems() { + setUp(20, 5); + matrix.startSelection(new Point(0, 10)); + matrix.resizeSelection(new Point(1, 11)); + assertSelected(new int[0]); + } + + @Test + public void testSelectionRightOfItems() { + setUp(20, 4); + matrix.startSelection(new Point(viewWidth - 1, 10)); + matrix.resizeSelection(new Point(viewWidth - 2, 11)); + assertSelected(new int[0]); + } + + @Test + public void testSelectionAboveItems() { + setUp(20, 4); + matrix.startSelection(new Point(10, 0)); + matrix.resizeSelection(new Point(11, 1)); + assertSelected(new int[0]); + } + + @Test + public void testSelectionBelowItems() { + setUp(5, 4); + matrix.startSelection(new Point(10, VIEWPORT_HEIGHT - 1)); + matrix.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2)); + assertSelected(new int[0]); + } + + @Test + public void testVerticalSelectionBetweenItems() { + setUp(20, 4); + matrix.startSelection(new Point(106, 0)); + matrix.resizeSelection(new Point(107, 200)); + assertSelected(new int[0]); + } + + @Test + public void testHorizontalSelectionBetweenItems() { + setUp(20, 4); + matrix.startSelection(new Point(0, 105)); + matrix.resizeSelection(new Point(200, 106)); + assertSelected(new int[0]); + } + + @Test + public void testGrowingAndShrinkingSelection() { + setUp(20, 4); + matrix.startSelection(new Point(0, 0)); + matrix.resizeSelection(new Point(5, 5)); + assertSelected(new int[] {0}); + matrix.resizeSelection(new Point(109, 109)); + assertSelected(new int[] {0}); + matrix.resizeSelection(new Point(110, 109)); + assertSelected(new int[] {0, 1}); + matrix.resizeSelection(new Point(110, 110)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(214, 214)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(215, 214)); + assertSelected(new int[] {0, 1, 2, 4, 5, 6}); + matrix.resizeSelection(new Point(214, 214)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(110, 110)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(110, 109)); + assertSelected(new int[] {0, 1}); + matrix.resizeSelection(new Point(109, 109)); + assertSelected(new int[] {0}); + matrix.resizeSelection(new Point(5, 5)); + assertSelected(new int[] {0}); + matrix.resizeSelection(new Point(0, 0)); + assertSelected(new int[0]); + } + + @Test + public void testSelectionMovingAroundOrigin() { + setUp(16, 4); + matrix.startSelection(new Point(210, 210)); + matrix.resizeSelection(new Point(viewWidth - 1, 0)); + assertSelected(new int[] {2, 3, 6, 7}); + matrix.resizeSelection(new Point(0, 0)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(0, 420)); + assertSelected(new int[] {8, 9, 12, 13}); + matrix.resizeSelection(new Point(viewWidth - 1, 420)); + assertSelected(new int[] {10, 11, 14, 15}); + } + + @Test + public void testScrollingBandSelect() { + setUp(40, 4); + matrix.startSelection(new Point(0, 0)); + matrix.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1)); + assertSelected(new int[] {0, 4, 8, 12, 16}); + scroll(CHILD_VIEW_EDGE_PX); + assertSelected(new int[] {0, 4, 8, 12, 16, 20}); + matrix.resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1)); + assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21}); + scroll(CHILD_VIEW_EDGE_PX); + assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25}); + scroll(-2 * CHILD_VIEW_EDGE_PX); + assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17}); + matrix.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1)); + assertSelected(new int[] {0, 4, 8, 12, 16}); + } + + private static void assertSelected(int[] selectedPositions) { + assertEquals(selectedPositions.length, lastSelection.size()); + for (int position : selectedPositions) { + assertTrue(lastSelection.get(position)); + } + } + + private static void scroll(int dy) { + assertTrue(helper.verticalOffset + VIEWPORT_HEIGHT + dy <= helper.getTotalHeight()); + helper.verticalOffset += dy; + matrix.onScrolled(null, 0, dy); + } + + private static final class TestHelper implements BandSelectMatrix.RecyclerViewHelper { + + public int horizontalOffset = 0; + public int verticalOffset = 0; + private final int mNumColumns; + private final int mNumRows; + private final int mNumChildren; + + public TestHelper(int numChildren, int numColumns) { + mNumChildren = numChildren; + mNumColumns = numColumns; + mNumRows = (int) Math.ceil((double) numChildren / mNumColumns); + } + + private int getTotalHeight() { + return CHILD_VIEW_EDGE_PX * mNumRows + VIEW_PADDING_PX * (mNumRows + 1); + } + + private int getFirstVisibleRowIndex() { + return verticalOffset / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); + } + + private int getLastVisibleRowIndex() { + int lastVisibleRowUncapped = + (VIEWPORT_HEIGHT + verticalOffset - 1) / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); + return Math.min(lastVisibleRowUncapped, mNumRows - 1); + } + + private int getNumItemsInRow(int index) { + assertTrue(index >= 0 && index < mNumRows); + if (index == mNumRows - 1 && mNumChildren % mNumColumns != 0) { + return mNumChildren % mNumColumns; + } + + return mNumColumns; + } + + @Override + public void addOnScrollListener(OnScrollListener listener) {} + + @Override + public void removeOnScrollListener(OnScrollListener listener) {} + + @Override + public Point createAbsolutePoint(Point relativePoint) { + return new Point( + relativePoint.x + horizontalOffset, relativePoint.y + verticalOffset); + } + + @Override + public int getVisibleChildCount() { + int childCount = 0; + for (int i = getFirstVisibleRowIndex(); i <= getLastVisibleRowIndex(); i++) { + childCount += getNumItemsInRow(i); + } + return childCount; + } + + @Override + public int getAdapterPositionAt(int index) { + return index + mNumColumns * (getFirstVisibleRowIndex()); + } + + @Override + public Rect getAbsoluteRectForChildViewAt(int index) { + int adapterPosition = getAdapterPositionAt(index); + int rowIndex = adapterPosition / mNumColumns; + int columnIndex = adapterPosition % mNumColumns; + + Rect rect = new Rect(); + rect.top = VIEW_PADDING_PX + rowIndex * (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); + rect.bottom = rect.top + CHILD_VIEW_EDGE_PX - 1; + rect.left = VIEW_PADDING_PX + columnIndex * (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); + rect.right = rect.left + CHILD_VIEW_EDGE_PX - 1; + return rect; + } + + @Override + public int getTotalChildCount() { + return mNumChildren; + } + + @Override + public int getNumColumns() { + return mNumColumns; + } + + @Override + public int getNumRows() { + return mNumRows; + } + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java index 8c5bac15442d..132570674b48 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java @@ -21,7 +21,8 @@ import android.test.suitebuilder.annotation.SmallTest; import com.android.documentsui.BaseActivity.State; import com.android.documentsui.model.RootInfo; -import com.google.android.collect.Lists; + +import com.google.common.collect.Lists; import java.util.List; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java index c2f176221f4b..6a2e03a3b809 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java @@ -36,8 +36,6 @@ import android.provider.DocumentsProvider; import android.support.annotation.VisibleForTesting; import android.util.Log; -import com.google.android.collect.Maps; - import libcore.io.IoUtils; import java.io.File; @@ -101,7 +99,7 @@ public class StubProvider extends DocumentsProvider { }); } // Create new roots. - mRoots = Maps.newHashMap(); + mRoots = new HashMap<>(); for (String rootId : rootIds) { final RootInfo rootInfo = new RootInfo(rootId, getSize(rootId)); mRoots.put(rootId, rootInfo); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java index 4ffe799119ec..055327090eb4 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java @@ -22,6 +22,7 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ + BandSelectMatrixTest.class, MultiSelectManager_SelectionTest.class, MultiSelectManagerTest.class }) diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index 40dcd0d86dc1..ead03074df44 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -29,36 +29,19 @@ import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.graphics.Bitmap; - -import static android.os.BatteryManager.BATTERY_STATUS_FULL; -import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; -import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; -import static android.os.BatteryManager.EXTRA_STATUS; -import static android.os.BatteryManager.EXTRA_PLUGGED; -import static android.os.BatteryManager.EXTRA_LEVEL; -import static android.os.BatteryManager.EXTRA_HEALTH; -import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; - +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; +import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.media.AudioManager; import android.os.BatteryManager; import android.os.CancellationSignal; import android.os.Handler; import android.os.IRemoteCallback; import android.os.Message; -import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; - -import com.android.internal.telephony.IccCardConstants; -import com.android.internal.telephony.IccCardConstants.State; -import com.android.internal.telephony.PhoneConstants; -import com.android.internal.telephony.TelephonyIntents; - -import android.hardware.fingerprint.FingerprintManager; -import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; -import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -71,6 +54,12 @@ import android.util.SparseIntArray; import com.google.android.collect.Lists; +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.IccCardConstants.State; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.widget.LockPatternUtils; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -79,6 +68,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map.Entry; +import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; +import static android.os.BatteryManager.BATTERY_STATUS_FULL; +import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; +import static android.os.BatteryManager.EXTRA_HEALTH; +import static android.os.BatteryManager.EXTRA_LEVEL; +import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; +import static android.os.BatteryManager.EXTRA_PLUGGED; +import static android.os.BatteryManager.EXTRA_STATUS; + /** * Watches for updates that may be interesting to the keyguard, and provides * the up to date information as well as a registration for callbacks that care @@ -138,6 +136,24 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final int MSG_SCREEN_TURNED_ON = 331; private static final int MSG_SCREEN_TURNED_OFF = 332; + /** Fingerprint state: Not listening to fingerprint. */ + private static final int FINGERPRINT_STATE_STOPPED = 0; + + /** Fingerprint state: Listening. */ + private static final int FINGERPRINT_STATE_RUNNING = 1; + + /** + * Fingerprint state: Cancelling and waiting for the confirmation from FingerprintService to + * send us the confirmation that cancellation has happened. + */ + private static final int FINGERPRINT_STATE_CANCELLING = 2; + + /** + * Fingerprint state: During cancelling we got another request to start listening, so when we + * receive the cancellation done signal, we should start listening again. + */ + private static final int FINGERPRINT_STATE_CANCELLING_RESTARTING = 3; + private static KeyguardUpdateMonitor sInstance; private final Context mContext; @@ -155,7 +171,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private boolean mFingerprintAlreadyAuthenticated; private boolean mBouncer; private boolean mBootCompleted; - private boolean mUserHasAuthenticatedSinceBoot; // Device provisioning state private boolean mDeviceProvisioned; @@ -167,7 +182,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private SparseIntArray mFailedAttempts = new SparseIntArray(); /** Tracks whether strong authentication hasn't been used since quite some time per user. */ - private ArraySet<Integer> mStrongAuthTimedOut = new ArraySet<>(); + private ArraySet<Integer> mStrongAuthNotTimedOut = new ArraySet<>(); + private final StrongAuthTracker mStrongAuthTracker = new StrongAuthTracker(); private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>> mCallbacks = Lists.newArrayList(); @@ -180,8 +196,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private SubscriptionManager mSubscriptionManager; private AlarmManager mAlarmManager; private List<SubscriptionInfo> mSubscriptionInfo; - private boolean mFingerprintDetectionRunning; private TrustManager mTrustManager; + private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED; private final Handler mHandler = new Handler() { @Override @@ -275,8 +291,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static int sCurrentUser; - private int mFpWakeMode; - public synchronized static void setCurrentUser(int currentUser) { sCurrentUser = currentUser; } @@ -429,7 +443,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } onFingerprintAuthenticated(userId); } finally { - setFingerprintRunningDetectionRunning(false); + setFingerprintRunningState(FINGERPRINT_STATE_STOPPED); } } @@ -443,7 +457,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } private void handleFingerprintError(int msgId, String errString) { - setFingerprintRunningDetectionRunning(false); + if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED + && mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) { + setFingerprintRunningState(FINGERPRINT_STATE_STOPPED); + startListeningForFingerprint(); + } else { + setFingerprintRunningState(FINGERPRINT_STATE_STOPPED); + } for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -452,9 +472,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } - private void setFingerprintRunningDetectionRunning(boolean running) { - if (running != mFingerprintDetectionRunning) { - mFingerprintDetectionRunning = running; + private void setFingerprintRunningState(int fingerprintRunningState) { + boolean wasRunning = mFingerprintRunningState == FINGERPRINT_STATE_RUNNING; + boolean isRunning = fingerprintRunningState == FINGERPRINT_STATE_RUNNING; + mFingerprintRunningState = fingerprintRunningState; + + // Clients of KeyguardUpdateMonitor don't care about the internal state about the + // asynchronousness of the cancel cycle. So only notify them if the actualy running state + // has changed. + if (wasRunning != isRunning) { notifyFingerprintRunningStateChanged(); } } @@ -463,7 +489,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onFingerprintRunningStateChanged(mFingerprintDetectionRunning); + cb.onFingerprintRunningStateChanged(isFingerprintDetectionRunning()); } } } @@ -482,7 +508,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } public boolean isFingerprintDetectionRunning() { - return mFingerprintDetectionRunning; + return mFingerprintRunningState == FINGERPRINT_STATE_RUNNING; } private boolean isTrustDisabled(int userId) { @@ -514,7 +540,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } public boolean isUnlockingWithFingerprintAllowed() { - return mUserHasAuthenticatedSinceBoot && !hasFingerprintUnlockTimedOut(sCurrentUser); + return mStrongAuthTracker.isUnlockingWithFingerprintAllowed() + && !hasFingerprintUnlockTimedOut(sCurrentUser); + } + + public StrongAuthTracker getStrongAuthTracker() { + return mStrongAuthTracker; } /** @@ -522,11 +553,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { * while and thus can't unlock with fingerprint, false otherwise */ public boolean hasFingerprintUnlockTimedOut(int userId) { - return mStrongAuthTimedOut.contains(userId); + return !mStrongAuthNotTimedOut.contains(userId); } public void reportSuccessfulStrongAuthUnlockAttempt() { - mStrongAuthTimedOut.remove(sCurrentUser); + mStrongAuthNotTimedOut.add(sCurrentUser); scheduleStrongAuthTimeout(); if (mFpm != null) { byte[] token = null; /* TODO: pass real auth token once fp HAL supports it */ @@ -541,14 +572,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { PendingIntent sender = PendingIntent.getBroadcast(mContext, sCurrentUser, intent, PendingIntent.FLAG_CANCEL_CURRENT); mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, sender); - notifyStrongAuthTimedOutChanged(sCurrentUser); + notifyStrongAuthStateChanged(sCurrentUser); } - private void notifyStrongAuthTimedOutChanged(int userId) { + private void notifyStrongAuthStateChanged(int userId) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onStrongAuthTimeoutExpiredChanged(userId); + cb.onStrongAuthStateChanged(userId); } } } @@ -643,8 +674,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { public void onReceive(Context context, Intent intent) { if (ACTION_STRONG_AUTH_TIMEOUT.equals(intent.getAction())) { int userId = intent.getIntExtra(USER_ID, -1); - mStrongAuthTimedOut.add(userId); - notifyStrongAuthTimedOutChanged(userId); + mStrongAuthNotTimedOut.remove(userId); + notifyStrongAuthStateChanged(userId); } } }; @@ -802,6 +833,25 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } + public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker { + + public boolean isUnlockingWithFingerprintAllowed() { + int userId = getCurrentUser(); + return isFingerprintAllowedForUser(userId); + } + + public boolean hasUserAuthenticatedSinceBoot() { + int userId = getCurrentUser(); + return (getStrongAuthForUser(userId) + & STRONG_AUTH_REQUIRED_AFTER_BOOT) == 0; + } + + @Override + public void onStrongAuthRequiredChanged(int userId) { + notifyStrongAuthStateChanged(userId); + } + } + public static KeyguardUpdateMonitor getInstance(Context context) { if (sInstance == null) { sInstance = new KeyguardUpdateMonitor(context); @@ -948,6 +998,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { PERMISSION_SELF, null /* handler */); mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE); mTrustManager.registerTrustListener(this); + new LockPatternUtils(context).registerStrongAuthTracker(mStrongAuthTracker); mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); updateFingerprintListeningState(); @@ -955,9 +1006,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private void updateFingerprintListeningState() { boolean shouldListenForFingerprint = shouldListenForFingerprint(); - if (mFingerprintDetectionRunning && !shouldListenForFingerprint) { + if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING && !shouldListenForFingerprint) { stopListeningForFingerprint(); - } else if (!mFingerprintDetectionRunning && shouldListenForFingerprint) { + } else if (mFingerprintRunningState != FINGERPRINT_STATE_RUNNING + && shouldListenForFingerprint) { startListeningForFingerprint(); } } @@ -968,17 +1020,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } private void startListeningForFingerprint() { + if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING) { + setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING_RESTARTING); + return; + } if (DEBUG) Log.v(TAG, "startListeningForFingerprint()"); int userId = ActivityManager.getCurrentUser(); if (isUnlockWithFingerprintPossible(userId)) { - mUserHasAuthenticatedSinceBoot = mTrustManager.hasUserAuthenticatedSinceBoot( - ActivityManager.getCurrentUser()); if (mFingerprintCancelSignal != null) { mFingerprintCancelSignal.cancel(); } mFingerprintCancelSignal = new CancellationSignal(); mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null, userId); - setFingerprintRunningDetectionRunning(true); + setFingerprintRunningState(FINGERPRINT_STATE_RUNNING); } } @@ -989,11 +1043,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private void stopListeningForFingerprint() { if (DEBUG) Log.v(TAG, "stopListeningForFingerprint()"); - if (isFingerprintDetectionRunning()) { + if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING) { mFingerprintCancelSignal.cancel(); mFingerprintCancelSignal = null; + setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING); + } + if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) { + setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING); } - setFingerprintRunningDetectionRunning(false); } private boolean isDeviceProvisionedInSettingsDb() { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 7ca67b085e9a..15ffe9f5ca7e 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -233,5 +233,5 @@ public class KeyguardUpdateMonitorCallback { * Called when the state that the user hasn't used strong authentication since quite some time * has changed. */ - public void onStrongAuthTimeoutExpiredChanged(int userId) { } + public void onStrongAuthStateChanged(int userId) { } } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java index 98775b36f796..7126694bc4a5 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java @@ -32,6 +32,7 @@ class MtpDocument { private final Date mDateModified; private final int mSize; private final int mThumbSize; + private final boolean mReadOnly; /** * Constructor for root document. @@ -40,9 +41,10 @@ class MtpDocument { this(DUMMY_HANDLE_FOR_ROOT, 0x3001, // Directory. root.mDescription, - null, // Unknown, + null, // Unknown name. (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE), - 0); + 0, // Total size. + true); // Writable. } MtpDocument(MtpObjectInfo objectInfo) { @@ -51,7 +53,8 @@ class MtpDocument { objectInfo.getName(), objectInfo.getDateModified() != 0 ? new Date(objectInfo.getDateModified()) : null, objectInfo.getCompressedSize(), - objectInfo.getThumbCompressedSize()); + objectInfo.getThumbCompressedSize(), + objectInfo.getProtectionStatus() != 0); } MtpDocument(int objectHandle, @@ -59,13 +62,15 @@ class MtpDocument { String name, Date dateModified, int size, - int thumbSize) { + int thumbSize, + boolean readOnly) { this.mObjectHandle = objectHandle; this.mFormat = format; this.mName = name; this.mDateModified = dateModified; this.mSize = size; this.mThumbSize = thumbSize; + this.mReadOnly = readOnly; } void addToCursor(Identifier rootIdentifier, MatrixCursor.RowBuilder builder) { @@ -82,7 +87,7 @@ class MtpDocument { builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId()); builder.add(Document.COLUMN_DISPLAY_NAME, mName); - builder.add(Document.COLUMN_MIME_TYPE, getMimeType()); + builder.add(Document.COLUMN_MIME_TYPE, formatTypeToMimeType(mFormat)); builder.add( Document.COLUMN_LAST_MODIFIED, mDateModified != null ? mDateModified.getTime() : null); @@ -90,9 +95,9 @@ class MtpDocument { builder.add(Document.COLUMN_SIZE, mSize); } - private String getMimeType() { + static String formatTypeToMimeType(int format) { // TODO: Add complete list of mime types. - switch (mFormat) { + switch (format) { case 0x3001: return DocumentsContract.Document.MIME_TYPE_DIR; case 0x3009: @@ -100,7 +105,21 @@ class MtpDocument { case 0x3801: return "image/jpeg"; default: - return ""; + return "application/octet-stream"; + } + } + + static int mimeTypeToFormatType(String mimeType) { + // TODO: Add complete list of mime types. + switch (mimeType.toLowerCase()) { + case Document.MIME_TYPE_DIR: + return 0x3001; + case "audio/mp3": + return 0x3009; + case "image/jpeg": + return 0x3801; + default: + return 0x3000; // Undefined object. } } } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java index 61b9fc53b98f..031cc074672a 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java @@ -96,7 +96,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { final Identifier rootIdentifier = new Identifier(root.mDeviceId, root.mStorageId); final MatrixCursor.RowBuilder builder = cursor.newRow(); builder.add(Root.COLUMN_ROOT_ID, rootIdentifier.toRootId()); - builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD); + builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); builder.add(Root.COLUMN_TITLE, root.mDescription); builder.add( Root.COLUMN_DOCUMENT_ID, @@ -214,6 +214,24 @@ public class MtpDocumentsProvider extends DocumentsProvider { mDocumentLoader.clearCache(); } + @Override + public String createDocument(String parentDocumentId, String mimeType, String displayName) + throws FileNotFoundException { + try { + final Identifier parentId = Identifier.createFromDocumentId(parentDocumentId); + final int objectHandle = mMtpManager.createDocument( + parentId.mDeviceId, parentId.mStorageId, parentId.mObjectHandle, mimeType, + displayName); + final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId, + objectHandle).toDocumentId(); + notifyChildDocumentsChange(parentDocumentId); + return documentId; + } catch (IOException error) { + Log.e(TAG, error.getMessage()); + throw new FileNotFoundException(error.getMessage()); + } + } + void openDevice(int deviceId) throws IOException { mMtpManager.openDevice(deviceId); mRootScanner.scanNow(); diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java index 3afc173fcc52..27ba794d3883 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java @@ -21,7 +21,10 @@ import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import android.mtp.MtpDevice; +import android.mtp.MtpObjectInfo; import android.os.ParcelFileDescriptor; +import android.provider.DocumentsContract.Document; +import android.provider.DocumentsContract; import android.util.SparseArray; import java.io.FileNotFoundException; @@ -134,6 +137,22 @@ class MtpManager { } } + synchronized int createDocument(int deviceId, int storageId, int parentObjectHandle, + String mimeType, String name) throws IOException { + final MtpDevice device = getDevice(deviceId); + final MtpObjectInfo objectInfo = new MtpObjectInfo.Builder() + .setName(name) + .setStorageId(storageId) + .setParent(parentObjectHandle) + .setFormat(MtpDocument.mimeTypeToFormatType(mimeType)) + .build(); + final MtpObjectInfo result = device.sendObjectInfo(objectInfo); + if (result == null) { + throw new IOException("Failed to create a document"); + } + return result.getObjectHandle(); + } + synchronized int getParent(int deviceId, int objectHandle) throws IOException { final MtpDevice device = getDevice(deviceId); final int result = (int) device.getParent(objectHandle); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java index 55041478f105..1e015bdc78d5 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java @@ -91,7 +91,8 @@ public class DocumentLoaderTest extends AndroidTestCase { "file" + objectHandle, new Date(), 1024, - 0 /* thumbnail size */)); + 0 /* thumbnail size */, + false /* not read only */)); } manager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, childDocuments); } diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java index c1da59f5cb43..f06e2ffacf9a 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java @@ -210,7 +210,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { "image.jpg" /* display name */, new Date(1422716400000L) /* modified date */, 1024 * 1024 * 5 /* file size */, - 1024 * 50 /* thumbnail size */)); + 1024 * 50 /* thumbnail size */, + true /* read only */)); final Cursor cursor = mProvider.queryDocument("0_1_2", null); assertEquals(1, cursor.getCount()); @@ -257,7 +258,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { "image.jpg" /* display name */, new Date(0) /* modified date */, 1024 * 1024 * 5 /* file size */, - 1024 * 50 /* thumbnail size */)); + 1024 * 50 /* thumbnail size */, + true /* read only */)); final Cursor cursor = mProvider.queryChildDocuments("0_0_0", null, null); assertEquals(1, cursor.getCount()); @@ -302,7 +304,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { "image.jpg" /* display name */, new Date(1422716400000L) /* modified date */, 1024 * 1024 * 5 /* file size */, - 1024 * 50 /* thumbnail size */)); + 1024 * 50 /* thumbnail size */, + false /* not read only */)); mMtpManager.setParent(0, 1, 2); mProvider.deleteDocument("0_0_1"); assertEquals(1, mResolver.getChangeCount( diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java index 096a5c43e12c..f52d75502e7d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java @@ -54,29 +54,7 @@ public class TetherUtil { public static boolean setWifiTethering(boolean enable, Context context) { final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - final ContentResolver cr = context.getContentResolver(); - /** - * Disable Wifi if enabling tethering - */ - int wifiState = wifiManager.getWifiState(); - if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) || - (wifiState == WifiManager.WIFI_STATE_ENABLED))) { - wifiManager.setWifiEnabled(false); - Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1); - } - - boolean success = wifiManager.setWifiApEnabled(null, enable); - /** - * If needed, restore Wifi on tether disable - */ - if (!enable) { - int wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE, 0); - if (wifiSavedState == 1) { - wifiManager.setWifiEnabled(true); - Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0); - } - } - return success; + return wifiManager.setWifiApEnabled(null, enable); } public static boolean isWifiTetherEnabled(Context context) { diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk index 51fea2a124ec..314b3c4bf326 100644 --- a/packages/SystemUI/Android.mk +++ b/packages/SystemUI/Android.mk @@ -3,13 +3,15 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-java-files-under, src) \ +LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) \ src/com/android/systemui/EventLogTags.logtags LOCAL_STATIC_JAVA_LIBRARIES := Keyguard LOCAL_JAVA_LIBRARIES := telephony-common LOCAL_PACKAGE_NAME := SystemUI +LOCAL_PROTOC_OPTIMIZE_TYPE := nano +LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors LOCAL_CERTIFICATE := platform LOCAL_PRIVILEGED_MODULE := true diff --git a/packages/SystemUI/res/anim/navbar_fade_in.xml b/packages/SystemUI/res/anim/navbar_fade_in.xml index e3429e68dd13..705173043fa3 100644 --- a/packages/SystemUI/res/anim/navbar_fade_in.xml +++ b/packages/SystemUI/res/anim/navbar_fade_in.xml @@ -19,4 +19,5 @@ android:fromAlpha="0.0" android:toAlpha="1.0" android:interpolator="@android:interpolator/linear_out_slow_in" + android:startDelay="32" android:duration="200"/> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 26d527478706..9294a16ca6b5 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Verwyder uit Instellings"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Verwyder Stelsel-UI-ontvanger uit Instellings en staak die gebruik van al sy kenmerke?"</string> <string name="activity_not_found" msgid="348423244327799974">"Program is nie op jou toestel geïnstalleer nie"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Wys horlosiesekondes"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Wys horlosiesekondes op die statusbalk. Sal batterylewe dalk beïnvloed."</string> </resources> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index e61b6c33a656..13f768d87953 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"ከቅንብሮች አስወግድ"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"ከቅንብሮች ላይ የስርዓት በይነገጽ መቃኛ ተወግዶ ሁሉም ባህሪዎቹን መጠቀም ይቁም?"</string> <string name="activity_not_found" msgid="348423244327799974">"መተግበሪያ በእርስዎ መሣሪያ ላይ አልተጫነም"</string> + <string name="clock_seconds" msgid="7689554147579179507">"የሰዓት ሰከንዶችን አሳይ"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"የሰዓት ሰከንዶችን በሁኔታ አሞሌ ውስጥ አሳይ። በባትሪ ዕድሜ ላይ ተጽዕኖ ሊኖርው ይችል ይሆናል።"</string> </resources> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 2e358a1955dc..6889a4352983 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -437,4 +437,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"إزالة من الإعدادات"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"هل تريد إزالة أداة ضبط واجهة مستخدم النظام من الإعدادات وإيقاف استخدام كل ميزاتها؟"</string> <string name="activity_not_found" msgid="348423244327799974">"التطبيق غير مثبّت على جهازك"</string> + <string name="clock_seconds" msgid="7689554147579179507">"عرض ثواني الساعة"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"عرض ثواني الساعة في شريط الحالة. قد يؤثر ذلك في عمر البطارية."</string> </resources> diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml index b82b42ecdca2..51513bd5ebf8 100644 --- a/packages/SystemUI/res/values-az-rAZ/strings.xml +++ b/packages/SystemUI/res/values-az-rAZ/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Ayarlardan Silin"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"System UI Tuner Ayarlardan silinsin və onun bütün funksiyalarından istifadə dayandırılsın?"</string> <string name="activity_not_found" msgid="348423244327799974">"Tətbiq cihazınızda quraşdırılmayıb"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Saatın saniyəsini göstərin"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Saatın saniyəsini status panelində göstərin. Batareyaya təsir edə bilər."</string> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 82ace5265441..014d802204ec 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Премахване от „Настройки“"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Да се премахне ли от „Настройки“ тунерът на системния потребителски интерфейс и да се спре ли използването на всичките му функции?"</string> <string name="activity_not_found" msgid="348423244327799974">"Приложението не е инсталирано на устройството ви"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Показване на секундите на часовника"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Показване на секундите на часовника в лентата на състоянието. Може да се отрази на живота на батерията."</string> </resources> diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml index 884fc428e333..08600193fc1c 100644 --- a/packages/SystemUI/res/values-bn-rBD/strings.xml +++ b/packages/SystemUI/res/values-bn-rBD/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"সেটিংস থেকে সরান"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"সেটিংস থেকে সিস্টেম UI টিউনার সরাতে এবং এটির সমস্ত বৈশিষ্ট্য ব্যবহার করা বন্ধ করতে চান?"</string> <string name="activity_not_found" msgid="348423244327799974">"আপনার ডিভাইসে অ্যাপ্লিকেশান ইনস্টল করা নেই"</string> + <string name="clock_seconds" msgid="7689554147579179507">"ঘড়ির সেকেন্ড দেখায়"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"স্থিতি দন্ডে ঘড়ির সেকেন্ড দেখায়৷ ব্যাটারি লাইফকে প্রভাবিত করতে পারে৷"</string> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index cc04fd6477e0..7dd11ebe0883 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Treu de Configuració"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vols treure el Configurador de la UI del sistema de Configuració i deixar d\'utilitzar-ne totes les funcions?"</string> <string name="activity_not_found" msgid="348423244327799974">"L\'aplicació no està instal·lada al dispositiu"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Mostra els segons del rellotge"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra els segons del rellotge a la barra d\'estat. Això pot afectar la durada de la bateria."</string> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 7f94577fca77..4d96a011a094 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -437,4 +437,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Odstranit z Nastavení"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Chcete nástroj na ladění uživatelského rozhraní systému odstranit z Nastavení a přestat používat všechny jeho funkce?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikace není v zařízení nainstalována."</string> + <string name="clock_seconds" msgid="7689554147579179507">"Zobrazit sekundovou ručičku"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Na stavovém řádku se bude zobrazovat sekundová ručička. Může být ovlivněna výdrž baterie."</string> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 2129f033c2dc..7f005a383d3b 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Fjern fra Indstillinger"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vil du fjerne System UI Tuner fra Indstillinger og stoppe med at bruge alle dens funktioner?"</string> <string name="activity_not_found" msgid="348423244327799974">"Applikationen er ikke installeret på din enhed."</string> + <string name="clock_seconds" msgid="7689554147579179507">"Vis sekunder"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Vis sekunder i statuslinjen. Dette kan påvirke batteriets levetid."</string> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 30a2baf1b796..36f3c985672e 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Aus \"Einstellungen\" entfernen"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"System UI Tuner aus \"Einstellungen\" entfernen und die Verwendung von allen zugehörigen Funktionen beenden?"</string> <string name="activity_not_found" msgid="348423244327799974">"Die App ist nicht auf Ihrem Gerät installiert."</string> + <string name="clock_seconds" msgid="7689554147579179507">"Uhrsekunden anzeigen"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Uhrsekunden in der Statusleiste anzeigen. Kann sich auf die Akkulaufzeit auswirken."</string> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index f2fc6023a3f4..8e73b21d5d9c 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Κατάργηση από τις Ρυθμίσεις"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Κατάργηση System UI Tuner από τις Ρυθμίσεις και διακοπή χρήσης όλων των λειτουργιών του;"</string> <string name="activity_not_found" msgid="348423244327799974">"Η εφαρμογή δεν έχει εγκατασταθεί στη συσκευή σας"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Εμφάνιση δευτερολέπτων ρολογιού"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Εμφάνιση δευτερολέπτων ρολογιού στη γραμμή κατάστασης. Ενδέχεται να επηρεάσει τη διάρκεια ζωής της μπαταρίας."</string> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index f1aafb64658e..0b499557f8fd 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Remove from settings"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Remove System UI Tuner from Settings and stop using all of its features?"</string> <string name="activity_not_found" msgid="348423244327799974">"Application is not installed on your device"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Show clock seconds"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index f1aafb64658e..0b499557f8fd 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Remove from settings"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Remove System UI Tuner from Settings and stop using all of its features?"</string> <string name="activity_not_found" msgid="348423244327799974">"Application is not installed on your device"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Show clock seconds"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index f1aafb64658e..0b499557f8fd 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Remove from settings"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Remove System UI Tuner from Settings and stop using all of its features?"</string> <string name="activity_not_found" msgid="348423244327799974">"Application is not installed on your device"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Show clock seconds"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 5136545eb135..c4dca9c491dc 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Quitar de Configuración"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"¿Quieres quitar el sintonizador de IU del sistema de Configuración y dejar de utilizar todas sus funciones?"</string> <string name="activity_not_found" msgid="348423244327799974">"La aplicación no está instalada en el dispositivo"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Mostrar los segundos del reloj"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Muestra los segundos del reloj en la barra de estado. Puede afectar la duración de la batería."</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 82fd6fc86da9..e316a944793c 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Eliminar de Ajustes"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"¿Eliminar el configurador de IU del sistema de Ajustes y dejar de utilizar sus funciones?"</string> <string name="activity_not_found" msgid="348423244327799974">"La aplicación no está instalada en tu dispositivo"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Mostrar los segundos del reloj"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Muestra los segundos del reloj en la barra de estado. Puede afectar a la duración de la batería."</string> </resources> diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml index dd3d8231de86..a151bce0f475 100644 --- a/packages/SystemUI/res/values-et-rEE/strings.xml +++ b/packages/SystemUI/res/values-et-rEE/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Eemalda seadetest"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Kas eemaldada seadetest süsteemi kasutajaliidese tuuner ja lõpetada kõikide selle funktsioonide kasutamine?"</string> <string name="activity_not_found" msgid="348423244327799974">"Rakendust pole teie seadmesse installitud"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Kella sekundite kuvamine"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Olekuribal kella sekundite kuvamine. See võib mõjutada aku kasutusaega."</string> </resources> diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml index a856c0956aaa..37cdee70163d 100644 --- a/packages/SystemUI/res/values-eu-rES/strings.xml +++ b/packages/SystemUI/res/values-eu-rES/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Kendu Ezarpenak ataletik"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Sistemako erabiltzaile-interfazearen konfiguratzailea ezarpenetatik kendu nahi duzu, eta haren eginbide guztiak erabiltzeari utzi nahi diozu?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikazioa ez dago gailuan instalatuta"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Erakutsi erlojuko segundoak"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Erakutsi erlojuko segundoak egoera-barran. Baliteke bateria gehiago erabiltzea."</string> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 412f808c3883..80d09185003d 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"حذف از تنظیمات"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"«تنظیمکننده واسط کاربری سیستم» از تنظیمات حذف شود و همه ویژگیهای آن متوقف شوند؟"</string> <string name="activity_not_found" msgid="348423244327799974">"برنامه در دستگاه شما نصب نیست"</string> + <string name="clock_seconds" msgid="7689554147579179507">"نمایش ثانیههای ساعت"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"ثانیههای ساعت را در نوار وضعیت نشان میدهد. ممکن است بر ماندگاری باتری تأثیر بگذارد."</string> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 836b136b097f..b86247acf597 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Poista Asetuksista"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Haluatko poistaa System UI Tunerin Asetuksista ja lopettaa sen ominaisuuksien käytön?"</string> <string name="activity_not_found" msgid="348423244327799974">"Sovellusta ei ole asennettu laitteellesi."</string> + <string name="clock_seconds" msgid="7689554147579179507">"Näytä sekunnit kellossa"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Näytä sekunnit tilapalkin kellossa. Tämä voi vaikuttaa akun kestoon."</string> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 8c6773fbe285..4e57f2b5cc5d 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Supprimer des paramètres"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Supprimer « System UI Tuner » des paramètres et arrêter d\'utiliser toutes ses fonctionnalités?"</string> <string name="activity_not_found" msgid="348423244327799974">"L\'application n\'est pas installée sur votre appareil"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Afficher les secondes de l\'horloge"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes de l\'horloge dans la barre d\'état. Cela peut influer sur l\'autonomie de la pile."</string> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 666c0ef67cf6..aada1eb83f7a 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Supprimer l\'outil des paramètres"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Supprimer System UI Tuner des paramètres et arrêter d\'utiliser toutes ses fonctionnalités ?"</string> <string name="activity_not_found" msgid="348423244327799974">"L\'application n\'est pas installée sur votre appareil."</string> + <string name="clock_seconds" msgid="7689554147579179507">"Afficher les secondes sur l\'horloge"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes dans la barre d\'état. Cela risque de réduire l\'autonomie de la batterie."</string> </resources> diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml index d192257f333a..951809fe1d8c 100644 --- a/packages/SystemUI/res/values-gl-rES/strings.xml +++ b/packages/SystemUI/res/values-gl-rES/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Eliminar da Configuración"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Queres eliminar o configurador da IU do sistema da Configuración e deixar de usar todas as súas funcións?"</string> <string name="activity_not_found" msgid="348423244327799974">"A aplicación non está instalada no teu dispositivo"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Mostrar segundos do reloxo"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra os segundos do reloxo na barra de estado. Pode influír na duración da batería."</string> </resources> diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml index 81dab352715f..02e4e5209892 100644 --- a/packages/SystemUI/res/values-gu-rIN/strings.xml +++ b/packages/SystemUI/res/values-gu-rIN/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"સેટિંગ્સમાંથી દૂર કરો"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"સેટિંગ્સમાંથી સિસ્ટમ UI ટ્યૂનર દૂર કરી અને તેની તમામ સુવિધાઓનો ઉપયોગ કરવાનું બંધ કરીએ?"</string> <string name="activity_not_found" msgid="348423244327799974">"તમારા ઉપકરણ પર એપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી"</string> + <string name="clock_seconds" msgid="7689554147579179507">"ઘડિયાળ સેકન્ડ બતાવો"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"ઘડિયાળ સેકન્ડ સ્થિતિ બારમાં બતાવો. બૅટરીની આવરદા પર અસર કરી શકે છે."</string> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index a4bf691f88db..3ed090c90111 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"सेटिंग से निकालें"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"सेटिंग से सिस्टम UI ट्यूनर निकालें और इसकी सभी सुविधाओं का उपयोग रोक दें?"</string> <string name="activity_not_found" msgid="348423244327799974">"ऐप्लिकेशन आपके डिवाइस पर इंस्टॉल नहीं है"</string> + <string name="clock_seconds" msgid="7689554147579179507">"घड़ी के सेकंड दिखाएं"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"स्थिति बार में घड़ी के सेकंड दिखाएं. इससे बैटरी के जीवनकाल पर प्रभाव पड़ सकता है."</string> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index d18c04bf5098..9698abac2dc5 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -434,4 +434,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Ukloni iz Postavki"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Želite li ukloniti Ugađanje korisničkog sučelja sustava iz Postavki i prestati upotrebljavati njegove značajke?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikacija nije instalirana na vašem uređaju"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Prikaži sekunde na satu"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Prikazuju se sekunde na satu na traci statusa. Može utjecati na trajanje baterije."</string> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 49543cbbd52e..37cb0a81610f 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Eltávolítás a Beállítások közül"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Eltávolítja a Kezelőfelület-hangolót a Beállításokból, és nem használja tovább egyik funkcióját sem?"</string> <string name="activity_not_found" msgid="348423244327799974">"Az alkalmazás nincs telepítve eszközén."</string> + <string name="clock_seconds" msgid="7689554147579179507">"Másodpercek megjelenítése az órán"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Másodpercek megjelenítése az állapotsor óráján. Ez hatással lehet az akkumulátor üzemidejére."</string> </resources> diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml index 8cf062f36aa5..e31cf497199a 100644 --- a/packages/SystemUI/res/values-hy-rAM/strings.xml +++ b/packages/SystemUI/res/values-hy-rAM/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Հեռացնել կարգավորումներից"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Հեռացնե՞լ Համակարգի ՕՄ-ի կարգավորիչը կարգավորումներից և չօգտվել այլևս նրա գործառույթներից:"</string> <string name="activity_not_found" msgid="348423244327799974">"Հավելվածը տեղադրված չէ սարքի վրա"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Ցույց տալ ժամացույցի վայրկյանները"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Ցույց տալ ժամացույցի վայրկյանները կարգավիճակի տողում: Կարող է ազդել մարտկոցի աշխատանքի ժամանակի վրա:"</string> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 6e738de3531b..ad8141bb2b34 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Hapus dari Setelan"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Hapus Penyetel Antarmuka Pengguna Sistem dari Setelan dan berhenti menggunakan semua fiturnya?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikasi tidak dipasang di perangkat"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Tampilkan detik jam"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Tampilkan detik jam di bilah status. Dapat memengaruhi masa pakai baterai."</string> </resources> diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml index 4c5bffbfe72b..02caf17e496c 100644 --- a/packages/SystemUI/res/values-is-rIS/strings.xml +++ b/packages/SystemUI/res/values-is-rIS/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Fjarlægja úr stillingum"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Viltu fjarlægja fínstillingar kerfisviðmóts úr stillingum og hætta að nota eiginleika þeirra?"</string> <string name="activity_not_found" msgid="348423244327799974">"Forritið er ekki uppsett í tækinu."</string> + <string name="clock_seconds" msgid="7689554147579179507">"Sýna sekúndur á klukku"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Sýna sekúndur á klukku í stöðustikunni. Getur haft áhrif á endingu rafhlöðu."</string> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 75ea3f6ffdd1..ab5d8beab1f9 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Rimuovi dalle impostazioni"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vuoi rimuovere il sintetizzatore interfaccia utente di sistema dalle impostazioni e smettere di utilizzare tutte le sue funzioni?"</string> <string name="activity_not_found" msgid="348423244327799974">"Applicazione non installata sul dispositivo"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Mostra i secondi nell\'orologio"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra i secondi nell\'orologio nella barra di stato. Ciò potrebbe ridurre la durata della carica della batteria."</string> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 4db88bdf5032..9dad8aaf464f 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"הסר מההגדרות"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"האם להסיר את System UI Tuner ולהפסיק להשתמש בכל התכונות שלו?"</string> <string name="activity_not_found" msgid="348423244327799974">"האפליקציה אינה מותקנת במכשיר"</string> + <string name="clock_seconds" msgid="7689554147579179507">"הצג שניות בשעון"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"הצג שניות בשעון בשורת הסטטוס. פעולה זו עשויה להשפיע על אורך חיי הסוללה."</string> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index d7b687b9953e..b9bfba9d9db4 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"設定から削除"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"設定からシステムUI調整ツールを削除して、全機能の使用を停止しますか?"</string> <string name="activity_not_found" msgid="348423244327799974">"アプリが端末にインストールされていません"</string> + <string name="clock_seconds" msgid="7689554147579179507">"時計の秒を表示"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"ステータスバーに時計の秒を表示します。電池使用量に影響する可能性があります。"</string> </resources> diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml index 2097996c5699..a9728b25a6bd 100644 --- a/packages/SystemUI/res/values-ka-rGE/strings.xml +++ b/packages/SystemUI/res/values-ka-rGE/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"პარამეტრებიდან წაშლა"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"გსურთ სისტემის UI ტუნერის პარამეტრებიდან წაშლა და მისი ყველა ფუნქციის გამოყენების შეწყვეტა?"</string> <string name="activity_not_found" msgid="348423244327799974">"აპლიკაცია თქვენს მოწყობილობაზე დაყენებული არ არის"</string> + <string name="clock_seconds" msgid="7689554147579179507">"საათის წამების ჩვენება"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"საათის წამების ჩვენება სტატუსის ზოლში. შეიძლება გავლენა იქონიოს ბატარეაზე."</string> </resources> diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml index bd49612bf50a..1d61372522aa 100644 --- a/packages/SystemUI/res/values-kk-rKZ/strings.xml +++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Параметрлерден жою"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Жүйелік пайдаланушылық интерфейс тюнерін \"Параметрлер\" тармағынан жойып, оның барлық мүмкіндіктерін пайдалануды тоқтату керек пе?"</string> <string name="activity_not_found" msgid="348423244327799974">"Қолданба құрылғыда орнатылмаған"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Сағат секундтарын көрсету"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Күйін көрсету жолағында сағат секундтарын көрсету. Батареяның қызмет көрсету мерзіміне әсер етуі мүмкін."</string> </resources> diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml index db5057ee9dfd..c233f83e2af7 100644 --- a/packages/SystemUI/res/values-km-rKH/strings.xml +++ b/packages/SystemUI/res/values-km-rKH/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"យកចេញពីការកំណត់"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"យកកម្មវិធីសម្រួល UI ប្រព័ន្ធចេញពីការកំណត់ ហើយឈប់ប្រើលក្ខណៈពិសេសរបស់វាទាំងអស់?"</string> <string name="activity_not_found" msgid="348423244327799974">"កម្មវិធីមិនបានដំឡើងនៅលើឧបករណ៍របស់អ្នកទេ"</string> + <string name="clock_seconds" msgid="7689554147579179507">"បង្ហាញវិនាទី"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"បង្ហាញវិនាទីនៅលើរបារស្ថានភាពអាចនឹងប៉ះពាល់ដល់ថាមពលថ្ម។"</string> </resources> diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml index 7d88681ed905..d486e56a7266 100644 --- a/packages/SystemUI/res/values-kn-rIN/strings.xml +++ b/packages/SystemUI/res/values-kn-rIN/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"ಸೆಟ್ಟಿಂಗ್ಗಳಿಂದ ತೆಗೆದುಹಾಕಿ"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"ಸೆಟ್ಟಿಂಗ್ಗಳಿಂದ ಸಿಸ್ಟಮ್ UI ಟ್ಯೂನರ್ ತೆಗೆದುಹಾಕುವುದೇ ಮತ್ತು ಅದರ ಎಲ್ಲಾ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಬಳಸುವುದನ್ನು ನಿಲ್ಲಿಸುವುದೇ?"</string> <string name="activity_not_found" msgid="348423244327799974">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"</string> + <string name="clock_seconds" msgid="7689554147579179507">"ಗಡಿಯಾರದ ಸೆಕೆಂಡುಗಳನ್ನು ತೋರಿಸು"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಲ್ಲಿ ಗಡಿಯಾರ ಸೆಕೆಂಡುಗಳನ್ನು ತೋರಿಸು. ಇದಕ್ಕೆ ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯು ಪರಿಣಾಮಬೀರಬಹುದು."</string> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index af02943be5e0..35057544cf8a 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"설정에서 삭제"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"시스템 UI 튜너를 설정에서 삭제하고 모든 관련 기능의 사용을 중지하시겠습니까?"</string> <string name="activity_not_found" msgid="348423244327799974">"기기에 애플리케이션이 설치되어 있지 않습니다."</string> + <string name="clock_seconds" msgid="7689554147579179507">"시계 초 단위 표시"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"상태 표시줄에 시계 초 단위를 표시합니다. 배터리 수명에 영향을 줄 수도 있습니다."</string> </resources> diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml index 058cc181fe56..968a65a76e5e 100644 --- a/packages/SystemUI/res/values-ky-rKG/strings.xml +++ b/packages/SystemUI/res/values-ky-rKG/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Жөндөөлөрдөн алып салуу"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"System UI Tuner Жөндөөлөрдөн алынып салынып, анын бардык функциялары токтотулсунбу?"</string> <string name="activity_not_found" msgid="348423244327799974">"Колдонмо сиздин түзмөгүңүздө орнотулган эмес"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Сааттын секунддары көрсөтүлсүн"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Абал тилкесинен сааттын секунддары көрсөтүлсүн. Батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string> </resources> diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml index 3067fb719781..f34c70eefbd4 100644 --- a/packages/SystemUI/res/values-lo-rLA/strings.xml +++ b/packages/SystemUI/res/values-lo-rLA/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"ເອົາອອກຈາກການຕັ້ງຄ່າ"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"ເອົາ System UI Tuner ອອກຈາກການຕັ້ງຄ່າ ແລະຢຸດການໃຊ້ທຸກຄຸນສົມບັດໃຊ້ງານຂອງມັນ?"</string> <string name="activity_not_found" msgid="348423244327799974">"ແອັບພລິເຄຊັນບໍ່ຖືກຕິດຕັ້ງຢູ່ໃນອຸປະກອນຂອງທ່ານ"</string> + <string name="clock_seconds" msgid="7689554147579179507">"ສະແດງວິນາທີໂມງ"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"ສະແດງວິນາທີໂມງຢູ່ໃນແຖບສະຖານະ. ອາດຈະມີຜົນກະທົບຕໍ່ອາຍຸແບັດເຕີຣີ."</string> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index e195444fd9a1..cf70d6bd5a31 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Pašalinti iš nustatymų"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Ar norite pašalinti sistemos naudotojo sąsajos derinimo priemonę iš nustatymų ir nebenaudoti jokių jos funkcijų?"</string> <string name="activity_not_found" msgid="348423244327799974">"Programa neįdiegta įrenginyje"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Rodyti laikrodžio sekundes"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Rodyti laikrodžio sekundes būsenos juostoje. Tai gali paveikti akumuliatoriaus naudojimo laiką."</string> </resources> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 4350876cc43b..5e2db80b9927 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -434,4 +434,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Noņemt no iestatījumiem"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vai noņemt sistēmas saskarnes regulatoru no iestatījumiem un pārtraukt izmantot visas tā funkcijas?"</string> <string name="activity_not_found" msgid="348423244327799974">"Lietojumprogramma nav instalēta jūsu ierīcē."</string> + <string name="clock_seconds" msgid="7689554147579179507">"Rādīt pulksteņa sekundes"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Statusa joslā rādīt pulksteņa sekundes. Var ietekmēt akumulatora darbības laiku."</string> </resources> diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml index 9e7a71740c8c..fe781b6b3219 100644 --- a/packages/SystemUI/res/values-mk-rMK/strings.xml +++ b/packages/SystemUI/res/values-mk-rMK/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Отстрани од поставки"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Да се отстрани Адаптерот на УИ на системот од Поставки и да престанат да се користат сите негови функции?"</string> <string name="activity_not_found" msgid="348423244327799974">"Апликацијата не е инсталирана на уредот"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Прикажи ги секундите на часовникот"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Прикажи ги секундите на часовникот на статусната лента. Може да влијае на траењето на батеријата."</string> </resources> diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml index e94142768a8a..6e6fc55e0214 100644 --- a/packages/SystemUI/res/values-ml-rIN/strings.xml +++ b/packages/SystemUI/res/values-ml-rIN/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"ക്രമീകരണത്തിൽ നിന്ന് നീക്കംചെയ്യുക"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"ക്രമീകരണത്തിൽ നിന്ന് സിസ്റ്റം UI ട്യൂണർ നീക്കംചെയ്യുകയും അതിന്റെ ഫീച്ചറുകളെല്ലാം ഉപയോഗിക്കുന്നത് നിർത്തുകയും ചെയ്യണോ?"</string> <string name="activity_not_found" msgid="348423244327799974">"നിങ്ങളുടെ ഉപകരണത്തിൽ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാൾ ചെയ്തിട്ടില്ല"</string> + <string name="clock_seconds" msgid="7689554147579179507">"ക്ലോക്ക് സെക്കൻഡ് കാണിക്കുക"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"സ്റ്റാറ്റസ് ബാറിൽ ക്ലോക്ക് സെക്കൻഡ് കാണിക്കുന്നത് ബാറ്ററിയുടെ ലൈഫിനെ ബാധിക്കാം."</string> </resources> diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml index 69a94c2c3939..65250bbbec60 100644 --- a/packages/SystemUI/res/values-mn-rMN/strings.xml +++ b/packages/SystemUI/res/values-mn-rMN/strings.xml @@ -431,4 +431,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Тохиргооноос устгах"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Системийн UI Тохируулагчийг тохиргооноос устгаж, үүнтэй холбоотой бүх тохиргоог ашиглахаа болих уу?"</string> <string name="activity_not_found" msgid="348423244327799974">"Апп-ыг таны төхөөрөмжид суулгаагүй байна"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Цагийн секундыг харуулах"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Статус талбарт цагийн секундыг харуулах. Энэ нь тэжээлийн цэнэгт нөлөөлж болно."</string> </resources> diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml index 3184de54c573..0f71b4bc446b 100644 --- a/packages/SystemUI/res/values-mr-rIN/strings.xml +++ b/packages/SystemUI/res/values-mr-rIN/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"सेटिंग्ज मधून काढा"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"सेटिंग्ज मधून सिस्टीम UI ट्यूनर काढून त्याची सर्व वैशिष्ट्ये वापरणे थांबवायचे?"</string> <string name="activity_not_found" msgid="348423244327799974">"अनुप्रयोग आपल्या डिव्हाइसवर स्थापित केलेला नाही"</string> + <string name="clock_seconds" msgid="7689554147579179507">"घड्याळ सेकंद दर्शवा"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"स्टेटस बारमध्ये घड्याळ सेकंद दर्शवा. कदाचित बॅटरी आयुष्य प्रभावित होऊ शकते."</string> </resources> diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index b12046bad1b8..596275c6e058 100644 --- a/packages/SystemUI/res/values-ms-rMY/strings.xml +++ b/packages/SystemUI/res/values-ms-rMY/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Alih keluar daripada Tetapan"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Alih keluar Penala UI Sistem daripada Tetapan dan berhenti menggunakan semua cirinya?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikasi tidak dipasang pada peranti anda"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Tunjukkan saat jam"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Tunjukkan saat jam dalam bar status. Mungkin menjejaskan hayat bateri."</string> </resources> diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml index 6f843b027c20..3147d0cc73f3 100644 --- a/packages/SystemUI/res/values-my-rMM/strings.xml +++ b/packages/SystemUI/res/values-my-rMM/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"ဆက်တင် အထဲမှ ဖယ်ရှားရန်"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"ဆက်တင် အထဲမှ စနစ် UI ဖမ်းစက်ကို ဖယ်ရှားလျက် ၎င်း၏ အင်္ဂါရပ်များ အားလုံး အသုံးပြုမှု ရပ်တန့်ရမလား?"</string> <string name="activity_not_found" msgid="348423244327799974">"အပလီကေးရှင်းကို သင်၏ ကိရိယာထဲသို့ တပ်ဆင်မပေးရသေးပါ။"</string> + <string name="clock_seconds" msgid="7689554147579179507">"နာရီ စက္ကန့်များကို ပြရန်"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"အခြေအနေပြနေရာမှာ နာရီ စက္ကန့်များကို ပြပါ။ ဘက်ထရီ သက်တမ်းကို အကျိုးသက်ရောက်နိုင်တယ်။"</string> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 3a34cdd4840b..f1bfbe1d8a98 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Fjern fra Innstillinger"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vil du fjerne System UI Tuner fra Innstillinger og slutte å bruke alle de tilknyttede funksjonene?"</string> <string name="activity_not_found" msgid="348423244327799974">"Appen er ikke installert på enheten din"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Vis sekunder på klokken"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Vis sekunder i statusfeltet på klokken. Det kan påvirke batteritiden."</string> </resources> diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml index 5fadbdee3fba..461a15f83173 100644 --- a/packages/SystemUI/res/values-ne-rNP/strings.xml +++ b/packages/SystemUI/res/values-ne-rNP/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"सेटिङहरूबाट हटाउनुहोस्"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"प्रणाली UI ट्युनर सेटिङहरूबाट हटाउने र यसका सबै सुविधाहरू प्रयोग गर्न रोक्ने हो?"</string> <string name="activity_not_found" msgid="348423244327799974">"तपाईँको यन्त्रमा अनुप्रयोग स्थापना भएको छैन"</string> + <string name="clock_seconds" msgid="7689554147579179507">"घडीमा सेकेन्ड देखाउनुहोस्"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"वस्तुस्थिति पट्टीको घडीमा सेकेन्ड देखाउनुहोस्। ब्याट्री आयु प्रभावित हुन सक्छ।"</string> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 9b7e3409c5c9..b74f91da327b 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Verwijderen uit Instellingen"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Systeem-UI-tuner uit Instellingen verwijderen en het gebruik van alle functies daarvan stopzetten?"</string> <string name="activity_not_found" msgid="348423244327799974">"Deze app is niet geïnstalleerd op uw apparaat"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Klokseconden weergeven"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Klokseconden op de statusbalk weergeven. Kan van invloed zijn op de accuduur."</string> </resources> diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml index 2d70c8a97ad4..06746ccf08b2 100644 --- a/packages/SystemUI/res/values-pa-rIN/strings.xml +++ b/packages/SystemUI/res/values-pa-rIN/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"ਸੈਟਿੰਗਜ਼ ਤੋਂ ਹਟਾਓ"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"ਕੀ ਸੈਟਿੰਗਜ਼ ਤੋਂ ਸਿਸਟਮ UI ਟਿਊਨਰ ਨੂੰ ਹਟਾਉਣਾ ਹੈ ਅਤੇ ਇਸਦੀਆਂ ਸਾਰੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਉਪਯੋਗ ਕਰਨ ਤੋਂ ਰੋਕਣਾ ਹੈ?"</string> <string name="activity_not_found" msgid="348423244327799974">"ਐਪਲੀਕੇਸ਼ਨ ਤੁਹਾਡੀ ਡਿਵਾਈਸ ਤੇ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤੀ ਗਈ ਹੈ"</string> + <string name="clock_seconds" msgid="7689554147579179507">"ਘੜੀ ਸਕਿੰਟ ਦਿਖਾਓ"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"ਸਥਿਤੀ ਬਾਰ ਵਿੱਚ ਘੜੀ ਸਕਿੰਟ ਦਿਖਾਓ। ਬੈਟਰੀ ਸਮਰੱਥਾ ਤੇ ਅਸਰ ਪੈ ਸਕਦਾ ਹੈ।"</string> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 93fa17083736..66100c19045e 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Usuń z Ustawień"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Usunąć Kalibrator System UI z Ustawień i przestać używać wszystkich jego funkcji?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikacja nie jest zainstalowana na urządzeniu"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Pokaż sekundy na zegarku"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Pokaż sekundy na zegarku na pasku stanu. Może mieć wpływ na czas pracy baterii."</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 78eee7b74ed1..53cea01a3ce5 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Remover das configurações"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Remover sintonizador System UI das configurações e parar de usar todos os seus recursos?"</string> <string name="activity_not_found" msgid="348423244327799974">"O app não está instalado no seu dispositivo"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Mostrar segundos do relógio"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de status. Pode afetar a duração da bateria."</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 9c221ddb0488..50064137ac98 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Remover das Definições"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Pretende remover o Sintonizador da interface do sistema das Definições e deixar de utilizar todas as respetivas funcionalidades?"</string> <string name="activity_not_found" msgid="348423244327799974">"A aplicação não está instalada no dispositivo"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Mostrar segundos do relógio"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de estado. Pode afetar a autonomia da bateria."</string> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 78eee7b74ed1..53cea01a3ce5 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Remover das configurações"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Remover sintonizador System UI das configurações e parar de usar todos os seus recursos?"</string> <string name="activity_not_found" msgid="348423244327799974">"O app não está instalado no seu dispositivo"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Mostrar segundos do relógio"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de status. Pode afetar a duração da bateria."</string> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index e9ff5953d00e..d97e16b820aa 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -434,4 +434,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Eliminați din Setări"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Eliminați System UI Tuner din Setări și încetați utilizarea tuturor funcțiilor sale?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplicația nu este instalată pe dispozitiv"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Afișează secundele pe ceas"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Afișează secundele pe ceas în bara de stare. Poate afecta autonomia bateriei."</string> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 8691233c4057..d67ad389b4c2 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -437,4 +437,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Удалить из настроек"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Убрать функцию System UI Tuner из меню настроек и прекратить ее работу?"</string> <string name="activity_not_found" msgid="348423244327799974">"Приложение не установлено на вашем устройстве"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Показывать секунды"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Показывать в строке состояния время с точностью до секунды (заряд батареи может расходоваться быстрее)."</string> </resources> diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml index 02533f52fc38..06cb62644399 100644 --- a/packages/SystemUI/res/values-si-rLK/strings.xml +++ b/packages/SystemUI/res/values-si-rLK/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"සැකසීම් වෙතින් ඉවත් කරන්න"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"සැකසීම් වෙතින් පද්ධති UI සුසරකය ඉවත් කර සහ එහි සියලු අංග භාවිතය නවත් වන්නද?"</string> <string name="activity_not_found" msgid="348423244327799974">"යෙදුම ඔබේ උපාංගය මත ස්ථාපනය කර නැත"</string> + <string name="clock_seconds" msgid="7689554147579179507">"ඔරලෝසු තත්පර පෙන්වන්න"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"තත්ත්ව තීරුවෙහි ඔරලෝසු තත්පර පෙන්වන්න. බැටරි ආයු කාලයට බලපෑමට හැකිය."</string> </resources> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 522d0a62a06b..037074bab323 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -437,4 +437,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Odstrániť z Nastavení"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Chcete odstrániť tuner používateľského rozhrania systému z Nastavení a prestať používať všetky jeho funkcie?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikácia nie je nainštalovaná na zariadení"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Zobraziť sekundy"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Zobrazí sekundy v stavovom riadku. Môže to ovplyvňovať výdrž batérie."</string> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 770844e9f242..31fe5d88027f 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Odstrani iz nastavitev"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Ali želite odstraniti Uglaševalnik uporabniškega vmesnika sistema iz nastavitev in prenehati uporabljati vse njegove funkcije?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikacija ni nameščena v napravi"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Prikaz sekund pri uri"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Prikaže sekunde pri uri v vrstici stanja. To lahko vpliva na čas delovanja pri akumulatorskem napajanju."</string> </resources> diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml index 1072b5455304..ffb8bea66147 100644 --- a/packages/SystemUI/res/values-sq-rAL/strings.xml +++ b/packages/SystemUI/res/values-sq-rAL/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Hiqe nga Cilësimet"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Të hiqet Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit nga Cilësimet dhe të ndërpritet përdorimi i të gjitha funksioneve të tij?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikacioni nuk është instaluar në pajisjen tënde."</string> + <string name="clock_seconds" msgid="7689554147579179507">"Trego sekondat e orës"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Trego sekondat e orës në shiritin e statusit. Mund të ndikojë te jeta e baterisë."</string> </resources> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 5df6d62d2f18..bae0284761c9 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -434,4 +434,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Уклони из Подешавања"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Желите ли да уклоните Тјунер за кориснички интерфејс система из Подешавања и да престанете да користите све његове функције?"</string> <string name="activity_not_found" msgid="348423244327799974">"Апликација није инсталирана на уређају"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Приказуј секунде на сату"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Секунде на сату се приказују на статусној траци. То може да утиче на трајање батерије."</string> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 807c53ec5b78..6588f10a0135 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Ta bort från inställningarna"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vill du ta bort inställningar för systemgränssnitt från inställningarna och sluta använda alla tillhörande funktioner?"</string> <string name="activity_not_found" msgid="348423244327799974">"Appen är inte installerad på enheten"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Visa klocksekunder"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Visa klocksekunder i statusfältet. Detta kan påverka batteritiden."</string> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1bb6c0b09da2..623f12cac3d4 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Ondoa kwenye Mipangilio"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Je, ungependa kuondoa Kipokea ishara cha SystemUI kwenye Mipangilio na uache kutumia vipengele vyake vyote?"</string> <string name="activity_not_found" msgid="348423244327799974">"Programu haijasakinishwa kwenye kifaa chako"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Onyesha sekunde za saa"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Onyesha sekunde za saa katika sehemu ya arifa. Inaweza kuathiri muda wa matumizi ya betri."</string> </resources> diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml index 8e64e85d9b0c..c6cb337e7d4d 100644 --- a/packages/SystemUI/res/values-ta-rIN/strings.xml +++ b/packages/SystemUI/res/values-ta-rIN/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"அமைப்புகளிலிருந்து அகற்று"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"அமைப்புகளிலிருந்து System UI Tunerஐ அகற்றிவிட்டு, அதன் எல்லா அம்சங்களையும் பயன்படுத்துவதை நிறுத்தவா?"</string> <string name="activity_not_found" msgid="348423244327799974">"சாதனத்தில் பயன்பாடு நிறுவப்படவில்லை"</string> + <string name="clock_seconds" msgid="7689554147579179507">"கடிகார வினாடிகளைக் காட்டு"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"நிலைப் பட்டியில் கடிகார வினாடிகளைக் காட்டும். பேட்டரியின் ஆயுளைக் குறைக்கலாம்."</string> </resources> diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml index 36b00f81a3f0..c0717c1fd90c 100644 --- a/packages/SystemUI/res/values-te-rIN/strings.xml +++ b/packages/SystemUI/res/values-te-rIN/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"సెట్టింగ్ల నుండి తీసివేయి"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"సిస్టమ్ UI ట్యూనర్ను సెట్టింగ్ల నుండి తీసివేసి, దాని అన్ని లక్షణాలను ఉపయోగించడం ఆపివేయాలా?"</string> <string name="activity_not_found" msgid="348423244327799974">"అనువర్తనం మీ పరికరంలో ఇన్స్టాల్ చేయలేదు"</string> + <string name="clock_seconds" msgid="7689554147579179507">"గడియారం సెకన్లు చూపు"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"స్థితి పట్టీలో గడియారం సెకన్లు చూపుతుంది. బ్యాటరీ శక్తి ప్రభావితం చేయవచ్చు."</string> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 0ef2c66a3725..e16fdd2cccae 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"นำออกจากการตั้งค่า"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"นำตัวรับสัญญาณ UI ระบบออกจากการตั้งค่าและหยุดใช้คุณลักษณะทั้งหมดของตัวรับสัญญาณใช่ไหม"</string> <string name="activity_not_found" msgid="348423244327799974">"ยังไม่ได้ติดตั้งแอปพลิเคชันบนอุปกรณ์ของคุณ"</string> + <string name="clock_seconds" msgid="7689554147579179507">"แสดงวินาทีของนาฬิกา"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"แสดงวินาทีของนาฬิกาในแถบสถานะ อาจส่งผลต่ออายุแบตเตอรี"</string> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index a208c9648f75..d970084ca02f 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Alisin sa Mga Setting"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Alisin ang Tuner ng System UI sa Mga Setting at ihinto ang paggamit ng lahat ng feature nito?"</string> <string name="activity_not_found" msgid="348423244327799974">"Hindi naka-install ang application sa iyong device"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Ipakita ang mga segundo ng orasan"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Ipakita ang mga segundo ng orasan sa status bar. Maaaring makaapekto sa tagal ng baterya."</string> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index d359cf1519f6..b22b4fbc7eb8 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Ayarlar\'dan kaldır"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Sistem Kullanıcı Arayüzü Ayarlayıcısı Ayarlar\'dan kaldırılsın ve tüm özelliklerinin kullanılması durdurulsun mu?"</string> <string name="activity_not_found" msgid="348423244327799974">"Uygulama, cihazınızda yüklü değil"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Saatin saniyelerini göster"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Durum çubuğunda saatin saniyelerini gösterir. Pil ömrünü etkileyebilir."</string> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 3e34a1f02a69..5c0966c6759b 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Видалити з додатка Налаштування"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Видалити інструмент System UI Tuner із додатка Налаштування та припинити користуватися всіма його функціями?"</string> <string name="activity_not_found" msgid="348423244327799974">"Додаток не встановлено на вашому пристрої"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Показувати секунди на годиннику"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Показувати секунди на годиннику в рядку стану. Акумулятор може розряджатися швидше."</string> </resources> diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml index 650095d9ce51..d5683e1daecc 100644 --- a/packages/SystemUI/res/values-ur-rPK/strings.xml +++ b/packages/SystemUI/res/values-ur-rPK/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"ترتیبات سے ہٹائیں"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"ترتیبات سے سسٹم UI ٹیونر کو ہٹائیں اور اس کی سبھی خصوصیات کا استعمال بند کریں؟"</string> <string name="activity_not_found" msgid="348423244327799974">"ایپلیکیشن آپ کے آلہ پر انسٹال نہیں ہے"</string> + <string name="clock_seconds" msgid="7689554147579179507">"گھڑی کے سیکنڈز دکھائیں"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"گھڑی کے سیکنڈز اسٹیٹس بار میں دکھائیں۔ اس کا بیٹری کی زندگی پر اثر پڑ سکتا ہے۔"</string> </resources> diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml index e3073bc155f0..e31af6b44342 100644 --- a/packages/SystemUI/res/values-uz-rUZ/strings.xml +++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Sozlamalardan olib tashlash"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"System UI Tuner Sozlamalardan olib tashlanib, uning barcha funksiyalaridan foydalanish to‘xtatilsinmi?"</string> <string name="activity_not_found" msgid="348423244327799974">"Ilova qurilmangizga o‘rnatilmagan"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Soat soniyalari ko‘rsatilsin"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Holat panelida soat soniyalari ko‘rsatilsin. Bu batareya resursiga ta’sir qilishi mumkin."</string> </resources> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 4459497f0258..030bbbe7ec70 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Xóa khỏi Cài đặt"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Xóa Bộ điều hướng giao diện người dùng hệ thống khỏi Cài đặt và ngừng sử dụng tất cả tính năng của ứng dụng này?"</string> <string name="activity_not_found" msgid="348423244327799974">"Ứng dụng chưa được cài đặt trên thiết bị của bạn"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Hiển thị giây đồng hồ"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Hiển thị giây đồng hồ trong thanh trạng thái. Có thể ảnh hưởng đến thời lượng pin."</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 027232813b10..b046415bac2c 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"从“设置”中移除"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"要将系统界面调谐器从“设置”中移除,并停止使用所有相关功能吗?"</string> <string name="activity_not_found" msgid="348423244327799974">"您的设备中未安装此应用"</string> + <string name="clock_seconds" msgid="7689554147579179507">"显示时钟的秒数"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"在状态栏中显示时钟的秒数。这可能会影响电池的续航时间。"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 8adaeac6bf8d..d568dba45d2d 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"從「設定」移除"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"要從「設定」移除系統使用者介面調諧器,並停止其所有功能嗎?"</string> <string name="activity_not_found" msgid="348423244327799974">"尚未在裝置安裝應用程式"</string> + <string name="clock_seconds" msgid="7689554147579179507">"顯示時鐘秒數"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"在狀態列中顯示時鐘秒數,但可能會影響電池壽命。"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 759b585d60e3..702ad53449e4 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -435,4 +435,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"從設定中移除"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"要將系統使用者介面調整精靈從設定中移除,並停止使用所有相關功能嗎?"</string> <string name="activity_not_found" msgid="348423244327799974">"您的裝置未安裝這個應用程式"</string> + <string name="clock_seconds" msgid="7689554147579179507">"顯示時鐘秒數"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"在狀態列中顯示時鐘秒數。這可能會影響電池續航力。"</string> </resources> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 1e7be90ec4ed..8774036f4749 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -433,4 +433,6 @@ <string name="remove_from_settings" msgid="8389591916603406378">"Susa kusuka kuzilungiselelo"</string> <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Susa isishuni se-UI yesistimu kusuka kuzilungiselelo futhi uyeke ukusebenzisa zonke izici zakhona?"</string> <string name="activity_not_found" msgid="348423244327799974">"Uhlelo lokusebenza alufakiwe kudivayisi yakho"</string> + <string name="clock_seconds" msgid="7689554147579179507">"Bonisa amasekhondi wewashi"</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Bonisa amasekhondi wewashi kubha yesimo. Ingathinta impilo yebhethri."</string> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 918d85666b79..8b3f2d8bab3c 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1140,4 +1140,9 @@ <!-- Displayed when user launches an app that was uninstalled [CHAR LIMIT=NONE] --> <string name="activity_not_found">Application is not installed on your device</string> + <!-- Name of setting to show clock seconds [CHAR LIMIT=40] --> + <string name="clock_seconds">Show clock seconds</string> + <!-- Description of setting to show clock seconds [CHAR LIMIT=NONE] --> + <string name="clock_seconds_desc">Show clock seconds in the status bar. May impact battery life.</string> + </resources> diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index 3a41c3c540bd..beb863c44925 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -72,10 +72,16 @@ android:summary="@string/show_battery_percentage_summary" android:persistent="false" /> + <com.android.systemui.tuner.TunerSwitch + android:key="clock_seconds" + android:title="@string/clock_seconds" + android:summary="@string/clock_seconds_desc" /> + <Preference android:key="demo_mode" android:title="@string/demo_mode" /> + <!-- Warning, this goes last. --> <Preference android:summary="@string/tuner_persistent_warning" android:selectable="false" /> diff --git a/packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java b/packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java new file mode 100644 index 000000000000..e458862295e1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.analytics; + +import android.content.Context; +import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.systemui.statusbar.StatusBarState; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session; +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.PhoneEvent; + +/** + * Tracks touch, sensor and phone events when the lockscreen is on. If the phone is unlocked + * the data containing these events is saved to a file. This data is collected + * to analyze how a human interaction looks like. + * + * A session starts when the screen is turned on. + * A session ends when the screen is turned off or user unlocks the phone. + */ +public class LockedPhoneAnalytics implements SensorEventListener { + private static final String TAG = "LockedPhoneAnalytics"; + private static final String ANALYTICS_ENABLE = "locked_phone_analytics_enable"; + private static final String ENFORCE_BOUNCER = "locked_phone_analytics_enforce_bouncer"; + private static final String COLLECT_BAD_TOCUHES = "locked_phone_analytics_collect_bad_touches"; + + private static final long TIMEOUT_MILLIS = 11000; // 11 seconds. + public static final boolean DEBUG = false; + + private static final int[] SENSORS = new int[] { + Sensor.TYPE_ACCELEROMETER, + Sensor.TYPE_GYROSCOPE, + Sensor.TYPE_PROXIMITY, + Sensor.TYPE_LIGHT, + Sensor.TYPE_ROTATION_VECTOR, + }; + + private final Handler mHandler = new Handler(); + protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + updateConfiguration(); + } + }; + + private final SensorManager mSensorManager; + private final Context mContext; + + // Err on the side of caution, so logging is not started after a crash even tough the screen + // is off. + private SensorLoggerSession mCurrentSession = null; + + private boolean mEnableAnalytics = false; + private boolean mEnforceBouncer = false; + private boolean mTimeoutActive = false; + private boolean mCollectBadTouches = false; + private boolean mBouncerOn = false; + private boolean mCornerSwiping = false; + private boolean mTrackingStarted = false; + + private int mState = StatusBarState.SHADE; + + private static LockedPhoneAnalytics sInstance = null; + + private LockedPhoneAnalytics(Context context) { + mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + mContext = context; + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(ANALYTICS_ENABLE), false, + mSettingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(ENFORCE_BOUNCER), false, + mSettingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(COLLECT_BAD_TOCUHES), false, + mSettingsObserver, + UserHandle.USER_ALL); + + updateConfiguration(); + } + + public static LockedPhoneAnalytics getInstance(Context context) { + if (sInstance == null) { + sInstance = new LockedPhoneAnalytics(context); + } + return sInstance; + } + + private void updateConfiguration() { + mEnableAnalytics = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt( + mContext.getContentResolver(), + ANALYTICS_ENABLE, 0); + mEnforceBouncer = mEnableAnalytics && 0 != Settings.Secure.getInt( + mContext.getContentResolver(), + ENFORCE_BOUNCER, 0); + mCollectBadTouches = mEnableAnalytics && 0 != Settings.Secure.getInt( + mContext.getContentResolver(), + COLLECT_BAD_TOCUHES, 0); + } + + private boolean sessionEntrypoint() { + if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) + && mEnableAnalytics && mCurrentSession == null) { + onSessionStart(); + return true; + } + return false; + } + + private void sessionExitpoint(int result) { + if (mEnableAnalytics && mCurrentSession != null) { + onSessionEnd(result); + } + } + + private void onSessionStart() { + mBouncerOn = false; + mCornerSwiping = false; + mTrackingStarted = false; + mCurrentSession = new SensorLoggerSession(System.currentTimeMillis(), System.nanoTime()); + for (int sensorType : SENSORS) { + Sensor s = mSensorManager.getDefaultSensor(sensorType); + if (s != null) { + mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME); + } + } + } + + private void onSessionEnd(int result) { + SensorLoggerSession session = mCurrentSession; + mCurrentSession = null; + + session.end(System.currentTimeMillis(), result); + queueSession(session); + } + + private void queueSession(final SensorLoggerSession currentSession) { + AsyncTask.execute(new Runnable() { + @Override + public void run() { + byte[] b = Session.toByteArray(currentSession.toProto()); + String dir = mContext.getFilesDir().getAbsolutePath(); + if (currentSession.getResult() != Session.SUCCESS) { + if (!mCollectBadTouches) { + return; + } + dir += "/bad_touches"; + } else { + dir += "/good_touches"; + } + + File file = new File(dir); + file.mkdir(); + File touch = new File(file, "trace_" + System.currentTimeMillis()); + + try { + new FileOutputStream(touch).write(b); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } + + + @Override + public synchronized void onSensorChanged(SensorEvent event) { + if (mEnableAnalytics && mCurrentSession != null) { + mCurrentSession.addSensorEvent(event, System.nanoTime()); + enforceTimeout(); + } + } + + private void enforceTimeout() { + if (mTimeoutActive) { + if (System.currentTimeMillis() - mCurrentSession.getStartTimestampMillis() + > TIMEOUT_MILLIS) { + onSessionEnd(Session.UNKNOWN); + if (DEBUG) { + Log.i(TAG, "Analytics timed out."); + } + } + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + public boolean shouldEnforceBouncer() { + return mEnforceBouncer; + } + + public void setStatusBarState(int state) { + mState = state; + } + + public void onScreenOn() { + if (sessionEntrypoint()) { + if (DEBUG) { + Log.d(TAG, "onScreenOn"); + } + addEvent(PhoneEvent.ON_SCREEN_ON); + } + } + + public void onScreenOnFromTouch() { + if (sessionEntrypoint()) { + if (DEBUG) { + Log.d(TAG, "onScreenOnFromTouch"); + } + addEvent(PhoneEvent.ON_SCREEN_ON_FROM_TOUCH); + } + } + + public void onScreenOff() { + if (DEBUG) { + Log.d(TAG, "onScreenOff"); + } + addEvent(PhoneEvent.ON_SCREEN_OFF); + sessionExitpoint(Session.FAILURE); + } + + public void onSucccessfulUnlock() { + if (DEBUG) { + Log.d(TAG, "onSuccessfulUnlock"); + } + addEvent(PhoneEvent.ON_SUCCESSFUL_UNLOCK); + sessionExitpoint(Session.SUCCESS); + } + + public void onBouncerShown() { + if (!mBouncerOn) { + if (DEBUG) { + Log.d(TAG, "onBouncerShown"); + } + mBouncerOn = true; + addEvent(PhoneEvent.ON_BOUNCER_SHOWN); + } + } + + public void onBouncerHidden() { + if (mBouncerOn) { + if (DEBUG) { + Log.d(TAG, "onBouncerHidden"); + } + mBouncerOn = false; + addEvent(PhoneEvent.ON_BOUNCER_HIDDEN); + } + } + + public void onQsDown() { + if (DEBUG) { + Log.d(TAG, "onQsDown"); + } + addEvent(PhoneEvent.ON_QS_DOWN); + } + + public void setQsExpanded(boolean expanded) { + if (DEBUG) { + Log.d(TAG, "setQsExpanded = " + expanded); + } + if (expanded) { + addEvent(PhoneEvent.SET_QS_EXPANDED_TRUE); + } else { + addEvent(PhoneEvent.SET_QS_EXPANDED_FALSE); + } + } + + public void onTrackingStarted() { + if (DEBUG) { + Log.d(TAG, "onTrackingStarted"); + } + mTrackingStarted = true; + addEvent(PhoneEvent.ON_TRACKING_STARTED); + } + + public void onTrackingStopped() { + if (mTrackingStarted) { + if (DEBUG) { + Log.d(TAG, "onTrackingStopped"); + } + mTrackingStarted = false; + addEvent(PhoneEvent.ON_TRACKING_STOPPED); + } + } + + public void onNotificationActive() { + if (DEBUG) { + Log.d(TAG, "onNotificationActive"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_ACTIVE); + } + + + public void onNotificationDoubleTap() { + if (DEBUG) { + Log.d(TAG, "onNotificationDoubleTap"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_DOUBLE_TAP); + } + + public void setNotificationExpanded() { + if (DEBUG) { + Log.d(TAG, "setNotificationExpanded"); + } + addEvent(PhoneEvent.SET_NOTIFICATION_EXPANDED); + } + + public void onNotificatonStartDraggingDown() { + if (DEBUG) { + Log.d(TAG, "onNotificationStartDraggingDown"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_START_DRAGGING_DOWN); + } + + public void onNotificatonStopDraggingDown() { + if (DEBUG) { + Log.d(TAG, "onNotificationStopDraggingDown"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DRAGGING_DOWN); + } + + public void onNotificationDismissed() { + if (DEBUG) { + Log.d(TAG, "onNotificationDismissed"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_DISMISSED); + } + + public void onNotificatonStartDismissing() { + if (DEBUG) { + Log.d(TAG, "onNotificationStartDismissing"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_START_DISMISSING); + } + + public void onNotificatonStopDismissing() { + if (DEBUG) { + Log.d(TAG, "onNotificationStopDismissing"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DISMISSING); + } + + public void onCameraOn() { + if (DEBUG) { + Log.d(TAG, "onCameraOn"); + } + addEvent(PhoneEvent.ON_CAMERA_ON); + } + + public void onLeftAffordanceOn() { + if (DEBUG) { + Log.d(TAG, "onLeftAffordanceOn"); + } + addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_ON); + } + + public void onAffordanceSwipingStarted(boolean rightCorner) { + if (DEBUG) { + Log.d(TAG, "onAffordanceSwipingStarted"); + } + mCornerSwiping = true; + if (rightCorner) { + addEvent(PhoneEvent.ON_RIGHT_AFFORDANCE_SWIPING_STARTED); + } else { + addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_SWIPING_STARTED); + } + } + + public void onAffordanceSwipingAborted() { + if (mCornerSwiping) { + if (DEBUG) { + Log.d(TAG, "onAffordanceSwipingAborted"); + } + mCornerSwiping = false; + addEvent(PhoneEvent.ON_AFFORDANCE_SWIPING_ABORTED); + } + } + + public void onUnlockHintStarted() { + if (DEBUG) { + Log.d(TAG, "onUnlockHintStarted"); + } + addEvent(PhoneEvent.ON_UNLOCK_HINT_STARTED); + } + + public void onCameraHintStarted() { + if (DEBUG) { + Log.d(TAG, "onCameraHintStarted"); + } + addEvent(PhoneEvent.ON_CAMERA_HINT_STARTED); + } + + public void onLeftAffordanceHintStarted() { + if (DEBUG) { + Log.d(TAG, "onLeftAffordanceHintStarted"); + } + addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_HINT_STARTED); + } + + public void onTouchEvent(MotionEvent ev, int width, int height) { + if (!mBouncerOn && mCurrentSession != null) { + if (DEBUG) { + Log.v(TAG, "onTouchEvent(ev.action=" + + MotionEvent.actionToString(ev.getAction()) + ")"); + } + mCurrentSession.addMotionEvent(ev); + mCurrentSession.setTouchArea(width, height); + enforceTimeout(); + } + } + + private void addEvent(int eventType) { + if (mEnableAnalytics && mCurrentSession != null) { + mCurrentSession.addPhoneEvent(eventType, System.nanoTime()); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java new file mode 100644 index 000000000000..09383f6aa8d1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.analytics; + +import android.os.Build; +import android.util.Log; +import android.view.MotionEvent; + +import java.util.ArrayList; + +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session; +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.TouchEvent; +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.SensorEvent; +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.PhoneEvent; + +/** + * Collects touch, sensor and phone events and converts the data to + * TouchAnalyticsProto.Session. + */ +public class SensorLoggerSession { + private static final String TAG = "SensorLoggerSession"; + + private final long mStartTimestampMillis; + private final long mStartSystemTimeNanos; + + private long mEndTimestampMillis; + private int mType; + + private ArrayList<TouchEvent> mMotionEvents = new ArrayList<>(); + private ArrayList<SensorEvent> mSensorEvents = new ArrayList<>(); + private ArrayList<PhoneEvent> mPhoneEvents = new ArrayList<>(); + private int mTouchAreaHeight; + private int mTouchAreaWidth; + private int mResult = Session.UNKNOWN; + + public SensorLoggerSession(long startTimestampMillis, long startSystemTimeNanos) { + mStartTimestampMillis = startTimestampMillis; + mStartSystemTimeNanos = startSystemTimeNanos; + mType = Session.REAL; + } + + public void end(long endTimestampMillis, int result) { + mResult = result; + mEndTimestampMillis = endTimestampMillis; + + if (LockedPhoneAnalytics.DEBUG) { + Log.d(TAG, "Ending session result=" + result + " it lasted for " + + (float) (mEndTimestampMillis - mStartTimestampMillis) / 1000f + "s"); + } + } + + public void addMotionEvent(MotionEvent motionEvent) { + TouchEvent event = motionEventToProto(motionEvent); + mMotionEvents.add(event); + } + + public void addSensorEvent(android.hardware.SensorEvent eventOrig, long systemTimeNanos) { + SensorEvent event = sensorEventToProto(eventOrig, systemTimeNanos); + mSensorEvents.add(event); + } + + public void addPhoneEvent(int eventType, long systemTimeNanos) { + PhoneEvent event = phoneEventToProto(eventType, systemTimeNanos); + mPhoneEvents.add(event); + } + + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Session{"); + sb.append("mStartTimestampMillis=").append(mStartTimestampMillis); + sb.append(", mStartSystemTimeNanos=").append(mStartSystemTimeNanos); + sb.append(", mEndTimestampMillis=").append(mEndTimestampMillis); + sb.append(", mResult=").append(mResult); + sb.append(", mTouchAreaHeight=").append(mTouchAreaHeight); + sb.append(", mTouchAreaWidth=").append(mTouchAreaWidth); + sb.append(", mMotionEvents=[size=").append(mMotionEvents.size()).append("]"); + sb.append(", mSensorEvents=[size=").append(mSensorEvents.size()).append("]"); + sb.append(", mPhoneEvents=[size=").append(mPhoneEvents.size()).append("]"); + sb.append('}'); + return sb.toString(); + } + + public Session toProto() { + Session proto = new Session(); + proto.setStartTimestampMillis(mStartTimestampMillis); + proto.setDurationMillis(mEndTimestampMillis - mStartTimestampMillis); + proto.setBuild(Build.FINGERPRINT); + proto.setResult(mResult); + proto.setType(mType); + proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents); + proto.touchEvents = mMotionEvents.toArray(proto.touchEvents); + proto.phoneEvents = mPhoneEvents.toArray(proto.phoneEvents); + proto.setTouchAreaWidth(mTouchAreaWidth); + proto.setTouchAreaHeight(mTouchAreaHeight); + return proto; + } + + private PhoneEvent phoneEventToProto(int eventType, long sysTimeNanos) { + PhoneEvent proto = new PhoneEvent(); + proto.setType(eventType); + proto.setTimeOffsetNanos(sysTimeNanos - mStartSystemTimeNanos); + return proto; + } + + private SensorEvent sensorEventToProto(android.hardware.SensorEvent ev, long sysTimeNanos) { + SensorEvent proto = new SensorEvent(); + proto.setType(ev.sensor.getType()); + proto.setTimeOffsetNanos(sysTimeNanos - mStartSystemTimeNanos); + proto.setTimestamp(ev.timestamp); + proto.values = ev.values.clone(); + return proto; + } + + private TouchEvent motionEventToProto(MotionEvent ev) { + int count = ev.getPointerCount(); + TouchEvent proto = new TouchEvent(); + proto.setTimeOffsetNanos(ev.getEventTimeNano() - mStartSystemTimeNanos); + proto.setAction(ev.getActionMasked()); + proto.setActionIndex(ev.getActionIndex()); + proto.pointers = new TouchEvent.Pointer[count]; + for (int i = 0; i < count; i++) { + TouchEvent.Pointer p = new TouchEvent.Pointer(); + p.setX(ev.getX(i)); + p.setY(ev.getY(i)); + p.setSize(ev.getSize(i)); + p.setPressure(ev.getPressure(i)); + p.setId(ev.getPointerId(i)); + proto.pointers[i] = p; + } + return proto; + } + + public void setTouchArea(int width, int height) { + mTouchAreaWidth = width; + mTouchAreaHeight = height; + } + + public int getResult() { + return mResult; + } + + public long getStartTimestampMillis() { + return mStartTimestampMillis; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index aafa9915ead5..12d5f364f014 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -69,6 +69,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.phone.FingerprintUnlockController; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -531,7 +532,7 @@ public class KeyguardViewMediator extends SystemUI { int currentUser = ActivityManager.getCurrentUser(); if ((mUpdateMonitor.getUserTrustIsManaged(currentUser) || mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser)) - && !mTrustManager.hasUserAuthenticatedSinceBoot(currentUser)) { + && !mUpdateMonitor.getStrongAuthTracker().hasUserAuthenticatedSinceBoot()) { return KeyguardSecurityView.PROMPT_REASON_RESTART; } else if (mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser) && mUpdateMonitor.hasFingerprintUnlockTimedOut(currentUser)) { @@ -1240,6 +1241,7 @@ public class KeyguardViewMediator extends SystemUI { case START_KEYGUARD_EXIT_ANIM: StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj; handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration); + LockedPhoneAnalytics.getInstance(mContext).onSucccessfulUnlock(); break; case KEYGUARD_DONE_PENDING_TIMEOUT: Log.w(TAG, "Timeout while waiting for activity drawn!"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 7f17885ab687..c1dfec3f954b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -34,6 +34,7 @@ import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; import com.android.systemui.R; +import com.android.systemui.analytics.LockedPhoneAnalytics; /** * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer} @@ -128,6 +129,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private final int mNormalColor; private final int mLowPriorityColor; private boolean mIsBelowSpeedBump; + private LockedPhoneAnalytics mLockedPhoneAnalytics; public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); @@ -151,6 +153,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView R.color.notification_ripple_color_low_priority); mNormalRippleColor = context.getColor( R.color.notification_ripple_untinted_color); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } @Override @@ -219,6 +222,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView makeActive(); postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS); } else { + mLockedPhoneAnalytics.onNotificationDoubleTap(); boolean performed = performClick(); if (performed) { removeCallbacks(mTapTimeoutRunnable); @@ -238,6 +242,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } private void makeActive() { + mLockedPhoneAnalytics.onNotificationActive(); startActivateAnimation(false /* reverse */); mActivated = true; if (mOnActivatedListener != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index a55256d9e216..de6c4b18b1ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -91,6 +91,7 @@ import com.android.internal.statusbar.StatusBarIconList; import com.android.internal.util.NotificationColorUtil; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.DejankUtils; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SwipeHelper; @@ -135,6 +136,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023; protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024; protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025; + protected static final int MSG_SHOW_KEYBOARD_SHORTCUTS_MENU = 1026; protected static final boolean ENABLE_HEADS_UP = true; // scores above this threshold should be displayed in heads up mode. @@ -1066,6 +1068,13 @@ public abstract class BaseStatusBar extends SystemUI implements mHandler.sendEmptyMessage(msg); } + @Override + public void showKeyboardShortcutsMenu() { + int msg = MSG_SHOW_KEYBOARD_SHORTCUTS_MENU; + mHandler.removeMessages(msg); + mHandler.sendEmptyMessage(msg); + } + /** Jumps to the next affiliated task in the group. */ public void showNextAffiliatedTask() { int msg = MSG_SHOW_NEXT_AFFILIATED_TASK; @@ -1143,6 +1152,10 @@ public abstract class BaseStatusBar extends SystemUI implements } } + protected void showKeyboardShortcuts() { + Toast.makeText(mContext, "Show keyboard shortcuts screen", Toast.LENGTH_LONG).show(); + } + protected void cancelPreloadingRecents() { if (mRecents != null) { mRecents.cancelPreloadingRecents(); @@ -1253,6 +1266,9 @@ public abstract class BaseStatusBar extends SystemUI implements case MSG_SHOW_PREV_AFFILIATED_TASK: showRecentsPreviousAffiliatedTask(); break; + case MSG_SHOW_KEYBOARD_SHORTCUTS_MENU: + showKeyboardShortcuts(); + break; } } } @@ -1609,6 +1625,15 @@ public abstract class BaseStatusBar extends SystemUI implements final PendingIntent intent = sbn.getNotification().contentIntent; final String notificationKey = sbn.getKey(); + // Mark notification for one frame. + row.setJustClicked(true); + DejankUtils.postAfterTraversal(new Runnable() { + @Override + public void run() { + row.setJustClicked(false); + } + }); + if (NOTIFICATION_CLICK_DEBUG) { Log.d(TAG, "Clicked on content of " + notificationKey); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index a1b07b50271f..d0cd6cbeb5f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -64,6 +64,7 @@ public class CommandQueue extends IStatusBar.Stub { private static final int MSG_APP_TRANSITION_STARTING = 21 << MSG_SHIFT; private static final int MSG_ASSIST_DISCLOSURE = 22 << MSG_SHIFT; private static final int MSG_START_ASSIST = 23 << MSG_SHIFT; + private static final int MSG_SHOW_KEYBOARD_SHORTCUTS = 24 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -98,6 +99,7 @@ public class CommandQueue extends IStatusBar.Stub { public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey); public void toggleRecentApps(); public void preloadRecentApps(); + public void showKeyboardShortcutsMenu(); public void cancelPreloadRecentApps(); public void setWindowState(int window, int state); public void buzzBeepBlinked(); @@ -224,6 +226,14 @@ public class CommandQueue extends IStatusBar.Stub { } } + @Override + public void showKeyboardShortcutsMenu() { + synchronized (mList) { + mHandler.removeMessages(MSG_SHOW_KEYBOARD_SHORTCUTS); + mHandler.obtainMessage(MSG_SHOW_KEYBOARD_SHORTCUTS).sendToTarget(); + } + } + public void setWindowState(int window, int state) { synchronized (mList) { // don't coalesce these @@ -360,6 +370,9 @@ public class CommandQueue extends IStatusBar.Stub { case MSG_CANCEL_PRELOAD_RECENT_APPS: mCallbacks.cancelPreloadRecentApps(); break; + case MSG_SHOW_KEYBOARD_SHORTCUTS: + mCallbacks.showKeyboardShortcutsMenu(); + break; case MSG_SET_WINDOW_STATE: mCallbacks.setWindowState(msg.arg1, msg.arg2); break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java index 15a092c37612..e2304c100517 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java @@ -29,6 +29,7 @@ import android.view.animation.Interpolator; import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; +import com.android.systemui.analytics.LockedPhoneAnalytics; /** * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand @@ -54,6 +55,7 @@ public class DragDownHelper implements Gefingerpoken { private ExpandableView mStartingChild; private Interpolator mInterpolator; private float mLastHeight; + private LockedPhoneAnalytics mLockedPhoneAnalytics; public DragDownHelper(Context context, View host, ExpandHelper.Callback callback, DragDownCallback dragDownCallback) { @@ -65,6 +67,7 @@ public class DragDownHelper implements Gefingerpoken { mCallback = callback; mDragDownCallback = dragDownCallback; mHost = host; + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } @Override @@ -84,6 +87,7 @@ public class DragDownHelper implements Gefingerpoken { case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) { + mLockedPhoneAnalytics.onNotificatonStartDraggingDown(); mDraggingDown = true; captureStartingChild(mInitialTouchX, mInitialTouchY); mInitialTouchY = y; @@ -201,6 +205,7 @@ public class DragDownHelper implements Gefingerpoken { } private void stopDragging() { + mLockedPhoneAnalytics.onNotificatonStopDraggingDown(); if (mStartingChild != null) { cancelExpansion(mStartingChild); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index b88e5ca7f5cb..2f6aec7eecbb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -34,6 +34,7 @@ import android.view.animation.LinearInterpolator; import android.widget.ImageView; import com.android.systemui.R; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.stack.NotificationChildrenContainer; @@ -109,6 +110,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { !mChildrenExpanded); } }; + private LockedPhoneAnalytics mLockedPhoneAnalytics; + + private boolean mJustClicked; public NotificationContentView getPrivateLayout() { return mPrivateLayout; @@ -301,12 +305,28 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { return mHeadsUpHeight; } + /** + * Mark whether this notification was just clicked, i.e. the user has just clicked this + * notification in this frame. + */ + public void setJustClicked(boolean justClicked) { + mJustClicked = justClicked; + } + + /** + * @return true if this notification has been clicked in this frame, false otherwise + */ + public boolean wasJustClicked() { + return mJustClicked; + } + public interface ExpansionLogger { public void logNotificationExpansion(String key, boolean userAction, boolean expanded); } public ExpandableNotificationRow(Context context, AttributeSet attrs) { super(context, attrs); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } /** @@ -494,6 +514,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { * @param userExpanded whether the user wants this notification to be expanded */ public void setUserExpanded(boolean userExpanded) { + mLockedPhoneAnalytics.setNotificationExpanded(); if (userExpanded && !mExpandable) return; final boolean wasExpanded = isExpanded(); mHasUserChangedExpansion = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java index 28d4a42efd72..4e69999678ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java @@ -183,8 +183,11 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { mStatusBarKeyguardViewManager.animateCollapsePanels( FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR); break; - case MODE_WAKE_AND_UNLOCK: case MODE_WAKE_AND_UNLOCK_PULSING: + mPhoneStatusBar.updateMediaMetaData(false /* metaDataChanged */); + // Fall through. + case MODE_WAKE_AND_UNLOCK: + mStatusBarWindowManager.setStatusBarFocusable(false); mDozeScrimController.abortPulsing(); mKeyguardViewMediator.onWakeAndUnlocking(); mScrimController.setWakeAndUnlocking(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 4da9acd563d5..579889d63edf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -649,7 +649,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } @Override - public void onStrongAuthTimeoutExpiredChanged(int userId) { + public void onStrongAuthStateChanged(int userId) { mLockIcon.update(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 8b96e5f52ae3..cbd23bb90ac0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -31,6 +31,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.R; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.DejankUtils; +import com.android.systemui.analytics.LockedPhoneAnalytics; import static com.android.keyguard.KeyguardHostView.OnDismissAction; import static com.android.keyguard.KeyguardSecurityModel.SecurityMode; @@ -49,10 +50,11 @@ public class KeyguardBouncer { private ViewGroup mRoot; private boolean mShowingSoon; private int mBouncerPromptReason; + private LockedPhoneAnalytics mLockedPhoneAnalytics; private KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override - public void onStrongAuthTimeoutExpiredChanged(int userId) { + public void onStrongAuthStateChanged(int userId) { mBouncerPromptReason = mCallback.getBouncerPromptReason(); } }; @@ -66,9 +68,11 @@ public class KeyguardBouncer { mContainer = container; mWindowManager = windowManager; KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(mContext); } public void show(boolean resetSecuritySelection) { + mLockedPhoneAnalytics.onBouncerShown(); ensureView(); if (resetSecuritySelection) { // showPrimarySecurityScreen() updates the current security method. This is needed in @@ -128,6 +132,7 @@ public class KeyguardBouncer { } public void hide(boolean destroyView) { + mLockedPhoneAnalytics.onBouncerHidden(); cancelShowRunnable(); if (mKeyguardView != null) { mKeyguardView.cancelDismissAction(); @@ -157,6 +162,7 @@ public class KeyguardBouncer { public void reset() { cancelShowRunnable(); inflateView(); + mLockedPhoneAnalytics.onBouncerHidden(); } public void onScreenTurnedOff() { @@ -244,6 +250,7 @@ public class KeyguardBouncer { // We need to show it in case it is secure. If not, it will get dismissed in any case. mRoot.setVisibility(View.VISIBLE); + mLockedPhoneAnalytics.onBouncerShown(); mKeyguardView.requestFocus(); mKeyguardView.onResume(); return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 3c3c325f3afe..24ae47d3797f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -203,6 +204,7 @@ public class NotificationPanelView extends PanelView implements private int mLastOrientation = -1; private boolean mClosingWithAlphaFadeOut; private boolean mHeadsUpAnimatingAway; + private LockedPhoneAnalytics mLockedPhoneAnalytics; private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() { @Override @@ -219,6 +221,7 @@ public class NotificationPanelView extends PanelView implements public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(!DEBUG); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } public void setStatusBar(PhoneStatusBar bar) { @@ -820,6 +823,7 @@ public class NotificationPanelView extends PanelView implements private void handleQsDown(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) { + mLockedPhoneAnalytics.onQsDown(); mQsTracking = true; onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; @@ -987,6 +991,7 @@ public class NotificationPanelView extends PanelView implements mQsExpanded = expanded; updateQsState(); requestPanelHeightUpdate(); + mLockedPhoneAnalytics.setQsExpanded(expanded); mNotificationStackScroller.setInterceptDelegateEnabled(expanded); mStatusBar.setQsExpanded(expanded); mQsPanel.setExpanded(expanded); @@ -1313,6 +1318,10 @@ public class NotificationPanelView extends PanelView implements R.string.accessibility_desc_quick_settings)); mLastAnnouncementWasQuickSettings = true; } + if (mQsFullyExpanded && mLockedPhoneAnalytics.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, + false /* dismissShade */, true /* afterKeyguardGone */); + } if (DEBUG) { invalidate(); } @@ -1840,6 +1849,7 @@ public class NotificationPanelView extends PanelView implements @Override protected void onTrackingStarted() { + mLockedPhoneAnalytics.onTrackingStarted(); super.onTrackingStarted(); if (mQsFullyExpanded) { mQsExpandImmediate = true; @@ -1853,6 +1863,7 @@ public class NotificationPanelView extends PanelView implements @Override protected void onTrackingStopped(boolean expand) { + mLockedPhoneAnalytics.onTrackingStopped(); super.onTrackingStopped(expand); if (expand) { mNotificationStackScroller.setOverScrolledPixels( @@ -1951,11 +1962,35 @@ public class NotificationPanelView extends PanelView implements if (start) { EventLogTags.writeSysuiLockscreenGesture( EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER, lengthDp, velocityDp); - mKeyguardBottomArea.launchLeftAffordance(); + + mLockedPhoneAnalytics.onLeftAffordanceOn(); + if (mLockedPhoneAnalytics.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard(new Runnable() { + @Override + public void run() { + mKeyguardBottomArea.launchLeftAffordance(); + } + }, null, true /* dismissShade */, false /* afterKeyguardGone */); + } + else { + mKeyguardBottomArea.launchLeftAffordance(); + } } else { EventLogTags.writeSysuiLockscreenGesture( EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp); - mSecureCameraLaunchManager.startSecureCameraLaunch(); + + mLockedPhoneAnalytics.onCameraOn(); + if (mLockedPhoneAnalytics.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard(new Runnable() { + @Override + public void run() { + mSecureCameraLaunchManager.startSecureCameraLaunch(); + } + }, null, true /* dismissShade */, false /* afterKeyguardGone */); + } + else { + mSecureCameraLaunchManager.startSecureCameraLaunch(); + } } mStatusBar.startLaunchTransitionTimeout(); mBlockTouches = true; @@ -1999,6 +2034,7 @@ public class NotificationPanelView extends PanelView implements @Override public void onSwipingStarted(boolean rightIcon) { + mLockedPhoneAnalytics.onAffordanceSwipingStarted(rightIcon); boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon : rightIcon; if (camera) { @@ -2012,6 +2048,7 @@ public class NotificationPanelView extends PanelView implements @Override public void onSwipingAborted() { + mLockedPhoneAnalytics.onAffordanceSwipingAborted(); mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index d8f5ccffe332..278a7e791965 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -130,6 +130,7 @@ import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.BatteryController; @@ -589,6 +590,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>(); private RankingMap mLatestRankingMap; private boolean mNoAnimationOnNextBarModeChange; + private LockedPhoneAnalytics mLockedPhoneAnalytics; @Override public void start() { @@ -636,6 +638,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, notifyUserAboutHiddenNotifications(); mScreenPinningRequest = new ScreenPinningRequest(mContext); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(mContext); } // ================================================================================ @@ -1721,7 +1724,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final boolean hasArtwork = artworkBitmap != null; - if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && mState != StatusBarState.SHADE) { + if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && mState != StatusBarState.SHADE + && mFingerprintUnlockController.getMode() + != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) { // time to show some art! if (mBackdrop.getVisibility() != View.VISIBLE) { mBackdrop.setVisibility(View.VISIBLE); @@ -1776,31 +1781,40 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork"); } - mBackdrop.animate() - // Never let the alpha become zero - otherwise the RenderNode - // won't draw anything and uninitialized memory will show through - // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in libhwui. - .alpha(0.002f) - .setInterpolator(mBackdropInterpolator) - .setDuration(300) - .setStartDelay(0) - .withEndAction(new Runnable() { - @Override - public void run() { - mBackdrop.setVisibility(View.GONE); - mBackdropFront.animate().cancel(); - mBackdropBack.animate().cancel(); - mHandler.post(mHideBackdropFront); - } - }); - if (mKeyguardFadingAway) { - mBackdrop.animate() + if (mFingerprintUnlockController.getMode() + == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) { - // Make it disappear faster, as the focus should be on the activity behind. - .setDuration(mKeyguardFadingAwayDuration / 2) - .setStartDelay(mKeyguardFadingAwayDelay) - .setInterpolator(mLinearInterpolator) - .start(); + // We are unlocking directly - no animation! + mBackdrop.setVisibility(View.GONE); + } else { + mBackdrop.animate() + // Never let the alpha become zero - otherwise the RenderNode + // won't draw anything and uninitialized memory will show through + // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in + // libhwui. + .alpha(0.002f) + .setInterpolator(mBackdropInterpolator) + .setDuration(300) + .setStartDelay(0) + .withEndAction(new Runnable() { + @Override + public void run() { + mBackdrop.setVisibility(View.GONE); + mBackdropFront.animate().cancel(); + mBackdropBack.animate().cancel(); + mHandler.post(mHideBackdropFront); + } + }); + if (mKeyguardFadingAway) { + mBackdrop.animate() + + // Make it disappear faster, as the focus should be on the activity + // behind. + .setDuration(mKeyguardFadingAwayDuration / 2) + .setStartDelay(mKeyguardFadingAwayDelay) + .setInterpolator(mLinearInterpolator) + .start(); + } } } } @@ -2462,8 +2476,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, || mStatusBarMode == MODE_LIGHTS_OUT_TRANSPARENT); boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave(); boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0; - - mIconController.setIconsDark(allowLight && light); + boolean animate = mFingerprintUnlockController == null + || (mFingerprintUnlockController.getMode() + != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING + && mFingerprintUnlockController.getMode() + != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK); + mIconController.setIconsDark(allowLight && light, animate); } // restore the recents bit if (wasRecentsVisible) { @@ -3478,7 +3496,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, .alpha(0f) .setStartDelay(0) .setDuration(FADE_KEYGUARD_DURATION_PULSING) - .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR); + .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR) + .start(); } /** @@ -3761,6 +3780,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } mState = state; mGroupManager.setStatusBarState(state); + mLockedPhoneAnalytics.setStatusBarState(state); mStatusBarWindowManager.setStatusBarState(state); updateDozing(); } @@ -3782,6 +3802,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void onUnlockHintStarted() { + mLockedPhoneAnalytics.onUnlockHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); } @@ -3791,14 +3812,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void onCameraHintStarted() { + mLockedPhoneAnalytics.onCameraHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); } public void onVoiceAssistHintStarted() { + mLockedPhoneAnalytics.onLeftAffordanceHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.voice_hint); } public void onPhoneHintStarted() { + mLockedPhoneAnalytics.onLeftAffordanceHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); } @@ -3873,7 +3897,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, row.setUserExpanded(true); } boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId) - || !mShowLockscreenNotifications; + || !mShowLockscreenNotifications || mLockedPhoneAnalytics.shouldEnforceBouncer(); if (isLockscreenPublicMode() && fullShadeNeedsBouncer) { mLeaveOpenOnKeyguardHide = true; showBouncer(); @@ -3918,6 +3942,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mDeviceInteractive = false; mWakeUpComingFromTouch = false; mWakeUpTouchLocation = null; + mLockedPhoneAnalytics.onScreenOff(); mStackScroller.setAnimationsEnabled(false); updateVisibleToUser(); } @@ -3927,6 +3952,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mStackScroller.setAnimationsEnabled(true); mNotificationPanel.setTouchDisabled(false); updateVisibleToUser(); + mLockedPhoneAnalytics.onScreenOn(); } public void onScreenTurningOn() { @@ -4055,6 +4081,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mWakeUpTouchLocation = new PointF(event.getX(), event.getY()); mNotificationPanel.setTouchDisabled(false); mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested(); + mLockedPhoneAnalytics.onScreenOnFromTouch(); } } @@ -4077,8 +4104,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void appTransitionStarting(long startTime, long duration) { // Use own timings when Keyguard is going away, see keyguardGoingAway and - // setKeyguardFadingAway - if (!mKeyguardFadingAway) { + // setKeyguardFadingAway. When duration is 0, skip this one because no animation is really + // playing. + if (!mKeyguardFadingAway && duration > 0) { mIconController.appTransitionStarting(startTime, duration); } if (mIconPolicy != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 5b009ee28c31..cc6f39661e76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -22,7 +22,6 @@ import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Color; -import android.util.Log; import android.view.View; import android.view.ViewTreeObserver; import android.view.animation.DecelerateInterpolator; @@ -87,6 +86,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private float mTopHeadsUpDragAmount; private View mDraggedHeadsUpView; private boolean mForceHideScrims; + private boolean mSkipFirstFrame; public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim, boolean scrimSrcEnabled) { @@ -134,14 +134,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, scheduleUpdate(); } - public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) { + public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished, + boolean skipFirstFrame) { mWakeAndUnlocking = false; mAnimateKeyguardFadingOut = true; mDurationOverride = duration; mAnimationDelay = delay; mAnimateChange = true; + mSkipFirstFrame = skipFirstFrame; mOnAnimationFinished = onAnimationFinished; scheduleUpdate(); + + // No need to wait for the next frame to be drawn for this case - onPreDraw will execute + // the changes we just scheduled. + onPreDraw(); } public void abortKeyguardFadingOut() { @@ -339,6 +345,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } }); anim.start(); + if (mSkipFirstFrame) { + anim.setCurrentPlayTime(16); + } scrim.setTag(TAG_KEY_ANIM, anim); scrim.setTag(TAG_KEY_ANIM_TARGET, target); } @@ -354,6 +363,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, updateScrims(); mDurationOverride = -1; mAnimationDelay = 0; + mSkipFirstFrame = false; // Make sure that we always call the listener even if we didn't start an animation. endAnimateKeyguardFadingOut(false /* force */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 067e50ee9d9b..5de1c1342197 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -335,8 +335,10 @@ public class StatusBarIconController implements Tunable { } } - public void setIconsDark(boolean dark) { - if (mTransitionPending) { + public void setIconsDark(boolean dark, boolean animate) { + if (!animate) { + setIconTintInternal(dark ? 1.0f : 0.0f); + } else if (mTransitionPending) { deferIconTintChange(dark ? 1.0f : 0.0f); } else if (mTransitionDeferring) { animateIconTint(dark ? 1.0f : 0.0f, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 7e83f2670986..59ee5c5afa6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -74,7 +74,6 @@ public class StatusBarKeyguardViewManager { private boolean mLastOccluded; private boolean mLastBouncerShowing; private boolean mLastBouncerDismissible; - private boolean mLastDeferScrimFadeOut; private OnDismissAction mAfterKeyguardGoneAction; private boolean mDeviceWillWakeUp; private boolean mDeferScrimFadeOut; @@ -184,7 +183,8 @@ public class StatusBarKeyguardViewManager { mScreenTurnedOn = true; if (mDeferScrimFadeOut) { mDeferScrimFadeOut = false; - animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS); + animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS, + true /* skipFirstFrame */); updateStates(); } mPhoneStatusBar.onScreenTurnedOn(); @@ -264,7 +264,8 @@ public class StatusBarKeyguardViewManager { updateStates(); mScrimController.animateKeyguardFadingOut( PhoneStatusBar.FADE_KEYGUARD_START_DELAY, - PhoneStatusBar.FADE_KEYGUARD_DURATION, null); + PhoneStatusBar.FADE_KEYGUARD_DURATION, null, + false /* skipFirstFrame */); } }, new Runnable() { @Override @@ -287,7 +288,7 @@ public class StatusBarKeyguardViewManager { public void run() { mPhoneStatusBar.hideKeyguard(); } - }); + }, false /* skipFirstFrame */); } else { mFingerprintUnlockController.startKeyguardFadingAway(); mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration); @@ -302,10 +303,12 @@ public class StatusBarKeyguardViewManager { // Screen is already on, don't defer with fading out. animateScrimControllerKeyguardFadingOut(0, - WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS); + WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS, + true /* skipFirstFrame */); } } else { - animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration); + animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration, + false /* skipFirstFrame */); } } else { mScrimController.animateGoingToFullShade(delay, fadeoutDuration); @@ -320,12 +323,14 @@ public class StatusBarKeyguardViewManager { } } - private void animateScrimControllerKeyguardFadingOut(long delay, long duration) { - animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */); + private void animateScrimControllerKeyguardFadingOut(long delay, long duration, + boolean skipFirstFrame) { + animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */, + skipFirstFrame); } private void animateScrimControllerKeyguardFadingOut(long delay, long duration, - final Runnable endRunnable) { + final Runnable endRunnable, boolean skipFirstFrame) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "Fading out", 0); mScrimController.animateKeyguardFadingOut(delay, duration, new Runnable() { @Override @@ -340,7 +345,7 @@ public class StatusBarKeyguardViewManager { ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0); } - }); + }, skipFirstFrame); } private void executeAfterKeyguardGoneAction() { @@ -414,7 +419,6 @@ public class StatusBarKeyguardViewManager { boolean occluded = mOccluded; boolean bouncerShowing = mBouncer.isShowing(); boolean bouncerDismissible = !mBouncer.isFullscreenBouncer(); - boolean deferScrimFadeOut = mDeferScrimFadeOut; if ((bouncerDismissible || !showing) != (mLastBouncerDismissible || !mLastShowing) || mFirstUpdate) { @@ -425,11 +429,8 @@ public class StatusBarKeyguardViewManager { } } - // Hide navigation bar on Keyguard but not on bouncer and also if we are deferring a scrim - // fade out, i.e. we are waiting for the screen to have turned on. - boolean navBarVisible = !deferScrimFadeOut && (!(showing && !occluded) || bouncerShowing); - boolean lastNavBarVisible = !mLastDeferScrimFadeOut && (!(mLastShowing && !mLastOccluded) - || mLastBouncerShowing); + boolean navBarVisible = (!(showing && !occluded) || bouncerShowing); + boolean lastNavBarVisible = (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing); if (navBarVisible != lastNavBarVisible || mFirstUpdate) { if (mPhoneStatusBar.getNavigationBarView() != null) { if (navBarVisible) { @@ -464,7 +465,6 @@ public class StatusBarKeyguardViewManager { mFirstUpdate = false; mLastShowing = showing; mLastOccluded = occluded; - mLastDeferScrimFadeOut = deferScrimFadeOut; mLastBouncerShowing = bouncerShowing; mLastBouncerDismissible = bouncerDismissible; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 0e22aa825b35..bbf981f3de4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -39,6 +39,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -55,12 +56,14 @@ public class StatusBarWindowView extends FrameLayout { private PhoneStatusBar mService; private final Paint mTransparentSrcPaint = new Paint(); + private LockedPhoneAnalytics mLockedPhoneAnalytics; public StatusBarWindowView(Context context, AttributeSet attrs) { super(context, attrs); setMotionEventSplittingEnabled(false); mTransparentSrcPaint.setColor(0); mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } @Override @@ -197,6 +200,7 @@ public class StatusBarWindowView extends FrameLayout { @Override public boolean dispatchTouchEvent(MotionEvent ev) { + mLockedPhoneAnalytics.onTouchEvent(ev, getWidth(), getHeight()); if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) { // Disallow new pointers while the brightness mirror is visible. This is so that you // can't touch anything other than the brightness slider while the mirror is showing diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java index 091db76b7e41..a91cd5177cfd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java @@ -145,7 +145,7 @@ public class UnlockMethodCache { } @Override - public void onStrongAuthTimeoutExpiredChanged(int userId) { + public void onStrongAuthStateChanged(int userId) { update(false /* updateAlways */); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto b/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto new file mode 100644 index 000000000000..afc8f77f6896 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +syntax = "proto2"; + +package systemui; + +option java_package = "com.android.systemui.statusbar.phone"; +option java_outer_classname = "TouchAnalyticsProto"; + +message Session { + message TouchEvent { + message BoundingBox { + optional float width = 1; + optional float height = 2; + } + + enum Action { + // Keep in sync with MotionEvent. + DOWN = 0; + UP = 1; + MOVE = 2; + CANCEL = 3; + OUTSIDE = 4; + POINTER_DOWN = 5; + POINTER_UP = 6; + } + + message Pointer { + optional float x = 1; + optional float y = 2; + optional float size = 3; + optional float pressure = 4; + optional int32 id = 5; + optional float removed_length = 6; + optional BoundingBox removed_boundingBox = 7; + } + + optional uint64 timeOffsetNanos = 1; + optional Action action = 2; + optional int32 actionIndex = 3; + repeated Pointer pointers = 4; + optional bool removed_redacted = 5; + optional BoundingBox removed_boundingBox = 6; + } + + message SensorEvent { + enum Type { + ACCELEROMETER = 1; + GYROSCOPE = 4; + LIGHT = 5; + PROXIMITY = 8; + ROTATION_VECTOR = 11; + } + + optional Type type = 1; + optional uint64 timeOffsetNanos = 2; + repeated float values = 3; + optional uint64 timestamp = 4; + } + + message PhoneEvent { + enum Type { + ON_SCREEN_ON = 0; + ON_SCREEN_ON_FROM_TOUCH = 1; + ON_SCREEN_OFF = 2; + ON_SUCCESSFUL_UNLOCK = 3; + ON_BOUNCER_SHOWN = 4; + ON_BOUNCER_HIDDEN = 5; + ON_QS_DOWN = 6; + SET_QS_EXPANDED_TRUE = 7; + SET_QS_EXPANDED_FALSE = 8; + ON_TRACKING_STARTED = 9; + ON_TRACKING_STOPPED = 10; + ON_NOTIFICATION_ACTIVE = 11; + ON_NOTIFICATION_INACTIVE = 12; + ON_NOTIFICATION_DOUBLE_TAP = 13; + SET_NOTIFICATION_EXPANDED = 14; + RESET_NOTIFICATION_EXPANDED = 15; + ON_NOTIFICATION_START_DRAGGING_DOWN = 16; + ON_NOTIFICATION_STOP_DRAGGING_DOWN = 17; + ON_NOTIFICATION_DISMISSED = 18; + ON_NOTIFICATION_START_DISMISSING = 19; + ON_NOTIFICATION_STOP_DISMISSING = 20; + ON_RIGHT_AFFORDANCE_SWIPING_STARTED = 21; + ON_LEFT_AFFORDANCE_SWIPING_STARTED = 22; + ON_AFFORDANCE_SWIPING_ABORTED = 23; + ON_CAMERA_ON = 24; + ON_LEFT_AFFORDANCE_ON = 25; + ON_UNLOCK_HINT_STARTED = 26; + ON_CAMERA_HINT_STARTED = 27; + ON_LEFT_AFFORDANCE_HINT_STARTED = 28; + } + + optional Type type = 1; + optional uint64 timeOffsetNanos = 2; + } + + enum Result { + FAILURE = 0; + SUCCESS = 1; + UNKNOWN = 2; + } + + enum Type { + RESERVED_1 = 0; + RESERVED_2 = 1; + RANDOM_WAKEUP = 2; + REAL = 3; + } + + optional uint64 startTimestampMillis = 1; + optional uint64 durationMillis = 2; + optional string build = 3; + optional Result result = 4; + repeated TouchEvent touchEvents = 5; + repeated SensorEvent sensorEvents = 6; + + optional int32 touchAreaWidth = 9; + optional int32 touchAreaHeight = 10; + optional Type type = 11; + repeated PhoneEvent phoneEvents = 12; +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 61986adc1d65..896bd62f9969 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -23,6 +23,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.TypedArray; import android.os.Bundle; +import android.os.Handler; +import android.os.SystemClock; import android.os.UserHandle; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -30,22 +32,28 @@ import android.text.format.DateFormat; import android.text.style.CharacterStyle; import android.text.style.RelativeSizeSpan; import android.util.AttributeSet; +import android.view.Display; import android.widget.TextView; import com.android.systemui.DemoMode; import com.android.systemui.R; +import com.android.systemui.tuner.TunerService; +import com.android.systemui.tuner.TunerService.Tunable; + +import libcore.icu.LocaleData; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; -import libcore.icu.LocaleData; - /** * Digital clock for the status bar. */ -public class Clock extends TextView implements DemoMode { +public class Clock extends TextView implements DemoMode, Tunable { + + public static final String CLOCK_SECONDS = "clock_seconds"; + private boolean mAttached; private Calendar mCalendar; private String mClockFormatString; @@ -57,6 +65,8 @@ public class Clock extends TextView implements DemoMode { private static final int AM_PM_STYLE_GONE = 2; private final int mAmPmStyle; + private boolean mShowSeconds; + private Handler mSecondsHandler; public Clock(Context context) { this(context, null); @@ -77,6 +87,7 @@ public class Clock extends TextView implements DemoMode { } finally { a.recycle(); } + TunerService.get(context).addTunable(this, CLOCK_SECONDS); } @Override @@ -105,6 +116,7 @@ public class Clock extends TextView implements DemoMode { // Make sure we update to the current time updateClock(); + updateShowSeconds(); } @Override @@ -143,6 +155,35 @@ public class Clock extends TextView implements DemoMode { setText(getSmallTime()); } + @Override + public void onTuningChanged(String key, String newValue) { + mShowSeconds = newValue != null && Integer.parseInt(newValue) != 0; + updateShowSeconds(); + } + + private void updateShowSeconds() { + if (mShowSeconds) { + // Wait until we have a display to start trying to show seconds. + if (mSecondsHandler == null && getDisplay() != null) { + mSecondsHandler = new Handler(); + if (getDisplay().getState() == Display.STATE_ON) { + mSecondsHandler.postAtTime(mSecondTick, + SystemClock.uptimeMillis() / 1000 * 1000 + 1000); + } + IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + mContext.registerReceiver(mScreenReceiver, filter); + } + } else { + if (mSecondsHandler != null) { + mContext.unregisterReceiver(mScreenReceiver); + mSecondsHandler.removeCallbacks(mSecondTick); + mSecondsHandler = null; + updateClock(); + } + } + } + private final CharSequence getSmallTime() { Context context = getContext(); boolean is24 = DateFormat.is24HourFormat(context, ActivityManager.getCurrentUser()); @@ -152,7 +193,9 @@ public class Clock extends TextView implements DemoMode { final char MAGIC2 = '\uEF01'; SimpleDateFormat sdf; - String format = is24 ? d.timeFormat_Hm : d.timeFormat_hm; + String format = mShowSeconds + ? is24 ? d.timeFormat_Hms : d.timeFormat_hms + : is24 ? d.timeFormat_Hm : d.timeFormat_hm; if (!format.equals(mClockFormatString)) { /* * Search for an unquoted "a" in the format string, so we can @@ -244,5 +287,32 @@ public class Clock extends TextView implements DemoMode { setText(getSmallTime()); } } + + private final BroadcastReceiver mScreenReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_SCREEN_OFF.equals(action)) { + if (mSecondsHandler != null) { + mSecondsHandler.removeCallbacks(mSecondTick); + } + } else if (Intent.ACTION_SCREEN_ON.equals(action)) { + if (mSecondsHandler != null) { + mSecondsHandler.postAtTime(mSecondTick, + SystemClock.uptimeMillis() / 1000 * 1000 + 1000); + } + } + } + }; + + private final Runnable mSecondTick = new Runnable() { + @Override + public void run() { + if (mCalendar != null) { + updateClock(); + } + mSecondsHandler.postAtTime(this, SystemClock.uptimeMillis() / 1000 * 1000 + 1000); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 6af98543b214..6d791bf3a892 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -178,7 +178,8 @@ public class SecurityControllerImpl implements SecurityController { mCurrentUserId = newUserId; if (mUserManager.getUserInfo(newUserId).isRestricted()) { // VPN for a restricted profile is routed through its owner user - mVpnUserId = UserHandle.USER_OWNER; + // TODO: http://b/22950929 + mVpnUserId = UserHandle.USER_SYSTEM; } else { mVpnUserId = mCurrentUserId; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java index 753a7f76ed3d..56f65645bafc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java @@ -34,6 +34,7 @@ public class AnimationFilter { boolean hasDelays; boolean hasGoToFullShadeEvent; boolean hasDarkEvent; + boolean hasHeadsUpDisappearClickEvent; int darkAnimationOriginIndex; public AnimationFilter animateAlpha() { @@ -106,6 +107,10 @@ public class AnimationFilter { hasDarkEvent = true; darkAnimationOriginIndex = ev.darkAnimationOriginIndex; } + if (ev.animationType == NotificationStackScrollLayout.AnimationEvent + .ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) { + hasHeadsUpDisappearClickEvent = true; + } } } @@ -135,6 +140,7 @@ public class AnimationFilter { hasDelays = false; hasGoToFullShadeEvent = false; hasDarkEvent = false; + hasHeadsUpDisappearClickEvent = false; darkAnimationOriginIndex = NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE; } 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 82c1aa8577cd..a1d73d28901d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.NotificationOverflowContainer; import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.StackScrollerDecorView; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.ScrimController; @@ -231,6 +232,7 @@ public class NotificationStackScrollLayout extends ViewGroup private boolean mForceNoOverlappingRendering; private NotificationOverflowContainer mOverflowContainer; private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>(); + private LockedPhoneAnalytics mLockedPhoneAnalytics; public NotificationStackScrollLayout(Context context) { this(context, null); @@ -264,6 +266,7 @@ public class NotificationStackScrollLayout extends ViewGroup mDebugPaint.setStrokeWidth(2); mDebugPaint.setStyle(Paint.Style.STROKE); } + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } @Override @@ -595,6 +598,12 @@ public class NotificationStackScrollLayout extends ViewGroup veto.performClick(); } if (DEBUG) Log.v(TAG, "onChildDismissed: " + v); + + mLockedPhoneAnalytics.onNotificationDismissed(); + if (mLockedPhoneAnalytics.shouldEnforceBouncer()) { + mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, + false /* dismissShade */, true /* afterKeyguardGone */); + } } @Override @@ -622,6 +631,7 @@ public class NotificationStackScrollLayout extends ViewGroup } public void onBeginDrag(View v) { + mLockedPhoneAnalytics.onNotificatonStartDismissing(); setSwipingInProgress(true); mAmbientState.onBeginDrag(v); if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) { @@ -648,6 +658,7 @@ public class NotificationStackScrollLayout extends ViewGroup } public void onDragCancelled(View v) { + mLockedPhoneAnalytics.onNotificatonStopDismissing(); setSwipingInProgress(false); } @@ -1915,7 +1926,9 @@ public class NotificationStackScrollLayout extends ViewGroup boolean onBottom = false; boolean pinnedAndClosed = row.isPinned() && !mIsExpanded; if (!mIsExpanded && !isHeadsUp) { - type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR; + type = row.wasJustClicked() + ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK + : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR; } else { StackViewState viewState = mCurrentStackScrollState.getViewStateForView(row); if (viewState == null) { @@ -3016,6 +3029,15 @@ public class NotificationStackScrollLayout extends ViewGroup .animateY() .animateZ(), + // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK + new AnimationFilter() + .animateAlpha() + .animateHeight() + .animateTopInset() + .animateY() + .animateZ() + .hasDelays(), + // ANIMATION_TYPE_HEADS_UP_OTHER new AnimationFilter() .animateAlpha() @@ -3087,6 +3109,9 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_HEADS_UP_DISAPPEAR StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR, + // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK + StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR, + // ANIMATION_TYPE_HEADS_UP_OTHER StackStateAnimator.ANIMATION_DURATION_STANDARD, @@ -3110,8 +3135,9 @@ public class NotificationStackScrollLayout extends ViewGroup static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13; static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14; static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15; - static final int ANIMATION_TYPE_HEADS_UP_OTHER = 16; - static final int ANIMATION_TYPE_EVERYTHING = 17; + static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16; + static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17; + static final int ANIMATION_TYPE_EVERYTHING = 18; static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1; static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 82064a7c9c27..cf696a1208c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -127,7 +127,7 @@ public class StackScrollAlgorithm { mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize( R.dimen.notification_collapse_second_card_padding); mScaleDimmed = context.getResources().getDisplayMetrics().densityDpi - >= DisplayMetrics.DENSITY_XXHIGH; + >= DisplayMetrics.DENSITY_420; } public boolean shouldScaleDimmed() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 97c7d301bd44..b4ab48a77ac8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -54,6 +54,7 @@ public class StackStateAnimator { public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24; public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN = 3; + public static final int ANIMATION_DELAY_HEADS_UP = 120; private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; @@ -320,6 +321,9 @@ public class StackStateAnimator { if (mAnimationFilter.hasGoToFullShadeEvent) { return calculateDelayGoToFullShade(viewState); } + if (mAnimationFilter.hasHeadsUpDisappearClickEvent) { + return ANIMATION_DELAY_HEADS_UP; + } long minDelay = 0; for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) { long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING; @@ -890,7 +894,9 @@ public class StackStateAnimator { mHeadsUpAppearChildren.add(changingView); finalState.applyState(changingView, mTmpState); } else if (event.animationType == NotificationStackScrollLayout - .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) { + .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR || + event.animationType == NotificationStackScrollLayout + .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) { mHeadsUpDisappearChildren.add(changingView); if (mHostLayout.indexOfChild(changingView) == -1) { // This notification was actually removed, so we need to add it to the overlay @@ -900,7 +906,11 @@ public class StackStateAnimator { // We temporarily enable Y animations, the real filter will be combined // afterwards anyway mAnimationFilter.animateY = true; - startViewAnimations(changingView, mTmpState, 0, + startViewAnimations(changingView, mTmpState, + event.animationType == NotificationStackScrollLayout + .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK + ? ANIMATION_DELAY_HEADS_UP + : 0, ANIMATION_DURATION_HEADS_UP_DISAPPEAR); mChildrenToClearFromOverlay.add(changingView); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java index e5b550e82bdb..dcb0d8d0c498 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java @@ -38,6 +38,18 @@ public class StatusBarSwitch extends SwitchPreference implements Tunable { } @Override + protected void onAttachedToActivity() { + super.onAttachedToActivity(); + TunerService.get(getContext()).addTunable(this, StatusBarIconController.ICON_BLACKLIST); + } + + @Override + protected void onDetachedFromActivity() { + TunerService.get(getContext()).removeTunable(this); + super.onDetachedFromActivity(); + } + + @Override public void onTuningChanged(String key, String newValue) { if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java index 71b5de56444b..96ad756b4e94 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -15,8 +15,6 @@ */ package com.android.systemui.tuner; -import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING; - import android.app.AlertDialog; import android.app.FragmentTransaction; import android.content.DialogInterface; @@ -29,7 +27,6 @@ import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceFragment; -import android.preference.PreferenceGroup; import android.preference.SwitchPreference; import android.provider.Settings; import android.provider.Settings.System; @@ -39,8 +36,8 @@ import android.view.MenuItem; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.StatusBarIconController; -import com.android.systemui.tuner.TunerService.Tunable; + +import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING; public class TunerFragment extends PreferenceFragment { @@ -108,7 +105,6 @@ public class TunerFragment extends PreferenceFragment { getContext().getContentResolver().registerContentObserver( System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver); - registerPrefs(getPreferenceScreen()); MetricsLogger.visibility(getContext(), MetricsLogger.TUNER, true); } @@ -117,36 +113,9 @@ public class TunerFragment extends PreferenceFragment { super.onPause(); getContext().getContentResolver().unregisterContentObserver(mSettingObserver); - unregisterPrefs(getPreferenceScreen()); MetricsLogger.visibility(getContext(), MetricsLogger.TUNER, false); } - private void registerPrefs(PreferenceGroup group) { - TunerService tunerService = TunerService.get(getContext()); - final int N = group.getPreferenceCount(); - for (int i = 0; i < N; i++) { - Preference pref = group.getPreference(i); - if (pref instanceof StatusBarSwitch) { - tunerService.addTunable((Tunable) pref, StatusBarIconController.ICON_BLACKLIST); - } else if (pref instanceof PreferenceGroup) { - registerPrefs((PreferenceGroup) pref); - } - } - } - - private void unregisterPrefs(PreferenceGroup group) { - TunerService tunerService = TunerService.get(getContext()); - final int N = group.getPreferenceCount(); - for (int i = 0; i < N; i++) { - Preference pref = group.getPreference(i); - if (pref instanceof Tunable) { - tunerService.removeTunable((Tunable) pref); - } else if (pref instanceof PreferenceGroup) { - registerPrefs((PreferenceGroup) pref); - } - } - } - @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.add(Menu.NONE, MENU_REMOVE, Menu.NONE, R.string.remove_from_settings); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java new file mode 100644 index 000000000000..0740e0808529 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java @@ -0,0 +1,39 @@ +package com.android.systemui.tuner; + +import android.content.Context; +import android.preference.SwitchPreference; +import android.provider.Settings; +import android.util.AttributeSet; + +import com.android.systemui.tuner.TunerService.Tunable; + +public class TunerSwitch extends SwitchPreference implements Tunable { + + public TunerSwitch(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onAttachedToActivity() { + super.onAttachedToActivity(); + TunerService.get(getContext()).addTunable(this, getKey()); + } + + @Override + protected void onDetachedFromActivity() { + TunerService.get(getContext()).removeTunable(this); + super.onDetachedFromActivity(); + } + + @Override + public void onTuningChanged(String key, String newValue) { + setChecked(newValue != null && Integer.parseInt(newValue) != 0); + } + + @Override + protected boolean persistBoolean(boolean value) { + Settings.Secure.putString(getContext().getContentResolver(), getKey(), value ? "1" : "0"); + return true; + } + +} diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index 6a7201c26a4f..d6d17cb10c2f 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -17,9 +17,14 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests +LOCAL_PROTOC_OPTIMIZE_TYPE := nano +LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/.. +LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors + LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.systemui:com.android.keyguard LOCAL_SRC_FILES := $(call all-java-files-under, src) \ $(call all-java-files-under, ../src) \ + $(call all-proto-files-under, ../src) \ src/com/android/systemui/EventLogTags.logtags LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \ diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 34aeb60a1104..f72474983182 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -401,21 +401,40 @@ public class BackupManagerService { public boolean isSystemRestore; public String[] filterSet; - // Restore a single package + /** + * Restore a single package; no kill after restore + */ RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, - long _token, PackageInfo _pkg, int _pmToken) { + long _token, PackageInfo _pkg) { transport = _transport; dirName = _dirName; observer = _obs; token = _token; pkgInfo = _pkg; - pmToken = _pmToken; + pmToken = 0; isSystemRestore = false; filterSet = null; } - // Restore everything possible. This is the form that Setup Wizard or similar - // restore UXes use. + /** + * Restore at install: PM token needed, kill after restore + */ + RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, + long _token, String _pkgName, int _pmToken) { + transport = _transport; + dirName = _dirName; + observer = _obs; + token = _token; + pkgInfo = null; + pmToken = _pmToken; + isSystemRestore = false; + filterSet = new String[] { _pkgName }; + } + + /** + * Restore everything possible. This is the form that Setup Wizard or similar + * restore UXes use. + */ RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, long _token) { transport = _transport; @@ -428,8 +447,10 @@ public class BackupManagerService { filterSet = null; } - // Restore some set of packages. Leave this one up to the caller to specify - // whether it's to be considered a system-level restore. + /** + * Restore some set of packages. Leave this one up to the caller to specify + * whether it's to be considered a system-level restore. + */ RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, long _token, String[] _filterSet, boolean _isSystemRestore) { transport = _transport; @@ -9137,19 +9158,13 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF // This can throw and so *must* happen before the wakelock is acquired String dirName = transport.transportDirName(); - // We can use a synthetic PackageInfo here because: - // 1. We know it's valid, since the Package Manager supplied the name - // 2. Only the packageName field will be used by the restore code - PackageInfo pkg = new PackageInfo(); - pkg.packageName = packageName; - mWakelock.acquire(); if (MORE_DEBUG) { Slog.d(TAG, "Restore at install of " + packageName); } Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); msg.obj = new RestoreParams(transport, dirName, null, - restoreSet, pkg, token); + restoreSet, packageName, token); mBackupHandler.sendMessage(msg); } catch (RemoteException e) { // Binding to the transport broke; back off and proceed with the installation. @@ -9528,8 +9543,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF Slog.d(TAG, "restorePackage() : " + packageName); } Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); - msg.obj = new RestoreParams(mRestoreTransport, dirName, - observer, token, app, 0); + msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app); mBackupHandler.sendMessage(msg); } finally { Binder.restoreCallingIdentity(oldId); diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 9427b61978df..7e4bcdcf0cd3 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -18,6 +18,7 @@ package com.android.server; import android.app.admin.DevicePolicyManager; import android.app.backup.BackupManager; +import android.app.trust.IStrongAuthTracker; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -70,6 +71,7 @@ public class LockSettingsService extends ILockSettings.Stub { private final Context mContext; private final LockSettingsStorage mStorage; + private final LockSettingsStrongAuth mStrongAuth = new LockSettingsStrongAuth(); private LockPatternUtils mLockPatternUtils; private boolean mFirstCallToVold; @@ -93,6 +95,7 @@ public class LockSettingsService extends ILockSettings.Stub { filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_STARTING); filter.addAction(Intent.ACTION_USER_REMOVED); + filter.addAction(Intent.ACTION_USER_PRESENT); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() { @@ -122,6 +125,8 @@ public class LockSettingsService extends ILockSettings.Stub { } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) { final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); mStorage.prefetchUser(userHandle); + } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) { + mStrongAuth.reportUnlock(getSendingUserId()); } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); if (userHandle > 0) { @@ -713,6 +718,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void removeUser(int userId) { mStorage.removeUser(userId); + mStrongAuth.removeUser(userId); final KeyStore ks = KeyStore.getInstance(); ks.onUserRemoved(userId); @@ -727,6 +733,24 @@ public class LockSettingsService extends ILockSettings.Stub { } } + @Override + public void registerStrongAuthTracker(IStrongAuthTracker tracker) { + checkPasswordReadPermission(UserHandle.USER_ALL); + mStrongAuth.registerStrongAuthTracker(tracker); + } + + @Override + public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) { + checkPasswordReadPermission(UserHandle.USER_ALL); + mStrongAuth.unregisterStrongAuthTracker(tracker); + } + + @Override + public void requireStrongAuth(int strongAuthReason, int userId) { + checkWritePermission(userId); + mStrongAuth.requireStrongAuth(strongAuthReason, userId); + } + private static final String[] VALID_SETTINGS = new String[] { LockPatternUtils.LOCKOUT_PERMANENT_KEY, LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, @@ -797,5 +821,4 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.e(TAG, "Unable to acquire GateKeeperService"); return null; } - } diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java new file mode 100644 index 000000000000..c023f4aa17a0 --- /dev/null +++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server; + +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker; + +import android.app.trust.IStrongAuthTracker; +import android.os.DeadObjectException; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Slog; +import android.util.SparseIntArray; + +import java.util.ArrayList; + +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; + +/** + * Keeps track of requests for strong authentication. + */ +public class LockSettingsStrongAuth { + + private static final String TAG = "LockSettings"; + + private static final int MSG_REQUIRE_STRONG_AUTH = 1; + private static final int MSG_REGISTER_TRACKER = 2; + private static final int MSG_UNREGISTER_TRACKER = 3; + private static final int MSG_REMOVE_USER = 4; + + private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>(); + private final SparseIntArray mStrongAuthForUser = new SparseIntArray(); + + private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) { + for (int i = 0; i < mStrongAuthTrackers.size(); i++) { + if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) { + return; + } + } + mStrongAuthTrackers.add(tracker); + + for (int i = 0; i < mStrongAuthForUser.size(); i++) { + int key = mStrongAuthForUser.keyAt(i); + int value = mStrongAuthForUser.valueAt(i); + try { + tracker.onStrongAuthRequiredChanged(value, key); + } catch (RemoteException e) { + Slog.e(TAG, "Exception while adding StrongAuthTracker.", e); + } + } + } + + private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) { + for (int i = 0; i < mStrongAuthTrackers.size(); i++) { + if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) { + mStrongAuthTrackers.remove(i); + return; + } + } + } + + private void handleRequireStrongAuth(int strongAuthReason, int userId) { + if (userId == UserHandle.USER_ALL) { + for (int i = 0; i < mStrongAuthForUser.size(); i++) { + int key = mStrongAuthForUser.keyAt(i); + handleRequireStrongAuthOneUser(strongAuthReason, key); + } + } else { + handleRequireStrongAuthOneUser(strongAuthReason, userId); + } + } + + private void handleRequireStrongAuthOneUser(int strongAuthReason, int userId) { + int oldValue = mStrongAuthForUser.get(userId, LockPatternUtils.StrongAuthTracker.DEFAULT); + int newValue = strongAuthReason == STRONG_AUTH_NOT_REQUIRED + ? STRONG_AUTH_NOT_REQUIRED + : (oldValue | strongAuthReason); + if (oldValue != newValue) { + mStrongAuthForUser.put(userId, newValue); + notifyStrongAuthTrackers(newValue, userId); + } + } + + private void handleRemoveUser(int userId) { + int index = mStrongAuthForUser.indexOfKey(userId); + if (index >= 0) { + mStrongAuthForUser.removeAt(index); + notifyStrongAuthTrackers(StrongAuthTracker.DEFAULT, userId); + } + } + + private void notifyStrongAuthTrackers(int strongAuthReason, int userId) { + for (int i = 0; i < mStrongAuthTrackers.size(); i++) { + try { + mStrongAuthTrackers.get(i).onStrongAuthRequiredChanged(strongAuthReason, userId); + } catch (DeadObjectException e) { + Slog.d(TAG, "Removing dead StrongAuthTracker."); + mStrongAuthTrackers.remove(i); + i--; + } catch (RemoteException e) { + Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e); + } + } + } + + public void registerStrongAuthTracker(IStrongAuthTracker tracker) { + mHandler.obtainMessage(MSG_REGISTER_TRACKER, tracker).sendToTarget(); + } + + public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) { + mHandler.obtainMessage(MSG_UNREGISTER_TRACKER, tracker).sendToTarget(); + } + + public void removeUser(int userId) { + mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget(); + } + + public void requireStrongAuth(int strongAuthReason, int userId) { + if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_OWNER) { + mHandler.obtainMessage(MSG_REQUIRE_STRONG_AUTH, strongAuthReason, + userId).sendToTarget(); + } else { + throw new IllegalArgumentException( + "userId must be an explicit user id or USER_ALL"); + } + } + + public void reportUnlock(int userId) { + requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId); + } + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REGISTER_TRACKER: + handleAddStrongAuthTracker((IStrongAuthTracker) msg.obj); + break; + case MSG_UNREGISTER_TRACKER: + handleRemoveStrongAuthTracker((IStrongAuthTracker) msg.obj); + break; + case MSG_REQUIRE_STRONG_AUTH: + handleRequireStrongAuth(msg.arg1, msg.arg2); + break; + case MSG_REMOVE_USER: + handleRemoveUser(msg.arg1); + break; + } + } + }; +} diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 0d64540672e4..72cece3430e8 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -165,6 +165,8 @@ class MountService extends IMountService.Stub public void onBootPhase(int phase) { if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { mMountService.systemReady(); + } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { + mMountService.bootCompleted(); } } @@ -405,6 +407,7 @@ class MountService extends IMountService.Stub private final NativeDaemonConnector mCryptConnector; private volatile boolean mSystemReady = false; + private volatile boolean mBootCompleted = false; private volatile boolean mDaemonConnected = false; private PackageManagerService mPms; @@ -1244,7 +1247,9 @@ class MountService extends IMountService.Stub mCallbacks.notifyVolumeStateChanged(vol, oldState, newState); - if (isBroadcastWorthy(vol)) { + // Do not broadcast before boot has completed to avoid launching the + // processes that receive the intent unnecessarily. + if (mBootCompleted && isBroadcastWorthy(vol)) { final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED); intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState); @@ -1429,6 +1434,10 @@ class MountService extends IMountService.Stub mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget(); } + private void bootCompleted() { + mBootCompleted = true; + } + private String getDefaultPrimaryStorageUuid() { if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) { return StorageManager.UUID_PRIMARY_PHYSICAL; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 55a26599a92d..007b14bcd264 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.DOCKED_STACK_ID; import static android.app.ActivityManager.HOME_STACK_ID; +import static android.app.ActivityManager.INVALID_STACK_ID; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; @@ -9005,8 +9006,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack == null) { - throw new IllegalArgumentException( - "getActivityStackId: No stack for token=" + token); + return INVALID_STACK_ID; } return stack.mStackId; } @@ -9028,7 +9028,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (DEBUG_STACK) Slog.d(TAG_STACK, "moveActivityToStack: moving r=" + r + " to stackId=" + stackId); - mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP, FORCE_FOCUS); + mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP, !FORCE_FOCUS); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 356565fc802f..965f5b626508 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -248,6 +248,8 @@ final class ActivityStack { /** Run all ActivityStacks through this */ final ActivityStackSupervisor mStackSupervisor; + private final LaunchingTaskPositioner mTaskPositioner; + static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1; static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2; static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3; @@ -363,6 +365,33 @@ final class ActivityStack { mStackId = activityContainer.mStackId; mCurrentUser = mService.mCurrentUserId; mRecentTasks = recentTasks; + mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID + ? new LaunchingTaskPositioner() : null; + } + + void attachDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) { + mDisplayId = activityDisplay.mDisplayId; + mStacks = activityDisplay.mStacks; + mBounds = mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop); + mFullscreen = mBounds == null; + if (mTaskPositioner != null) { + mTaskPositioner.setDisplay(activityDisplay.mDisplay); + mTaskPositioner.configure(mBounds); + } + } + + void detachDisplay() { + mDisplayId = Display.INVALID_DISPLAY; + mStacks = null; + if (mTaskPositioner != null) { + mTaskPositioner.reset(); + } + mWindowManager.detachStack(mStackId); + } + + void setBounds(Rect bounds) { + mBounds = mFullscreen ? null : new Rect(bounds); + mTaskPositioner.configure(bounds); } boolean okToShowLocked(ActivityRecord r) { @@ -2223,7 +2252,7 @@ final class ActivityStack { + task, new RuntimeException("here").fillInStackTrace()); task.addActivityToTop(r); r.putInHistory(); - addAppToken(r, task); + addConfigOverride(r, task); if (VALIDATE_TOKENS) { validateAppTokensLocked(); } @@ -2283,7 +2312,7 @@ final class ActivityStack { : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition); mNoAnimActivities.remove(r); } - addAppToken(r, task); + addConfigOverride(r, task); boolean doShow = true; if (newTask) { // Even though this activity is starting fresh, we still need @@ -2332,7 +2361,7 @@ final class ActivityStack { } else { // If this is the first activity, don't do any fancy animations, // because there is nothing for it to animate on top of. - addAppToken(r, task); + addConfigOverride(r, task); ActivityOptions.abort(options); options = null; } @@ -4513,6 +4542,9 @@ final class ActivityStack { boolean toTop) { TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession, voiceInteractor); + if (mTaskPositioner != null) { + mTaskPositioner.updateDefaultBounds(task, mTaskHistory, info.initialLayout); + } addTask(task, toTop, false); return task; } @@ -4548,7 +4580,7 @@ final class ActivityStack { } } - void addAppToken(ActivityRecord r, TaskRecord task) { + void addConfigOverride(ActivityRecord r, TaskRecord task) { final Rect bounds = task.getLaunchBounds(); final Configuration config = mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 12b848be6ea3..c86056b1a5f0 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2977,7 +2977,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - stack.mBounds = stack.mFullscreen ? null : new Rect(bounds); + stack.setBounds(bounds); if (r != null) { final boolean updated = stack.ensureActivityConfigurationLocked(r, 0); @@ -3017,7 +3017,6 @@ public final class ActivityStackSupervisor implements DisplayListener { // Place the task in the right stack if it isn't there already based on the requested // bounds. int stackId = task.stack.mStackId; - final boolean wasFrontStack = isFrontStack(task.stack); if (bounds == null && stackId != FULLSCREEN_WORKSPACE_STACK_ID) { stackId = FULLSCREEN_WORKSPACE_STACK_ID; } else if (bounds != null @@ -3027,12 +3026,7 @@ public final class ActivityStackSupervisor implements DisplayListener { if (stackId != task.stack.mStackId) { final String reason = "resizeTask"; final ActivityStack stack = - moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, reason); - if (wasFrontStack) { - // Since the stack was previously in front, - // move the stack in which we are placing the task to the front. - stack.moveToFront(reason); - } + moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, reason); } final Configuration overrideConfig = mWindowManager.resizeTask(task.taskId, bounds); @@ -3112,7 +3106,7 @@ public final class ActivityStackSupervisor implements DisplayListener { "Added restored task=" + task + " to stack=" + stack); final ArrayList<ActivityRecord> activities = task.mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - stack.addAppToken(activities.get(activityNdx), task); + stack.addConfigOverride(activities.get(activityNdx), task); } return true; } @@ -3126,17 +3120,36 @@ public final class ActivityStackSupervisor implements DisplayListener { * @param task Task to move. * @param stackId Id of stack to move task to. * @param toTop True if the task should be placed at the top of the stack. + * @param forceFocus if focus should be moved to the new stack * @param reason Reason the task is been moved. * @return The stack the task was moved to. */ private ActivityStack moveTaskToStackUncheckedLocked( - TaskRecord task, int stackId, boolean toTop, String reason) { + TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) { + final ActivityRecord r = task.getTopActivity(); + final boolean wasFocused = isFrontStack(task.stack) && (topRunningActivityLocked() == r); + final boolean wasResumed = wasFocused && (task.stack.mResumedActivity == r); + final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop); mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop); if (task.stack != null) { task.stack.removeTask(task, reason, MOVING); } stack.addTask(task, toTop, MOVING); + + // If the task had focus before (or we're requested to move focus), + // move focus to the new stack. + if (forceFocus || wasFocused) { + // If the task owns the last resumed activity, transfer that together, + // so that we don't resume the same activity again in the new stack. + // Apps may depend on onResume()/onPause() being called in pairs. + if (wasResumed) { + stack.mResumedActivity = r; + } + // move the stack in which we are placing the task to the front. + stack.moveToFront(reason); + } + return stack; } @@ -3147,10 +3160,8 @@ public final class ActivityStackSupervisor implements DisplayListener { return; } final String reason = "moveTaskToStack"; - final ActivityRecord top = task.topRunningActivityLocked(null); - final boolean adjustFocus = forceFocus || mService.mFocusedActivity == top; final ActivityStack stack = - moveTaskToStackUncheckedLocked(task, stackId, toTop, reason); + moveTaskToStackUncheckedLocked(task, stackId, toTop, forceFocus, reason); // Make sure the task has the appropriate bounds/size for the stack it is in. if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) { @@ -3162,14 +3173,6 @@ public final class ActivityStackSupervisor implements DisplayListener { resizeTaskLocked(task, stack.mBounds); } - if (top != null && adjustFocus) { - if (mService.mFocusedActivity != top) { - mService.setFocusedActivityLocked(top, reason); - } else { - setFocusedStack(top, reason); - } - } - // The task might have already been running and its visibility needs to be synchronized with // the visibility of the stack / windows. ensureActivitiesVisibleLocked(null, 0); @@ -4387,13 +4390,8 @@ public final class ActivityStackSupervisor implements DisplayListener { if (DEBUG_STACK) Slog.d(TAG_STACK, "attachToDisplayLocked: " + this + " to display=" + activityDisplay + " onTop=" + onTop); mActivityDisplay = activityDisplay; - mStack.mDisplayId = activityDisplay.mDisplayId; - mStack.mStacks = activityDisplay.mStacks; - + mStack.attachDisplay(activityDisplay, onTop); activityDisplay.attachActivities(mStack, onTop); - mStack.mBounds = - mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop); - mStack.mFullscreen = mStack.mBounds == null; } @Override @@ -4465,9 +4463,7 @@ public final class ActivityStackSupervisor implements DisplayListener { if (mActivityDisplay != null) { mActivityDisplay.detachActivitiesLocked(mStack); mActivityDisplay = null; - mStack.mDisplayId = -1; - mStack.mStacks = null; - mWindowManager.detachStack(mStackId); + mStack.detachDisplay(); } } diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java new file mode 100644 index 000000000000..5c4fd138c19c --- /dev/null +++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; + +import android.annotation.Nullable; +import android.content.pm.ActivityInfo; +import android.graphics.Point; +import android.graphics.Rect; +import android.util.Slog; +import android.view.Display; +import android.view.Gravity; + +import java.util.ArrayList; + +/** + * Determines where a launching task should be positioned and sized on the display. + * + * The positioner is fairly simple. For the new task it tries default position based on the gravity + * and compares corners of the task with corners of existing tasks. If some two pairs of corners are + * sufficiently close enough, it shifts the bounds of the new task and tries again. When it exhausts + * all possible shifts, it gives up and puts the task in the original position. + */ +class LaunchingTaskPositioner { + private static final String TAG = TAG_WITH_CLASS_NAME ? "LaunchingTaskPositioner" : TAG_AM; + + // Determines how close window frames/corners have to be to call them colliding. + private static final int BOUNDS_CONFLICT_MIN_DISTANCE = 4; + + // Task will receive dimensions based on available dimensions divided by this. + private static final int WINDOW_SIZE_DENOMINATOR = 2; + + // Task will receive margins based on available dimensions divided by this. + private static final int MARGIN_SIZE_DENOMINATOR = 4; + + // If task bounds collide with some other, we will step and try again until we find a good + // position. The step will be determined by using dimensions and dividing it by this. + private static final int STEP_DENOMINATOR = 16; + + // We always want to step by at least this. + private static final int MINIMAL_STEP = 1; + + // Used to indicate if positioning algorithm is allowed to restart from the beginning, when it + // reaches the end of stack bounds. + private static final boolean ALLOW_RESTART = true; + + private static final int SHIFT_POLICY_DIAGONAL_DOWN = 1; + private static final int SHIFT_POLICY_HORIZONTAL_RIGHT = 2; + private static final int SHIFT_POLICY_HORIZONTAL_LEFT = 3; + + private boolean mDefaultStartBoundsConfigurationSet = false; + private final Rect mAvailableRect = new Rect(); + private final Rect mTmpProposal = new Rect(); + private final Rect mTmpOriginal = new Rect(); + + private int mDefaultFreeformStartX; + private int mDefaultFreeformStartY; + private int mDefaultFreeformWidth; + private int mDefaultFreeformHeight; + private int mDefaultFreeformStepHorizontal; + private int mDefaultFreeformStepVertical; + private int mDisplayWidth; + private int mDisplayHeight; + + void setDisplay(Display display) { + Point size = new Point(); + display.getSize(size); + mDisplayWidth = size.x; + mDisplayHeight = size.y; + } + + void configure(Rect stackBounds) { + if (stackBounds == null) { + mAvailableRect.set(0, 0, mDisplayWidth, mDisplayHeight); + } else { + mAvailableRect.set(stackBounds); + } + int width = mAvailableRect.width(); + int height = mAvailableRect.height(); + mDefaultFreeformStartX = mAvailableRect.left + width / MARGIN_SIZE_DENOMINATOR; + mDefaultFreeformStartY = mAvailableRect.top + height / MARGIN_SIZE_DENOMINATOR; + mDefaultFreeformWidth = width / WINDOW_SIZE_DENOMINATOR; + mDefaultFreeformHeight = height / WINDOW_SIZE_DENOMINATOR; + mDefaultFreeformStepHorizontal = Math.max(width / STEP_DENOMINATOR, MINIMAL_STEP); + mDefaultFreeformStepVertical = Math.max(height / STEP_DENOMINATOR, MINIMAL_STEP); + mDefaultStartBoundsConfigurationSet = true; + } + + /** + * Tries to set task's bound in a way that it won't collide with any other task. By colliding + * we mean that two tasks have left-top corner very close to each other, so one might get + * obfuscated by the other one. + * + * @param task Task for which we want to find bounds that won't collide with other. + * @param tasks Existing tasks with which we don't want to collide. + * @param initialLayout Optional information from the client about how it would like to be sized + * and positioned. + */ + void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks, + @Nullable ActivityInfo.InitialLayout initialLayout) { + if (!mDefaultStartBoundsConfigurationSet) { + return; + } + if (initialLayout == null) { + positionCenter(task, tasks, mDefaultFreeformWidth, mDefaultFreeformHeight); + return; + } + int width = getFinalWidth(initialLayout); + int height = getFinalHeight(initialLayout); + int verticalGravity = initialLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK; + int horizontalGravity = initialLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; + if (verticalGravity == Gravity.TOP) { + if (horizontalGravity == Gravity.RIGHT) { + positionTopRight(task, tasks, width, height); + } else { + positionTopLeft(task, tasks, width, height); + } + } else if (verticalGravity == Gravity.BOTTOM) { + if (horizontalGravity == Gravity.RIGHT) { + positionBottomRight(task, tasks, width, height); + } else { + positionBottomLeft(task, tasks, width, height); + } + } else { + // Some fancy gravity setting that we don't support yet. We just put the activity in the + // center. + Slog.w(TAG, "Received unsupported gravity: " + initialLayout.gravity + + ", positioning in the center instead."); + positionCenter(task, tasks, width, height); + } + } + + private int getFinalWidth(ActivityInfo.InitialLayout initialLayout) { + int width = mDefaultFreeformWidth; + if (initialLayout.width > 0) { + width = initialLayout.width; + } + if (initialLayout.widthFraction > 0) { + width = (int) (mAvailableRect.width() * initialLayout.widthFraction); + } + return width; + } + + private int getFinalHeight(ActivityInfo.InitialLayout initialLayout) { + int height = mDefaultFreeformHeight; + if (initialLayout.height > 0) { + height = initialLayout.height; + } + if (initialLayout.heightFraction > 0) { + height = (int) (mAvailableRect.height() * initialLayout.heightFraction); + } + return height; + } + + private void positionBottomLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width, + int height) { + mTmpProposal.set(mAvailableRect.left, mAvailableRect.bottom - height, + mAvailableRect.left + width, mAvailableRect.bottom); + position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT); + } + + private void positionBottomRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width, + int height) { + mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.bottom - height, + mAvailableRect.right, mAvailableRect.bottom); + position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT); + } + + private void positionTopLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width, + int height) { + mTmpProposal.set(mAvailableRect.left, mAvailableRect.top, + mAvailableRect.left + width, mAvailableRect.top + height); + position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT); + } + + private void positionTopRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width, + int height) { + mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.top, + mAvailableRect.right, mAvailableRect.top + height); + position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT); + } + + private void positionCenter(TaskRecord task, ArrayList<TaskRecord> tasks, int width, + int height) { + mTmpProposal.set(mDefaultFreeformStartX, mDefaultFreeformStartY, + mDefaultFreeformStartX + width, mDefaultFreeformStartY + height); + position(task, tasks, mTmpProposal, ALLOW_RESTART, SHIFT_POLICY_DIAGONAL_DOWN); + } + + private void position(TaskRecord task, ArrayList<TaskRecord> tasks, Rect proposal, + boolean allowRestart, int shiftPolicy) { + mTmpOriginal.set(proposal); + boolean restarted = false; + while (boundsConflict(proposal, tasks)) { + // Unfortunately there is already a task at that spot, so we need to look for some + // other place. + shiftStartingPoint(proposal, shiftPolicy); + if (shiftedToFar(proposal, shiftPolicy)) { + // We don't want the task to go outside of the stack, because it won't look + // nice. Depending on the starting point we either restart, or immediately give up. + if (!allowRestart) { + proposal.set(mTmpOriginal); + break; + } + // We must have started not from the top. Let's restart from there because there + // might be some space there. + proposal.set(mAvailableRect.left, mAvailableRect.top, + mAvailableRect.left + proposal.width(), + mAvailableRect.top + proposal.height()); + restarted = true; + } + if (restarted && (proposal.left > mDefaultFreeformStartX + || proposal.top > mDefaultFreeformStartY)) { + // If we restarted and crossed the initial position, let's not struggle anymore. + // The user already must have ton of tasks visible, we can just smack the new + // one in the center. + proposal.set(mTmpOriginal); + break; + } + } + task.setInitialBounds(proposal); + } + + private boolean shiftedToFar(Rect start, int shiftPolicy) { + switch (shiftPolicy) { + case SHIFT_POLICY_HORIZONTAL_LEFT: + return start.left < mAvailableRect.left; + case SHIFT_POLICY_HORIZONTAL_RIGHT: + return start.right > mAvailableRect.right; + default: // SHIFT_POLICY_DIAGONAL_DOWN + return start.right > mAvailableRect.right || start.bottom > mAvailableRect.bottom; + } + } + + private void shiftStartingPoint(Rect posposal, int shiftPolicy) { + switch (shiftPolicy) { + case SHIFT_POLICY_HORIZONTAL_LEFT: + posposal.offset(-mDefaultFreeformStepHorizontal, 0); + break; + case SHIFT_POLICY_HORIZONTAL_RIGHT: + posposal.offset(mDefaultFreeformStepHorizontal, 0); + break; + default: // SHIFT_POLICY_DIAGONAL_DOWN: + posposal.offset(mDefaultFreeformStepHorizontal, mDefaultFreeformStepVertical); + break; + } + } + + private static boolean boundsConflict(Rect proposal, ArrayList<TaskRecord> tasks) { + for (int i = tasks.size() - 1; i >= 0; i--) { + TaskRecord task = tasks.get(i); + if (!task.mActivities.isEmpty() && task.mBounds != null) { + Rect bounds = task.mBounds; + if (closeLeftTopCorner(proposal, bounds) || closeRightTopCorner(proposal, bounds) + || closeLeftBottomCorner(proposal, bounds) + || closeRightBottomCorner(proposal, bounds)) { + return true; + } + } + } + return false; + } + + private static final boolean closeLeftTopCorner(Rect first, Rect second) { + return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE + && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE; + } + + private static final boolean closeRightTopCorner(Rect first, Rect second) { + return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE + && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE; + } + + private static final boolean closeLeftBottomCorner(Rect first, Rect second) { + return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE + && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE; + } + + private static final boolean closeRightBottomCorner(Rect first, Rect second) { + return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE + && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE; + } + + void reset() { + mDefaultStartBoundsConfigurationSet = false; + } +} diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index ed935a139f45..712b8cc9af6b 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -1217,6 +1217,14 @@ final class TaskRecord { return mLastNonFullscreenBounds; } + void setInitialBounds(Rect rect) { + if (mBounds == null) { + mBounds = new Rect(); + } + mBounds.set(rect); + mLastNonFullscreenBounds = mBounds; + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("userId="); pw.print(userId); pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid); diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java index 0be24f4c10ea..f82454a1d9f8 100644 --- a/services/core/java/com/android/server/camera/CameraService.java +++ b/services/core/java/com/android/server/camera/CameraService.java @@ -23,13 +23,17 @@ import android.content.IntentFilter; import android.content.pm.UserInfo; import android.hardware.ICameraService; import android.hardware.ICameraServiceProxy; +import android.nfc.INfcAdapter; import android.os.Handler; import android.os.IBinder; +import android.os.Binder; import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.UserManager; +import android.os.SystemProperties; import android.util.Slog; +import android.util.ArraySet; import com.android.server.ServiceThread; import com.android.server.SystemService; @@ -44,8 +48,10 @@ import java.util.Set; * * @hide */ -public class CameraService extends SystemService implements Handler.Callback { +public class CameraService extends SystemService + implements Handler.Callback, IBinder.DeathRecipient { private static final String TAG = "CameraService_proxy"; + private static final boolean DEBUG = false; /** * This must match the ICameraService.aidl definition @@ -58,6 +64,16 @@ public class CameraService extends SystemService implements Handler.Callback { public static final int NO_EVENT = 0; // NOOP public static final int USER_SWITCHED = 1; // User changed, argument is the new user handle + // State arguments to use with the notifyCameraState call from camera service: + public static final int CAMERA_STATE_OPEN = 0; + public static final int CAMERA_STATE_ACTIVE = 1; + public static final int CAMERA_STATE_IDLE = 2; + public static final int CAMERA_STATE_CLOSED = 3; + + // Flags arguments to NFC adapter to enable/disable NFC + public static final int DISABLE_POLLING_FLAGS = 0x1000; + public static final int ENABLE_POLLING_FLAGS = 0x0000; + // Handler message codes private static final int MSG_SWITCH_USER = 1; @@ -72,6 +88,17 @@ public class CameraService extends SystemService implements Handler.Callback { private Set<Integer> mEnabledCameraUsers; private int mLastUser; + private ICameraService mCameraServiceRaw; + + private final ArraySet<String> mActiveCameraIds = new ArraySet<>(); + + private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc"; + private static final String NFC_SERVICE_BINDER_NAME = "nfc"; + private static final IBinder nfcInterfaceToken = new Binder(); + + private final boolean mNotifyNfc; + private int mActiveCameraCount = 0; + private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -102,6 +129,14 @@ public class CameraService extends SystemService implements Handler.Callback { public void pingForUserUpdate() { notifySwitchWithRetries(30); } + + @Override + public void notifyCameraState(String cameraId, int newCameraState) { + String state = cameraStateToString(newCameraState); + if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " state now " + state); + + updateActivityCount(cameraId, newCameraState); + } }; public CameraService(Context context) { @@ -110,6 +145,9 @@ public class CameraService extends SystemService implements Handler.Callback { mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper(), this); + + mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0; + if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled")); } @Override @@ -161,13 +199,32 @@ public class CameraService extends SystemService implements Handler.Callback { } } + /** + * Handle the death of the native camera service + */ + @Override + public void binderDied() { + if (DEBUG) Slog.w(TAG, "Native camera service has died"); + synchronized(mLock) { + mCameraServiceRaw = null; + + // All cameras reset to idle on camera service death + boolean wasEmpty = mActiveCameraIds.isEmpty(); + mActiveCameraIds.clear(); + + if ( mNotifyNfc && !wasEmpty ) { + notifyNfcService(/*enablePolling*/ true); + } + } + } + private void switchUserLocked(int userHandle) { Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle); mLastUser = userHandle; if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) { // Some user handles have been added or removed, update mediaserver. mEnabledCameraUsers = currentUserHandles; - notifyMediaserver(USER_SWITCHED, currentUserHandles); + notifyMediaserverLocked(USER_SWITCHED, currentUserHandles); } } @@ -187,7 +244,7 @@ public class CameraService extends SystemService implements Handler.Callback { if (mEnabledCameraUsers == null) { return; } - if (notifyMediaserver(USER_SWITCHED, mEnabledCameraUsers)) { + if (notifyMediaserverLocked(USER_SWITCHED, mEnabledCameraUsers)) { retries = 0; } } @@ -199,19 +256,27 @@ public class CameraService extends SystemService implements Handler.Callback { RETRY_DELAY_TIME); } - private boolean notifyMediaserver(int eventType, Set<Integer> updatedUserHandles) { + private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) { // Forward the user switch event to the native camera service running in the mediaserver // process. - IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME); - if (cameraServiceBinder == null) { - Slog.w(TAG, "Could not notify mediaserver, camera service not available."); - return false; // Camera service not active, cannot evict user clients. - } + if (mCameraServiceRaw == null) { + IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME); + if (cameraServiceBinder == null) { + Slog.w(TAG, "Could not notify mediaserver, camera service not available."); + return false; // Camera service not active, cannot evict user clients. + } + try { + cameraServiceBinder.linkToDeath(this, /*flags*/ 0); + } catch (RemoteException e) { + Slog.w(TAG, "Could not link to death of native camera service"); + return false; + } - ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder); + mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder); + } try { - cameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles)); + mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles)); } catch (RemoteException e) { Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e); // Not much we can do if camera service is dead. @@ -220,6 +285,44 @@ public class CameraService extends SystemService implements Handler.Callback { return true; } + private void updateActivityCount(String cameraId, int newCameraState) { + synchronized(mLock) { + boolean wasEmpty = mActiveCameraIds.isEmpty(); + switch (newCameraState) { + case CAMERA_STATE_OPEN: + break; + case CAMERA_STATE_ACTIVE: + mActiveCameraIds.add(cameraId); + break; + case CAMERA_STATE_IDLE: + case CAMERA_STATE_CLOSED: + mActiveCameraIds.remove(cameraId); + break; + } + boolean isEmpty = mActiveCameraIds.isEmpty(); + if ( mNotifyNfc && (wasEmpty != isEmpty) ) { + notifyNfcService(isEmpty); + } + } + } + + private void notifyNfcService(boolean enablePolling) { + + IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME); + if (nfcServiceBinder == null) { + Slog.w(TAG, "Could not connect to NFC service to notify it of camera state"); + return; + } + INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder); + int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS; + if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags); + try { + nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null); + } catch (RemoteException e) { + Slog.w(TAG, "Could not notify NFC service, remote exception: " + e); + } + } + private static int[] toArray(Collection<Integer> c) { int len = c.size(); int[] ret = new int[len]; @@ -229,4 +332,15 @@ public class CameraService extends SystemService implements Handler.Callback { } return ret; } + + private static String cameraStateToString(int newCameraState) { + switch (newCameraState) { + case CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN"; + case CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE"; + case CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE"; + case CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED"; + default: break; + } + return "CAMERA_STATE_UNKNOWN"; + } } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 284618515487..3df3cd0b587b 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -146,7 +146,8 @@ public class Vpn { } catch (RemoteException e) { Log.wtf(TAG, "Problem registering observer", e); } - if (userHandle == UserHandle.USER_OWNER) { + // TODO: http://b/22950929 + if (userHandle == UserHandle.USER_SYSTEM) { // Owner's VPN also needs to handle restricted users mUserIntentReceiver = new BroadcastReceiver() { @Override diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index befa3116bc8c..932662fe72bc 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -764,6 +764,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe mHandler.post(new Runnable() { @Override public void run() { + MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0); startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted); } }); diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java index 57ca552d353e..5c60a610e392 100644 --- a/services/core/java/com/android/server/location/GpsLocationProvider.java +++ b/services/core/java/com/android/server/location/GpsLocationProvider.java @@ -2200,14 +2200,18 @@ public class GpsLocationProvider implements LocationProviderInterface { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { StringBuilder s = new StringBuilder(); - s.append(" mFixInterval=").append(mFixInterval).append("\n"); - s.append(" mDisableGps (battery saver mode)=").append(mDisableGps).append("\n"); - s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" ("); - if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED "); + s.append(" mFixInterval=").append(mFixInterval).append('\n'); + s.append(" mDisableGps (battery saver mode)=").append(mDisableGps).append('\n'); + s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)); + s.append(" ( "); + if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHEDULING "); if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB "); if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA "); if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT "); if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME "); + if (hasCapability(GPS_CAPABILITY_GEOFENCING)) s.append("GEOFENCING "); + if (hasCapability(GPS_CAPABILITY_MEASUREMENTS)) s.append("MEASUREMENTS "); + if (hasCapability(GPS_CAPABILITY_NAV_MESSAGES)) s.append("NAV_MESSAGES "); s.append(")\n"); s.append(native_get_internal_state()); diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 40956c1c5747..19d853851820 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -67,7 +67,7 @@ public class ConditionProviders extends ManagedServices { public void addSystemProvider(SystemConditionProviderService service) { mSystemConditionProviders.add(service); service.attachBase(mContext); - registerService(service.asInterface(), service.getComponent(), UserHandle.USER_OWNER); + registerService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM); } public Iterable<SystemConditionProviderService> getSystemProviders() { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4351798b1fc1..0c884f1558fa 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -716,7 +716,7 @@ public class NotificationManagerService extends SystemService { final IPackageManager pm = AppGlobals.getPackageManager(); final int enabled = pm.getApplicationEnabledSetting(pkgName, changeUserId != UserHandle.USER_ALL ? changeUserId : - UserHandle.USER_OWNER); + UserHandle.USER_SYSTEM); if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { cancelNotifications = false; @@ -1420,7 +1420,7 @@ public class NotificationManagerService extends SystemService { if (!r.isSeen()) { if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]); mAppUsageStats.reportEvent(r.sbn.getPackageName(), - userId == UserHandle.USER_ALL ? UserHandle.USER_OWNER + userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId, UsageEvents.Event.USER_INTERACTION); r.setSeen(); @@ -1701,7 +1701,8 @@ public class NotificationManagerService extends SystemService { @Override public byte[] getBackupPayload(int user) { if (DBG) Slog.d(TAG, "getBackupPayload u=" + user); - if (user != UserHandle.USER_OWNER) { + //TODO: http://b/22388012 + if (user != UserHandle.USER_SYSTEM) { Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user); return null; } @@ -1723,7 +1724,8 @@ public class NotificationManagerService extends SystemService { Slog.w(TAG, "applyRestore: no payload to restore for user " + user); return; } - if (user != UserHandle.USER_OWNER) { + //TODO: http://b/22388012 + if (user != UserHandle.USER_SYSTEM) { Slog.w(TAG, "applyRestore: cannot restore policy for user " + user); return; } diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index a089518c53b8..66381f5ac09a 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -148,7 +148,8 @@ public class RankingHelper implements RankingConfig { if (!TextUtils.isEmpty(name)) { if (forRestore) { try { - uid = pm.getPackageUid(name, UserHandle.USER_OWNER); + //TODO: http://b/22388012 + uid = pm.getPackageUid(name, UserHandle.USER_SYSTEM); } catch (NameNotFoundException e) { // noop } @@ -213,7 +214,8 @@ public class RankingHelper implements RankingConfig { final int N = mRecords.size(); for (int i = 0; i < N; i++) { final Record r = mRecords.valueAt(i); - if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_OWNER) { + //TODO: http://b/22388012 + if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) { continue; } out.startTag(null, TAG_PACKAGE); @@ -437,7 +439,8 @@ public class RankingHelper implements RankingConfig { final Record r = mRestoredWithoutUids.get(pkg); if (r != null) { try { - r.uid = pm.getPackageUid(r.pkg, UserHandle.USER_OWNER); + //TODO: http://b/22388012 + r.uid = pm.getPackageUid(r.pkg, UserHandle.USER_SYSTEM); mRestoredWithoutUids.remove(pkg); mRecords.put(recordKey(r.pkg, r.uid), r); updated = true; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 57d7758fb6aa..edd274bb1000 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -85,7 +85,7 @@ public class ZenModeHelper { private final Metrics mMetrics = new Metrics(); private int mZenMode; - private int mUser = UserHandle.USER_OWNER; + private int mUser = UserHandle.USER_SYSTEM; private ZenModeConfig mConfig; private AudioManagerInternal mAudioManager; private boolean mEffectsSuppressed; @@ -99,7 +99,7 @@ public class ZenModeHelper { appendDefaultScheduleRules(mDefaultConfig); appendDefaultEventRules(mDefaultConfig); mConfig = mDefaultConfig; - mConfigs.put(UserHandle.USER_OWNER, mConfig); + mConfigs.put(UserHandle.USER_SYSTEM, mConfig); mSettingsObserver = new SettingsObserver(mHandler); mSettingsObserver.observe(); mFiltering = new ZenModeFiltering(mContext); @@ -152,7 +152,7 @@ public class ZenModeHelper { } public void onUserSwitched(int user) { - if (mUser == user || user < UserHandle.USER_OWNER) return; + if (mUser == user || user < UserHandle.USER_SYSTEM) return; mUser = user; if (DEBUG) Log.d(TAG, "onUserSwitched u=" + user); ZenModeConfig config = mConfigs.get(user); @@ -165,7 +165,7 @@ public class ZenModeHelper { } public void onUserRemoved(int user) { - if (user < UserHandle.USER_OWNER) return; + if (user < UserHandle.USER_SYSTEM) return; if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user); mConfigs.remove(user); } @@ -265,7 +265,8 @@ public class ZenModeHelper { final ZenModeConfig config = ZenModeConfig.readXml(parser, mConfigMigration); if (config != null) { if (forRestore) { - if (config.user != UserHandle.USER_OWNER) { + //TODO: http://b/22388012 + if (config.user != UserHandle.USER_SYSTEM) { return; } config.manualRule = null; // don't restore the manual rule @@ -285,7 +286,8 @@ public class ZenModeHelper { public void writeXml(XmlSerializer out, boolean forBackup) throws IOException { final int N = mConfigs.size(); for (int i = 0; i < N; i++) { - if (forBackup && mConfigs.keyAt(i) != UserHandle.USER_OWNER) { + //TODO: http://b/22388012 + if (forBackup && mConfigs.keyAt(i) != UserHandle.USER_SYSTEM) { continue; } mConfigs.valueAt(i).writeXml(out); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 153bd3b94e80..a55911609c6d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -71,7 +71,7 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; -import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER; +import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT; import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; import static com.android.internal.util.ArrayUtils.appendInt; @@ -4949,18 +4949,25 @@ public class PackageManagerService extends IPackageManager.Stub { private ResolveInfo createForwardingResolveInfo(IntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); + long ident = Binder.clearCallingIdentity(); + boolean targetIsProfile; + try { + targetIsProfile = sUserManager.getUserInfo(targetUserId).isManagedProfile(); + } finally { + Binder.restoreCallingIdentity(ident); + } String className; - if (targetUserId == UserHandle.USER_OWNER) { - className = FORWARD_INTENT_TO_USER_OWNER; - } else { + if (targetIsProfile) { className = FORWARD_INTENT_TO_MANAGED_PROFILE; + } else { + className = FORWARD_INTENT_TO_PARENT; } ComponentName forwardingActivityComponentName = new ComponentName( mAndroidApplication.packageName, className); ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, sourceUserId); - if (targetUserId == UserHandle.USER_OWNER) { - forwardingActivityInfo.showUserIcon = UserHandle.USER_OWNER; + if (!targetIsProfile) { + forwardingActivityInfo.showUserIcon = targetUserId; forwardingResolveInfo.noResourceId = true; } forwardingResolveInfo.activityInfo = forwardingActivityInfo; @@ -6112,7 +6119,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // Give priority to system apps that listen for pre boot complete. Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); - ArraySet<String> pkgNames = getPackageNamesForIntent(intent); + ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM); for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { PackageParser.Package pkg = it.next(); if (pkgNames.contains(pkg.packageName)) { @@ -6147,7 +6154,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // Give priority to apps that listen for boot complete. intent = new Intent(Intent.ACTION_BOOT_COMPLETED); - pkgNames = getPackageNamesForIntent(intent); + pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM); for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { PackageParser.Package pkg = it.next(); if (pkgNames.contains(pkg.packageName)) { @@ -6218,11 +6225,11 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private ArraySet<String> getPackageNamesForIntent(Intent intent) { + private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) { List<ResolveInfo> ris = null; try { ris = AppGlobals.getPackageManager().queryIntentReceivers( - intent, null, 0, UserHandle.USER_OWNER); + intent, null, 0, userId); } catch (RemoteException e) { } ArraySet<String> pkgNames = new ArraySet<String>(); @@ -11845,8 +11852,9 @@ public class PackageManagerService extends IPackageManager.Stub { String pkgName = pkg.packageName; if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg); + // TODO: b/23350563 final boolean dataDirExists = Environment - .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_OWNER, pkgName).exists(); + .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_SYSTEM, pkgName).exists(); synchronized(mPackages) { if (mSettings.mRenamedPackages.containsKey(pkgName)) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index fe205e52d43d..4bfcb902fb65 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -133,7 +133,7 @@ public class UserManagerService extends IUserManager.Stub { private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms - // Maximum number of managed profiles permitted is 1. This cannot be increased + // Maximum number of managed profiles permitted per user is 1. This cannot be increased // without first making sure that the rest of the framework is prepared for it. private static final int MAX_MANAGED_PROFILES = 1; @@ -627,7 +627,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean canAddMoreManagedProfiles() { + public boolean canAddMoreManagedProfiles(int userId) { checkManageUsersPermission("check if more managed profiles can be added."); if (ActivityManager.isLowRamDeviceStatic()) { return false; @@ -636,10 +636,14 @@ public class UserManagerService extends IUserManager.Stub { PackageManager.FEATURE_MANAGED_USERS)) { return false; } + // Limit number of managed profiles that can be created + int managedProfilesCount = getProfiles(userId, true).size() - 1; + if (managedProfilesCount >= MAX_MANAGED_PROFILES) { + return false; + } synchronized(mPackagesLock) { - // Limit number of managed profiles that can be created - if (numberOfUsersOfTypeLocked(UserInfo.FLAG_MANAGED_PROFILE, true) - >= MAX_MANAGED_PROFILES) { + UserInfo userInfo = getUserInfoLocked(userId); + if (!userInfo.canHaveProfile()) { return false; } int usersCount = getAliveUsersExcludingGuestsCountLocked(); @@ -1238,10 +1242,6 @@ public class UserManagerService extends IUserManager.Stub { @Override public UserInfo createProfileForUser(String name, int flags, int userId) { checkManageUsersPermission("Only the system can create users"); - if (userId != UserHandle.USER_OWNER) { - Slog.w(LOG_TAG, "Only user owner can have profiles"); - return null; - } return createUserInternal(name, flags, userId); } @@ -1273,7 +1273,8 @@ public class UserManagerService extends IUserManager.Stub { parent = getUserInfoLocked(parentId); if (parent == null) return null; } - if (isManagedProfile && !canAddMoreManagedProfiles()) { + if (isManagedProfile && !canAddMoreManagedProfiles(parentId)) { + Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId); return null; } if (!isGuest && !isManagedProfile && isUserLimitReachedLocked()) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 3992a6ef17bf..e0da33f31c9d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2835,6 +2835,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } + } else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) { + if (down) { + if (repeatCount == 0) { + showKeyboardShortcutsMenu(); + } + } } else if (keyCode == KeyEvent.KEYCODE_ASSIST) { if (down) { if (repeatCount == 0) { @@ -3255,6 +3261,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void showKeyboardShortcutsMenu() { + try { + IStatusBarService statusbar = getStatusBarService(); + if (statusbar != null) { + statusbar.showKeyboardShortcutsMenu(); + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when showing keyboard shortcuts menu", e); + } + } + private void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHome) { mPreloadedRecentApps = false; // preloading no longer needs to be canceled try { diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java index da23f451a6b5..b935f5abc74b 100644 --- a/services/core/java/com/android/server/policy/StatusBarController.java +++ b/services/core/java/com/android/server/policy/StatusBarController.java @@ -72,7 +72,9 @@ public class StatusBarController extends BarController { if (statusbar != null) { long startTime = calculateStatusBarTransitionStartTime(openAnimation, closeAnimation); - statusbar.appTransitionStarting(startTime, TRANSITION_DURATION); + long duration = closeAnimation != null || openAnimation != null + ? TRANSITION_DURATION : 0; + statusbar.appTransitionStarting(startTime, duration); } } catch (RemoteException e) { Slog.e(mTag, "RemoteException when app transition is starting", e); diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java index c71b48fa8b18..8b3c036594c6 100644 --- a/services/core/java/com/android/server/policy/WindowOrientationListener.java +++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java @@ -24,10 +24,12 @@ import android.hardware.SensorManager; import android.os.Handler; import android.os.SystemClock; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Slog; import java.io.PrintWriter; import java.util.Arrays; +import java.util.List; /** * A special helper class used by the WindowManager @@ -52,8 +54,9 @@ public abstract class WindowOrientationListener { private SensorManager mSensorManager; private boolean mEnabled; private int mRate; + private String mSensorType; private Sensor mSensor; - private SensorEventListenerImpl mSensorEventListener; + private OrientationJudge mOrientationJudge; private int mCurrentRotation = -1; private final Object mLock = new Object(); @@ -67,7 +70,7 @@ public abstract class WindowOrientationListener { public WindowOrientationListener(Context context, Handler handler) { this(context, handler, SensorManager.SENSOR_DELAY_UI); } - + /** * Creates a new WindowOrientationListener. * @@ -84,11 +87,31 @@ public abstract class WindowOrientationListener { mHandler = handler; mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; - mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR - ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER); - if (mSensor != null) { - // Create listener only if sensors do exist - mSensorEventListener = new SensorEventListenerImpl(context); + + mSensorType = context.getResources().getString( + com.android.internal.R.string.config_orientationSensorType); + if (!TextUtils.isEmpty(mSensorType)) { + List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); + final int N = sensors.size(); + for (int i = 0; i < N; i++) { + Sensor sensor = sensors.get(i); + if (mSensorType.equals(sensor.getStringType())) { + mSensor = sensor; + break; + } + } + if (mSensor != null) { + mOrientationJudge = new OrientationSensorJudge(); + } + } + + if (mOrientationJudge == null) { + mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR + ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER); + if (mSensor != null) { + // Create listener only if sensors do exist + mOrientationJudge = new AccelSensorJudge(context); + } } } @@ -106,8 +129,8 @@ public abstract class WindowOrientationListener { if (LOG) { Slog.d(TAG, "WindowOrientationListener enabled"); } - mSensorEventListener.resetLocked(); - mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler); + mOrientationJudge.resetLocked(); + mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler); mEnabled = true; } } @@ -126,7 +149,7 @@ public abstract class WindowOrientationListener { if (LOG) { Slog.d(TAG, "WindowOrientationListener disabled"); } - mSensorManager.unregisterListener(mSensorEventListener); + mSensorManager.unregisterListener(mOrientationJudge); mEnabled = false; } } @@ -134,8 +157,8 @@ public abstract class WindowOrientationListener { public void onTouchStart() { synchronized (mLock) { - if (mSensorEventListener != null) { - mSensorEventListener.onTouchStartLocked(); + if (mOrientationJudge != null) { + mOrientationJudge.onTouchStartLocked(); } } } @@ -144,8 +167,8 @@ public abstract class WindowOrientationListener { long whenElapsedNanos = SystemClock.elapsedRealtimeNanos(); synchronized (mLock) { - if (mSensorEventListener != null) { - mSensorEventListener.onTouchEndLocked(whenElapsedNanos); + if (mOrientationJudge != null) { + mOrientationJudge.onTouchEndLocked(whenElapsedNanos); } } } @@ -172,7 +195,7 @@ public abstract class WindowOrientationListener { public int getProposedRotation() { synchronized (mLock) { if (mEnabled) { - return mSensorEventListener.getProposedRotationLocked(); + return mOrientationJudge.getProposedRotationLocked(); } return -1; } @@ -205,15 +228,77 @@ public abstract class WindowOrientationListener { prefix += " "; pw.println(prefix + "mEnabled=" + mEnabled); pw.println(prefix + "mCurrentRotation=" + mCurrentRotation); + pw.println(prefix + "mSensorType=" + mSensorType); pw.println(prefix + "mSensor=" + mSensor); pw.println(prefix + "mRate=" + mRate); - if (mSensorEventListener != null) { - mSensorEventListener.dumpLocked(pw, prefix); + if (mOrientationJudge != null) { + mOrientationJudge.dumpLocked(pw, prefix); } } } + abstract class OrientationJudge implements SensorEventListener { + // Number of nanoseconds per millisecond. + protected static final long NANOS_PER_MS = 1000000; + + // Number of milliseconds per nano second. + protected static final float MILLIS_PER_NANO = 0.000001f; + + // The minimum amount of time that must have elapsed since the screen was last touched + // before the proposed rotation can change. + protected static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS = + 500 * NANOS_PER_MS; + + /** + * Gets the proposed rotation. + * + * This method only returns a rotation if the orientation listener is certain + * of its proposal. If the rotation is indeterminate, returns -1. + * + * Should only be called when holding WindowOrientationListener lock. + * + * @return The proposed rotation, or -1 if unknown. + */ + public abstract int getProposedRotationLocked(); + + /** + * Notifies the orientation judge that the screen is being touched. + * + * Should only be called when holding WindowOrientationListener lock. + */ + public abstract void onTouchStartLocked(); + + /** + * Notifies the orientation judge that the screen is no longer being touched. + * + * Should only be called when holding WindowOrientationListener lock. + * + * @param whenElapsedNanos Given in the elapsed realtime nanos time base. + */ + public abstract void onTouchEndLocked(long whenElapsedNanos); + + /** + * Resets the state of the judge. + * + * Should only be called when holding WindowOrientationListener lock. + */ + public abstract void resetLocked(); + + /** + * Dumps internal state of the orientation judge. + * + * Should only be called when holding WindowOrientationListener lock. + */ + public abstract void dumpLocked(PrintWriter pw, String prefix); + + @Override + public abstract void onAccuracyChanged(Sensor sensor, int accuracy); + + @Override + public abstract void onSensorChanged(SensorEvent event); + } + /** * This class filters the raw accelerometer data and tries to detect actual changes in * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters, @@ -252,13 +337,10 @@ public abstract class WindowOrientationListener { * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for * signal processing background. */ - final class SensorEventListenerImpl implements SensorEventListener { + final class AccelSensorJudge extends OrientationJudge { // We work with all angles in degrees in this class. private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI); - // Number of nanoseconds per millisecond. - private static final long NANOS_PER_MS = 1000000; - // Indices into SensorEvent.values for the accelerometer sensor. private static final int ACCELEROMETER_DATA_X = 0; private static final int ACCELEROMETER_DATA_Y = 1; @@ -286,11 +368,6 @@ public abstract class WindowOrientationListener { private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS = 500 * NANOS_PER_MS; - // The minimum amount of time that must have elapsed since the screen was last touched - // before the proposed rotation can change. - private static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS = - 500 * NANOS_PER_MS; - // If the tilt angle remains greater than the specified angle for a minimum of // the specified time, then the device is deemed to be lying flat // (just chillin' on a table). @@ -434,7 +511,7 @@ public abstract class WindowOrientationListener { private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE]; private int mTiltHistoryIndex; - public SensorEventListenerImpl(Context context) { + public AccelSensorJudge(Context context) { // Load tilt tolerance configuration. int[] tiltTolerance = context.getResources().getIntArray( com.android.internal.R.array.config_autoRotationTiltTolerance); @@ -455,11 +532,15 @@ public abstract class WindowOrientationListener { } } + @Override public int getProposedRotationLocked() { return mProposedRotation; } + @Override public void dumpLocked(PrintWriter pw, String prefix) { + pw.println(prefix + "AccelSensorJudge"); + prefix += " "; pw.println(prefix + "mProposedRotation=" + mProposedRotation); pw.println(prefix + "mPredictedRotation=" + mPredictedRotation); pw.println(prefix + "mLastFilteredX=" + mLastFilteredX); @@ -689,6 +770,33 @@ public abstract class WindowOrientationListener { } } + @Override + public void onTouchStartLocked() { + mTouched = true; + } + + @Override + public void onTouchEndLocked(long whenElapsedNanos) { + mTouched = false; + mTouchEndedTimestampNanos = whenElapsedNanos; + } + + @Override + public void resetLocked() { + mLastFilteredTimestampNanos = Long.MIN_VALUE; + mProposedRotation = -1; + mFlatTimestampNanos = Long.MIN_VALUE; + mFlat = false; + mSwingTimestampNanos = Long.MIN_VALUE; + mSwinging = false; + mAccelerationTimestampNanos = Long.MIN_VALUE; + mAccelerating = false; + mOverhead = false; + clearPredictedRotationLocked(); + clearTiltHistoryLocked(); + } + + /** * Returns true if the tilt angle is acceptable for a given predicted rotation. */ @@ -787,20 +895,6 @@ public abstract class WindowOrientationListener { return true; } - private void resetLocked() { - mLastFilteredTimestampNanos = Long.MIN_VALUE; - mProposedRotation = -1; - mFlatTimestampNanos = Long.MIN_VALUE; - mFlat = false; - mSwingTimestampNanos = Long.MIN_VALUE; - mSwinging = false; - mAccelerationTimestampNanos = Long.MIN_VALUE; - mAccelerating = false; - mOverhead = false; - clearPredictedRotationLocked(); - clearTiltHistoryLocked(); - } - private void clearPredictedRotationLocked() { mPredictedRotation = -1; mPredictedRotationTimestampNanos = Long.MIN_VALUE; @@ -869,14 +963,138 @@ public abstract class WindowOrientationListener { private float remainingMS(long now, long until) { return now >= until ? 0 : (until - now) * 0.000001f; } + } - private void onTouchStartLocked() { - mTouched = true; + final class OrientationSensorJudge extends OrientationJudge { + private boolean mTouching; + private long mTouchEndedTimestampNanos = Long.MIN_VALUE; + private int mProposedRotation = -1; + private int mDesiredRotation = -1; + private boolean mRotationEvaluationScheduled; + + @Override + public int getProposedRotationLocked() { + return mProposedRotation; } - private void onTouchEndLocked(long whenElapsedNanos) { - mTouched = false; + @Override + public void onTouchStartLocked() { + mTouching = true; + } + + @Override + public void onTouchEndLocked(long whenElapsedNanos) { + mTouching = false; mTouchEndedTimestampNanos = whenElapsedNanos; + if (mDesiredRotation != mProposedRotation) { + final long now = SystemClock.elapsedRealtimeNanos(); + scheduleRotationEvaluationIfNecessaryLocked(now); + } + } + + + @Override + public void onSensorChanged(SensorEvent event) { + synchronized (mLock) { + mDesiredRotation = (int) event.values[0]; + evaluateRotationChangeLocked(); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { } + + @Override + public void dumpLocked(PrintWriter pw, String prefix) { + pw.println(prefix + "OrientationSensorJudge"); + prefix += " "; + pw.println(prefix + "mDesiredRotation=" + mDesiredRotation); + pw.println(prefix + "mProposedRotation=" + mProposedRotation); + pw.println(prefix + "mTouching=" + mTouching); + pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos); } + + @Override + public void resetLocked() { + mProposedRotation = -1; + mDesiredRotation = -1; + mTouching = false; + mTouchEndedTimestampNanos = Long.MIN_VALUE; + unscheduleRotationEvaluationLocked(); + } + + public void evaluateRotationChangeLocked() { + unscheduleRotationEvaluationLocked(); + if (mDesiredRotation == mProposedRotation) { + return; + } + final long now = SystemClock.elapsedRealtimeNanos(); + if (isDesiredRotationAcceptableLocked(now)) { + mProposedRotation = mDesiredRotation; + onProposedRotationChanged(mProposedRotation); + } else { + scheduleRotationEvaluationIfNecessaryLocked(now); + } + } + + private boolean isDesiredRotationAcceptableLocked(long now) { + if (mTouching) { + return false; + } + if (now < mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) { + return false; + } + return true; + } + + private void scheduleRotationEvaluationIfNecessaryLocked(long now) { + if (mRotationEvaluationScheduled || mDesiredRotation == mProposedRotation) { + if (LOG) { + Slog.d(TAG, "scheduleRotationEvaluationLocked: " + + "ignoring, an evaluation is already scheduled or is unnecessary."); + } + return; + } + if (mTouching) { + if (LOG) { + Slog.d(TAG, "scheduleRotationEvaluationLocked: " + + "ignoring, user is still touching the screen."); + } + return; + } + long timeOfNextPossibleRotationNanos = + mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS; + if (now >= timeOfNextPossibleRotationNanos) { + if (LOG) { + Slog.d(TAG, "scheduleRotationEvaluationLocked: " + + "ignoring, already past the next possible time of rotation."); + } + return; + } + // Use a delay instead of an absolute time since handlers are in uptime millis and we + // use elapsed realtime. + final long delayMs = + (long) Math.ceil((timeOfNextPossibleRotationNanos - now) * MILLIS_PER_NANO); + mHandler.postDelayed(mRotationEvaluator, delayMs); + mRotationEvaluationScheduled = true; + } + + private void unscheduleRotationEvaluationLocked() { + if (!mRotationEvaluationScheduled) { + return; + } + mHandler.removeCallbacks(mRotationEvaluator); + mRotationEvaluationScheduled = false; + } + + private Runnable mRotationEvaluator = new Runnable() { + @Override + public void run() { + synchronized (mLock) { + mRotationEvaluationScheduled = false; + evaluateRotationChangeLocked(); + } + } + }; } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 2a817eac3a09..8663254df10e 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -494,6 +494,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override + public void showKeyboardShortcutsMenu() { + if (mBar != null) { + try { + mBar.showKeyboardShortcutsMenu(); + } catch (RemoteException ex) {} + } + } + + @Override public void setCurrentUser(int newUserId) { if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId); mCurrentUserId = newUserId; diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index b9e8851d53f1..f9d29ac14389 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -19,6 +19,7 @@ package com.android.server.trust; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker; import com.android.server.SystemService; import org.xmlpull.v1.XmlPullParser; @@ -59,6 +60,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.Slog; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.util.Xml; import android.view.IWindowManager; import android.view.WindowManagerGlobal; @@ -96,16 +98,15 @@ public class TrustManagerService extends SystemService { private static final int MSG_UNREGISTER_LISTENER = 2; private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3; private static final int MSG_ENABLED_AGENTS_CHANGED = 4; - private static final int MSG_REQUIRE_CREDENTIAL_ENTRY = 5; private static final int MSG_KEYGUARD_SHOWING_CHANGED = 6; private static final int MSG_START_USER = 7; private static final int MSG_CLEANUP_USER = 8; private static final int MSG_SWITCH_USER = 9; - private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>(); - private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>(); + private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>(); + private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>(); private final Receiver mReceiver = new Receiver(); - private final SparseBooleanArray mUserHasAuthenticated = new SparseBooleanArray(); + /* package */ final TrustArchive mArchive = new TrustArchive(); private final Context mContext; private final LockPatternUtils mLockPatternUtils; @@ -118,9 +119,6 @@ public class TrustManagerService extends SystemService { @GuardedBy("mDeviceLockedForUser") private final SparseBooleanArray mDeviceLockedForUser = new SparseBooleanArray(); - @GuardedBy("mUserHasAuthenticatedSinceBoot") - private final SparseBooleanArray mUserHasAuthenticatedSinceBoot = new SparseBooleanArray(); - private boolean mTrustAgentsCanRun = false; private int mCurrentUser = UserHandle.USER_SYSTEM; @@ -146,6 +144,7 @@ public class TrustManagerService extends SystemService { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true); mReceiver.register(mContext); + mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { mTrustAgentsCanRun = true; refreshAgentList(UserHandle.USER_ALL); @@ -230,7 +229,7 @@ public class TrustManagerService extends SystemService { if (!userInfo.supportsSwitchToByUser()) continue; if (!mActivityManager.isUserRunning(userInfo.id)) continue; if (!lockPatternUtils.isSecure(userInfo.id)) continue; - if (!getUserHasAuthenticated(userInfo.id)) continue; + if (!mStrongAuthTracker.isTrustAllowedForUser(userInfo.id)) continue; DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager(); int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id); final boolean disableTrustAgents = @@ -509,7 +508,7 @@ public class TrustManagerService extends SystemService { // Agent dispatch and aggregation private boolean aggregateIsTrusted(int userId) { - if (!getUserHasAuthenticated(userId)) { + if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) { return false; } for (int i = 0; i < mActiveAgents.size(); i++) { @@ -524,7 +523,7 @@ public class TrustManagerService extends SystemService { } private boolean aggregateIsTrustManaged(int userId) { - if (!getUserHasAuthenticated(userId)) { + if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) { return false; } for (int i = 0; i < mActiveAgents.size(); i++) { @@ -545,54 +544,6 @@ public class TrustManagerService extends SystemService { info.agent.onUnlockAttempt(successful); } } - - if (successful) { - updateUserHasAuthenticated(userId); - } - } - - private void updateUserHasAuthenticated(int userId) { - boolean changed = setUserHasAuthenticated(userId); - if (changed) { - refreshAgentList(userId); - } - } - - private boolean getUserHasAuthenticated(int userId) { - return mUserHasAuthenticated.get(userId); - } - - /** - * @return whether the value has changed - */ - private boolean setUserHasAuthenticated(int userId) { - if (!mUserHasAuthenticated.get(userId)) { - mUserHasAuthenticated.put(userId, true); - synchronized (mUserHasAuthenticatedSinceBoot) { - mUserHasAuthenticatedSinceBoot.put(userId, true); - } - return true; - } - return false; - } - - private void clearUserHasAuthenticated(int userId) { - if (userId == UserHandle.USER_ALL) { - mUserHasAuthenticated.clear(); - } else { - mUserHasAuthenticated.put(userId, false); - } - } - - private boolean getUserHasAuthenticatedSinceBoot(int userId) { - synchronized (mUserHasAuthenticatedSinceBoot) { - return mUserHasAuthenticatedSinceBoot.get(userId); - } - } - - private void requireCredentialEntry(int userId) { - clearUserHasAuthenticated(userId); - refreshAgentList(userId); } // Listeners @@ -681,17 +632,6 @@ public class TrustManagerService extends SystemService { } @Override - public void reportRequireCredentialEntry(int userId) throws RemoteException { - enforceReportPermission(); - if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_SYSTEM) { - mHandler.obtainMessage(MSG_REQUIRE_CREDENTIAL_ENTRY, userId, 0).sendToTarget(); - } else { - throw new IllegalArgumentException( - "userId must be an explicit user id or USER_ALL"); - } - } - - @Override public void reportKeyguardShowingChanged() throws RemoteException { enforceReportPermission(); // coalesce refresh messages. @@ -734,18 +674,6 @@ public class TrustManagerService extends SystemService { } } - @Override - public boolean hasUserAuthenticatedSinceBoot(int userId) throws RemoteException { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, null); - long token = Binder.clearCallingIdentity(); - try { - return getUserHasAuthenticatedSinceBoot(userId); - } finally { - Binder.restoreCallingIdentity(token); - } - } - private void enforceReportPermission() { mContext.enforceCallingOrSelfPermission( Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events"); @@ -794,9 +722,8 @@ public class TrustManagerService extends SystemService { fout.print(": trusted=" + dumpBool(aggregateIsTrusted(user.id))); fout.print(", trustManaged=" + dumpBool(aggregateIsTrustManaged(user.id))); fout.print(", deviceLocked=" + dumpBool(isDeviceLockedInner(user.id))); - fout.print(", hasAuthenticated=" + dumpBool(getUserHasAuthenticated(user.id))); - fout.print(", hasAuthenticatedSinceBoot=" - + dumpBool(getUserHasAuthenticatedSinceBoot(user.id))); + fout.print(", strongAuthRequired=" + dumpHex( + mStrongAuthTracker.getStrongAuthForUser(user.id))); fout.println(); fout.println(" Enabled agents:"); boolean duplicateSimpleNames = false; @@ -831,6 +758,10 @@ public class TrustManagerService extends SystemService { private String dumpBool(boolean b) { return b ? "1" : "0"; } + + private String dumpHex(int i) { + return "0x" + Integer.toHexString(i); + } }; private int resolveProfileParent(int userId) { @@ -864,9 +795,6 @@ public class TrustManagerService extends SystemService { // This is also called when the security mode of a user changes. refreshDeviceLockedForUser(UserHandle.USER_ALL); break; - case MSG_REQUIRE_CREDENTIAL_ENTRY: - requireCredentialEntry(msg.arg1); - break; case MSG_KEYGUARD_SHOWING_CHANGED: refreshDeviceLockedForUser(mCurrentUser); break; @@ -900,6 +828,13 @@ public class TrustManagerService extends SystemService { } }; + private final StrongAuthTracker mStrongAuthTracker = new StrongAuthTracker() { + @Override + public void onStrongAuthRequiredChanged(int userId) { + refreshAgentList(userId); + } + }; + private class Receiver extends BroadcastReceiver { @Override @@ -908,8 +843,6 @@ public class TrustManagerService extends SystemService { if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) { refreshAgentList(getSendingUserId()); updateDevicePolicyFeatures(); - } else if (Intent.ACTION_USER_PRESENT.equals(action)) { - updateUserHasAuthenticated(getSendingUserId()); } else if (Intent.ACTION_USER_ADDED.equals(action)) { int userId = getUserId(intent); if (userId > 0) { @@ -918,7 +851,6 @@ public class TrustManagerService extends SystemService { } else if (Intent.ACTION_USER_REMOVED.equals(action)) { int userId = getUserId(intent); if (userId > 0) { - mUserHasAuthenticated.delete(userId); synchronized (mUserIsTrusted) { mUserIsTrusted.delete(userId); } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 222945c2f594..e87dcdea77e6 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH; import com.android.server.input.InputApplicationHandle; import com.android.server.input.InputWindowHandle; import com.android.server.wm.WindowManagerService.DragInputEventReceiver; @@ -413,7 +414,7 @@ class DragState { continue; } - child.getTaskBounds(mTmpRect); + child.getTaskBounds(mTmpRect, !BOUNDS_FOR_TOUCH); if (!mTmpRect.contains(x, y)) { // outside of this window's activity stack == don't tell about drags continue; diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 65f26c1c0afa..b3244ffb980f 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH; import android.app.ActivityManagerNative; import android.graphics.Rect; import android.os.RemoteException; @@ -177,7 +178,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { if (modal && child.mAppToken != null) { // Limit the outer touch to the activity stack region. flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; - child.getTaskBounds(mTmpRect); + child.getTaskBounds(mTmpRect, BOUNDS_FOR_TOUCH); inputWindowHandle.touchableRegion.set(mTmpRect); } else { // Not modal or full screen modal diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index b871d7f0f501..d0962f46ddcf 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -98,6 +99,8 @@ class WallpaperController { private static final int WALLPAPER_DRAW_TIMEOUT = 2; private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; + private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult(); + public WallpaperController(WindowManagerService service) { mService = service; } @@ -466,39 +469,24 @@ class WallpaperController { } } - boolean adjustWallpaperWindows() { - mService.mInnerFields.mWallpaperMayChange = false; - boolean targetChanged = false; - - // TODO(multidisplay): Wallpapers on main screen only. - final DisplayInfo displayInfo = mService.getDefaultDisplayContentLocked().getDisplayInfo(); - final int dw = displayInfo.logicalWidth; - final int dh = displayInfo.logicalHeight; + private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) { final WindowAnimator winAnimator = mService.mAnimator; - - // First find top-most window that has asked to be on top of the - // wallpaper; all wallpapers go behind it. - final WindowList windows = mService.getDefaultWindowListLocked(); - int N = windows.size(); + result.reset(); WindowState w = null; - WindowState foundW = null; - int foundI = 0; - WindowState topCurW = null; - int topCurI = 0; int windowDetachedI = -1; - int i = N; - while (i > 0) { - i--; + boolean resetTopWallpaper = false; + boolean inFreeformSpace = false; + for (int i = windows.size() - 1; i >= 0; i--) { w = windows.get(i); if ((w.mAttrs.type == TYPE_WALLPAPER)) { - if (topCurW == null) { - topCurW = w; - topCurI = i; + if (result.topWallpaper == null || resetTopWallpaper) { + result.setTopWallpaper(w, i); + resetTopWallpaper = false; } continue; } - topCurW = null; + resetTopWallpaper = true; if (w != winAnimator.mWindowDetachedWallpaper && w.mAppToken != null) { // If this window's app token is hidden and not animating, // it is of no interest to us. @@ -511,23 +499,24 @@ class WallpaperController { if (DEBUG_WALLPAPER) Slog.v(TAG, "Win #" + i + " " + w + ": isOnScreen=" + w.isOnScreen() + " mDrawState=" + w.mWinAnimator.mDrawState); - // If the app is executing an animation because the keyguard is going away, keep the - // wallpaper during the animation so it doesn't flicker out. - final boolean hasWallpaper = (w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 - || (w.mAppToken != null - && w.mWinAnimator.mKeyguardGoingAwayAnimation); + if (!inFreeformSpace) { + TaskStack stack = w.getStack(); + inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID; + } + + // If the app is executing an animation because the keyguard is going away, + // keep the wallpaper during the animation so it doesn't flicker out. + final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 + || (w.mAppToken != null && w.mWinAnimator.mKeyguardGoingAwayAnimation); if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) { - if (DEBUG_WALLPAPER) Slog.v(TAG, - "Found wallpaper target: #" + i + "=" + w); - foundW = w; - foundI = i; + if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w); + result.setWallpaperTarget(w, i); if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) { - // The current wallpaper target is animating, so we'll - // look behind it for another possible target and figure - // out what is going on below. - if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w - + ": token animating, looking behind."); + // The current wallpaper target is animating, so we'll look behind it for + // another possible target and figure out what is going on later. + if (DEBUG_WALLPAPER) Slog.v(TAG, + "Win " + w + ": token animating, looking behind."); continue; } break; @@ -536,76 +525,78 @@ class WallpaperController { } } - if (foundW == null && windowDetachedI >= 0) { + if (result.wallpaperTarget == null && windowDetachedI >= 0) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, - "Found animating detached wallpaper activity: #" + i + "=" + w); - foundW = w; - foundI = windowDetachedI; + "Found animating detached wallpaper activity: #" + windowDetachedI + "=" + w); + result.setWallpaperTarget(w, windowDetachedI); + } + if (result.wallpaperTarget == null && inFreeformSpace) { + // In freeform mode we set the wallpaper as its own target, so we don't need an + // additional window to make it visible. + result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex); } + } - if (mWallpaperTarget != foundW - && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != foundW)) { - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "New wallpaper target: " + foundW - + " oldTarget: " + mWallpaperTarget); - } + private boolean updateWallpaperWindowsTarget( + WindowList windows, FindWallpaperTargetResult result) { + + boolean targetChanged = false; + WindowState wallpaperTarget = result.wallpaperTarget; + int wallpaperTargetIndex = result.wallpaperTargetIndex; + + if (mWallpaperTarget != wallpaperTarget + && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != wallpaperTarget)) { + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "New wallpaper target: " + wallpaperTarget + " oldTarget: " + mWallpaperTarget); mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; WindowState oldW = mWallpaperTarget; - mWallpaperTarget = foundW; + mWallpaperTarget = wallpaperTarget; targetChanged = true; - // Now what is happening... if the current and new targets are - // animating, then we are in our super special mode! - if (foundW != null && oldW != null) { + // Now what is happening... if the current and new targets are animating, + // then we are in our super special mode! + if (wallpaperTarget != null && oldW != null) { boolean oldAnim = oldW.isAnimatingLw(); - boolean foundAnim = foundW.isAnimatingLw(); - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "New animation: " + foundAnim - + " old animation: " + oldAnim); - } + boolean foundAnim = wallpaperTarget.isAnimatingLw(); + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "New animation: " + foundAnim + " old animation: " + oldAnim); if (foundAnim && oldAnim) { int oldI = windows.indexOf(oldW); - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "New i: " + foundI + " old i: " + oldI); - } + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "New i: " + wallpaperTargetIndex + " old i: " + oldI); if (oldI >= 0) { - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "Animating wallpapers: old#" + oldI - + "=" + oldW + "; new#" + foundI - + "=" + foundW); - } + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Animating wallpapers: old#" + oldI + "=" + oldW + "; new#" + + wallpaperTargetIndex + "=" + wallpaperTarget); // Set the new target correctly. - if (foundW.mAppToken != null && foundW.mAppToken.hiddenRequested) { - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "Old wallpaper still the target."); - } + if (wallpaperTarget.mAppToken != null + && wallpaperTarget.mAppToken.hiddenRequested) { + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Old wallpaper still the target."); mWallpaperTarget = oldW; - foundW = oldW; - foundI = oldI; + wallpaperTarget = oldW; + wallpaperTargetIndex = oldI; } - // Now set the upper and lower wallpaper targets - // correctly, and make sure that we are positioning - // the wallpaper below the lower. - else if (foundI > oldI) { + // Now set the upper and lower wallpaper targets correctly, + // and make sure that we are positioning the wallpaper below the lower. + else if (wallpaperTargetIndex > oldI) { // The new target is on top of the old one. - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "Found target above old target."); - } - mUpperWallpaperTarget = foundW; + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Found target above old target."); + mUpperWallpaperTarget = wallpaperTarget; mLowerWallpaperTarget = oldW; - foundW = oldW; - foundI = oldI; + wallpaperTarget = oldW; + wallpaperTargetIndex = oldI; } else { // The new target is below the old one. - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "Found target below old target."); - } + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Found target below old target."); mUpperWallpaperTarget = oldW; - mLowerWallpaperTarget = foundW; + mLowerWallpaperTarget = wallpaperTarget; } } } @@ -614,29 +605,36 @@ class WallpaperController { } else if (mLowerWallpaperTarget != null) { // Is it time to stop animating? if (!mLowerWallpaperTarget.isAnimatingLw() || !mUpperWallpaperTarget.isAnimatingLw()) { - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "No longer animating wallpaper targets!"); - } + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "No longer animating wallpaper targets!"); mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; - mWallpaperTarget = foundW; + mWallpaperTarget = wallpaperTarget; targetChanged = true; } } - boolean visible = foundW != null; + result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex); + return targetChanged; + } + + boolean updateWallpaperWindowsTargetByLayer( + WindowList windows, FindWallpaperTargetResult result) { + + WindowState wallpaperTarget = result.wallpaperTarget; + int wallpaperTargetIndex = result.wallpaperTargetIndex; + boolean visible = wallpaperTarget != null; + if (visible) { - // The window is visible to the compositor... but is it visible - // to the user? That is what the wallpaper cares about. - visible = isWallpaperVisible(foundW); + // The window is visible to the compositor...but is it visible to the user? + // That is what the wallpaper cares about. + visible = isWallpaperVisible(wallpaperTarget); if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible); - // If the wallpaper target is animating, we may need to copy - // its layer adjustment. Only do this if we are not transfering - // between two wallpaper targets. + // If the wallpaper target is animating, we may need to copy its layer adjustment. + // Only do this if we are not transferring between two wallpaper targets. mWallpaperAnimLayerAdjustment = - (mLowerWallpaperTarget == null && foundW.mAppToken != null) - ? foundW.mAppToken.mAppAnimator.animLayerAdjustment : 0; + (mLowerWallpaperTarget == null && wallpaperTarget.mAppToken != null) + ? wallpaperTarget.mAppToken.mAppAnimator.animLayerAdjustment : 0; final int maxLayer = (mService.mPolicy.getMaxWallpaperLayer() * TYPE_LAYER_MULTIPLIER) + TYPE_LAYER_OFFSET; @@ -645,52 +643,37 @@ class WallpaperController { // need to be sure to also be behind any of its attached windows, // AND any starting window associated with it, AND below the // maximum layer the policy allows for wallpapers. - while (foundI > 0) { - WindowState wb = windows.get(foundI - 1); + while (wallpaperTargetIndex > 0) { + WindowState wb = windows.get(wallpaperTargetIndex - 1); if (wb.mBaseLayer < maxLayer && - wb.mAttachedWindow != foundW && - (foundW.mAttachedWindow == null || - wb.mAttachedWindow != foundW.mAttachedWindow) && - (wb.mAttrs.type != TYPE_APPLICATION_STARTING || - foundW.mToken == null || wb.mToken != foundW.mToken)) { + wb.mAttachedWindow != wallpaperTarget && + (wallpaperTarget.mAttachedWindow == null || + wb.mAttachedWindow != wallpaperTarget.mAttachedWindow) && + (wb.mAttrs.type != TYPE_APPLICATION_STARTING + || wallpaperTarget.mToken == null + || wb.mToken != wallpaperTarget.mToken)) { // This window is not related to the previous one in any // interesting way, so stop here. break; } - foundW = wb; - foundI--; + wallpaperTarget = wb; + wallpaperTargetIndex--; } } else { if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target"); } - if (foundW == null && topCurW != null) { - // There is no wallpaper target, so it goes at the bottom. - // We will assume it is the same place as last time, if known. - foundW = topCurW; - foundI = topCurI+1; - } else { - // Okay i is the position immediately above the wallpaper. Look at - // what is below it for later. - foundW = foundI > 0 ? windows.get(foundI - 1) : null; - } + result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex); + return visible; + } - if (visible) { - if (mWallpaperTarget.mWallpaperX >= 0) { - mLastWallpaperX = mWallpaperTarget.mWallpaperX; - mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; - } - if (mWallpaperTarget.mWallpaperY >= 0) { - mLastWallpaperY = mWallpaperTarget.mWallpaperY; - mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; - } - if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { - mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX; - } - if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { - mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY; - } - } + boolean updateWallpaperWindowsPlacement(WindowList windows, + WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible) { + + // TODO(multidisplay): Wallpapers on main screen only. + final DisplayInfo displayInfo = mService.getDefaultDisplayContentLocked().getDisplayInfo(); + final int dw = displayInfo.logicalWidth; + final int dh = displayInfo.logicalHeight; // Start stepping backwards from here, ensuring that our wallpaper windows // are correctly placed. @@ -722,41 +705,40 @@ class WallpaperController { + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer); // First, if this window is at the current index, then all is well. - if (wallpaper == foundW) { - foundI--; - foundW = foundI > 0 ? windows.get(foundI - 1) : null; + if (wallpaper == wallpaperTarget) { + wallpaperTargetIndex--; + wallpaperTarget = wallpaperTargetIndex > 0 + ? windows.get(wallpaperTargetIndex - 1) : null; continue; } // The window didn't match... the current wallpaper window, - // wherever it is, is in the wrong place, so make sure it is - // not in the list. + // wherever it is, is in the wrong place, so make sure it is not in the list. int oldIndex = windows.indexOf(wallpaper); if (oldIndex >= 0) { - if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at " - + oldIndex + ": " + wallpaper); + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, + "Wallpaper removing at " + oldIndex + ": " + wallpaper); windows.remove(oldIndex); mService.mWindowsChanged = true; - if (oldIndex < foundI) { - foundI--; + if (oldIndex < wallpaperTargetIndex) { + wallpaperTargetIndex--; } } // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost // layer. For keyguard over wallpaper put the wallpaper under the keyguard. int insertionIndex = 0; - if (visible && foundW != null) { - final int type = foundW.mAttrs.type; - final int privateFlags = foundW.mAttrs.privateFlags; + if (visible && wallpaperTarget != null) { + final int type = wallpaperTarget.mAttrs.type; + final int privateFlags = wallpaperTarget.mAttrs.privateFlags; if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 || type == TYPE_KEYGUARD_SCRIM) { - insertionIndex = windows.indexOf(foundW); + insertionIndex = windows.indexOf(wallpaperTarget); } } - if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) { - Slog.v(TAG, "Moving wallpaper " + wallpaper - + " from " + oldIndex + " to " + insertionIndex); - } + if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "Moving wallpaper " + wallpaper + + " from " + oldIndex + " to " + insertionIndex); windows.add(insertionIndex, wallpaper); mService.mWindowsChanged = true; @@ -764,6 +746,53 @@ class WallpaperController { } } + return changed; + } + + boolean adjustWallpaperWindows() { + mService.mInnerFields.mWallpaperMayChange = false; + + final WindowList windows = mService.getDefaultWindowListLocked(); + // First find top-most window that has asked to be on top of the wallpaper; + // all wallpapers go behind it. + findWallpaperTarget(windows, mFindResults); + final boolean targetChanged = updateWallpaperWindowsTarget(windows, mFindResults); + final boolean visible = updateWallpaperWindowsTargetByLayer(windows, mFindResults); + WindowState wallpaperTarget = mFindResults.wallpaperTarget; + int wallpaperTargetIndex = mFindResults.wallpaperTargetIndex; + + if (wallpaperTarget == null && mFindResults.topWallpaper != null) { + // There is no wallpaper target, so it goes at the bottom. + // We will assume it is the same place as last time, if known. + wallpaperTarget = mFindResults.topWallpaper; + wallpaperTargetIndex = mFindResults.topWallpaperIndex + 1; + } else { + // Okay i is the position immediately above the wallpaper. + // Look at what is below it for later. + wallpaperTarget = wallpaperTargetIndex > 0 + ? windows.get(wallpaperTargetIndex - 1) : null; + } + + if (visible) { + if (mWallpaperTarget.mWallpaperX >= 0) { + mLastWallpaperX = mWallpaperTarget.mWallpaperX; + mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; + } + if (mWallpaperTarget.mWallpaperY >= 0) { + mLastWallpaperY = mWallpaperTarget.mWallpaperY; + mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; + } + if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { + mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX; + } + if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { + mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY; + } + } + + final boolean changed = updateWallpaperWindowsPlacement( + windows, wallpaperTarget, wallpaperTargetIndex, visible); + if (targetChanged && DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget + " lower=" + mLowerWallpaperTarget + " upper=" + mUpperWallpaperTarget); @@ -859,4 +888,29 @@ class WallpaperController { } } } + + /** Helper class for storing the results of a wallpaper target find operation. */ + final private static class FindWallpaperTargetResult { + int topWallpaperIndex = 0; + WindowState topWallpaper = null; + int wallpaperTargetIndex = 0; + WindowState wallpaperTarget = null; + + void setTopWallpaper(WindowState win, int index) { + topWallpaper = win; + topWallpaperIndex = index; + } + + void setWallpaperTarget(WindowState win, int index) { + wallpaperTarget = win; + wallpaperTargetIndex = index; + } + + void reset() { + topWallpaperIndex = 0; + topWallpaper = null; + wallpaperTargetIndex = 0; + wallpaperTarget = null; + } + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7e7d009a8675..7b4c2a0c65e8 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -114,6 +114,7 @@ import android.view.WindowManagerPolicy.PointerEventListener; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH; import com.android.internal.app.IAssistScreenshotReceiver; import com.android.internal.app.IBatteryStats; import com.android.internal.util.FastPrintWriter; @@ -597,6 +598,7 @@ public class WindowManagerService extends IWindowManager.Stub final InputManagerService mInputManager; final DisplayManagerInternal mDisplayManagerInternal; final DisplayManager mDisplayManager; + final Display[] mDisplays; // Who is holding the screen on. Session mHoldingScreenOn; @@ -870,8 +872,8 @@ public class WindowManagerService extends IWindowManager.Stub mFxSession = new SurfaceSession(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); - Display[] displays = mDisplayManager.getDisplays(); - for (Display display : displays) { + mDisplays = mDisplayManager.getDisplays(); + for (Display display : mDisplays) { createDisplayContentLocked(display); } @@ -5633,7 +5635,7 @@ public class WindowManagerService extends IWindowManager.Stub int right = wf.right - cr.right; int bottom = wf.bottom - cr.bottom; frame.union(left, top, right, bottom); - ws.getTaskBounds(stackBounds); + ws.getTaskBounds(stackBounds, !BOUNDS_FOR_TOUCH); if (!frame.intersect(stackBounds)) { // Set frame empty if there's no intersection. frame.setEmpty(); @@ -7003,7 +7005,9 @@ public class WindowManagerService extends IWindowManager.Stub } public void displayReady() { - displayReady(Display.DEFAULT_DISPLAY); + for (Display display : mDisplays) { + displayReady(display.getDisplayId()); + } synchronized(mWindowMap) { final DisplayContent displayContent = getDefaultDisplayContentLocked(); @@ -8553,312 +8557,322 @@ public class WindowManagerService extends IWindowManager.Stub * @return bitmap indicating if another pass through layout must be made. */ public int handleAppTransitionReadyLocked(WindowList windows) { - int changes = 0; - int i; int appsCount = mOpeningApps.size(); - boolean goodToGo = true; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Checking " + appsCount + " opening apps (frozen=" - + mDisplayFrozen + " timeout=" - + mAppTransition.isTimeout() + ")..."); - if (!mAppTransition.isTimeout()) { - for (i = 0; i < appsCount && goodToGo; i++) { - AppWindowToken wtoken = mOpeningApps.valueAt(i); - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Check opening app=" + wtoken + ": allDrawn=" - + wtoken.allDrawn + " startingDisplayed=" - + wtoken.startingDisplayed + " startingMoved=" - + wtoken.startingMoved); - if (!wtoken.allDrawn && !wtoken.startingDisplayed - && !wtoken.startingMoved) { - goodToGo = false; - } - } - - if (goodToGo && mWallpaperControllerLocked.isWallpaperVisible()) { - goodToGo &= mWallpaperControllerLocked.wallpaperTransitionReady(); - } + if (!checkIfTransitionGoodToGo(appsCount)) { + return 0; } - if (goodToGo) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); - int transit = mAppTransition.getAppTransition(); - if (mSkipAppTransitionAnimation) { - transit = AppTransition.TRANSIT_UNSET; - } - mSkipAppTransitionAnimation = false; - mNoAnimationNotifyOnTransitionFinished.clear(); - - mH.removeMessages(H.APP_TRANSITION_TIMEOUT); - - rebuildAppWindowListLocked(); - - // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper - final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); - final WindowState oldWallpaper = mWallpaperControllerLocked.isWallpaperTargetAnimating() - ? null : wallpaperTarget; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); + int transit = mAppTransition.getAppTransition(); + if (mSkipAppTransitionAnimation) { + transit = AppTransition.TRANSIT_UNSET; + } + mSkipAppTransitionAnimation = false; + mNoAnimationNotifyOnTransitionFinished.clear(); - mInnerFields.mWallpaperMayChange = false; + mH.removeMessages(H.APP_TRANSITION_TIMEOUT); - // The top-most window will supply the layout params, - // and we will determine it below. - LayoutParams animLp = null; - int bestAnimLayer = -1; - boolean fullscreenAnim = false; - boolean voiceInteraction = false; + rebuildAppWindowListLocked(); - final WindowState lowerWallpaperTarget = - mWallpaperControllerLocked.getLowerWallpaperTarget(); - final WindowState upperWallpaperTarget = - mWallpaperControllerLocked.getUpperWallpaperTarget(); + mInnerFields.mWallpaperMayChange = false; + + // The top-most window will supply the layout params, + // and we will determine it below. + LayoutParams animLp = null; + int bestAnimLayer = -1; + boolean fullscreenAnim = false; + boolean voiceInteraction = false; + + final WindowState lowerWallpaperTarget = + mWallpaperControllerLocked.getLowerWallpaperTarget(); + final WindowState upperWallpaperTarget = + mWallpaperControllerLocked.getUpperWallpaperTarget(); + + boolean openingAppHasWallpaper = false; + boolean closingAppHasWallpaper = false; + final AppWindowToken lowerWallpaperAppToken; + final AppWindowToken upperWallpaperAppToken; + if (lowerWallpaperTarget == null) { + lowerWallpaperAppToken = upperWallpaperAppToken = null; + } else { + lowerWallpaperAppToken = lowerWallpaperTarget.mAppToken; + upperWallpaperAppToken = upperWallpaperTarget.mAppToken; + } - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New wallpaper target=" + wallpaperTarget - + ", oldWallpaper=" + oldWallpaper - + ", lower target=" + lowerWallpaperTarget - + ", upper target=" + upperWallpaperTarget); - - boolean openingAppHasWallpaper = false; - boolean closingAppHasWallpaper = false; - final AppWindowToken lowerWallpaperAppToken; - final AppWindowToken upperWallpaperAppToken; - if (lowerWallpaperTarget == null) { - lowerWallpaperAppToken = upperWallpaperAppToken = null; + int i; + // Do a first pass through the tokens for two + // things: + // (1) Determine if both the closing and opening + // app token sets are wallpaper targets, in which + // case special animations are needed + // (since the wallpaper needs to stay static + // behind them). + // (2) Find the layout params of the top-most + // application window in the tokens, which is + // what will control the animation theme. + final int closingAppsCount = mClosingApps.size(); + appsCount = closingAppsCount + mOpeningApps.size(); + for (i = 0; i < appsCount; i++) { + final AppWindowToken wtoken; + if (i < closingAppsCount) { + wtoken = mClosingApps.valueAt(i); + if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { + closingAppHasWallpaper = true; + } } else { - lowerWallpaperAppToken = lowerWallpaperTarget.mAppToken; - upperWallpaperAppToken = upperWallpaperTarget.mAppToken; - } - - // Do a first pass through the tokens for two - // things: - // (1) Determine if both the closing and opening - // app token sets are wallpaper targets, in which - // case special animations are needed - // (since the wallpaper needs to stay static - // behind them). - // (2) Find the layout params of the top-most - // application window in the tokens, which is - // what will control the animation theme. - final int closingAppsCount = mClosingApps.size(); - appsCount = closingAppsCount + mOpeningApps.size(); - for (i = 0; i < appsCount; i++) { - final AppWindowToken wtoken; - if (i < closingAppsCount) { - wtoken = mClosingApps.valueAt(i); - if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { - closingAppHasWallpaper = true; - } - } else { - wtoken = mOpeningApps.valueAt(i - closingAppsCount); - if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { - openingAppHasWallpaper = true; - } + wtoken = mOpeningApps.valueAt(i - closingAppsCount); + if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { + openingAppHasWallpaper = true; } + } - voiceInteraction |= wtoken.voiceInteraction; + voiceInteraction |= wtoken.voiceInteraction; - if (wtoken.appFullscreen) { - WindowState ws = wtoken.findMainWindow(); - if (ws != null) { + if (wtoken.appFullscreen) { + WindowState ws = wtoken.findMainWindow(); + if (ws != null) { + animLp = ws.mAttrs; + bestAnimLayer = ws.mLayer; + fullscreenAnim = true; + } + } else if (!fullscreenAnim) { + WindowState ws = wtoken.findMainWindow(); + if (ws != null) { + if (ws.mLayer > bestAnimLayer) { animLp = ws.mAttrs; bestAnimLayer = ws.mLayer; - fullscreenAnim = true; - } - } else if (!fullscreenAnim) { - WindowState ws = wtoken.findMainWindow(); - if (ws != null) { - if (ws.mLayer > bestAnimLayer) { - animLp = ws.mAttrs; - bestAnimLayer = ws.mLayer; - } } } } + } - mAnimateWallpaperWithTarget = false; - if (closingAppHasWallpaper && openingAppHasWallpaper) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); - switch (transit) { - case AppTransition.TRANSIT_ACTIVITY_OPEN: - case AppTransition.TRANSIT_TASK_OPEN: - case AppTransition.TRANSIT_TASK_TO_FRONT: - transit = AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN; - break; - case AppTransition.TRANSIT_ACTIVITY_CLOSE: - case AppTransition.TRANSIT_TASK_CLOSE: - case AppTransition.TRANSIT_TASK_TO_BACK: - transit = AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE; - break; + transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper, + closingAppHasWallpaper, lowerWallpaperTarget, upperWallpaperTarget); + + // If all closing windows are obscured, then there is + // no need to do an animation. This is the case, for + // example, when this transition is being done behind + // the lock screen. + if (!mPolicy.allowAppAnimationsLw()) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Animations disallowed by keyguard or dream."); + animLp = null; + } + + processApplicationsAnimatingInPlace(transit); + + AppWindowToken topClosingApp = null; + int topClosingLayer = 0; + appsCount = mClosingApps.size(); + for (i = 0; i < appsCount; i++) { + AppWindowToken wtoken = mClosingApps.valueAt(i); + final AppWindowAnimator appAnimator = wtoken.mAppAnimator; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); + appAnimator.clearThumbnail(); + appAnimator.animation = null; + wtoken.inPendingTransaction = false; + setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction); + wtoken.updateReportedVisibilityLocked(); + // Force the allDrawn flag, because we want to start + // this guy's animations regardless of whether it's + // gotten drawn. + wtoken.allDrawn = true; + wtoken.deferClearAllDrawn = false; + // Ensure that apps that are mid-starting are also scheduled to have their + // starting windows removed after the animation is complete + if (wtoken.startingWindow != null && !wtoken.startingWindow.mExiting) { + scheduleRemoveStartingWindowLocked(wtoken); + } + mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); + + if (animLp != null) { + int layer = -1; + for (int j = 0; j < wtoken.windows.size(); j++) { + WindowState win = wtoken.windows.get(j); + if (win.mWinAnimator.mAnimLayer > layer) { + layer = win.mWinAnimator.mAnimLayer; + } + } + if (topClosingApp == null || layer > topClosingLayer) { + topClosingApp = wtoken; + topClosingLayer = layer; } - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New transit: " + AppTransition.appTransitionToString(transit)); - } else if ((oldWallpaper != null) && !mOpeningApps.isEmpty() - && !mOpeningApps.contains(oldWallpaper.mAppToken)) { - // We are transitioning from an activity with - // a wallpaper to one without. - transit = AppTransition.TRANSIT_WALLPAPER_CLOSE; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New transit away from wallpaper: " - + AppTransition.appTransitionToString(transit)); - } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()) { - // We are transitioning from an activity without - // a wallpaper to now showing the wallpaper - transit = AppTransition.TRANSIT_WALLPAPER_OPEN; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New transit into wallpaper: " - + AppTransition.appTransitionToString(transit)); - } else { - mAnimateWallpaperWithTarget = true; } + } - // If all closing windows are obscured, then there is - // no need to do an animation. This is the case, for - // example, when this transition is being done behind - // the lock screen. - if (!mPolicy.allowAppAnimationsLw()) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Animations disallowed by keyguard or dream."); - animLp = null; - } - - // Process all applications animating in place - if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) { - // Find the focused window - final WindowState win = - findFocusedWindowLocked(getDefaultDisplayContentLocked()); - if (win != null) { - final AppWindowToken wtoken = win.mAppToken; - final AppWindowAnimator appAnimator = wtoken.mAppAnimator; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now animating app in place " + wtoken); - appAnimator.clearThumbnail(); - appAnimator.animation = null; - updateTokenInPlaceLocked(wtoken, transit); - wtoken.updateReportedVisibilityLocked(); - - appAnimator.mAllAppWinAnimators.clear(); - final int N = wtoken.allAppWindows.size(); - for (int j = 0; j < N; j++) { - appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); - } - mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); - mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); - } - } - - AppWindowToken topClosingApp = null; - int topClosingLayer = 0; - appsCount = mClosingApps.size(); - for (i = 0; i < appsCount; i++) { - AppWindowToken wtoken = mClosingApps.valueAt(i); - final AppWindowAnimator appAnimator = wtoken.mAppAnimator; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); + AppWindowToken topOpeningApp = null; + appsCount = mOpeningApps.size(); + for (i = 0; i < appsCount; i++) { + AppWindowToken wtoken = mOpeningApps.valueAt(i); + final AppWindowAnimator appAnimator = wtoken.mAppAnimator; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); + + if (!appAnimator.usingTransferredAnimation) { appAnimator.clearThumbnail(); appAnimator.animation = null; - wtoken.inPendingTransaction = false; - setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction); - wtoken.updateReportedVisibilityLocked(); - // Force the allDrawn flag, because we want to start - // this guy's animations regardless of whether it's - // gotten drawn. - wtoken.allDrawn = true; - wtoken.deferClearAllDrawn = false; - // Ensure that apps that are mid-starting are also scheduled to have their - // starting windows removed after the animation is complete - if (wtoken.startingWindow != null && !wtoken.startingWindow.mExiting) { - scheduleRemoveStartingWindowLocked(wtoken); - } - mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); + } + wtoken.inPendingTransaction = false; + if (!setTokenVisibilityLocked( + wtoken, animLp, true, transit, false, voiceInteraction)){ + // This token isn't going to be animating. Add it to the list of tokens to + // be notified of app transition complete since the notification will not be + // sent be the app window animator. + mNoAnimationNotifyOnTransitionFinished.add(wtoken.token); + } + wtoken.updateReportedVisibilityLocked(); + wtoken.waitingToShow = false; - if (animLp != null) { - int layer = -1; - for (int j = 0; j < wtoken.windows.size(); j++) { - WindowState win = wtoken.windows.get(j); - if (win.mWinAnimator.mAnimLayer > layer) { - layer = win.mWinAnimator.mAnimLayer; - } - } - if (topClosingApp == null || layer > topClosingLayer) { - topClosingApp = wtoken; - topClosingLayer = layer; + appAnimator.mAllAppWinAnimators.clear(); + final int windowsCount = wtoken.allAppWindows.size(); + for (int j = 0; j < windowsCount; j++) { + appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); + } + mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); + mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); + + int topOpeningLayer = 0; + if (animLp != null) { + int layer = -1; + for (int j = 0; j < wtoken.windows.size(); j++) { + WindowState win = wtoken.windows.get(j); + if (win.mWinAnimator.mAnimLayer > layer) { + layer = win.mWinAnimator.mAnimLayer; } } + if (topOpeningApp == null || layer > topOpeningLayer) { + topOpeningApp = wtoken; + topOpeningLayer = layer; + } } + createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer); + } - AppWindowToken topOpeningApp = null; - appsCount = mOpeningApps.size(); - for (i = 0; i < appsCount; i++) { - AppWindowToken wtoken = mOpeningApps.valueAt(i); - final AppWindowAnimator appAnimator = wtoken.mAppAnimator; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); + AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null : + topOpeningApp.mAppAnimator; + AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null : + topClosingApp.mAppAnimator; - if (!appAnimator.usingTransferredAnimation) { - appAnimator.clearThumbnail(); - appAnimator.animation = null; - } - wtoken.inPendingTransaction = false; - if (!setTokenVisibilityLocked( - wtoken, animLp, true, transit, false, voiceInteraction)){ - // This token isn't going to be animating. Add it to the list of tokens to - // be notified of app transition complete since the notification will not be - // sent be the app window animator. - mNoAnimationNotifyOnTransitionFinished.add(wtoken.token); - } + mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator); + mAppTransition.postAnimationCallback(); + mAppTransition.clear(); + + mOpeningApps.clear(); + mClosingApps.clear(); + + // This has changed the visibility of windows, so perform + // a new layout to get them all up-to-date. + getDefaultDisplayContentLocked().layoutNeeded = true; + + // TODO(multidisplay): IMEs are only supported on the default display. + if (windows == getDefaultWindowListLocked() + && !moveInputMethodWindowsIfNeededLocked(true)) { + assignLayersLocked(windows); + } + updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, true /*updateInputWindows*/); + mFocusMayChange = false; + notifyActivityDrawnForKeyguard(); + return WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT + | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; + + } + + private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper, + boolean closingAppHasWallpaper, WindowState lowerWallpaperTarget, + WindowState upperWallpaperTarget) { + // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper + final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); + final WindowState oldWallpaper = mWallpaperControllerLocked.isWallpaperTargetAnimating() + ? null : wallpaperTarget; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New wallpaper target=" + wallpaperTarget + + ", oldWallpaper=" + oldWallpaper + + ", lower target=" + lowerWallpaperTarget + + ", upper target=" + upperWallpaperTarget); + mAnimateWallpaperWithTarget = false; + if (closingAppHasWallpaper && openingAppHasWallpaper) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); + switch (transit) { + case AppTransition.TRANSIT_ACTIVITY_OPEN: + case AppTransition.TRANSIT_TASK_OPEN: + case AppTransition.TRANSIT_TASK_TO_FRONT: + transit = AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN; + break; + case AppTransition.TRANSIT_ACTIVITY_CLOSE: + case AppTransition.TRANSIT_TASK_CLOSE: + case AppTransition.TRANSIT_TASK_TO_BACK: + transit = AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE; + break; + } + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit: " + AppTransition.appTransitionToString(transit)); + } else if ((oldWallpaper != null) && !mOpeningApps.isEmpty() + && !mOpeningApps.contains(oldWallpaper.mAppToken)) { + // We are transitioning from an activity with + // a wallpaper to one without. + transit = AppTransition.TRANSIT_WALLPAPER_CLOSE; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit away from wallpaper: " + + AppTransition.appTransitionToString(transit)); + } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()) { + // We are transitioning from an activity without + // a wallpaper to now showing the wallpaper + transit = AppTransition.TRANSIT_WALLPAPER_OPEN; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit into wallpaper: " + + AppTransition.appTransitionToString(transit)); + } else { + mAnimateWallpaperWithTarget = true; + } + return transit; + } + + private void processApplicationsAnimatingInPlace(int transit) { + if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) { + // Find the focused window + final WindowState win = + findFocusedWindowLocked(getDefaultDisplayContentLocked()); + if (win != null) { + final AppWindowToken wtoken = win.mAppToken; + final AppWindowAnimator appAnimator = wtoken.mAppAnimator; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now animating app in place " + wtoken); + appAnimator.clearThumbnail(); + appAnimator.animation = null; + updateTokenInPlaceLocked(wtoken, transit); wtoken.updateReportedVisibilityLocked(); - wtoken.waitingToShow = false; appAnimator.mAllAppWinAnimators.clear(); - final int windowsCount = wtoken.allAppWindows.size(); - for (int j = 0; j < windowsCount; j++) { + final int N = wtoken.allAppWindows.size(); + for (int j = 0; j < N; j++) { appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); } - mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); + mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); + } + } + } - int topOpeningLayer = 0; - if (animLp != null) { - int layer = -1; - for (int j = 0; j < wtoken.windows.size(); j++) { - WindowState win = wtoken.windows.get(j); - if (win.mWinAnimator.mAnimLayer > layer) { - layer = win.mWinAnimator.mAnimLayer; - } - } - if (topOpeningApp == null || layer > topOpeningLayer) { - topOpeningApp = wtoken; - topOpeningLayer = layer; - } + private boolean checkIfTransitionGoodToGo(int appsCount) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Checking " + appsCount + " opening apps (frozen=" + + mDisplayFrozen + " timeout=" + + mAppTransition.isTimeout() + ")..."); + if (!mAppTransition.isTimeout()) { + for (int i = 0; i < appsCount; i++) { + AppWindowToken wtoken = mOpeningApps.valueAt(i); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Check opening app=" + wtoken + ": allDrawn=" + + wtoken.allDrawn + " startingDisplayed=" + + wtoken.startingDisplayed + " startingMoved=" + + wtoken.startingMoved); + if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { + return false; } - createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer); } - AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null : - topOpeningApp.mAppAnimator; - AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null : - topClosingApp.mAppAnimator; - - mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator); - mAppTransition.postAnimationCallback(); - mAppTransition.clear(); - - mOpeningApps.clear(); - mClosingApps.clear(); - - // This has changed the visibility of windows, so perform - // a new layout to get them all up-to-date. - changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT - | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; - getDefaultDisplayContentLocked().layoutNeeded = true; - - // TODO(multidisplay): IMEs are only supported on the default display. - if (windows == getDefaultWindowListLocked() - && !moveInputMethodWindowsIfNeededLocked(true)) { - assignLayersLocked(windows); - } - updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, true /*updateInputWindows*/); - mFocusMayChange = false; - notifyActivityDrawnForKeyguard(); + // If the wallpaper is visible, we need to check it's ready too. + return !mWallpaperControllerLocked.isWallpaperVisible() || + mWallpaperControllerLocked.wallpaperTransitionReady(); } - - return changes; + return true; } private void createThumbnailAppAnimator(int transit, AppWindowToken appToken, @@ -9764,8 +9778,8 @@ public class WindowManagerService extends IWindowManager.Stub ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() + " mHasSurface=" + win.mHasSurface + " drawState=" + win.mWinAnimator.mDrawState); - if (win.mRemoved || !win.mHasSurface) { - // Window has been removed; no draw will now happen, so stop waiting. + if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) { + // Window has been removed or hidden; no draw will now happen, so stop waiting. if (DEBUG_SCREEN_ON) Slog.w(TAG, "Aborted waiting for drawn: " + win); mWaitingForDrawn.remove(win); } else if (win.hasDrawnLw()) { @@ -11285,12 +11299,18 @@ public class WindowManagerService extends IWindowManager.Stub final WindowList windows = getDefaultWindowListLocked(); for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { final WindowState win = windows.get(winNdx); + final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs); if (win.isVisibleLw() - && (win.mAppToken != null || mPolicy.isForceHiding(win.mAttrs))) { + && (win.mAppToken != null || isForceHiding)) { win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING; // Force add to mResizingWindows. win.mLastContentInsets.set(-1, -1, -1, -1); mWaitingForDrawn.add(win); + + // No need to wait for the windows below Keyguard. + if (isForceHiding) { + break; + } } } requestTraversalLocked(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b56b1f9cadc6..092a6d1ad348 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -80,8 +80,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { static final String TAG = "WindowState"; // The minimal size of a window within the usable area of the freeform stack. - static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48; - static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32; + private static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48; + private static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32; + + // The thickness of a window resize handle outside the window bounds on the free form workspace + // to capture touch events in that area. + private static final int RESIZE_HANDLE_WIDTH_IN_DP = 10; + + static final boolean BOUNDS_FOR_TOUCH = true; final WindowManagerService mService; final WindowManagerPolicy mPolicy; @@ -541,9 +547,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { mHaveFrame = true; final Task task = mAppToken != null ? getTask() : null; - final boolean isFreeFormWorkspace = task != null && task.mStack != null && - task.mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID; final boolean nonFullscreenTask = task != null && !task.isFullscreen(); + final boolean freeformWorkspace = inFreeformWorkspace(); if (nonFullscreenTask) { task.getBounds(mContainingFrame); final WindowState imeWin = mService.mInputMethodWindow; @@ -553,7 +558,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mContainingFrame.top -= mContainingFrame.bottom - cf.bottom; } - if (isFreeFormWorkspace) { + if (freeformWorkspace) { // In free form mode we have only to set the rectangle if it wasn't set already. No // need to intersect it with the (visible) "content frame" since it is allowed to // be outside the visible desktop. @@ -669,7 +674,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Make sure the content and visible frames are inside of the // final window frame. - if (isFreeFormWorkspace && !mFrame.isEmpty()) { + if (freeformWorkspace && !mFrame.isEmpty()) { // Keep the frame out of the blocked system area, limit it in size to the content area // and make sure that there is always a minimum visible so that the user can drag it // into a usable area.. @@ -910,10 +915,22 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mDisplayContent.getHomeStack(); } - void getTaskBounds(Rect bounds) { + /** + * Retrieves the bounds for a task. + * @param bounds The rect which gets the bounds. + * @param forTouch Pass in BOUNDS_FOR_TOUCH to get touch related bounds, otherwise visible + * bounds will be returned. + */ + void getTaskBounds(Rect bounds, boolean forTouch) { final Task task = getTask(); if (task != null) { task.getBounds(bounds); + if (forTouch == BOUNDS_FOR_TOUCH) { + if (inFreeformWorkspace()) { + final int delta = calculatePixelFromDp(RESIZE_HANDLE_WIDTH_IN_DP); + bounds.inset(-delta, -delta); + } + } return; } bounds.set(mFrame); @@ -1612,6 +1629,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } + private boolean inFreeformWorkspace() { + final Task task = getTask(); + return task != null && task.mStack != null && + task.mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID; + } + private int calculatePixelFromDp(int dp) { final Configuration serviceConfig = mService.mCurConfiguration; // TODO(multidisplay): Update Dp to that of display stack is on. diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 7aa48ab5119f..109e6273fe05 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1590,8 +1590,6 @@ class WindowStateAnimator { final int left = ((int) shownFrame.left) - attrs.surfaceInsets.left; final int top = ((int) shownFrame.top) - attrs.surfaceInsets.top; if (mSurfaceX != left || mSurfaceY != top) { - mSurfaceX = left; - mSurfaceY = top; if (mAnimating) { // If this window (or its app token) is animating, then the position // of the surface will be re-computed on the next animation frame. @@ -1599,6 +1597,8 @@ class WindowStateAnimator { // transformation is being applied by the animation. return; } + mSurfaceX = left; + mSurfaceY = top; if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset"); SurfaceControl.openTransaction(); try { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index cd2885b9fdd4..0f60cc8a3899 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE; import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA; import static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.TEXT; @@ -44,6 +45,7 @@ import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.IDevicePolicyManager; import android.app.admin.SystemUpdatePolicy; import android.app.backup.IBackupManager; +import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -2957,7 +2959,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0; if (requireEntry) { - utils.requireCredentialEntry(UserHandle.USER_ALL); + utils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, + UserHandle.USER_ALL); } synchronized (this) { int newOwner = requireEntry ? callingUid : -1; @@ -3089,7 +3092,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mPowerManager.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN, 0); // Ensure the device is locked - new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL); + new LockPatternUtils(mContext).requireStrongAuth( + STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, UserHandle.USER_ALL); getWindowManager().lockNow(null); } catch (RemoteException e) { } finally { @@ -4523,12 +4527,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long id = Binder.clearCallingIdentity(); try { mUserManager.setUserEnabled(userId); + UserInfo parent = mUserManager.getProfileParent(userId); Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED); intent.putExtra(Intent.EXTRA_USER, new UserHandle(userHandle)); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); - // TODO This should send to parent of profile (which is always owner at the moment). - mContext.sendBroadcastAsUser(intent, UserHandle.OWNER); + mContext.sendBroadcastAsUser(intent, new UserHandle(parent.id)); } finally { restoreCallingIdentity(id); } @@ -4963,13 +4967,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { IPackageManager pm = AppGlobals.getPackageManager(); long id = Binder.clearCallingIdentity(); try { + UserInfo parent = mUserManager.getProfileParent(callingUserId); + if (parent == null) { + Slog.e(LOG_TAG, "Cannot call addCrossProfileIntentFilter if there is no " + + "parent"); + return; + } if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) { pm.addCrossProfileIntentFilter(filter, who.getPackageName(), callingUserId, - UserHandle.USER_OWNER, 0); + parent.id, 0); } if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) { pm.addCrossProfileIntentFilter(filter, who.getPackageName(), - UserHandle.USER_OWNER, callingUserId, 0); + parent.id, callingUserId, 0); } } catch (RemoteException re) { // Shouldn't happen @@ -4988,12 +4998,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { IPackageManager pm = AppGlobals.getPackageManager(); long id = Binder.clearCallingIdentity(); try { - // Removing those that go from the managed profile to the primary user. + UserInfo parent = mUserManager.getProfileParent(callingUserId); + if (parent == null) { + Slog.e(LOG_TAG, "Cannot call clearCrossProfileIntentFilter if there is no " + + "parent"); + return; + } + // Removing those that go from the managed profile to the parent. pm.clearCrossProfileIntentFilters(callingUserId, who.getPackageName()); - // And those that go from the primary user to the managed profile. + // And those that go from the parent to the managed profile. // If we want to support multiple managed profiles, we will have to only remove // those that have callingUserId as their target. - pm.clearCrossProfileIntentFilters(UserHandle.USER_OWNER, who.getPackageName()); + pm.clearCrossProfileIntentFilters(parent.id, who.getPackageName()); } catch (RemoteException re) { // Shouldn't happen } finally { diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index 2d47c248abf6..86f5ed7346cb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -38,7 +38,8 @@ public class UserManagerTest extends AndroidTestCase { @Override public void setUp() throws Exception { - mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); + super.setUp(); + mUserManager = UserManager.get(getContext()); IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED); getContext().registerReceiver(new BroadcastReceiver() { @Override @@ -64,14 +65,18 @@ public class UserManagerTest extends AndroidTestCase { private void removeExistingUsers() { List<UserInfo> list = mUserManager.getUsers(); for (UserInfo user : list) { - if (user.id != UserHandle.USER_OWNER) { + // Keep system and primary user. + // We do not have to keep primary user, but in split system user mode, we need it + // until http://b/22976637 is fixed. Right now in split system user mode, you need to + // switch to primary user and run tests under primary user. + if (user.id != UserHandle.USER_SYSTEM && !user.isPrimary()) { removeUser(user.id); } } } - public void testHasPrimary() throws Exception { - assertTrue(findUser(0)); + public void testHasSystemUser() throws Exception { + assertTrue(findUser(UserHandle.USER_SYSTEM)); } public void testAddUser() throws Exception { @@ -122,10 +127,11 @@ public class UserManagerTest extends AndroidTestCase { // Make sure only one managed profile can be created public void testAddManagedProfile() throws Exception { + final int primaryUserId = mUserManager.getPrimaryUser().id; UserInfo userInfo1 = createProfileForUser("Managed 1", - UserInfo.FLAG_MANAGED_PROFILE, UserHandle.USER_OWNER); + UserInfo.FLAG_MANAGED_PROFILE, primaryUserId); UserInfo userInfo2 = createProfileForUser("Managed 2", - UserInfo.FLAG_MANAGED_PROFILE, UserHandle.USER_OWNER); + UserInfo.FLAG_MANAGED_PROFILE, primaryUserId); assertNotNull(userInfo1); assertNull(userInfo2); // Verify that current user is not a managed profile @@ -133,17 +139,18 @@ public class UserManagerTest extends AndroidTestCase { } public void testGetUserCreationTime() throws Exception { + final int primaryUserId = mUserManager.getPrimaryUser().id; UserInfo profile = createProfileForUser("Managed 1", - UserInfo.FLAG_MANAGED_PROFILE, UserHandle.USER_OWNER); + UserInfo.FLAG_MANAGED_PROFILE, primaryUserId); assertNotNull(profile); assertTrue("creationTime must be set when the profile is created", profile.creationTime > 0); assertEquals(profile.creationTime, mUserManager.getUserCreationTime( new UserHandle(profile.id))); - long ownerCreationTime = mUserManager.getUserInfo(UserHandle.USER_OWNER).creationTime; + long ownerCreationTime = mUserManager.getUserInfo(primaryUserId).creationTime; assertEquals(ownerCreationTime, mUserManager.getUserCreationTime( - new UserHandle(UserHandle.USER_OWNER))); + new UserHandle(primaryUserId))); try { int noSuchUserId = 100500; @@ -177,11 +184,11 @@ public class UserManagerTest extends AndroidTestCase { } public void testSerialNumber() { - UserInfo user1 = createUser("User 1", UserInfo.FLAG_RESTRICTED); + UserInfo user1 = createUser("User 1", 0); int serialNumber1 = user1.serialNumber; assertEquals(serialNumber1, mUserManager.getUserSerialNumber(user1.id)); assertEquals(user1.id, mUserManager.getUserHandle(serialNumber1)); - UserInfo user2 = createUser("User 2", UserInfo.FLAG_RESTRICTED); + UserInfo user2 = createUser("User 2", 0); int serialNumber2 = user2.serialNumber; assertFalse(serialNumber1 == serialNumber2); assertEquals(serialNumber2, mUserManager.getUserSerialNumber(user2.id)); @@ -203,17 +210,15 @@ public class UserManagerTest extends AndroidTestCase { } public void testRestrictions() { - List<UserInfo> users = mUserManager.getUsers(); - if (users.size() > 1) { - Bundle restrictions = new Bundle(); - restrictions.putBoolean(UserManager.DISALLOW_INSTALL_APPS, true); - restrictions.putBoolean(UserManager.DISALLOW_CONFIG_WIFI, false); - mUserManager.setUserRestrictions(restrictions, new UserHandle(users.get(1).id)); - Bundle stored = mUserManager.getUserRestrictions(new UserHandle(users.get(1).id)); - assertEquals(stored.getBoolean(UserManager.DISALLOW_CONFIG_WIFI), false); - assertEquals(stored.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS), false); - assertEquals(stored.getBoolean(UserManager.DISALLOW_INSTALL_APPS), true); - } + UserInfo testUser = createUser("User 1", 0); + Bundle restrictions = new Bundle(); + restrictions.putBoolean(UserManager.DISALLOW_INSTALL_APPS, true); + restrictions.putBoolean(UserManager.DISALLOW_CONFIG_WIFI, false); + mUserManager.setUserRestrictions(restrictions, new UserHandle(testUser.id)); + Bundle stored = mUserManager.getUserRestrictions(new UserHandle(testUser.id)); + assertEquals(stored.getBoolean(UserManager.DISALLOW_CONFIG_WIFI), false); + assertEquals(stored.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS), false); + assertEquals(stored.getBoolean(UserManager.DISALLOW_INSTALL_APPS), true); } private void removeUser(int userId) { diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 263c5e9b3ce9..31f1d7fdc61d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -358,6 +358,13 @@ public class CarrierConfigManager { public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int"; /** + * Determines whether conference calls are supported by a carrier. When {@code true}, + * conference calling is supported, {@code false otherwise}. + * @hide + */ + public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; + + /** * If this is true, the SIM card (through Customer Service Profile EF file) will be able to * prevent manual operator selection. If false, this SIM setting will be ignored and manual * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more @@ -457,6 +464,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false); sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0); sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100); + sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); // MMS defaults sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false); |