diff options
174 files changed, 3318 insertions, 2271 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 806283229d98..7e382870b016 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -518,7 +518,9 @@ public abstract class AccessibilityService extends Service { public static final int GLOBAL_ACTION_POWER_DIALOG = 6; /** - * Action to toggle docking the current app's window + * Action to toggle docking the current app's window. + * <p> + * <strong>Note:</strong> It is effective only if it appears in {@link #getSystemActions()}. */ public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 45df0d9a8a9f..46f6597bb042 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -321,7 +321,8 @@ public final class ActivityThread extends ClientTransactionHandler @UnsupportedAppUsage private ContextImpl mSystemContext; - private final SparseArray<ContextImpl> mDisplaySystemUiContexts = new SparseArray<>(); + @GuardedBy("this") + private SparseArray<ContextImpl> mDisplaySystemUiContexts; @UnsupportedAppUsage static volatile IPackageManager sPackageManager; @@ -2650,7 +2651,6 @@ public final class ActivityThread extends ClientTransactionHandler } } - @Override @NonNull public ContextImpl getSystemUiContext() { return getSystemUiContext(DEFAULT_DISPLAY); @@ -2664,6 +2664,9 @@ public final class ActivityThread extends ClientTransactionHandler @NonNull public ContextImpl getSystemUiContext(int displayId) { synchronized (this) { + if (mDisplaySystemUiContexts == null) { + mDisplaySystemUiContexts = new SparseArray<>(); + } ContextImpl systemUiContext = mDisplaySystemUiContexts.get(displayId); if (systemUiContext == null) { systemUiContext = ContextImpl.createSystemUiContext(getSystemContext(), displayId); @@ -2673,6 +2676,25 @@ public final class ActivityThread extends ClientTransactionHandler } } + @Nullable + @Override + public ContextImpl getSystemUiContextNoCreate() { + synchronized (this) { + if (mDisplaySystemUiContexts == null) return null; + return mDisplaySystemUiContexts.get(DEFAULT_DISPLAY); + } + } + + void onSystemUiContextCleanup(ContextImpl context) { + synchronized (this) { + if (mDisplaySystemUiContexts == null) return; + final int index = mDisplaySystemUiContexts.indexOfValue(context); + if (index >= 0) { + mDisplaySystemUiContexts.removeAt(index); + } + } + } + public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { synchronized (this) { getSystemContext().installSystemApplicationInfo(info, classLoader); diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java index bc698f657305..b9ad5c337813 100644 --- a/core/java/android/app/ActivityThreadInternal.java +++ b/core/java/android/app/ActivityThreadInternal.java @@ -28,7 +28,7 @@ import java.util.ArrayList; interface ActivityThreadInternal { ContextImpl getSystemContext(); - ContextImpl getSystemUiContext(); + ContextImpl getSystemUiContextNoCreate(); boolean isInDensityCompatMode(); diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java index 8637e31eb122..58f60a6a59a7 100644 --- a/core/java/android/app/ConfigurationController.java +++ b/core/java/android/app/ConfigurationController.java @@ -154,9 +154,12 @@ class ConfigurationController { int configDiff; boolean equivalent; + // Get theme outside of synchronization to avoid nested lock. + final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme(); + final ContextImpl systemUiContext = mActivityThread.getSystemUiContextNoCreate(); + final Resources.Theme systemUiTheme = + systemUiContext != null ? systemUiContext.getTheme() : null; synchronized (mResourcesManager) { - final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme(); - final Resources.Theme systemUiTheme = mActivityThread.getSystemUiContext().getTheme(); if (mPendingConfiguration != null) { if (!mPendingConfiguration.isOtherSeqNewer(config)) { config = mPendingConfiguration; @@ -207,7 +210,8 @@ class ConfigurationController { systemTheme.rebase(); } - if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) { + if (systemUiTheme != null + && (systemUiTheme.getChangingConfigurations() & configDiff) != 0) { systemUiTheme.rebase(); } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index db3c7d9bcb02..fc1884a41653 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -3191,6 +3191,10 @@ class ContextImpl extends Context { final void performFinalCleanup(String who, String what) { //Log.i(TAG, "Cleanup up context: " + this); mPackageInfo.removeContextRegistrations(getOuterContext(), who, what); + if (mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI + && mToken instanceof WindowTokenClient) { + mMainThread.onSystemUiContextCleanup(this); + } } @UnsupportedAppUsage diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 9605cbdb48c6..2c02be7dc6b9 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5810,6 +5810,7 @@ public class Notification implements Parcelable p, result); buildCustomContentIntoTemplate(mContext, standard, customContent, p, result); + makeHeaderExpanded(standard); return standard; } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 8daf9f0450c4..d1ef5917ba47 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1367,18 +1367,18 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * Returns if the activity should never be sandboxed to the activity window bounds. * @hide */ - public boolean neverSandboxDisplayApis() { + public boolean neverSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) { return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS) - || ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo); + || constrainDisplayApisConfig.getNeverConstrainDisplayApis(applicationInfo); } /** * Returns if the activity should always be sandboxed to the activity window bounds. * @hide */ - public boolean alwaysSandboxDisplayApis() { + public boolean alwaysSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) { return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS) - || ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo); + || constrainDisplayApisConfig.getAlwaysConstrainDisplayApis(applicationInfo); } /** @hide */ diff --git a/core/java/android/content/pm/ConstrainDisplayApisConfig.java b/core/java/android/content/pm/ConstrainDisplayApisConfig.java index 11ba3d4ba9a2..98b73aa8860a 100644 --- a/core/java/android/content/pm/ConstrainDisplayApisConfig.java +++ b/core/java/android/content/pm/ConstrainDisplayApisConfig.java @@ -19,10 +19,15 @@ package android.content.pm; import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS; import android.provider.DeviceConfig; +import android.util.ArrayMap; +import android.util.Pair; import android.util.Slog; +import com.android.internal.os.BackgroundThread; + import java.util.Arrays; import java.util.List; +import java.util.Map; /** * Class for processing flags in the Device Config namespace 'constrain_display_apis'. @@ -55,19 +60,45 @@ public final class ConstrainDisplayApisConfig { "always_constrain_display_apis"; /** + * Indicates that display APIs should never be constrained to the activity window bounds for all + * packages. + */ + private boolean mNeverConstrainDisplayApisAllPackages; + + /** + * Indicates that display APIs should never be constrained to the activity window bounds for + * a set of defined packages. Map keys are package names, and entries are a + * 'Pair(<min-version-code>, <max-version-code>)'. + */ + private ArrayMap<String, Pair<Long, Long>> mNeverConstrainConfigMap; + + /** + * Indicates that display APIs should always be constrained to the activity window bounds for + * a set of defined packages. Map keys are package names, and entries are a + * 'Pair(<min-version-code>, <max-version-code>)'. + */ + private ArrayMap<String, Pair<Long, Long>> mAlwaysConstrainConfigMap; + + public ConstrainDisplayApisConfig() { + updateCache(); + + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_CONSTRAIN_DISPLAY_APIS, + BackgroundThread.getExecutor(), properties -> updateCache()); + } + + /** * Returns true if either the flag 'never_constrain_display_apis_all_packages' is true or the * flag 'never_constrain_display_apis' contains a package entry that matches the given {@code * applicationInfo}. * * @param applicationInfo Information about the application/package. */ - public static boolean neverConstrainDisplayApis(ApplicationInfo applicationInfo) { - if (DeviceConfig.getBoolean(NAMESPACE_CONSTRAIN_DISPLAY_APIS, - FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false)) { + public boolean getNeverConstrainDisplayApis(ApplicationInfo applicationInfo) { + if (mNeverConstrainDisplayApisAllPackages) { return true; } - return flagHasMatchingPackageEntry(FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, applicationInfo); + return flagHasMatchingPackageEntry(mNeverConstrainConfigMap, applicationInfo); } /** @@ -76,73 +107,106 @@ public final class ConstrainDisplayApisConfig { * * @param applicationInfo Information about the application/package. */ - public static boolean alwaysConstrainDisplayApis(ApplicationInfo applicationInfo) { - return flagHasMatchingPackageEntry(FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, applicationInfo); + public boolean getAlwaysConstrainDisplayApis(ApplicationInfo applicationInfo) { + return flagHasMatchingPackageEntry(mAlwaysConstrainConfigMap, applicationInfo); } + /** - * Returns true if the flag with the given {@code flagName} contains a package entry that - * matches the given {@code applicationInfo}. - * - * @param applicationInfo Information about the application/package. + * Updates {@link #mNeverConstrainDisplayApisAllPackages}, {@link #mNeverConstrainConfigMap}, + * and {@link #mAlwaysConstrainConfigMap} from the {@link DeviceConfig}. */ - private static boolean flagHasMatchingPackageEntry(String flagName, - ApplicationInfo applicationInfo) { - String configStr = DeviceConfig.getString(NAMESPACE_CONSTRAIN_DISPLAY_APIS, - flagName, /* defaultValue= */ ""); + private void updateCache() { + mNeverConstrainDisplayApisAllPackages = DeviceConfig.getBoolean( + NAMESPACE_CONSTRAIN_DISPLAY_APIS, + FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false); + + final String neverConstrainConfigStr = DeviceConfig.getString( + NAMESPACE_CONSTRAIN_DISPLAY_APIS, + FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ ""); + mNeverConstrainConfigMap = buildConfigMap(neverConstrainConfigStr); + + final String alwaysConstrainConfigStr = DeviceConfig.getString( + NAMESPACE_CONSTRAIN_DISPLAY_APIS, + FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ ""); + mAlwaysConstrainConfigMap = buildConfigMap(alwaysConstrainConfigStr); + } + /** + * Processes the configuration string into a map of version codes, for the given + * configuration to be applied to the specified packages. If the given package + * entry string is invalid, then the map will not contain an entry for the package. + * + * @param configStr A configuration string expected to be in the format of a list of package + * entries separated by ','. A package entry expected to be in the format + * '<package-name>:<min-version-code>?:<max-version-code>?'. + * @return a map of configuration entries, where each key is a package name. Each value is + * a pair of version codes, in the format 'Pair(<min-version-code>, <max-version-code>)'. + */ + private static ArrayMap<String, Pair<Long, Long>> buildConfigMap(String configStr) { + ArrayMap<String, Pair<Long, Long>> configMap = new ArrayMap<>(); // String#split returns a non-empty array given an empty string. if (configStr.isEmpty()) { - return false; + return configMap; } - for (String packageEntryString : configStr.split(",")) { - if (matchesApplicationInfo(packageEntryString, applicationInfo)) { - return true; + List<String> packageAndVersions = Arrays.asList(packageEntryString.split(":", 3)); + if (packageAndVersions.size() != 3) { + Slog.w(TAG, "Invalid package entry in flag 'never/always_constrain_display_apis': " + + packageEntryString); + // Skip this entry. + continue; + } + String packageName = packageAndVersions.get(0); + String minVersionCodeStr = packageAndVersions.get(1); + String maxVersionCodeStr = packageAndVersions.get(2); + try { + final long minVersion = + minVersionCodeStr.isEmpty() ? Long.MIN_VALUE : Long.parseLong( + minVersionCodeStr); + final long maxVersion = + maxVersionCodeStr.isEmpty() ? Long.MAX_VALUE : Long.parseLong( + maxVersionCodeStr); + Pair<Long, Long> minMaxVersionCodes = new Pair<>(minVersion, maxVersion); + configMap.put(packageName, minMaxVersionCodes); + } catch (NumberFormatException e) { + Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryString); + // Skip this entry. } } - - return false; + return configMap; } /** - * Parses the given {@code packageEntryString} and returns true if {@code - * applicationInfo.packageName} matches the package name in the config and {@code - * applicationInfo.longVersionCode} is within the version range in the config. - * - * <p>Logs a warning and returns false in case the given {@code packageEntryString} is invalid. + * Returns true if the flag with the given {@code flagName} contains a package entry that + * matches the given {@code applicationInfo}. * - * @param packageEntryStr A package entry expected to be in the format - * '<package-name>:<min-version-code>?:<max-version-code>?'. + * @param configMap the map representing the current configuration value to examine * @param applicationInfo Information about the application/package. */ - private static boolean matchesApplicationInfo(String packageEntryStr, + private static boolean flagHasMatchingPackageEntry(Map<String, Pair<Long, Long>> configMap, ApplicationInfo applicationInfo) { - List<String> packageAndVersions = Arrays.asList(packageEntryStr.split(":", 3)); - if (packageAndVersions.size() != 3) { - Slog.w(TAG, "Invalid package entry in flag 'never_constrain_display_apis': " - + packageEntryStr); + if (configMap.isEmpty()) { return false; } - String packageName = packageAndVersions.get(0); - String minVersionCodeStr = packageAndVersions.get(1); - String maxVersionCodeStr = packageAndVersions.get(2); - - if (!packageName.equals(applicationInfo.packageName)) { + if (!configMap.containsKey(applicationInfo.packageName)) { return false; } - long version = applicationInfo.longVersionCode; - try { - if (!minVersionCodeStr.isEmpty() && version < Long.parseLong(minVersionCodeStr)) { - return false; - } - if (!maxVersionCodeStr.isEmpty() && version > Long.parseLong(maxVersionCodeStr)) { - return false; - } - } catch (NumberFormatException e) { - Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryStr); - return false; - } - return true; + return matchesApplicationInfo(configMap.get(applicationInfo.packageName), applicationInfo); + } + + /** + * Parses the given {@code minMaxVersionCodes} and returns true if {@code + * applicationInfo.longVersionCode} is within the version range in the pair. + * Returns false otherwise. + * + * @param minMaxVersionCodes A pair expected to be in the format + * 'Pair(<min-version-code>, <max-version-code>)'. + * @param applicationInfo Information about the application/package. + */ + private static boolean matchesApplicationInfo(Pair<Long, Long> minMaxVersionCodes, + ApplicationInfo applicationInfo) { + return applicationInfo.longVersionCode >= minMaxVersionCodes.first + && applicationInfo.longVersionCode <= minMaxVersionCodes.second; } } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 9f77a7e72e70..0b02a919b2d9 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2632,7 +2632,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * </tbody> * </table> * <p>For applications targeting SDK version 31 or newer, if the mobile device declares to be - * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS media performance class} S, + * media performance class 12 or higher by setting + * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger, * the primary camera devices (first rear/front camera in the camera ID list) will not * support JPEG sizes smaller than 1080p. If the application configures a JPEG stream * smaller than 1080p, the camera device will round up the JPEG image size to at least @@ -2705,9 +2706,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * </tbody> * </table> * <p>For applications targeting SDK version 31 or newer, if the mobile device doesn't declare - * to be media performance class S, or if the camera device isn't a primary rear/front - * camera, the minimum required output stream configurations are the same as for applications - * targeting SDK version older than 31.</p> + * to be media performance class 12 or better by setting + * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger, + * or if the camera device isn't a primary rear/front camera, the minimum required output + * stream configurations are the same as for applications targeting SDK version older than + * 31.</p> * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional * mandatory stream configurations on a per-capability basis.</p> * <p>Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java index 74b814ea4159..c8b4226ecae0 100644 --- a/core/java/android/os/AppZygote.java +++ b/core/java/android/os/AppZygote.java @@ -45,6 +45,8 @@ public class AppZygote { // Last UID/GID of the range the AppZygote can setuid()/setgid() to private final int mZygoteUidGidMax; + private final int mZygoteRuntimeFlags; + private final Object mLock = new Object(); /** @@ -56,11 +58,13 @@ public class AppZygote { private final ApplicationInfo mAppInfo; - public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax) { + public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax, + int runtimeFlags) { mAppInfo = appInfo; mZygoteUid = zygoteUid; mZygoteUidGidMin = uidGidMin; mZygoteUidGidMax = uidGidMax; + mZygoteRuntimeFlags = runtimeFlags; } /** @@ -110,7 +114,7 @@ public class AppZygote { mZygoteUid, mZygoteUid, null, // gids - 0, // runtimeFlags + mZygoteRuntimeFlags, // runtimeFlags "app_zygote", // seInfo abi, // abi abi, // acceptedAbiList diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 4d0fc1642874..ee32ce43821c 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1750,8 +1750,9 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall /** * Called when there has been a failure transferring the {@link AssistStructure} to * the assistant. This may happen, for example, if the data is too large and results - * in an out of memory exception, or the client has provided corrupt data. This will - * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied + * in an out of memory exception, the data has been cleared during transferring due to + * the new incoming assist data, or the client has provided corrupt data. This will be + * called immediately before {@link #onHandleAssist} and the AssistStructure supplied * there afterwards will be null. * * @param failure The failure exception that was thrown when building the @@ -1789,7 +1790,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall * Called to receive data from the application that the user was currently viewing when * an assist session is started. If the original show request did not specify * {@link #SHOW_WITH_ASSIST}, {@link AssistState} parameter will only provide - * {@link ActivityId}. + * {@link ActivityId}. If there was a failure to write the assist data to + * {@link AssistStructure}, the {@link AssistState#getAssistStructure()} will return null. * * <p>This method is called for all activities along with an index and count that indicates * which activity the data is for. {@code index} will be between 0 and {@code count}-1 and @@ -2214,7 +2216,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall * @return If available, the structure definition of all windows currently * displayed by the app. May be null if assist data has been disabled by the user * or device policy; will be null if the original show request did not specify - * {@link #SHOW_WITH_ASSIST}; will be an empty stub if the application has disabled assist + * {@link #SHOW_WITH_ASSIST} or the assist data has been corrupt when writing the data to + * {@link AssistStructure}; will be an empty stub if the application has disabled assist * by marking its window as secure. */ public @Nullable AssistStructure getAssistStructure() { diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index 4edff27d0ced..0b50192bfa48 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -646,7 +646,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { e.fillInStackTrace(); Log.w(TAG, "New hash " + hash + " is before end of array hash " + mHashes[index-1] - + " at index " + index + " key " + key, e); + + " at index " + index + (DEBUG ? " key " + key : ""), e); put(key, value); return; } diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 9d1bf171128e..fb534c7885e4 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -435,7 +435,10 @@ public class UiTranslationController { if (view.getViewTranslationResponse() != null && view.getViewTranslationResponse().equals(response)) { if (callback instanceof TextViewTranslationCallback) { - if (((TextViewTranslationCallback) callback).isShowingTranslation()) { + TextViewTranslationCallback textViewCallback = + (TextViewTranslationCallback) callback; + if (textViewCallback.isShowingTranslation() + || textViewCallback.isAnimationRunning()) { if (DEBUG) { Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId + ". Ignoring."); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 3d4d9eca6b16..dfd853acaf0d 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -3696,18 +3696,21 @@ public class RemoteViews implements Parcelable, Filter { } private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) { + if (hierarchyRoot == null) { + mBitmapCache = src.mBitmapCache; + mApplicationInfoCache = src.mApplicationInfoCache; + } else { + mBitmapCache = hierarchyRoot.mBitmapCache; + mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache; + } if (hierarchyRoot == null || src.mIsRoot) { // If there's no provided root, or if src was itself a root, then this RemoteViews is // the root of the new hierarchy. mIsRoot = true; - mBitmapCache = new BitmapCache(); - mApplicationInfoCache = new ApplicationInfoCache(); hierarchyRoot = this; } else { // Otherwise, we're a descendant in the hierarchy. mIsRoot = false; - mBitmapCache = hierarchyRoot.mBitmapCache; - mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache; } mApplication = src.mApplication; mLayoutId = src.mLayoutId; diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java index 4a78f3ee6fac..942be21b1ade 100644 --- a/core/java/android/widget/TextViewTranslationCallback.java +++ b/core/java/android/widget/TextViewTranslationCallback.java @@ -46,6 +46,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { private TranslationTransformationMethod mTranslationTransformation; private boolean mIsShowingTranslation = false; + private boolean mAnimationRunning = false; private boolean mIsTextPaddingEnabled = false; private CharSequence mPaddedText; private int mAnimationDurationMillis = 250; // default value @@ -92,6 +93,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { (TextView) view, () -> { mIsShowingTranslation = true; + mAnimationRunning = false; // TODO(b/178353965): well-handle setTransformationMethod. ((TextView) view).setTransformationMethod(transformation); }); @@ -124,6 +126,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { (TextView) view, () -> { mIsShowingTranslation = false; + mAnimationRunning = false; ((TextView) view).setTransformationMethod(transformation); }); if (!TextUtils.isEmpty(mContentDescription)) { @@ -162,6 +165,13 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { return mIsShowingTranslation; } + /** + * Returns whether the view is running animation to show or hide the translation. + */ + public boolean isAnimationRunning() { + return mAnimationRunning; + } + @Override public void enableContentPadding() { mIsTextPaddingEnabled = true; @@ -230,6 +240,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { mAnimator.end(); // Note: mAnimator is now null; do not use again here. } + mAnimationRunning = true; int fadedOutColor = colorWithAlpha(view.getCurrentTextColor(), 0); mAnimator = ValueAnimator.ofArgb(view.getCurrentTextColor(), fadedOutColor); mAnimator.addUpdateListener( diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.java b/core/java/android/window/PictureInPictureSurfaceTransaction.java index dbf7eb34e273..2bf2f3193789 100644 --- a/core/java/android/window/PictureInPictureSurfaceTransaction.java +++ b/core/java/android/window/PictureInPictureSurfaceTransaction.java @@ -19,6 +19,7 @@ package android.window; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Matrix; +import android.graphics.PointF; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; @@ -34,9 +35,10 @@ import java.util.Objects; * @hide */ public final class PictureInPictureSurfaceTransaction implements Parcelable { + private static final float NOT_SET = -1f; - public final float mPositionX; - public final float mPositionY; + public final float mAlpha; + public final PointF mPosition; public final float[] mFloat9; @@ -45,33 +47,37 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable { public final float mCornerRadius; - private final Rect mWindowCrop = new Rect(); + private final Rect mWindowCrop; - public PictureInPictureSurfaceTransaction(Parcel in) { - mPositionX = in.readFloat(); - mPositionY = in.readFloat(); + private PictureInPictureSurfaceTransaction(Parcel in) { + mAlpha = in.readFloat(); + mPosition = in.readTypedObject(PointF.CREATOR); mFloat9 = new float[9]; in.readFloatArray(mFloat9); mRotation = in.readFloat(); mCornerRadius = in.readFloat(); - mWindowCrop.set(Objects.requireNonNull(in.readTypedObject(Rect.CREATOR))); + mWindowCrop = in.readTypedObject(Rect.CREATOR); } - public PictureInPictureSurfaceTransaction(float positionX, float positionY, - float[] float9, float rotation, float cornerRadius, + private PictureInPictureSurfaceTransaction(float alpha, @Nullable PointF position, + @Nullable float[] float9, float rotation, float cornerRadius, @Nullable Rect windowCrop) { - mPositionX = positionX; - mPositionY = positionY; - mFloat9 = Arrays.copyOf(float9, 9); - mRotation = rotation; - mCornerRadius = cornerRadius; - if (windowCrop != null) { - mWindowCrop.set(windowCrop); + mAlpha = alpha; + mPosition = position; + if (float9 == null) { + mFloat9 = new float[9]; + Matrix.IDENTITY_MATRIX.getValues(mFloat9); + mRotation = 0; + } else { + mFloat9 = Arrays.copyOf(float9, 9); + mRotation = rotation; } + mCornerRadius = cornerRadius; + mWindowCrop = (windowCrop == null) ? null : new Rect(windowCrop); } public PictureInPictureSurfaceTransaction(PictureInPictureSurfaceTransaction other) { - this(other.mPositionX, other.mPositionY, + this(other.mAlpha, other.mPosition, other.mFloat9, other.mRotation, other.mCornerRadius, other.mWindowCrop); } @@ -82,13 +88,18 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable { return matrix; } + /** @return {@code true} if this transaction contains setting corner radius. */ + public boolean hasCornerRadiusSet() { + return mCornerRadius > 0; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PictureInPictureSurfaceTransaction)) return false; PictureInPictureSurfaceTransaction that = (PictureInPictureSurfaceTransaction) o; - return Objects.equals(mPositionX, that.mPositionX) - && Objects.equals(mPositionY, that.mPositionY) + return Objects.equals(mAlpha, that.mAlpha) + && Objects.equals(mPosition, that.mPosition) && Arrays.equals(mFloat9, that.mFloat9) && Objects.equals(mRotation, that.mRotation) && Objects.equals(mCornerRadius, that.mCornerRadius) @@ -97,7 +108,7 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable { @Override public int hashCode() { - return Objects.hash(mPositionX, mPositionY, Arrays.hashCode(mFloat9), + return Objects.hash(mAlpha, mPosition, Arrays.hashCode(mFloat9), mRotation, mCornerRadius, mWindowCrop); } @@ -108,8 +119,8 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeFloat(mPositionX); - out.writeFloat(mPositionY); + out.writeFloat(mAlpha); + out.writeTypedObject(mPosition, 0 /* flags */); out.writeFloatArray(mFloat9); out.writeFloat(mRotation); out.writeFloat(mCornerRadius); @@ -120,8 +131,8 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable { public String toString() { final Matrix matrix = getMatrix(); return "PictureInPictureSurfaceTransaction(" - + " posX=" + mPositionX - + " posY=" + mPositionY + + " alpha=" + mAlpha + + " position=" + mPosition + " matrix=" + matrix.toShortString() + " rotation=" + mRotation + " cornerRadius=" + mCornerRadius @@ -134,11 +145,20 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable { @NonNull SurfaceControl surfaceControl, @NonNull SurfaceControl.Transaction tx) { final Matrix matrix = surfaceTransaction.getMatrix(); - tx.setMatrix(surfaceControl, matrix, new float[9]) - .setPosition(surfaceControl, - surfaceTransaction.mPositionX, surfaceTransaction.mPositionY) - .setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop) - .setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius); + tx.setMatrix(surfaceControl, matrix, new float[9]); + if (surfaceTransaction.mPosition != null) { + tx.setPosition(surfaceControl, + surfaceTransaction.mPosition.x, surfaceTransaction.mPosition.y); + } + if (surfaceTransaction.mWindowCrop != null) { + tx.setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop); + } + if (surfaceTransaction.hasCornerRadiusSet()) { + tx.setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius); + } + if (surfaceTransaction.mAlpha != NOT_SET) { + tx.setAlpha(surfaceControl, surfaceTransaction.mAlpha); + } } public static final @android.annotation.NonNull Creator<PictureInPictureSurfaceTransaction> @@ -151,4 +171,44 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable { return new PictureInPictureSurfaceTransaction[size]; } }; + + public static class Builder { + private float mAlpha = NOT_SET; + private PointF mPosition; + private float[] mFloat9; + private float mRotation; + private float mCornerRadius = NOT_SET; + private Rect mWindowCrop; + + public Builder setAlpha(float alpha) { + mAlpha = alpha; + return this; + } + + public Builder setPosition(float x, float y) { + mPosition = new PointF(x, y); + return this; + } + + public Builder setTransform(@NonNull float[] float9, float rotation) { + mFloat9 = Arrays.copyOf(float9, 9); + mRotation = rotation; + return this; + } + + public Builder setCornerRadius(float cornerRadius) { + mCornerRadius = cornerRadius; + return this; + } + + public Builder setWindowCrop(@NonNull Rect windowCrop) { + mWindowCrop = new Rect(windowCrop); + return this; + } + + public PictureInPictureSurfaceTransaction build() { + return new PictureInPictureSurfaceTransaction(mAlpha, mPosition, + mFloat9, mRotation, mCornerRadius, mWindowCrop); + } + } } diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java index b331a9e81e27..4ba7ef26e9cb 100644 --- a/core/java/android/window/WindowTokenClient.java +++ b/core/java/android/window/WindowTokenClient.java @@ -15,7 +15,6 @@ */ package android.window; -import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets; import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; import static android.window.ConfigurationHelper.isDifferentDisplay; import static android.window.ConfigurationHelper.shouldUpdateResources; @@ -222,14 +221,7 @@ public class WindowTokenClient extends IWindowToken.Stub { () -> windowContext.dispatchConfigurationChanged(newConfig)); } - // Dispatch onConfigurationChanged only if there's a significant public change to - // make it compatible with the original behavior. - final Configuration[] sizeConfigurations = context.getResources() - .getSizeConfigurations(); - final SizeConfigurationBuckets buckets = sizeConfigurations != null - ? new SizeConfigurationBuckets(sizeConfigurations) : null; - final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets); - + final int diff = mConfiguration.diffPublicOnly(newConfig); if (shouldReportConfigChange && diff != 0 && context instanceof WindowProviderService) { final WindowProviderService windowProviderService = (WindowProviderService) context; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 786af5f0823e..7bb1ed8abc0a 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -163,9 +163,6 @@ public class ChooserActivity extends ResolverActivity implements private AppPredictor mWorkAppPredictor; private boolean mShouldDisplayLandscape; - private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4; - private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8; - @UnsupportedAppUsage public ChooserActivity() { } @@ -275,6 +272,7 @@ public class ChooserActivity extends ResolverActivity implements private int mCurrAvailableWidth = 0; private int mLastNumberOfChildren = -1; + private int mMaxTargetsPerRow = 1; private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment"; @@ -741,8 +739,9 @@ public class ChooserActivity extends ResolverActivity implements mCallerChooserTargets = targets; } - mShouldDisplayLandscape = shouldDisplayLandscape( - getResources().getConfiguration().orientation); + mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row); + mShouldDisplayLandscape = + shouldDisplayLandscape(getResources().getConfiguration().orientation); setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false)); super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); @@ -916,7 +915,7 @@ public class ChooserActivity extends ResolverActivity implements adapter, getPersonalProfileUserHandle(), /* workProfileUserHandle= */ null, - isSendAction(getTargetIntent()), getMaxTargetsPerRow()); + isSendAction(getTargetIntent()), mMaxTargetsPerRow); } private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles( @@ -945,7 +944,7 @@ public class ChooserActivity extends ResolverActivity implements selectedProfile, getPersonalProfileUserHandle(), getWorkProfileUserHandle(), - isSendAction(getTargetIntent()), getMaxTargetsPerRow()); + isSendAction(getTargetIntent()), mMaxTargetsPerRow); } private int findSelectedProfile() { @@ -1107,6 +1106,7 @@ public class ChooserActivity extends ResolverActivity implements } mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation); + mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row); adjustPreviewWidth(newConfig.orientation, null); updateStickyContentPreview(); } @@ -2690,7 +2690,7 @@ public class ChooserActivity extends ResolverActivity implements // and b/150936654 recyclerView.setAdapter(gridAdapter); ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount( - getMaxTargetsPerRow()); + mMaxTargetsPerRow); } UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle(); @@ -2855,7 +2855,7 @@ public class ChooserActivity extends ResolverActivity implements @Override // ChooserListCommunicator public int getMaxRankedTargets() { - return getMaxTargetsPerRow(); + return mMaxTargetsPerRow; } @Override // ChooserListCommunicator @@ -3203,13 +3203,6 @@ public class ChooserActivity extends ResolverActivity implements } } - int getMaxTargetsPerRow() { - int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT; - if (mShouldDisplayLandscape) { - maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE; - } - return maxTargets; - } /** * Adapter for all types of items and targets in ShareSheet. * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the @@ -3277,7 +3270,11 @@ public class ChooserActivity extends ResolverActivity implements return false; } - int newWidth = width / getMaxTargetsPerRow(); + // Limit width to the maximum width of the chooser activity + int maxWidth = getResources().getDimensionPixelSize(R.dimen.chooser_width); + width = Math.min(maxWidth, width); + + int newWidth = width / mMaxTargetsPerRow; if (newWidth != mChooserTargetWidth) { mChooserTargetWidth = newWidth; return true; @@ -3312,7 +3309,7 @@ public class ChooserActivity extends ResolverActivity implements + getAzLabelRowCount() + Math.ceil( (float) mChooserListAdapter.getAlphaTargetCount() - / getMaxTargetsPerRow()) + / mMaxTargetsPerRow) ); } @@ -3352,7 +3349,7 @@ public class ChooserActivity extends ResolverActivity implements public int getCallerAndRankedTargetRowCount() { return (int) Math.ceil( ((float) mChooserListAdapter.getCallerTargetCount() - + mChooserListAdapter.getRankedTargetCount()) / getMaxTargetsPerRow()); + + mChooserListAdapter.getRankedTargetCount()) / mMaxTargetsPerRow); } // There can be at most one row in the listview, that is internally @@ -3551,7 +3548,7 @@ public class ChooserActivity extends ResolverActivity implements parentGroup.addView(row2); mDirectShareViewHolder = new DirectShareViewHolder(parentGroup, - Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType); + Lists.newArrayList(row1, row2), mMaxTargetsPerRow, viewType); loadViewsIntoGroup(mDirectShareViewHolder); return mDirectShareViewHolder; @@ -3559,7 +3556,7 @@ public class ChooserActivity extends ResolverActivity implements ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent, false); ItemGroupViewHolder holder = - new SingleRowViewHolder(row, getMaxTargetsPerRow(), viewType); + new SingleRowViewHolder(row, mMaxTargetsPerRow, viewType); loadViewsIntoGroup(holder); return holder; @@ -3651,7 +3648,7 @@ public class ChooserActivity extends ResolverActivity implements final int serviceCount = mChooserListAdapter.getServiceTargetCount(); final int serviceRows = (int) Math.ceil((float) serviceCount / getMaxRankedTargets()); if (position < serviceRows) { - return position * getMaxTargetsPerRow(); + return position * mMaxTargetsPerRow; } position -= serviceRows; @@ -3660,7 +3657,7 @@ public class ChooserActivity extends ResolverActivity implements + mChooserListAdapter.getRankedTargetCount(); final int callerAndRankedRows = getCallerAndRankedTargetRowCount(); if (position < callerAndRankedRows) { - return serviceCount + position * getMaxTargetsPerRow(); + return serviceCount + position * mMaxTargetsPerRow; } position -= getAzLabelRowCount() + callerAndRankedRows; @@ -3673,7 +3670,7 @@ public class ChooserActivity extends ResolverActivity implements if (mDirectShareViewHolder != null && canExpandDirectShare) { mDirectShareViewHolder.handleScroll( mChooserMultiProfilePagerAdapter.getActiveAdapterView(), y, oldy, - getMaxTargetsPerRow()); + mMaxTargetsPerRow); } } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index c750eadf845a..6faa04690473 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -366,6 +366,7 @@ message ActivityRecordProto { optional bool pip_auto_enter_enabled = 31; optional bool in_size_compat_mode = 32; optional float min_aspect_ratio = 33; + optional bool provides_max_bounds = 34; } /* represents WindowToken */ diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 90caaccfbff4..933b4d243df9 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -20,8 +20,10 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_gravity="center" android:maxCollapsedHeight="0dp" android:maxCollapsedHeightSmall="56dp" + android:maxWidth="@dimen/chooser_width" android:id="@id/contentPanel"> <RelativeLayout diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml index 0d04d7f319b8..52692b03a44d 100644 --- a/core/res/res/layout/chooser_grid_preview_image.xml +++ b/core/res/res/layout/chooser_grid_preview_image.xml @@ -34,7 +34,7 @@ <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView" android:id="@+id/content_preview_image_1_large" android:layout_width="120dp" - android:layout_height="140dp" + android:layout_height="104dp" android:layout_alignParentTop="true" android:adjustViewBounds="true" android:gravity="center" @@ -44,7 +44,7 @@ android:id="@+id/content_preview_image_2_large" android:visibility="gone" android:layout_width="120dp" - android:layout_height="140dp" + android:layout_height="104dp" android:layout_alignParentTop="true" android:layout_toRightOf="@id/content_preview_image_1_large" android:layout_marginLeft="10dp" diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml index 861e329f2de9..624581aba7dd 100644 --- a/core/res/res/values-sw600dp/config.xml +++ b/core/res/res/values-sw600dp/config.xml @@ -56,5 +56,7 @@ to be aligned to one side of the screen when in landscape mode. --> <bool name="config_enableDynamicKeyguardPositioning">true</bool> + <integer name="config_chooser_max_targets_per_row">6</integer> + </resources> diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml index 02ed848fc7f1..e8f15fd022d7 100644 --- a/core/res/res/values-sw600dp/dimens.xml +++ b/core/res/res/values-sw600dp/dimens.xml @@ -110,4 +110,7 @@ <dimen name="immersive_mode_cling_width">380dp</dimen> <dimen name="floating_toolbar_preferred_width">544dp</dimen> + + <dimen name="chooser_width">624dp</dimen> + </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4d851644903e..1c31b1b76f85 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5284,4 +5284,6 @@ <string name="config_work_badge_path_24" translatable="false"> M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z </string> + + <integer name="config_chooser_max_targets_per_row">4</integer> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 0706d8a8e4c6..f331f1ab720f 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -900,7 +900,8 @@ <dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen> <!-- chooser/resolver (sharesheet) spacing --> - <dimen name="chooser_corner_radius">16dp</dimen> + <dimen name="chooser_width">412dp</dimen> + <dimen name="chooser_corner_radius">28dp</dimen> <dimen name="chooser_row_text_option_translate">25dp</dimen> <dimen name="chooser_view_spacing">18dp</dimen> <dimen name="chooser_edge_margin_thin">16dp</dimen> @@ -917,7 +918,7 @@ <dimen name="resolver_icon_size">32dp</dimen> <dimen name="resolver_button_bar_spacing">0dp</dimen> <dimen name="resolver_badge_size">18dp</dimen> - <dimen name="resolver_icon_margin">16dp</dimen> + <dimen name="resolver_icon_margin">8dp</dimen> <dimen name="resolver_small_margin">18dp</dimen> <dimen name="resolver_edge_margin">24dp</dimen> <dimen name="resolver_elevation">1dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 54282bef00f0..b017a30cb5f2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -275,6 +275,7 @@ <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" /> <java-symbol type="bool" name="config_avoidGfxAccel" /> <java-symbol type="bool" name="config_bluetooth_address_validation" /> + <java-symbol type="integer" name="config_chooser_max_targets_per_row" /> <java-symbol type="bool" name="config_flipToScreenOffEnabled" /> <java-symbol type="integer" name="config_flipToScreenOffMaxLatencyMicros" /> <java-symbol type="bool" name="config_bluetooth_sco_off_call" /> @@ -2844,6 +2845,7 @@ <java-symbol type="layout" name="date_picker_month_item_material" /> <java-symbol type="id" name="month_view" /> <java-symbol type="integer" name="config_zen_repeat_callers_threshold" /> + <java-symbol type="dimen" name="chooser_width" /> <java-symbol type="dimen" name="chooser_corner_radius" /> <java-symbol type="string" name="chooser_no_direct_share_targets" /> <java-symbol type="drawable" name="chooser_row_layer_list" /> diff --git a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java index 0456029f45a0..98485c024a59 100644 --- a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java +++ b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java @@ -18,8 +18,7 @@ package android.content.pm; import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import android.annotation.Nullable; import android.platform.test.annotations.Presubmit; @@ -146,24 +145,17 @@ public final class ConstrainDisplayApisConfigTest { private static void testNeverConstrainDisplayApis(String packageName, long version, boolean expected) { - boolean result = ConstrainDisplayApisConfig.neverConstrainDisplayApis( - buildApplicationInfo(packageName, version)); - if (expected) { - assertTrue(result); - } else { - assertFalse(result); - } + ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig(); + assertEquals(expected, + config.getNeverConstrainDisplayApis(buildApplicationInfo(packageName, version))); } private static void testAlwaysConstrainDisplayApis(String packageName, long version, boolean expected) { - boolean result = ConstrainDisplayApisConfig.alwaysConstrainDisplayApis( - buildApplicationInfo(packageName, version)); - if (expected) { - assertTrue(result); - } else { - assertFalse(result); - } + ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig(); + + assertEquals(expected, + config.getAlwaysConstrainDisplayApis(buildApplicationInfo(packageName, version))); } private static ApplicationInfo buildApplicationInfo(String packageName, long version) { diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 756425eedabb..0b8dc3fe0dec 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -180,6 +180,7 @@ <assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" /> <assign-permission name="android.permission.MANAGE_APP_OPS_MODES" uid="cameraserver" /> <assign-permission name="android.permission.OBSERVE_SENSOR_PRIVACY" uid="cameraserver" /> + <assign-permission name="android.permission.REAL_GET_TASKS" uid="cameraserver" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" /> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index af19bd0a79ac..b8e8b0114b47 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -295,11 +295,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) { + SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity, + secondaryContainer, splitRule); + // Remove container later to prevent pinning escaping toast showing in lock task mode. if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) { removeExistingSecondaryContainers(wct, primaryContainer); } - SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity, - secondaryContainer, splitRule); mSplitContainers.add(splitContainer); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 81be21cbd7aa..ade573132eef 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -112,8 +112,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryContainer.setLastRequestedBounds(secondaryRectBounds); // Set adjacent to each other so that the containers below will be invisible. - setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), - secondaryContainer.getTaskFragmentToken(), rule); + setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -149,8 +148,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryActivity, secondaryRectBounds, primaryContainer); // Set adjacent to each other so that the containers below will be invisible. - setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), - secondaryContainer.getTaskFragmentToken(), rule); + setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -269,8 +267,22 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer(); resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds); - setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), - secondaryContainer.getTaskFragmentToken(), rule); + setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); + } + + private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentContainer primaryContainer, + @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) { + final Rect parentBounds = getParentContainerBounds(primaryContainer); + // Clear adjacent TaskFragments if the container is shown in fullscreen, or the + // secondaryContainer could not be finished. + if (!shouldShowSideBySide(parentBounds, splitRule)) { + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + null /* secondary */, null /* splitRule */); + } else { + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + secondaryContainer.getTaskFragmentToken(), splitRule); + } } /** diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml index 22cd384e1be0..26848b13a1bc 100644 --- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml +++ b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml @@ -16,6 +16,6 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - <solid android:color="@color/size_compat_background"/> - <corners android:radius="@dimen/size_compat_hint_corner_radius"/> + <solid android:color="@color/compat_controls_background"/> + <corners android:radius="@dimen/compat_hint_corner_radius"/> </shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml index af9063a94afb..0e0ca37aaf25 100644 --- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml +++ b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml @@ -15,11 +15,11 @@ ~ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="@dimen/size_compat_hint_point_width" + android:width="@dimen/compat_hint_point_width" android:height="8dp" android:viewportWidth="10" android:viewportHeight="8"> <path - android:fillColor="@color/size_compat_background" + android:fillColor="@color/compat_controls_background" android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/> </vector> diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml index 18caa3582537..ab74e43472c3 100644 --- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml +++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml @@ -20,16 +20,16 @@ android:viewportWidth="48" android:viewportHeight="48"> <path - android:fillColor="@color/size_compat_background" + android:fillColor="@color/compat_controls_background" android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" /> <group android:translateX="12" android:translateY="12"> <path - android:fillColor="@color/size_compat_text" + android:fillColor="@color/compat_controls_text" android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/> <path - android:fillColor="@color/size_compat_text" + android:fillColor="@color/compat_controls_text" android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/> </group> </vector> diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml new file mode 100644 index 000000000000..c04e258ea784 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:clipToPadding="false" + android:paddingEnd="@dimen/compat_hint_padding_end" + android:paddingBottom="5dp" + android:clickable="true"> + + <TextView + android:id="@+id/compat_mode_hint_text" + android:layout_width="188dp" + android:layout_height="wrap_content" + android:lineSpacingExtra="4sp" + android:background="@drawable/compat_hint_bubble" + android:padding="16dp" + android:textAlignment="viewStart" + android:textColor="@color/compat_controls_text" + android:textSize="14sp"/> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:src="@drawable/compat_hint_point" + android:paddingHorizontal="@dimen/compat_hint_corner_radius" + android:contentDescription="@null"/> + +</LinearLayout> diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml index 82ebee263a64..6f946b25eaa5 100644 --- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml +++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml @@ -14,10 +14,15 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.wm.shell.sizecompatui.SizeCompatRestartButton +<com.android.wm.shell.compatui.CompatUILayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="bottom|end"> + + <include android:id="@+id/size_compat_hint" + layout="@layout/compat_mode_hint"/> <FrameLayout android:layout_width="@dimen/size_compat_button_width" @@ -36,4 +41,4 @@ </FrameLayout> -</com.android.wm.shell.sizecompatui.SizeCompatRestartButton> +</com.android.wm.shell.compatui.CompatUILayout> diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml deleted file mode 100644 index d0e7c42dbf8b..000000000000 --- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml +++ /dev/null @@ -1,58 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 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. ---> -<com.android.wm.shell.sizecompatui.SizeCompatHintPopup - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - - <FrameLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:clipToPadding="false" - android:paddingBottom="5dp"> - - <LinearLayout - android:id="@+id/size_compat_hint_popup" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" - android:clickable="true"> - - <TextView - android:layout_width="188dp" - android:layout_height="wrap_content" - android:lineSpacingExtra="4sp" - android:background="@drawable/size_compat_hint_bubble" - android:padding="16dp" - android:text="@string/restart_button_description" - android:textAlignment="viewStart" - android:textColor="@color/size_compat_text" - android:textSize="14sp"/> - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="end" - android:src="@drawable/size_compat_hint_point" - android:paddingHorizontal="@dimen/size_compat_hint_corner_radius" - android:contentDescription="@null"/> - - </LinearLayout> - - </FrameLayout> - -</com.android.wm.shell.sizecompatui.SizeCompatHintPopup> diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml index 23a21724e43d..cf596f7d15dc 100644 --- a/libs/WindowManager/Shell/res/values/colors.xml +++ b/libs/WindowManager/Shell/res/values/colors.xml @@ -30,9 +30,9 @@ <color name="bubbles_dark">@color/GM2_grey_800</color> <color name="bubbles_icon_tint">@color/GM2_grey_700</color> - <!-- Size Compat Restart Button --> - <color name="size_compat_background">@android:color/system_neutral1_800</color> - <color name="size_compat_text">@android:color/system_neutral1_50</color> + <!-- Compat controls UI --> + <color name="compat_controls_background">@android:color/system_neutral1_800</color> + <color name="compat_controls_text">@android:color/system_neutral1_50</color> <!-- GM2 colors --> <color name="GM2_grey_200">#E8EAED</color> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 9e77578eafd8..18e91f41a698 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -206,11 +206,15 @@ <!-- The height of the size compat restart button including padding. --> <dimen name="size_compat_button_height">64dp</dimen> - <!-- The radius of the corners of the size compat hint bubble. --> - <dimen name="size_compat_hint_corner_radius">28dp</dimen> + <!-- The radius of the corners of the compat hint bubble. --> + <dimen name="compat_hint_corner_radius">28dp</dimen> - <!-- The width of the size compat hint point. --> - <dimen name="size_compat_hint_point_width">10dp</dimen> + <!-- The width of the compat hint point. --> + <dimen name="compat_hint_point_width">10dp</dimen> + + <!-- The end padding for the compat hint. Computed as (size_compat_button_width / 2 + - compat_hint_corner_radius - compat_hint_point_width /2). --> + <dimen name="compat_hint_padding_end">7dp</dimen> <!-- The width of the brand image on staring surface. --> <dimen name="starting_surface_brand_image_width">200dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 8e98b82088dc..8b3a35688f11 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -52,8 +52,8 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.recents.RecentTasksController; -import com.android.wm.shell.sizecompatui.SizeCompatUIController; import com.android.wm.shell.startingsurface.StartingWindowController; import java.io.PrintWriter; @@ -69,7 +69,7 @@ import java.util.function.Consumer; * TODO(b/167582004): may consider consolidating this class and TaskOrganizer */ public class ShellTaskOrganizer extends TaskOrganizer implements - SizeCompatUIController.SizeCompatUICallback { + CompatUIController.CompatUICallback { // Intentionally using negative numbers here so the positive numbers can be used // for task id specific listeners that will be added later. @@ -98,9 +98,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements default void onTaskInfoChanged(RunningTaskInfo taskInfo) {} default void onTaskVanished(RunningTaskInfo taskInfo) {} default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {} - /** Whether this task listener supports size compat UI. */ - default boolean supportSizeCompatUI() { - // All TaskListeners should support size compat except PIP. + /** Whether this task listener supports compat UI. */ + default boolean supportCompatUI() { + // All TaskListeners should support compat UI except PIP. return true; } /** Attaches the a child window surface to the task surface. */ @@ -159,11 +159,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements private StartingWindowController mStartingWindow; /** - * In charge of showing size compat UI. Can be {@code null} if device doesn't support size + * In charge of showing compat UI. Can be {@code null} if device doesn't support size * compat. */ @Nullable - private final SizeCompatUIController mSizeCompatUI; + private final CompatUIController mCompatUI; @Nullable private final Optional<RecentTasksController> mRecentTasks; @@ -172,32 +172,32 @@ public class ShellTaskOrganizer extends TaskOrganizer implements private RunningTaskInfo mLastFocusedTaskInfo; public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) { - this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */, + this(null /* taskOrganizerController */, mainExecutor, context, null /* compatUI */, Optional.empty() /* recentTasksController */); } public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable - SizeCompatUIController sizeCompatUI) { - this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI, + CompatUIController compatUI) { + this(null /* taskOrganizerController */, mainExecutor, context, compatUI, Optional.empty() /* recentTasksController */); } public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable - SizeCompatUIController sizeCompatUI, + CompatUIController compatUI, Optional<RecentTasksController> recentTasks) { - this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI, + this(null /* taskOrganizerController */, mainExecutor, context, compatUI, recentTasks); } @VisibleForTesting ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor, - Context context, @Nullable SizeCompatUIController sizeCompatUI, + Context context, @Nullable CompatUIController compatUI, Optional<RecentTasksController> recentTasks) { super(taskOrganizerController, mainExecutor); - mSizeCompatUI = sizeCompatUI; + mCompatUI = compatUI; mRecentTasks = recentTasks; - if (sizeCompatUI != null) { - sizeCompatUI.setSizeCompatUICallback(this); + if (compatUI != null) { + compatUI.setCompatUICallback(this); } } @@ -428,7 +428,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements listener.onTaskAppeared(info.getTaskInfo(), info.getLeash()); } notifyLocusVisibilityIfNeeded(info.getTaskInfo()); - notifySizeCompatUI(info.getTaskInfo(), listener); + notifyCompatUI(info.getTaskInfo(), listener); } /** @@ -459,8 +459,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } notifyLocusVisibilityIfNeeded(taskInfo); if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) { - // Notify the size compat UI if the listener or task info changed. - notifySizeCompatUI(taskInfo, newListener); + // Notify the compat UI if the listener or task info changed. + notifyCompatUI(taskInfo, newListener); } if (data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode()) { // Notify the recent tasks when a task changes windowing modes @@ -504,8 +504,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements listener.onTaskVanished(taskInfo); } notifyLocusVisibilityIfNeeded(taskInfo); - // Pass null for listener to remove the size compat UI on this task if there is any. - notifySizeCompatUI(taskInfo, null /* taskListener */); + // Pass null for listener to remove the compat UI on this task if there is any. + notifyCompatUI(taskInfo, null /* taskListener */); // Notify the recent tasks that a task has been removed mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo)); } @@ -618,28 +618,28 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } /** - * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task + * Notifies {@link CompatUIController} about the compat info changed on the give Task * to update the UI accordingly. * * @param taskInfo the new Task info * @param taskListener listener to handle the Task Surface placement. {@code null} if task is * vanished. */ - private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) { - if (mSizeCompatUI == null) { + private void notifyCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) { + if (mCompatUI == null) { return; } - // The task is vanished or doesn't support size compat UI, notify to remove size compat UI + // The task is vanished or doesn't support compat UI, notify to remove compat UI // on this Task if there is any. - if (taskListener == null || !taskListener.supportSizeCompatUI() + if (taskListener == null || !taskListener.supportCompatUI() || !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) { - mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, + mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, null /* taskConfig */, null /* taskListener */); return; } - mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, + mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, taskInfo.configuration, taskListener); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 519a856538c7..cd635c10fd8e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -328,6 +328,7 @@ public class BubbleData { if (prevBubble == null) { // Create a new bubble bubble.setSuppressFlyout(suppressFlyout); + bubble.markUpdatedAt(mTimeSource.currentTimeMillis()); doAdd(bubble); trim(); } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java index a703114194a0..99dbfe01964c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.wm.shell.sizecompatui; +package com.android.wm.shell.compatui; import com.android.wm.shell.common.annotations.ExternalThread; /** - * Interface to engage size compat UI. + * Interface to engage compat UI. */ @ExternalThread -public interface SizeCompatUI { +public interface CompatUI { /** - * Called when the keyguard occluded state changes. Removes all size compat UIs if the + * Called when the keyguard occluded state changes. Removes all compat UIs if the * keyguard is now occluded. * @param occluded indicates if the keyguard is now occluded. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index e06070ab12e5..e0b23873a980 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.sizecompatui; +package com.android.wm.shell.compatui; import android.annotation.Nullable; import android.content.Context; @@ -48,20 +48,20 @@ import java.util.function.Predicate; /** * Controls to show/update restart-activity buttons on Tasks based on whether the foreground - * activities are in size compatibility mode. + * activities are in compatibility mode. */ -public class SizeCompatUIController implements OnDisplaysChangedListener, +public class CompatUIController implements OnDisplaysChangedListener, DisplayImeController.ImePositionProcessor { /** Callback for size compat UI interaction. */ - public interface SizeCompatUICallback { + public interface CompatUICallback { /** Called when the size compat restart button appears. */ void onSizeCompatRestartButtonAppeared(int taskId); /** Called when the size compat restart button is clicked. */ void onSizeCompatRestartButtonClicked(int taskId); } - private static final String TAG = "SizeCompatUIController"; + private static final String TAG = "CompatUIController"; /** Whether the IME is shown on display id. */ private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1); @@ -71,7 +71,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, new SparseArray<>(0); /** The showing UIs by task id. */ - private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0); + private final SparseArray<CompatUIWindowManager> mActiveLayouts = new SparseArray<>(0); /** Avoid creating display context frequently for non-default display. */ private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0); @@ -82,17 +82,17 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, private final DisplayImeController mImeController; private final SyncTransactionQueue mSyncQueue; private final ShellExecutor mMainExecutor; - private final SizeCompatUIImpl mImpl = new SizeCompatUIImpl(); + private final CompatUIImpl mImpl = new CompatUIImpl(); - private SizeCompatUICallback mCallback; + private CompatUICallback mCallback; /** Only show once automatically in the process life. */ private boolean mHasShownHint; - /** Indicates if the keyguard is currently occluded, in which case size compat UIs shouldn't + /** Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't * be shown. */ private boolean mKeyguardOccluded; - public SizeCompatUIController(Context context, + public CompatUIController(Context context, DisplayController displayController, DisplayInsetsController displayInsetsController, DisplayImeController imeController, @@ -108,35 +108,36 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, mImeController.addPositionProcessor(this); } - public SizeCompatUI asSizeCompatUI() { + /** Returns implementation of {@link CompatUI}. */ + public CompatUI asCompatUI() { return mImpl; } /** Sets the callback for UI interactions. */ - public void setSizeCompatUICallback(SizeCompatUICallback callback) { + public void setCompatUICallback(CompatUICallback callback) { mCallback = callback; } /** - * Called when the Task info changed. Creates and updates the size compat UI if there is an + * Called when the Task info changed. Creates and updates the compat UI if there is an * activity in size compat, or removes the UI if there is no size compat activity. * * @param displayId display the task and activity are in. * @param taskId task the activity is in. - * @param taskConfig task config to place the size compat UI with. + * @param taskConfig task config to place the compat UI with. * @param taskListener listener to handle the Task Surface placement. */ - public void onSizeCompatInfoChanged(int displayId, int taskId, + public void onCompatInfoChanged(int displayId, int taskId, @Nullable Configuration taskConfig, @Nullable ShellTaskOrganizer.TaskListener taskListener) { if (taskConfig == null || taskListener == null) { - // Null token means the current foreground activity is not in size compatibility mode. + // Null token means the current foreground activity is not in compatibility mode. removeLayout(taskId); } else if (mActiveLayouts.contains(taskId)) { // UI already exists, update the UI layout. updateLayout(taskId, taskConfig, taskListener); } else { - // Create a new size compat UI. + // Create a new compat UI. createLayout(displayId, taskId, taskConfig, taskListener); } } @@ -151,7 +152,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, mDisplayContextCache.remove(displayId); removeOnInsetsChangedListener(displayId); - // Remove all size compat UIs on the removed display. + // Remove all compat UIs on the removed display. final List<Integer> toRemoveTaskIds = new ArrayList<>(); forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId())); for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) { @@ -194,7 +195,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, mDisplaysWithIme.remove(displayId); } - // Hide the size compat UIs when input method is showing. + // Hide the compat UIs when input method is showing. forAllLayoutsOnDisplay(displayId, layout -> layout.updateVisibility(showOnDisplay(displayId))); } @@ -202,7 +203,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, @VisibleForTesting void onKeyguardOccludedChanged(boolean occluded) { mKeyguardOccluded = occluded; - // Hide the size compat UIs when keyguard is occluded. + // Hide the compat UIs when keyguard is occluded. forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId()))); } @@ -222,34 +223,34 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, return; } - final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig, - taskListener); - mActiveLayouts.put(taskId, layout); - layout.createSizeCompatButton(showOnDisplay(displayId)); + final CompatUIWindowManager compatUIWindowManager = + createLayout(context, displayId, taskId, taskConfig, taskListener); + mActiveLayouts.put(taskId, compatUIWindowManager); + compatUIWindowManager.createLayout(showOnDisplay(displayId)); } @VisibleForTesting - SizeCompatUILayout createLayout(Context context, int displayId, int taskId, + CompatUIWindowManager createLayout(Context context, int displayId, int taskId, Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) { - final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, mCallback, context, - taskConfig, taskId, taskListener, mDisplayController.getDisplayLayout(displayId), - mHasShownHint); + final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context, + taskConfig, mSyncQueue, mCallback, taskId, taskListener, + mDisplayController.getDisplayLayout(displayId), mHasShownHint); // Only show hint for the first time. mHasShownHint = true; - return layout; + return compatUIWindowManager; } private void updateLayout(int taskId, Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) { - final SizeCompatUILayout layout = mActiveLayouts.get(taskId); + final CompatUIWindowManager layout = mActiveLayouts.get(taskId); if (layout == null) { return; } - layout.updateSizeCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId())); + layout.updateCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId())); } private void removeLayout(int taskId) { - final SizeCompatUILayout layout = mActiveLayouts.get(taskId); + final CompatUIWindowManager layout = mActiveLayouts.get(taskId); if (layout != null) { layout.release(); mActiveLayouts.remove(taskId); @@ -275,19 +276,19 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, return context; } - private void forAllLayoutsOnDisplay(int displayId, Consumer<SizeCompatUILayout> callback) { + private void forAllLayoutsOnDisplay(int displayId, Consumer<CompatUIWindowManager> callback) { forAllLayouts(layout -> layout.getDisplayId() == displayId, callback); } - private void forAllLayouts(Consumer<SizeCompatUILayout> callback) { + private void forAllLayouts(Consumer<CompatUIWindowManager> callback) { forAllLayouts(layout -> true, callback); } - private void forAllLayouts(Predicate<SizeCompatUILayout> condition, - Consumer<SizeCompatUILayout> callback) { + private void forAllLayouts(Predicate<CompatUIWindowManager> condition, + Consumer<CompatUIWindowManager> callback) { for (int i = 0; i < mActiveLayouts.size(); i++) { final int taskId = mActiveLayouts.keyAt(i); - final SizeCompatUILayout layout = mActiveLayouts.get(taskId); + final CompatUIWindowManager layout = mActiveLayouts.get(taskId); if (layout != null && condition.test(layout)) { callback.accept(layout); } @@ -298,11 +299,11 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, * The interface for calls from outside the Shell, within the host process. */ @ExternalThread - private class SizeCompatUIImpl implements SizeCompatUI { + private class CompatUIImpl implements CompatUI { @Override public void onKeyguardOccludedChanged(boolean occluded) { mMainExecutor.execute(() -> { - SizeCompatUIController.this.onKeyguardOccludedChanged(occluded); + CompatUIController.this.onKeyguardOccludedChanged(occluded); }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java new file mode 100644 index 000000000000..ea4f20968438 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 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.wm.shell.compatui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.wm.shell.R; + +/** + * Container for compat UI controls. + */ +public class CompatUILayout extends LinearLayout { + + private CompatUIWindowManager mWindowManager; + + public CompatUILayout(Context context) { + this(context, null); + } + + public CompatUILayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + void inject(CompatUIWindowManager windowManager) { + mWindowManager = windowManager; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + // Need to relayout after changes like hiding / showing a hint since they affect size. + // Doing this directly in setSizeCompatHintVisibility can result in flaky animation. + mWindowManager.relayout(); + } + + void setSizeCompatHintVisibility(boolean show) { + final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint); + int visibility = show ? View.VISIBLE : View.GONE; + if (sizeCompatHint.getVisibility() == visibility) { + return; + } + sizeCompatHint.setVisibility(visibility); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + final ImageButton restartButton = findViewById(R.id.size_compat_restart_button); + restartButton.setOnClickListener(view -> mWindowManager.onRestartButtonClicked()); + restartButton.setOnLongClickListener(view -> { + mWindowManager.onRestartButtonLongClicked(); + return true; + }); + + final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint); + ((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text)) + .setText(R.string.restart_button_description); + sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false)); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java new file mode 100644 index 000000000000..997ad04e3b57 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2021 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.wm.shell.compatui; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.Binder; +import android.util.Log; +import android.view.IWindow; +import android.view.LayoutInflater; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowlessWindowManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; + +/** + * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat + * controls. + */ +class CompatUIWindowManager extends WindowlessWindowManager { + + private static final String TAG = "CompatUIWindowManager"; + + private final SyncTransactionQueue mSyncQueue; + private final CompatUIController.CompatUICallback mCallback; + private final int mDisplayId; + private final int mTaskId; + private final Rect mStableBounds; + + private Context mContext; + private Configuration mTaskConfig; + private ShellTaskOrganizer.TaskListener mTaskListener; + private DisplayLayout mDisplayLayout; + + @VisibleForTesting + boolean mShouldShowHint; + + @Nullable + @VisibleForTesting + CompatUILayout mCompatUILayout; + + @Nullable + private SurfaceControlViewHost mViewHost; + @Nullable + private SurfaceControl mLeash; + + CompatUIWindowManager(Context context, Configuration taskConfig, + SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback, + int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, + boolean hasShownHint) { + super(taskConfig, null /* rootSurface */, null /* hostInputToken */); + mContext = context; + mSyncQueue = syncQueue; + mCallback = callback; + mTaskConfig = taskConfig; + mDisplayId = mContext.getDisplayId(); + mTaskId = taskId; + mTaskListener = taskListener; + mDisplayLayout = displayLayout; + mShouldShowHint = !hasShownHint; + mStableBounds = new Rect(); + mDisplayLayout.getStableBounds(mStableBounds); + } + + @Override + public void setConfiguration(Configuration configuration) { + super.setConfiguration(configuration); + mContext = mContext.createConfigurationContext(configuration); + } + + @Override + protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { + // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. + final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + .setContainerLayer() + .setName("CompatUILeash") + .setHidden(false) + .setCallsite("CompatUIWindowManager#attachToParentSurface"); + attachToParentSurface(builder); + mLeash = builder.build(); + b.setParent(mLeash); + } + + /** Creates the layout for compat controls. */ + void createLayout(boolean show) { + if (!show || mCompatUILayout != null) { + // Wait until compat controls should be visible. + return; + } + + initCompatUi(); + updateSurfacePosition(); + + mCallback.onSizeCompatRestartButtonAppeared(mTaskId); + } + + /** Called when compat info changed. */ + void updateCompatInfo(Configuration taskConfig, + ShellTaskOrganizer.TaskListener taskListener, boolean show) { + final Configuration prevTaskConfig = mTaskConfig; + final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener; + mTaskConfig = taskConfig; + mTaskListener = taskListener; + + // Update configuration. + mContext = mContext.createConfigurationContext(taskConfig); + setConfiguration(taskConfig); + + if (mCompatUILayout == null || prevTaskListener != taskListener) { + // TaskListener changed, recreate the layout for new surface parent. + release(); + createLayout(show); + return; + } + + if (!taskConfig.windowConfiguration.getBounds() + .equals(prevTaskConfig.windowConfiguration.getBounds())) { + // Reposition the UI surfaces. + updateSurfacePosition(); + } + + if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { + // Update layout for RTL. + mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection()); + updateSurfacePosition(); + } + } + + /** Called when the visibility of the UI should change. */ + void updateVisibility(boolean show) { + if (mCompatUILayout == null) { + // Layout may not have been created because it was hidden previously. + createLayout(show); + return; + } + + // Hide compat UIs when IME is showing. + final int newVisibility = show ? View.VISIBLE : View.GONE; + if (mCompatUILayout.getVisibility() != newVisibility) { + mCompatUILayout.setVisibility(newVisibility); + } + } + + /** Called when display layout changed. */ + void updateDisplayLayout(DisplayLayout displayLayout) { + final Rect prevStableBounds = mStableBounds; + final Rect curStableBounds = new Rect(); + displayLayout.getStableBounds(curStableBounds); + mDisplayLayout = displayLayout; + if (!prevStableBounds.equals(curStableBounds)) { + // Stable bounds changed, update UI surface positions. + updateSurfacePosition(); + mStableBounds.set(curStableBounds); + } + } + + /** Called when it is ready to be placed compat UI surface. */ + void attachToParentSurface(SurfaceControl.Builder b) { + mTaskListener.attachChildSurfaceToTask(mTaskId, b); + } + + /** Called when the restart button is clicked. */ + void onRestartButtonClicked() { + mCallback.onSizeCompatRestartButtonClicked(mTaskId); + } + + /** Called when the restart button is long clicked. */ + void onRestartButtonLongClicked() { + if (mCompatUILayout == null) { + return; + } + mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true); + } + + int getDisplayId() { + return mDisplayId; + } + + int getTaskId() { + return mTaskId; + } + + /** Releases the surface control and tears down the view hierarchy. */ + void release() { + mCompatUILayout = null; + + if (mViewHost != null) { + mViewHost.release(); + mViewHost = null; + } + + if (mLeash != null) { + final SurfaceControl leash = mLeash; + mSyncQueue.runInSync(t -> t.remove(leash)); + mLeash = null; + } + } + + void relayout() { + mViewHost.relayout(getWindowLayoutParams()); + updateSurfacePosition(); + } + + @VisibleForTesting + void updateSurfacePosition() { + if (mCompatUILayout == null || mLeash == null) { + return; + } + + // Use stable bounds to prevent controls from overlapping with system bars. + final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); + final Rect stableBounds = new Rect(); + mDisplayLayout.getStableBounds(stableBounds); + stableBounds.intersect(taskBounds); + + // Position of the button in the container coordinate. + final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? stableBounds.left - taskBounds.left + : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth(); + final int positionY = stableBounds.bottom - taskBounds.top + - mCompatUILayout.getMeasuredHeight(); + + updateSurfacePosition(positionX, positionY); + } + + private int getLayoutDirection() { + return mContext.getResources().getConfiguration().getLayoutDirection(); + } + + private void updateSurfacePosition(int positionX, int positionY) { + mSyncQueue.runInSync(t -> { + if (mLeash == null || !mLeash.isValid()) { + Log.w(TAG, "The leash has been released."); + return; + } + t.setPosition(mLeash, positionX, positionY); + // The compat UI should be the topmost child of the Task in case there can be more + // than one children. + t.setLayer(mLeash, Integer.MAX_VALUE); + }); + } + + /** Inflates {@link CompatUILayout} on to the root surface. */ + private void initCompatUi() { + if (mViewHost != null) { + throw new IllegalStateException( + "A UI has already been created with this window manager."); + } + + // Construction extracted into the separate methods to allow injection for tests. + mViewHost = createSurfaceViewHost(); + mCompatUILayout = inflateCompatUILayout(); + mCompatUILayout.inject(this); + + mCompatUILayout.setSizeCompatHintVisibility(mShouldShowHint); + + mViewHost.setView(mCompatUILayout, getWindowLayoutParams()); + + // Only show by default for the first time. + mShouldShowHint = false; + } + + @VisibleForTesting + CompatUILayout inflateCompatUILayout() { + return (CompatUILayout) LayoutInflater.from(mContext) + .inflate(R.layout.compat_ui_layout, null); + } + + @VisibleForTesting + SurfaceControlViewHost createSurfaceViewHost() { + return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + } + + /** Gets the layout params. */ + private WindowManager.LayoutParams getWindowLayoutParams() { + // Measure how big the hint is since its size depends on the text size. + mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( + // Cannot be wrap_content as this determines the actual window size + mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(), + TYPE_APPLICATION_OVERLAY, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, + PixelFormat.TRANSLUCENT); + winParams.token = new Binder(); + winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId); + winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; + return winParams; + } + +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 9500e8aecb57..6d4b2fa60b55 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -54,6 +54,8 @@ import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellAnimationThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; +import com.android.wm.shell.compatui.CompatUI; +import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.displayareahelper.DisplayAreaHelperController; import com.android.wm.shell.draganddrop.DragAndDrop; @@ -75,8 +77,6 @@ import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; -import com.android.wm.shell.sizecompatui.SizeCompatUI; -import com.android.wm.shell.sizecompatui.SizeCompatUIController; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.startingsurface.StartingSurface; @@ -173,25 +173,25 @@ public abstract class WMShellBaseModule { @Provides static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor, Context context, - SizeCompatUIController sizeCompatUI, + CompatUIController compatUI, Optional<RecentTasksController> recentTasksOptional ) { - return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional); + return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional); } @WMSingleton @Provides - static SizeCompatUI provideSizeCompatUI(SizeCompatUIController sizeCompatUIController) { - return sizeCompatUIController.asSizeCompatUI(); + static CompatUI provideCompatUI(CompatUIController compatUIController) { + return compatUIController.asCompatUI(); } @WMSingleton @Provides - static SizeCompatUIController provideSizeCompatUIController(Context context, + static CompatUIController provideCompatUIController(Context context, DisplayController displayController, DisplayInsetsController displayInsetsController, DisplayImeController imeController, SyncTransactionQueue syncQueue, @ShellMainThread ShellExecutor mainExecutor) { - return new SizeCompatUIController(context, displayController, displayInsetsController, + return new CompatUIController(context, displayController, displayInsetsController, imeController, syncQueue, mainExecutor); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 854fc60e15e8..f0b2716f05d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -778,8 +778,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } @Override - public boolean supportSizeCompatUI() { - // PIP doesn't support size compat. + public boolean supportCompatUI() { + // PIP doesn't support compat. return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index eb512afa644d..101a55d8d367 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -191,6 +191,7 @@ public class PhonePipMenuController implements PipMenuController { mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), 0, SHELL_ROOT_LAYER_PIP); + setShellRootAccessibilityWindow(); } private void detachPipMenuView() { @@ -546,6 +547,10 @@ public class PhonePipMenuController implements PipMenuController { mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState)); } mMenuState = menuState; + setShellRootAccessibilityWindow(); + } + + private void setShellRootAccessibilityWindow() { switch (mMenuState) { case MENU_STATE_NONE: mSystemWindows.setShellRootAccessibilityWindow(0, SHELL_ROOT_LAYER_PIP, null); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java index 8467cc5fc591..92a359891003 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java @@ -122,7 +122,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen if (mTargetViewContainer != null) { // init can be called multiple times, remove the old one from view hierarchy first. - mWindowManager.removeViewImmediate(mTargetViewContainer); + cleanUpDismissTarget(); } mTargetView = new DismissCircleView(mContext); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java deleted file mode 100644 index ff6f913207f6..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.sizecompatui; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.LinearLayout; - -import androidx.annotation.Nullable; - -import com.android.wm.shell.R; - -/** Popup to show the hint about the {@link SizeCompatRestartButton}. */ -public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener { - - private SizeCompatUILayout mLayout; - - public SizeCompatHintPopup(Context context) { - super(context); - } - - public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - void inject(SizeCompatUILayout layout) { - mLayout = layout; - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - final LinearLayout hintPopup = findViewById(R.id.size_compat_hint_popup); - hintPopup.setOnClickListener(this); - } - - @Override - public void onClick(View v) { - mLayout.dismissHint(); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java deleted file mode 100644 index d75fe5173c5f..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.sizecompatui; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageButton; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.wm.shell.R; - -/** Button to restart the size compat activity. */ -public class SizeCompatRestartButton extends FrameLayout implements View.OnClickListener, - View.OnLongClickListener { - - private SizeCompatUILayout mLayout; - - public SizeCompatRestartButton(@NonNull Context context) { - super(context); - } - - public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - void inject(SizeCompatUILayout layout) { - mLayout = layout; - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - final ImageButton restartButton = findViewById(R.id.size_compat_restart_button); - restartButton.setOnClickListener(this); - restartButton.setOnLongClickListener(this); - } - - @Override - public void onClick(View v) { - mLayout.onRestartButtonClicked(); - } - - @Override - public boolean onLongClick(View v) { - mLayout.onRestartButtonLongClicked(); - return true; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java deleted file mode 100644 index c35b89af6c1b..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.sizecompatui; - -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.os.Binder; -import android.util.Log; -import android.view.SurfaceControl; -import android.view.View; -import android.view.WindowManager; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.SyncTransactionQueue; - -/** - * Records and handles layout of size compat UI on a task with size compat activity. Helps to - * calculate proper bounds when configuration or UI position changes. - */ -class SizeCompatUILayout { - private static final String TAG = "SizeCompatUILayout"; - - final SyncTransactionQueue mSyncQueue; - private final SizeCompatUIController.SizeCompatUICallback mCallback; - private Context mContext; - private Configuration mTaskConfig; - private final int mDisplayId; - private final int mTaskId; - private ShellTaskOrganizer.TaskListener mTaskListener; - private DisplayLayout mDisplayLayout; - private final Rect mStableBounds; - private final int mButtonWidth; - private final int mButtonHeight; - private final int mPopupOffsetX; - private final int mPopupOffsetY; - - @VisibleForTesting - final SizeCompatUIWindowManager mButtonWindowManager; - @VisibleForTesting - @Nullable - SizeCompatUIWindowManager mHintWindowManager; - @VisibleForTesting - @Nullable - SizeCompatRestartButton mButton; - @VisibleForTesting - @Nullable - SizeCompatHintPopup mHint; - @VisibleForTesting - boolean mShouldShowHint; - - SizeCompatUILayout(SyncTransactionQueue syncQueue, - SizeCompatUIController.SizeCompatUICallback callback, Context context, - Configuration taskConfig, int taskId, ShellTaskOrganizer.TaskListener taskListener, - DisplayLayout displayLayout, boolean hasShownHint) { - mSyncQueue = syncQueue; - mCallback = callback; - mContext = context.createConfigurationContext(taskConfig); - mTaskConfig = taskConfig; - mDisplayId = mContext.getDisplayId(); - mTaskId = taskId; - mTaskListener = taskListener; - mDisplayLayout = displayLayout; - mShouldShowHint = !hasShownHint; - mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); - - mStableBounds = new Rect(); - mDisplayLayout.getStableBounds(mStableBounds); - - final Resources resources = mContext.getResources(); - mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width); - mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height); - mPopupOffsetX = (mButtonWidth / 2) - resources.getDimensionPixelSize( - R.dimen.size_compat_hint_corner_radius) - (resources.getDimensionPixelSize( - R.dimen.size_compat_hint_point_width) / 2); - mPopupOffsetY = mButtonHeight; - } - - /** Creates the activity restart button window. */ - void createSizeCompatButton(boolean show) { - if (!show || mButton != null) { - // Wait until button should be visible. - return; - } - mButton = mButtonWindowManager.createSizeCompatButton(); - updateButtonSurfacePosition(); - - if (mShouldShowHint) { - // Only show by default for the first time. - mShouldShowHint = false; - createSizeCompatHint(); - } - - mCallback.onSizeCompatRestartButtonAppeared(mTaskId); - } - - /** Creates the restart button hint window. */ - private void createSizeCompatHint() { - if (mHint != null) { - // Hint already shown. - return; - } - mHintWindowManager = createHintWindowManager(); - mHint = mHintWindowManager.createSizeCompatHint(); - updateHintSurfacePosition(); - } - - @VisibleForTesting - SizeCompatUIWindowManager createHintWindowManager() { - return new SizeCompatUIWindowManager(mContext, mTaskConfig, this); - } - - /** Dismisses the hint window. */ - void dismissHint() { - mHint = null; - if (mHintWindowManager != null) { - mHintWindowManager.release(); - mHintWindowManager = null; - } - } - - /** Releases the UI windows. */ - void release() { - dismissHint(); - mButton = null; - mButtonWindowManager.release(); - } - - /** Called when size compat info changed. */ - void updateSizeCompatInfo(Configuration taskConfig, - ShellTaskOrganizer.TaskListener taskListener, boolean show) { - final Configuration prevTaskConfig = mTaskConfig; - final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener; - mTaskConfig = taskConfig; - mTaskListener = taskListener; - - // Update configuration. - mContext = mContext.createConfigurationContext(taskConfig); - mButtonWindowManager.setConfiguration(taskConfig); - if (mHintWindowManager != null) { - mHintWindowManager.setConfiguration(taskConfig); - } - - if (mButton == null || prevTaskListener != taskListener) { - // TaskListener changed, recreate the button for new surface parent. - release(); - createSizeCompatButton(show); - return; - } - - if (!taskConfig.windowConfiguration.getBounds() - .equals(prevTaskConfig.windowConfiguration.getBounds())) { - // Reposition the UI surfaces. - updateAllSurfacePositions(); - } - - if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { - // Update layout for RTL. - mButton.setLayoutDirection(taskConfig.getLayoutDirection()); - updateButtonSurfacePosition(); - if (mHint != null) { - mHint.setLayoutDirection(taskConfig.getLayoutDirection()); - updateHintSurfacePosition(); - } - } - } - - /** Called when display layout changed. */ - void updateDisplayLayout(DisplayLayout displayLayout) { - final Rect prevStableBounds = mStableBounds; - final Rect curStableBounds = new Rect(); - displayLayout.getStableBounds(curStableBounds); - mDisplayLayout = displayLayout; - if (!prevStableBounds.equals(curStableBounds)) { - // Stable bounds changed, update UI surface positions. - updateAllSurfacePositions(); - mStableBounds.set(curStableBounds); - } - } - - /** Called when the visibility of the UI should change. */ - void updateVisibility(boolean show) { - if (mButton == null) { - // Button may not have been created because it was hidden previously. - createSizeCompatButton(show); - return; - } - - // Hide size compat UIs when IME is showing. - final int newVisibility = show ? View.VISIBLE : View.GONE; - if (mButton.getVisibility() != newVisibility) { - mButton.setVisibility(newVisibility); - } - if (mHint != null && mHint.getVisibility() != newVisibility) { - mHint.setVisibility(newVisibility); - } - } - - /** Gets the layout params for restart button. */ - WindowManager.LayoutParams getButtonWindowLayoutParams() { - final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( - // Cannot be wrap_content as this determines the actual window size - mButtonWidth, mButtonHeight, - TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, - PixelFormat.TRANSLUCENT); - winParams.token = new Binder(); - winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId()); - winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - return winParams; - } - - /** Gets the layout params for hint popup. */ - WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) { - final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( - // Cannot be wrap_content as this determines the actual window size - hint.getMeasuredWidth(), hint.getMeasuredHeight(), - TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, - PixelFormat.TRANSLUCENT); - winParams.token = new Binder(); - winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId()); - winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - winParams.windowAnimations = android.R.style.Animation_InputMethod; - return winParams; - } - - /** Called when it is ready to be placed size compat UI surface. */ - void attachToParentSurface(SurfaceControl.Builder b) { - mTaskListener.attachChildSurfaceToTask(mTaskId, b); - } - - /** Called when the restart button is clicked. */ - void onRestartButtonClicked() { - mCallback.onSizeCompatRestartButtonClicked(mTaskId); - } - - /** Called when the restart button is long clicked. */ - void onRestartButtonLongClicked() { - createSizeCompatHint(); - } - - private void updateAllSurfacePositions() { - updateButtonSurfacePosition(); - updateHintSurfacePosition(); - } - - @VisibleForTesting - void updateButtonSurfacePosition() { - if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) { - return; - } - final SurfaceControl leash = mButtonWindowManager.getSurfaceControl(); - - // Use stable bounds to prevent the button from overlapping with system bars. - final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); - final Rect stableBounds = new Rect(); - mDisplayLayout.getStableBounds(stableBounds); - stableBounds.intersect(taskBounds); - - // Position of the button in the container coordinate. - final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL - ? stableBounds.left - taskBounds.left - : stableBounds.right - taskBounds.left - mButtonWidth; - final int positionY = stableBounds.bottom - taskBounds.top - mButtonHeight; - - updateSurfacePosition(leash, positionX, positionY); - } - - @VisibleForTesting - void updateHintSurfacePosition() { - if (mHint == null || mHintWindowManager == null - || mHintWindowManager.getSurfaceControl() == null) { - return; - } - final SurfaceControl leash = mHintWindowManager.getSurfaceControl(); - - // Use stable bounds to prevent the hint from overlapping with system bars. - final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); - final Rect stableBounds = new Rect(); - mDisplayLayout.getStableBounds(stableBounds); - stableBounds.intersect(taskBounds); - - // Position of the hint in the container coordinate. - final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL - ? stableBounds.left - taskBounds.left + mPopupOffsetX - : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth(); - final int positionY = - stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight(); - - updateSurfacePosition(leash, positionX, positionY); - } - - private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) { - mSyncQueue.runInSync(t -> { - if (!leash.isValid()) { - Log.w(TAG, "The leash has been released."); - return; - } - t.setPosition(leash, positionX, positionY); - // The size compat UI should be the topmost child of the Task in case there can be more - // than one children. - t.setLayer(leash, Integer.MAX_VALUE); - }); - } - - int getDisplayId() { - return mDisplayId; - } - - int getTaskId() { - return mTaskId; - } - - private int getLayoutDirection() { - return mContext.getResources().getConfiguration().getLayoutDirection(); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java deleted file mode 100644 index 82f69c3e2985..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.sizecompatui; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Configuration; -import android.view.IWindow; -import android.view.LayoutInflater; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; -import android.view.View; -import android.view.WindowlessWindowManager; - -import com.android.wm.shell.R; - -/** - * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or - * {@link SizeCompatHintPopup}. - */ -class SizeCompatUIWindowManager extends WindowlessWindowManager { - - private Context mContext; - private final SizeCompatUILayout mLayout; - - @Nullable - private SurfaceControlViewHost mViewHost; - @Nullable - private SurfaceControl mLeash; - - SizeCompatUIWindowManager(Context context, Configuration config, SizeCompatUILayout layout) { - super(config, null /* rootSurface */, null /* hostInputToken */); - mContext = context; - mLayout = layout; - } - - @Override - public void setConfiguration(Configuration configuration) { - super.setConfiguration(configuration); - mContext = mContext.createConfigurationContext(configuration); - } - - @Override - protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { - // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) - .setContainerLayer() - .setName("SizeCompatUILeash") - .setHidden(false) - .setCallsite("SizeCompatUIWindowManager#attachToParentSurface"); - mLayout.attachToParentSurface(builder); - mLeash = builder.build(); - b.setParent(mLeash); - } - - /** Inflates {@link SizeCompatRestartButton} on to the root surface. */ - SizeCompatRestartButton createSizeCompatButton() { - if (mViewHost != null) { - throw new IllegalStateException( - "A UI has already been created with this window manager."); - } - - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); - - final SizeCompatRestartButton button = (SizeCompatRestartButton) - LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null); - button.inject(mLayout); - mViewHost.setView(button, mLayout.getButtonWindowLayoutParams()); - return button; - } - - /** Inflates {@link SizeCompatHintPopup} on to the root surface. */ - SizeCompatHintPopup createSizeCompatHint() { - if (mViewHost != null) { - throw new IllegalStateException( - "A UI has already been created with this window manager."); - } - - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); - - final SizeCompatHintPopup hint = (SizeCompatHintPopup) - LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null); - // Measure how big the hint is. - hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - hint.inject(mLayout); - mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint)); - return hint; - } - - /** Releases the surface control and tears down the view hierarchy. */ - void release() { - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - - if (mLeash != null) { - final SurfaceControl leash = mLeash; - mLayout.mSyncQueue.runInSync(t -> t.remove(leash)); - mLeash = null; - } - } - - /** - * Gets {@link SurfaceControl} of the surface holding size compat UI view. @return {@code null} - * if not feasible. - */ - @Nullable - SurfaceControl getSurfaceControl() { - return mLeash; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 05552aad2303..cf4e56e128bf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -102,6 +102,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, static final int EXIT_REASON_ROOT_TASK_VANISHED = 6; static final int EXIT_REASON_SCREEN_LOCKED = 7; static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8; + static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9; @IntDef(value = { EXIT_REASON_UNKNOWN, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, @@ -112,6 +113,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, EXIT_REASON_ROOT_TASK_VANISHED, EXIT_REASON_SCREEN_LOCKED, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP, + EXIT_REASON_CHILD_TASK_ENTER_PIP, }) @Retention(RetentionPolicy.SOURCE) @interface ExitReason{} @@ -406,6 +408,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return "APP_FINISHED"; case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW: return "APP_DOES_NOT_SUPPORT_MULTIWINDOW"; + case EXIT_REASON_CHILD_TASK_ENTER_PIP: + return "CHILD_TASK_ENTER_PIP"; default: return "unknown reason, reason int = " + exitReason; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index e30e6c537c93..dd538dc4602c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -35,6 +35,7 @@ import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED; +import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME; @@ -629,8 +630,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, }); mShouldUpdateRecents = false; - mSideStage.removeAllTasks(wct, childrenToTop == mSideStage); - mMainStage.deactivate(wct, childrenToTop == mMainStage); + // When the exit split-screen is caused by one of the task enters auto pip, + // we want the tasks to be put to bottom instead of top, otherwise it will end up + // a fullscreen plus a pinned task instead of pinned only at the end of the transition. + final boolean fromEnteringPip = exitReason == EXIT_REASON_CHILD_TASK_ENTER_PIP; + mSideStage.removeAllTasks(wct, !fromEnteringPip && childrenToTop == mSideStage); + mMainStage.deactivate(wct, !fromEnteringPip && childrenToTop == mMainStage); mTaskOrganizer.applyTransaction(wct); mSyncQueue.runInSync(t -> t .setWindowCrop(mMainStage.mRootLeash, null) @@ -660,6 +665,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, case EXIT_REASON_DRAG_DIVIDER: // Either of the split apps have finished case EXIT_REASON_APP_FINISHED: + // One of the children enters PiP + case EXIT_REASON_CHILD_TASK_ENTER_PIP: return true; default: return false; @@ -749,6 +756,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } + private void onStageChildTaskEnterPip(StageListenerImpl stageListener, int taskId) { + exitSplitScreen(stageListener == mMainStageListener ? mMainStage : mSideStage, + EXIT_REASON_CHILD_TASK_ENTER_PIP); + } + private void updateRecentTasksSplitPair() { if (!mShouldUpdateRecents) { return; @@ -1437,6 +1449,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } @Override + public void onChildTaskEnterPip(int taskId) { + StageCoordinator.this.onStageChildTaskEnterPip(this, taskId); + } + + @Override public void onRootTaskVanished() { reset(); StageCoordinator.this.onStageRootTaskVanished(this); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index cd10b9fde444..2c853c1c474c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; @@ -73,6 +74,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { void onChildTaskStatusChanged(int taskId, boolean present, boolean visible); + void onChildTaskEnterPip(int taskId); + void onRootTaskVanished(); void onNoLongerSupportMultiWindow(); @@ -256,6 +259,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { mChildrenTaskInfo.remove(taskId); mChildrenLeashes.remove(taskId); mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible); + if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) { + mCallbacks.onChildTaskEnterPip(taskId); + } if (ENABLE_SHELL_TRANSITIONS) { // Status is managed/synchronized by the transition lifecycle. return; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 1fcbf14fb732..a3b98a8fc880 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -56,7 +56,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.sizecompatui.SizeCompatUIController; +import com.android.wm.shell.compatui.CompatUIController; import org.junit.Before; import org.junit.Test; @@ -82,7 +82,7 @@ public class ShellTaskOrganizerTests { @Mock private Context mContext; @Mock - private SizeCompatUIController mSizeCompatUI; + private CompatUIController mCompatUI; ShellTaskOrganizer mOrganizer; private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class); @@ -132,7 +132,7 @@ public class ShellTaskOrganizerTests { .when(mTaskOrganizerController).registerTaskOrganizer(any()); } catch (RemoteException e) {} mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext, - mSizeCompatUI, Optional.empty())); + mCompatUI, Optional.empty())); } @Test @@ -334,34 +334,34 @@ public class ShellTaskOrganizerTests { mOrganizer.onTaskAppeared(taskInfo1, null); // sizeCompatActivity is null if top activity is not in size compat. - verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, + verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, null /* taskConfig */, null /* taskListener */); // sizeCompatActivity is non-null if top activity is in size compat. - clearInvocations(mSizeCompatUI); + clearInvocations(mCompatUI); final RunningTaskInfo taskInfo2 = createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode()); taskInfo2.displayId = taskInfo1.displayId; taskInfo2.topActivityInSizeCompat = true; taskInfo2.isVisible = true; mOrganizer.onTaskInfoChanged(taskInfo2); - verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, + verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, taskInfo1.configuration, taskListener); // Not show size compat UI if task is not visible. - clearInvocations(mSizeCompatUI); + clearInvocations(mCompatUI); final RunningTaskInfo taskInfo3 = createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode()); taskInfo3.displayId = taskInfo1.displayId; taskInfo3.topActivityInSizeCompat = true; taskInfo3.isVisible = false; mOrganizer.onTaskInfoChanged(taskInfo3); - verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, + verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, null /* taskConfig */, null /* taskListener */); - clearInvocations(mSizeCompatUI); + clearInvocations(mCompatUI); mOrganizer.onTaskVanished(taskInfo1); - verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, + verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, null /* taskConfig */, null /* taskListener */); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index bc701d0c70bc..8bc1223cfd64 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -39,6 +39,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.Log; import android.util.Pair; import android.view.WindowManager; @@ -913,6 +914,31 @@ public class BubbleDataTest extends ShellTestCase { assertSelectionChangedTo(mBubbleA2); } + /** + * - have a maxed out bubble stack & all of the bubbles have been recently accessed + * - bubble a notification that was posted before any of those bubbles were accessed + * => that bubble should be added + * + */ + @Test + public void test_addOldNotifWithNewerBubbles() { + sendUpdatedEntryAtTime(mEntryA1, 2000); + sendUpdatedEntryAtTime(mEntryA2, 3000); + sendUpdatedEntryAtTime(mEntryA3, 4000); + sendUpdatedEntryAtTime(mEntryB1, 5000); + sendUpdatedEntryAtTime(mEntryB2, 6000); + + mBubbleData.setListener(mListener); + sendUpdatedEntryAtTime(mEntryB3, 1000 /* postTime */, 7000 /* currentTime */); + verifyUpdateReceived(); + + // B3 is in the stack + assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleB3.getKey())).isNotNull(); + // A1 is the oldest so it's in the overflow + assertThat(mBubbleData.getOverflowBubbleWithKey(mEntryA1.getKey())).isNotNull(); + assertOrderChangedTo(mBubbleB3, mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2); + } + private void verifyUpdateReceived() { verify(mListener).applyUpdate(mUpdateCaptor.capture()); reset(mListener); @@ -1014,6 +1040,12 @@ public class BubbleDataTest extends ShellTestCase { } private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) { + setCurrentTime(postTime); + sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */); + } + + private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, long currentTime) { + setCurrentTime(currentTime); sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index 877b19223bf6..f622edb7f134 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.sizecompatui; +package com.android.wm.shell.compatui; import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; @@ -56,18 +56,18 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** - * Tests for {@link SizeCompatUIController}. + * Tests for {@link CompatUIController}. * * Build/Install/Run: - * atest WMShellUnitTests:SizeCompatUIControllerTest + * atest WMShellUnitTests:CompatUIControllerTest */ @RunWith(AndroidTestingRunner.class) @SmallTest -public class SizeCompatUIControllerTest extends ShellTestCase { +public class CompatUIControllerTest extends ShellTestCase { private static final int DISPLAY_ID = 0; private static final int TASK_ID = 12; - private SizeCompatUIController mController; + private CompatUIController mController; private @Mock DisplayController mMockDisplayController; private @Mock DisplayInsetsController mMockDisplayInsetsController; private @Mock DisplayLayout mMockDisplayLayout; @@ -75,7 +75,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener; private @Mock SyncTransactionQueue mMockSyncQueue; private @Mock ShellExecutor mMockExecutor; - private @Mock SizeCompatUILayout mMockLayout; + private @Mock CompatUIWindowManager mMockLayout; @Captor ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor; @@ -87,10 +87,10 @@ public class SizeCompatUIControllerTest extends ShellTestCase { doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt()); doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId(); doReturn(TASK_ID).when(mMockLayout).getTaskId(); - mController = new SizeCompatUIController(mContext, mMockDisplayController, + mController = new CompatUIController(mContext, mMockDisplayController, mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) { @Override - SizeCompatUILayout createLayout(Context context, int displayId, int taskId, + CompatUIWindowManager createLayout(Context context, int displayId, int taskId, Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) { return mMockLayout; } @@ -105,24 +105,24 @@ public class SizeCompatUIControllerTest extends ShellTestCase { } @Test - public void testOnSizeCompatInfoChanged() { + public void testOnCompatInfoChanged() { final Configuration taskConfig = new Configuration(); // Verify that the restart button is added with non-null size compat info. - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig), eq(mMockTaskListener)); // Verify that the restart button is updated with non-null new size compat info. final Configuration newTaskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener); - verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener, + verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener, true /* show */); // Verify that the restart button is removed with null size compat info. - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener); verify(mMockLayout).release(); } @@ -140,7 +140,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { public void testOnDisplayRemoved() { mController.onDisplayAdded(DISPLAY_ID); final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); mController.onDisplayRemoved(DISPLAY_ID + 1); @@ -158,7 +158,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testOnDisplayConfigurationChanged() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); final Configuration newTaskConfig = new Configuration(); @@ -175,7 +175,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { public void testInsetsChanged() { mController.onDisplayAdded(DISPLAY_ID); final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); InsetsState insetsState = new InsetsState(); InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR); @@ -197,7 +197,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testChangeButtonVisibilityOnImeShowHide() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); // Verify that the restart button is hidden after IME is showing. mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */); @@ -205,9 +205,9 @@ public class SizeCompatUIControllerTest extends ShellTestCase { verify(mMockLayout).updateVisibility(false); // Verify button remains hidden while IME is showing. - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); - verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener, + verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener, false /* show */); // Verify button is shown after IME is hidden. @@ -219,7 +219,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testChangeButtonVisibilityOnKeyguardOccludedChanged() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); // Verify that the restart button is hidden after keyguard becomes occluded. mController.onKeyguardOccludedChanged(true); @@ -227,9 +227,9 @@ public class SizeCompatUIControllerTest extends ShellTestCase { verify(mMockLayout).updateVisibility(false); // Verify button remains hidden while keyguard is occluded. - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); - verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener, + verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener, false /* show */); // Verify button is shown after keyguard becomes not occluded. @@ -241,7 +241,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */); mController.onKeyguardOccludedChanged(true); @@ -264,7 +264,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */); mController.onKeyguardOccludedChanged(true); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java index a20a5e9e8d91..2c3987bc358d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java @@ -14,17 +14,20 @@ * limitations under the License. */ -package com.android.wm.shell.sizecompatui; +package com.android.wm.shell.compatui; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import android.content.res.Configuration; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; +import android.view.SurfaceControlViewHost; import android.widget.ImageButton; +import android.widget.LinearLayout; import androidx.test.filters.SmallTest; @@ -41,55 +44,68 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** - * Tests for {@link SizeCompatRestartButton}. + * Tests for {@link CompatUILayout}. * * Build/Install/Run: - * atest WMShellUnitTests:SizeCompatRestartButtonTest + * atest WMShellUnitTests:CompatUILayoutTest */ @RunWith(AndroidTestingRunner.class) @SmallTest -public class SizeCompatRestartButtonTest extends ShellTestCase { +public class CompatUILayoutTest extends ShellTestCase { private static final int TASK_ID = 1; @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private SizeCompatUIController.SizeCompatUICallback mCallback; + @Mock private CompatUIController.CompatUICallback mCallback; @Mock private ShellTaskOrganizer.TaskListener mTaskListener; - @Mock private DisplayLayout mDisplayLayout; + @Mock private SurfaceControlViewHost mViewHost; - private SizeCompatUILayout mLayout; - private SizeCompatRestartButton mButton; + private CompatUIWindowManager mWindowManager; + private CompatUILayout mCompatUILayout; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, - new Configuration(), TASK_ID, mTaskListener, mDisplayLayout, + mWindowManager = new CompatUIWindowManager(mContext, new Configuration(), + mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(), false /* hasShownHint */); - mButton = (SizeCompatRestartButton) - LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null); - mButton.inject(mLayout); - spyOn(mLayout); + mCompatUILayout = (CompatUILayout) + LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null); + mCompatUILayout.inject(mWindowManager); + + spyOn(mWindowManager); + spyOn(mCompatUILayout); + doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost(); } @Test - public void testOnClick() { - final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button); + public void testOnClickForRestartButton() { + final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button); button.performClick(); - verify(mLayout).onRestartButtonClicked(); + verify(mWindowManager).onRestartButtonClicked(); + doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout(); verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID); } @Test - public void testOnLongClick() { - doNothing().when(mLayout).onRestartButtonLongClicked(); + public void testOnLongClickForRestartButton() { + doNothing().when(mWindowManager).onRestartButtonLongClicked(); - final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button); + final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button); button.performLongClick(); - verify(mLayout).onRestartButtonLongClicked(); + verify(mWindowManager).onRestartButtonLongClicked(); + } + + @Test + public void testOnClickForSizeCompatHint() { + mWindowManager.createLayout(true /* show */); + final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint); + sizeCompatHint.performClick(); + + verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java new file mode 100644 index 000000000000..d5dcf2e11a46 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2021 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.wm.shell.compatui; + +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.view.DisplayInfo; +import android.view.InsetsSource; +import android.view.InsetsState; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link CompatUIWindowManager}. + * + * Build/Install/Run: + * atest WMShellUnitTests:CompatUIWindowManagerTest + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class CompatUIWindowManagerTest extends ShellTestCase { + + private static final int TASK_ID = 1; + + @Mock private SyncTransactionQueue mSyncTransactionQueue; + @Mock private CompatUIController.CompatUICallback mCallback; + @Mock private ShellTaskOrganizer.TaskListener mTaskListener; + @Mock private CompatUILayout mCompatUILayout; + @Mock private SurfaceControlViewHost mViewHost; + private Configuration mTaskConfig; + + private CompatUIWindowManager mWindowManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTaskConfig = new Configuration(); + + mWindowManager = new CompatUIWindowManager(mContext, new Configuration(), + mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(), + false /* hasShownHint */); + + spyOn(mWindowManager); + doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout(); + doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost(); + } + + @Test + public void testCreateSizeCompatButton() { + // Not create layout if show is false. + mWindowManager.createLayout(false /* show */); + + verify(mWindowManager, never()).inflateCompatUILayout(); + + // Not create hint popup. + mWindowManager.mShouldShowHint = false; + mWindowManager.createLayout(true /* show */); + + verify(mWindowManager).inflateCompatUILayout(); + verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */); + + // Create hint popup. + mWindowManager.release(); + mWindowManager.mShouldShowHint = true; + mWindowManager.createLayout(true /* show */); + + verify(mWindowManager, times(2)).inflateCompatUILayout(); + assertNotNull(mCompatUILayout); + verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */); + assertFalse(mWindowManager.mShouldShowHint); + } + + @Test + public void testRelease() { + mWindowManager.createLayout(true /* show */); + + verify(mWindowManager).inflateCompatUILayout(); + + mWindowManager.release(); + + verify(mViewHost).release(); + } + + @Test + public void testUpdateCompatInfo() { + mWindowManager.createLayout(true /* show */); + + // No diff + clearInvocations(mWindowManager); + mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */); + + verify(mWindowManager, never()).updateSurfacePosition(); + verify(mWindowManager, never()).release(); + verify(mWindowManager, never()).createLayout(anyBoolean()); + + // Change task listener, recreate button. + clearInvocations(mWindowManager); + final ShellTaskOrganizer.TaskListener newTaskListener = mock( + ShellTaskOrganizer.TaskListener.class); + mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, + true /* show */); + + verify(mWindowManager).release(); + verify(mWindowManager).createLayout(anyBoolean()); + + // Change task bounds, update position. + clearInvocations(mWindowManager); + final Configuration newTaskConfiguration = new Configuration(); + newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000)); + mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener, + true /* show */); + + verify(mWindowManager).updateSurfacePosition(); + } + + @Test + public void testUpdateDisplayLayout() { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = 1000; + displayInfo.logicalHeight = 2000; + final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); + + mWindowManager.updateDisplayLayout(displayLayout1); + verify(mWindowManager).updateSurfacePosition(); + + // No update if the display bounds is the same. + clearInvocations(mWindowManager); + final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); + mWindowManager.updateDisplayLayout(displayLayout2); + verify(mWindowManager, never()).updateSurfacePosition(); + } + + @Test + public void testUpdateDisplayLayoutInsets() { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = 1000; + displayInfo.logicalHeight = 2000; + final DisplayLayout displayLayout = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false); + + mWindowManager.updateDisplayLayout(displayLayout); + verify(mWindowManager).updateSurfacePosition(); + + // Update if the insets change on the existing display layout + clearInvocations(mWindowManager); + InsetsState insetsState = new InsetsState(); + InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR); + insetsSource.setFrame(0, 0, 1000, 1000); + insetsState.addSource(insetsSource); + displayLayout.setInsets(mContext.getResources(), insetsState); + mWindowManager.updateDisplayLayout(displayLayout); + verify(mWindowManager).updateSurfacePosition(); + } + + @Test + public void testUpdateVisibility() { + // Create button if it is not created. + mWindowManager.mCompatUILayout = null; + mWindowManager.updateVisibility(true /* show */); + + verify(mWindowManager).createLayout(true /* show */); + + // Hide button. + clearInvocations(mWindowManager); + doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility(); + mWindowManager.updateVisibility(false /* show */); + + verify(mWindowManager, never()).createLayout(anyBoolean()); + verify(mCompatUILayout).setVisibility(View.GONE); + + // Show button. + doReturn(View.GONE).when(mCompatUILayout).getVisibility(); + mWindowManager.updateVisibility(true /* show */); + + verify(mWindowManager, never()).createLayout(anyBoolean()); + verify(mCompatUILayout).setVisibility(View.VISIBLE); + } + + @Test + public void testAttachToParentSurface() { + final SurfaceControl.Builder b = new SurfaceControl.Builder(); + mWindowManager.attachToParentSurface(b); + + verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b); + } + + @Test + public void testOnRestartButtonClicked() { + mWindowManager.onRestartButtonClicked(); + + verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID); + } + + @Test + public void testOnRestartButtonLongClicked_showHint() { + // Not create hint popup. + mWindowManager.mShouldShowHint = false; + mWindowManager.createLayout(true /* show */); + + verify(mWindowManager).inflateCompatUILayout(); + verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */); + + mWindowManager.onRestartButtonLongClicked(); + + verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */); + } + +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java deleted file mode 100644 index 3a14a336190d..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.sizecompatui; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; - -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.verify; - -import android.content.res.Configuration; -import android.testing.AndroidTestingRunner; -import android.view.LayoutInflater; -import android.widget.LinearLayout; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.SyncTransactionQueue; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Tests for {@link SizeCompatHintPopup}. - * - * Build/Install/Run: - * atest WMShellUnitTests:SizeCompatHintPopupTest - */ -@RunWith(AndroidTestingRunner.class) -@SmallTest -public class SizeCompatHintPopupTest extends ShellTestCase { - - @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private SizeCompatUIController.SizeCompatUICallback mCallback; - @Mock private ShellTaskOrganizer.TaskListener mTaskListener; - @Mock private DisplayLayout mDisplayLayout; - - private SizeCompatUILayout mLayout; - private SizeCompatHintPopup mHint; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - final int taskId = 1; - mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, - new Configuration(), taskId, mTaskListener, mDisplayLayout, - false /* hasShownHint */); - mHint = (SizeCompatHintPopup) - LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null); - mHint.inject(mLayout); - - spyOn(mLayout); - } - - @Test - public void testOnClick() { - doNothing().when(mLayout).dismissHint(); - - final LinearLayout hintPopup = mHint.findViewById(R.id.size_compat_hint_popup); - hintPopup.performClick(); - - verify(mLayout).dismissHint(); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java deleted file mode 100644 index eb9305b2e995..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.sizecompatui; - -import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.res.Configuration; -import android.graphics.Rect; -import android.testing.AndroidTestingRunner; -import android.view.DisplayInfo; -import android.view.InsetsSource; -import android.view.InsetsState; -import android.view.SurfaceControl; -import android.view.View; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.SyncTransactionQueue; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Tests for {@link SizeCompatUILayout}. - * - * Build/Install/Run: - * atest WMShellUnitTests:SizeCompatUILayoutTest - */ -@RunWith(AndroidTestingRunner.class) -@SmallTest -public class SizeCompatUILayoutTest extends ShellTestCase { - - private static final int TASK_ID = 1; - - @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private SizeCompatUIController.SizeCompatUICallback mCallback; - @Mock private ShellTaskOrganizer.TaskListener mTaskListener; - @Mock private DisplayLayout mDisplayLayout; - @Mock private SizeCompatRestartButton mButton; - @Mock private SizeCompatHintPopup mHint; - private Configuration mTaskConfig; - - private SizeCompatUILayout mLayout; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mTaskConfig = new Configuration(); - - mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, - new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(), - false /* hasShownHint */); - - spyOn(mLayout); - spyOn(mLayout.mButtonWindowManager); - doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton(); - - final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager(); - spyOn(hintWindowManager); - doReturn(mHint).when(hintWindowManager).createSizeCompatHint(); - doReturn(hintWindowManager).when(mLayout).createHintWindowManager(); - } - - @Test - public void testCreateSizeCompatButton() { - // Not create button if show is false. - mLayout.createSizeCompatButton(false /* show */); - - verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton(); - assertNull(mLayout.mButton); - assertNull(mLayout.mHintWindowManager); - assertNull(mLayout.mHint); - - // Not create hint popup. - mLayout.mShouldShowHint = false; - mLayout.createSizeCompatButton(true /* show */); - - verify(mLayout.mButtonWindowManager).createSizeCompatButton(); - assertNotNull(mLayout.mButton); - assertNull(mLayout.mHintWindowManager); - assertNull(mLayout.mHint); - - // Create hint popup. - mLayout.release(); - mLayout.mShouldShowHint = true; - mLayout.createSizeCompatButton(true /* show */); - - verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton(); - assertNotNull(mLayout.mButton); - assertNotNull(mLayout.mHintWindowManager); - verify(mLayout.mHintWindowManager).createSizeCompatHint(); - assertNotNull(mLayout.mHint); - assertFalse(mLayout.mShouldShowHint); - } - - @Test - public void testRelease() { - mLayout.createSizeCompatButton(true /* show */); - final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager; - - mLayout.release(); - - assertNull(mLayout.mButton); - assertNull(mLayout.mHint); - verify(hintWindowManager).release(); - assertNull(mLayout.mHintWindowManager); - verify(mLayout.mButtonWindowManager).release(); - } - - @Test - public void testUpdateSizeCompatInfo() { - mLayout.createSizeCompatButton(true /* show */); - - // No diff - clearInvocations(mLayout); - mLayout.updateSizeCompatInfo(mTaskConfig, mTaskListener, true /* show */); - - verify(mLayout, never()).updateButtonSurfacePosition(); - verify(mLayout, never()).release(); - verify(mLayout, never()).createSizeCompatButton(anyBoolean()); - - // Change task listener, recreate button. - clearInvocations(mLayout); - final ShellTaskOrganizer.TaskListener newTaskListener = mock( - ShellTaskOrganizer.TaskListener.class); - mLayout.updateSizeCompatInfo(mTaskConfig, newTaskListener, - true /* show */); - - verify(mLayout).release(); - verify(mLayout).createSizeCompatButton(anyBoolean()); - - // Change task bounds, update position. - clearInvocations(mLayout); - final Configuration newTaskConfiguration = new Configuration(); - newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000)); - mLayout.updateSizeCompatInfo(newTaskConfiguration, newTaskListener, - true /* show */); - - verify(mLayout).updateButtonSurfacePosition(); - verify(mLayout).updateHintSurfacePosition(); - } - - @Test - public void testUpdateDisplayLayout() { - final DisplayInfo displayInfo = new DisplayInfo(); - displayInfo.logicalWidth = 1000; - displayInfo.logicalHeight = 2000; - final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo, - mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); - - mLayout.updateDisplayLayout(displayLayout1); - verify(mLayout).updateButtonSurfacePosition(); - verify(mLayout).updateHintSurfacePosition(); - - // No update if the display bounds is the same. - clearInvocations(mLayout); - final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo, - mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); - mLayout.updateDisplayLayout(displayLayout2); - verify(mLayout, never()).updateButtonSurfacePosition(); - verify(mLayout, never()).updateHintSurfacePosition(); - } - - @Test - public void testUpdateDisplayLayoutInsets() { - final DisplayInfo displayInfo = new DisplayInfo(); - displayInfo.logicalWidth = 1000; - displayInfo.logicalHeight = 2000; - final DisplayLayout displayLayout = new DisplayLayout(displayInfo, - mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false); - - mLayout.updateDisplayLayout(displayLayout); - verify(mLayout).updateButtonSurfacePosition(); - verify(mLayout).updateHintSurfacePosition(); - - // Update if the insets change on the existing display layout - clearInvocations(mLayout); - InsetsState insetsState = new InsetsState(); - InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR); - insetsSource.setFrame(0, 0, 1000, 1000); - insetsState.addSource(insetsSource); - displayLayout.setInsets(mContext.getResources(), insetsState); - mLayout.updateDisplayLayout(displayLayout); - verify(mLayout).updateButtonSurfacePosition(); - verify(mLayout).updateHintSurfacePosition(); - } - - @Test - public void testUpdateVisibility() { - // Create button if it is not created. - mLayout.mButton = null; - mLayout.updateVisibility(true /* show */); - - verify(mLayout).createSizeCompatButton(true /* show */); - - // Hide button. - clearInvocations(mLayout); - doReturn(View.VISIBLE).when(mButton).getVisibility(); - mLayout.updateVisibility(false /* show */); - - verify(mLayout, never()).createSizeCompatButton(anyBoolean()); - verify(mButton).setVisibility(View.GONE); - - // Show button. - doReturn(View.GONE).when(mButton).getVisibility(); - mLayout.updateVisibility(true /* show */); - - verify(mLayout, never()).createSizeCompatButton(anyBoolean()); - verify(mButton).setVisibility(View.VISIBLE); - } - - @Test - public void testAttachToParentSurface() { - final SurfaceControl.Builder b = new SurfaceControl.Builder(); - mLayout.attachToParentSurface(b); - - verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b); - } - - @Test - public void testOnRestartButtonClicked() { - mLayout.onRestartButtonClicked(); - - verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID); - } - - @Test - public void testOnRestartButtonLongClicked_showHint() { - mLayout.dismissHint(); - - assertNull(mLayout.mHint); - - mLayout.onRestartButtonLongClicked(); - - assertNotNull(mLayout.mHint); - } - - @Test - public void testDismissHint() { - mLayout.onRestartButtonLongClicked(); - final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager; - assertNotNull(mLayout.mHint); - assertNotNull(hintWindowManager); - - mLayout.dismissHint(); - - assertNull(mLayout.mHint); - assertNull(mLayout.mHintWindowManager); - verify(hintWindowManager).release(); - } -} diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java index 36c2bda8b03a..7f17d26b156a 100644 --- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java +++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java @@ -18,7 +18,6 @@ package com.android.settingslib.activityembedding; import android.content.Context; import android.content.Intent; -import android.content.pm.ResolveInfo; import com.android.settingslib.utils.BuildCompatUtils; @@ -37,11 +36,10 @@ public class ActivityEmbeddingUtils { if (BuildCompatUtils.isAtLeastS()) { final Intent intent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY); intent.setPackage(PACKAGE_NAME_SETTINGS); - final ResolveInfo resolveInfo = - context.getPackageManager().resolveActivity(intent, 0 /* flags */); - return resolveInfo != null - && resolveInfo.activityInfo != null - && resolveInfo.activityInfo.enabled; + final boolean isEmbeddingActivityEnabled = + intent.resolveActivity(context.getPackageManager()) != null; + + return isEmbeddingActivityEnabled; } return false; } diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java index 8b17be1e8ec9..dee68948a16e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java @@ -85,6 +85,7 @@ public class EnableZenModeDialog { @VisibleForTesting protected Context mContext; private final int mThemeResId; + private final boolean mCancelIsNeutral; @VisibleForTesting protected TextView mZenAlarmWarning; @VisibleForTesting @@ -101,8 +102,13 @@ public class EnableZenModeDialog { } public EnableZenModeDialog(Context context, int themeResId) { + this(context, themeResId, false /* cancelIsNeutral */); + } + + public EnableZenModeDialog(Context context, int themeResId, boolean cancelIsNeutral) { mContext = context; mThemeResId = themeResId; + mCancelIsNeutral = cancelIsNeutral; } public AlertDialog createDialog() { @@ -115,7 +121,6 @@ public class EnableZenModeDialog { final AlertDialog.Builder builder = new AlertDialog.Builder(mContext, mThemeResId) .setTitle(R.string.zen_mode_settings_turn_on_dialog_title) - .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.zen_mode_enable_dialog_turn_on, new DialogInterface.OnClickListener() { @Override @@ -145,6 +150,12 @@ public class EnableZenModeDialog { } }); + if (mCancelIsNeutral) { + builder.setNeutralButton(R.string.cancel, null); + } else { + builder.setNegativeButton(R.string.cancel, null); + } + View contentView = getContentView(); bindConditions(forever()); builder.setView(contentView); diff --git a/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml new file mode 100644 index 000000000000..230a25628c40 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46"> + <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125"> + <path android:name="_R_G_L_2_G_D_0_P_0" + android:fillColor="#FF000000" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " /> + </group> + <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125"> + <path android:name="_R_G_L_1_G_D_0_P_0" + android:strokeColor="#FF000000" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:strokeWidth="1.5" + android:strokeAlpha="1" + android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " /> + </group> + <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5"> + <path android:name="_R_G_L_0_G_D_0_P_0" + android:strokeColor="#FF000000" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:strokeWidth="1.5" + android:strokeAlpha="1" + android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " /> + </group> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml index c58e2e3266d0..b3987f1aeeda 100644 --- a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml +++ b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml @@ -50,6 +50,11 @@ android:state_first="true" android:state_single="true" android:drawable="@drawable/ic_lock_aod" /> + <item + android:id="@+id/unlocked_aod" + android:state_last="true" + android:state_single="true" + android:drawable="@drawable/ic_unlocked_aod" /> <item android:id="@+id/no_icon" @@ -79,4 +84,19 @@ android:fromId="@id/locked" android:toId="@id/locked_aod" android:drawable="@drawable/lock_ls_to_aod" /> + + <transition + android:fromId="@id/unlocked_aod" + android:toId="@id/unlocked" + android:drawable="@drawable/unlocked_aod_to_ls" /> + + <transition + android:fromId="@id/unlocked" + android:toId="@id/unlocked_aod" + android:drawable="@drawable/unlocked_ls_to_aod" /> + + <transition + android:fromId="@id/unlocked" + android:toId="@id/locked_aod" + android:drawable="@drawable/unlocked_to_aod_lock" /> </animated-selector> diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml new file mode 100644 index 000000000000..3b59ba8815b8 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2021 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. +--> +<animated-vector xmlns:aapt="http://schemas.android.com/aapt" + xmlns:android="http://schemas.android.com/apk/res/android"> + <aapt:attr name="android:drawable"> + <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125"> + <path android:name="_R_G_L_2_G_D_0_P_0" + android:fillColor="#FF000000" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " /> + </group> + <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125"> + <path android:name="_R_G_L_1_G_D_0_P_0" + android:strokeColor="#FF000000" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:strokeWidth="1.5" + android:strokeAlpha="1" + android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " /> + </group> + <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5"> + <path android:name="_R_G_L_0_G_D_0_P_0" + android:strokeColor="#FF000000" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:strokeWidth="1.5" + android:strokeAlpha="1" + android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> + <target android:name="_R_G_L_2_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="333" android:startOffset="0" android:valueFrom="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueTo="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="strokeWidth" + android:duration="333" + android:startOffset="0" + android:valueFrom="1.5" + android:valueTo="2" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.386,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" + android:duration="333" + android:startOffset="0" + android:valueFrom="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueTo="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="strokeWidth" + android:duration="333" + android:startOffset="0" + android:valueFrom="1.5" + android:valueTo="2.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" + android:duration="333" + android:startOffset="0" + android:valueFrom="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueTo="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" + android:duration="517" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> +</animated-vector>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml new file mode 100644 index 000000000000..1c6d0b5193eb --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2021 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. +--> +<animated-vector xmlns:aapt="http://schemas.android.com/aapt" + xmlns:android="http://schemas.android.com/apk/res/android"> + <aapt:attr name="android:drawable"> + <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125"> + <path android:name="_R_G_L_2_G_D_0_P_0" + android:fillColor="#FF000000" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " /> + </group> + <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125"> + <path android:name="_R_G_L_1_G_D_0_P_0" + android:strokeColor="#FF000000" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:strokeAlpha="1" + android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " /> + </group> + <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5"> + <path android:name="_R_G_L_0_G_D_0_P_0" + android:strokeColor="#FF000000" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:strokeWidth="2.5" + android:strokeAlpha="1" + android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> + <target android:name="_R_G_L_2_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" + android:duration="333" + android:startOffset="0" + android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="strokeWidth" + android:duration="333" + android:startOffset="0" + android:valueFrom="2" + android:valueTo="1.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" + android:duration="333" + android:startOffset="0" + android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="strokeWidth" + android:duration="333" + android:startOffset="0" + android:valueFrom="2.5" + android:valueTo="1.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" + android:duration="333" + android:startOffset="0" + android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" + android:duration="517" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> +</animated-vector>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml new file mode 100644 index 000000000000..b6d76e01ad95 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml @@ -0,0 +1,169 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2021 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. +--> +<animated-vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android"> + <aapt:attr name="android:drawable"> + <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G_T_1" android:translateX="22.75" android:translateY="22.25" android:scaleX="1.02" android:scaleY="1.02"> + <group android:name="_R_G_L_2_G" android:translateX="-8.75" android:translateY="-8.75"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#FF000000" + android:strokeLineJoin="round" + android:strokeWidth="2.5" + android:strokeAlpha="1" + android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " /> + </group> + </group> + <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125"> + <path android:name="_R_G_L_1_G_D_0_P_0" + android:strokeColor="#FF000000" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:strokeAlpha="1" + android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " /> + </group> + <group android:name="_R_G_L_0_G" android:translateX="23" android:translateY="32.125"> + <path android:name="_R_G_L_0_G_D_0_P_0" + android:fillColor="#FF000000" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> + <target android:name="_R_G_L_2_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="strokeWidth" + android:duration="333" android:startOffset="0" android:valueFrom="2.5" android:valueTo="2" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0.833,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" + android:duration="83" android:startOffset="0" android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.456,0 0.464,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" + android:duration="133" android:startOffset="83" android:valueFrom="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueTo="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.606,0 0.035,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" + android:duration="117" android:startOffset="217" android:valueFrom="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueTo="M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.511,0 0.409,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" + android:duration="333" android:startOffset="0" android:valueFrom="22.75" android:valueTo="23" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateY" android:duration="333" android:startOffset="0" android:valueFrom="22.25" android:valueTo="25.5" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="scaleX" + android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" + android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="strokeWidth" + android:duration="333" android:startOffset="0" android:valueFrom="2" android:valueTo="1.5" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.38,0 0.274,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" + android:duration="333" android:startOffset="0" android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" + android:duration="333" android:startOffset="0" android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" + android:duration="850" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" /> + </set> + </aapt:attr> + </target> +</animated-vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml deleted file mode 100644 index 50267fda0b25..000000000000 --- a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 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 - --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:shape="rectangle"> - <stroke - android:color="?androidprv:attr/colorAccentPrimaryVariant" - android:width="1dp"/> - <corners android:radius="20dp"/> - <padding - android:left="8dp" - android:right="8dp" - android:top="4dp" - android:bottom="4dp" /> - <solid android:color="@android:color/transparent" /> -</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml index 665b4564ebf1..a47299d6f854 100644 --- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml +++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml @@ -29,7 +29,7 @@ <shape android:shape="rectangle"> <corners android:radius="?android:attr/buttonCornerRadius"/> <solid android:color="@android:color/transparent"/> - <stroke android:color="?androidprv:attr/colorAccentPrimary" + <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant" android:width="1dp" /> <padding android:left="@dimen/dialog_button_horizontal_padding" diff --git a/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml b/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml deleted file mode 100644 index 59a31e8f6136..000000000000 --- a/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 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 - --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:shape="rectangle"> - <stroke - android:color="?androidprv:attr/colorAccentPrimary" - android:width="1dp"/> - <corners android:radius="24dp"/> - <padding - android:left="16dp" - android:right="16dp" - android:top="8dp" - android:bottom="8dp" /> - <solid android:color="@android:color/transparent" /> -</shape> diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml index 3c9e44e2dba9..89690e8ff0ec 100644 --- a/packages/SystemUI/res/layout/auth_biometric_contents.xml +++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml @@ -73,6 +73,9 @@ android:accessibilityLiveRegion="polite" android:singleLine="true" android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:scrollHorizontally="true" + android:fadingEdge="horizontal" android:textColor="@color/biometric_dialog_gray"/> <LinearLayout diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml index 46cbc253175e..1e0ce0026d8e 100644 --- a/packages/SystemUI/res/layout/auth_credential_password_view.xml +++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml @@ -18,71 +18,64 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:elevation="@dimen/biometric_dialog_elevation" - android:orientation="vertical"> + android:orientation="vertical" + android:gravity="center_horizontal" + android:elevation="@dimen/biometric_dialog_elevation"> - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <LinearLayout - android:id="@+id/auth_credential_header" - style="@style/AuthCredentialHeaderStyle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_alignParentTop="true"> - - <ImageView - android:id="@+id/icon" - android:layout_width="48dp" - android:layout_height="48dp" - android:contentDescription="@null" /> - - <TextView - android:id="@+id/title" - style="@style/TextAppearance.AuthCredential.Title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> + <Space + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"/> - <TextView - android:id="@+id/subtitle" - style="@style/TextAppearance.AuthCredential.Subtitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> + <ImageView + android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> - <TextView - android:id="@+id/description" - style="@style/TextAppearance.AuthCredential.Description" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> + <TextView + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/TextAppearance.AuthCredential.Title"/> - </LinearLayout> + <TextView + android:id="@+id/subtitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/TextAppearance.AuthCredential.Subtitle"/> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center" - android:orientation="vertical" - android:layout_alignParentBottom="true"> + <TextView + android:id="@+id/description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/TextAppearance.AuthCredential.Description"/> - <ImeAwareEditText - android:id="@+id/lockPassword" - style="@style/TextAppearance.AuthCredential.PasswordEntry" - android:layout_width="208dp" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii" - android:inputType="textPassword" - android:minHeight="48dp" /> + <Space + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"/> - <TextView - android:id="@+id/error" - style="@style/TextAppearance.AuthCredential.Error" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> + <ImeAwareEditText + android:id="@+id/lockPassword" + android:layout_width="208dp" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:minHeight="48dp" + android:gravity="center" + android:inputType="textPassword" + android:maxLength="500" + android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii" + style="@style/TextAppearance.AuthCredential.PasswordEntry"/> - </LinearLayout> + <TextView + android:id="@+id/error" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/TextAppearance.AuthCredential.Error"/> - </RelativeLayout> + <Space + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="5"/> </com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml index 470298ea0b13..4939ea2c99ee 100644 --- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml +++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml @@ -22,81 +22,76 @@ android:gravity="center_horizontal" android:elevation="@dimen/biometric_dialog_elevation"> - <RelativeLayout + <Space + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"/> + + <ImageView + android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <TextView + android:id="@+id/title" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> + android:layout_height="wrap_content" + style="@style/TextAppearance.AuthCredential.Title"/> - <LinearLayout - android:id="@+id/auth_credential_header" - style="@style/AuthCredentialHeaderStyle" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <ImageView - android:id="@+id/icon" - android:layout_width="48dp" - android:layout_height="48dp" - android:contentDescription="@null" /> - - <TextView - android:id="@+id/title" - style="@style/TextAppearance.AuthCredential.Title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - - <TextView - android:id="@+id/subtitle" - style="@style/TextAppearance.AuthCredential.Subtitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - - <TextView - android:id="@+id/description" - style="@style/TextAppearance.AuthCredential.Description" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - </LinearLayout> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/auth_credential_header" - android:gravity="center" - android:orientation="vertical" - android:paddingBottom="16dp" - android:paddingTop="60dp"> + <TextView + android:id="@+id/subtitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/TextAppearance.AuthCredential.Subtitle"/> - <FrameLayout - style="@style/LockPatternContainerStyle" - android:layout_width="wrap_content" - android:layout_height="0dp" - android:layout_weight="1"> + <TextView + android:id="@+id/description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/TextAppearance.AuthCredential.Description"/> - <com.android.internal.widget.LockPatternView - android:id="@+id/lockPattern" - style="@style/LockPatternStyle" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" /> + <Space + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"/> - </FrameLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="center" + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:paddingTop="0dp" + android:paddingBottom="16dp" + android:clipToPadding="false"> + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="0dp" + android:layout_weight="1" + style="@style/LockPatternContainerStyle"> + + <com.android.internal.widget.LockPatternView + android:id="@+id/lockPattern" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + style="@style/LockPatternStyleBiometricPrompt"/> - </LinearLayout> + </FrameLayout> - <LinearLayout + <TextView + android:id="@+id/error" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_alignParentBottom="true"> - - <TextView - android:id="@+id/error" - style="@style/TextAppearance.AuthCredential.Error" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> + style="@style/TextAppearance.AuthCredential.Error"/> - </LinearLayout> + </LinearLayout> - </RelativeLayout> + <Space + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"/> </com.android.systemui.biometrics.AuthCredentialPatternView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml index c575855d9af8..275e0a52ab45 100644 --- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml +++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml @@ -37,7 +37,7 @@ android:ellipsize="end" android:gravity="center_vertical|center_horizontal" android:layout_width="wrap_content" - android:layout_height="32dp" + android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.InternetDialog" android:textSize="24sp"/> @@ -45,7 +45,7 @@ android:id="@+id/internet_dialog_subtitle" android:gravity="center_vertical|center_horizontal" android:layout_width="wrap_content" - android:layout_height="20dp" + android:layout_height="wrap_content" android:layout_marginTop="4dp" android:ellipsize="end" android:maxLines="1" @@ -150,6 +150,7 @@ android:gravity="start|center_vertical"> <TextView android:id="@+id/mobile_title" + android:maxLines="1" style="@style/InternetDialog.NetworkTitle"/> <TextView android:id="@+id/mobile_summary" @@ -380,54 +381,44 @@ android:id="@+id/button_layout" android:orientation="horizontal" android:layout_width="match_parent" - android:layout_height="48dp" - android:layout_marginStart="24dp" - android:layout_marginEnd="24dp" + android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:layout_marginBottom="34dp" + android:layout_marginStart="@dimen/dialog_side_padding" + android:layout_marginEnd="@dimen/dialog_side_padding" + android:layout_marginBottom="@dimen/dialog_bottom_padding" android:clickable="false" android:focusable="false"> <FrameLayout android:id="@+id/apm_layout" android:layout_width="wrap_content" - android:layout_height="48dp" + android:layout_height="wrap_content" android:clickable="true" android:focusable="true" android:layout_gravity="start|center_vertical" android:orientation="vertical"> <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="@string/turn_off_airplane_mode" android:ellipsize="end" - style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" - android:layout_width="wrap_content" - android:layout_height="36dp" - android:layout_gravity="start|center_vertical" - android:textAppearance="@style/TextAppearance.InternetDialog" - android:textSize="14sp" - android:background="@drawable/internet_dialog_footer_background" + style="@style/Widget.Dialog.Button.BorderButton" android:clickable="false"/> </FrameLayout> <FrameLayout android:id="@+id/done_layout" android:layout_width="wrap_content" - android:layout_height="48dp" + android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:clickable="true" - android:focusable="true" android:layout_gravity="end|center_vertical" - android:orientation="vertical"> + android:clickable="true" + android:focusable="true"> <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="@string/inline_done_button" - android:ellipsize="end" - style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" - android:layout_width="67dp" - android:layout_height="36dp" - android:layout_gravity="end|center_vertical" - android:textAppearance="@style/TextAppearance.InternetDialog" - android:textSize="14sp" - android:background="@drawable/internet_dialog_footer_background" + style="@style/Widget.Dialog.Button" android:clickable="false"/> </FrameLayout> </FrameLayout> diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml index 07fd1b0c001c..3a186d2cc8d2 100644 --- a/packages/SystemUI/res/layout/media_output_dialog.xml +++ b/packages/SystemUI/res/layout/media_output_dialog.xml @@ -89,17 +89,16 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" - android:layout_marginStart="24dp" - android:layout_marginBottom="24dp" - android:layout_marginEnd="24dp" + android:layout_marginStart="@dimen/dialog_side_padding" + android:layout_marginEnd="@dimen/dialog_side_padding" + android:layout_marginBottom="@dimen/dialog_bottom_padding" android:orientation="horizontal"> <Button android:id="@+id/stop" - style="@style/MediaOutputRoundedOutlinedButton" + style="@style/Widget.Dialog.Button.BorderButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:minWidth="0dp" android:text="@string/keyboard_key_media_stop" android:visibility="gone"/> @@ -110,10 +109,9 @@ <Button android:id="@+id/done" - style="@style/MediaOutputRoundedOutlinedButton" + style="@style/Widget.Dialog.Button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:minWidth="0dp" android:text="@string/inline_done_button"/> </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml index 6c5ad5060495..6012b58d0293 100644 --- a/packages/SystemUI/res/layout/screen_record_dialog.xml +++ b/packages/SystemUI/res/layout/screen_record_dialog.xml @@ -27,10 +27,10 @@ <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="24dp" - android:paddingEnd="24dp" - android:paddingTop="26dp" - android:paddingBottom="30dp" + android:paddingStart="@dimen/dialog_side_padding" + android:paddingEnd="@dimen/dialog_side_padding" + android:paddingTop="@dimen/dialog_top_padding" + android:paddingBottom="@dimen/dialog_bottom_padding" android:orientation="vertical"> <!-- Header --> @@ -108,10 +108,7 @@ android:layout_weight="0" android:layout_gravity="start" android:text="@string/cancel" - android:textColor="?android:textColorPrimary" - android:background="@drawable/screenrecord_button_background_outline" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="14sp"/> + style="@style/Widget.Dialog.Button.BorderButton" /> <Space android:layout_width="0dp" android:layout_height="match_parent" @@ -123,10 +120,7 @@ android:layout_weight="0" android:layout_gravity="end" android:text="@string/screenrecord_start" - android:textColor="@android:color/system_neutral1_900" - android:background="@drawable/screenrecord_button_background_solid" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="14sp"/> + style="@style/Widget.Dialog.Button" /> </LinearLayout> </LinearLayout> </ScrollView> diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml index 332dc6ee8656..a2abdb211602 100644 --- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml +++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml @@ -26,8 +26,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="64dp" - android:paddingTop="12dp" android:textAppearance="?android:attr/textAppearanceButton" - android:gravity="top|center_horizontal" + android:gravity="center" android:text="@string/empty_shade_text"/> </com.android.systemui.statusbar.EmptyShadeView> diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml index f057603e2cd0..1d6f279afc66 100644 --- a/packages/SystemUI/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/res/values-h800dp/dimens.xml @@ -22,5 +22,5 @@ <dimen name="large_clock_text_size">200dp</dimen> <!-- With the large clock, move up slightly from the center --> - <dimen name="keyguard_large_clock_top_margin">-104dp</dimen> + <dimen name="keyguard_large_clock_top_margin">-112dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index b98694e2fae7..fcb369876cf2 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -77,7 +77,7 @@ <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 --> <!-- UDFPS colors --> - <color name="udfps_enroll_icon">#ffffff</color> <!-- 100% white --> + <color name="udfps_enroll_icon">#7DA7F1</color> <color name="GM2_green_500">#FF41Af6A</color> <color name="GM2_blue_500">#5195EA</color> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 8a3bacae9243..e455aaa225be 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -134,10 +134,10 @@ <color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 --> <!-- UDFPS colors --> - <color name="udfps_enroll_icon">#000000</color> <!-- 100% black --> - <color name="udfps_moving_target_fill">#cc4285f4</color> <!-- 80% blue --> - <color name="udfps_enroll_progress">#ff669DF6</color> <!-- blue 400 --> - <color name="udfps_enroll_progress_help">#ffEE675C</color> <!-- red 400 --> + <color name="udfps_enroll_icon">#7DA7F1</color> + <color name="udfps_moving_target_fill">#475670</color> + <color name="udfps_enroll_progress">#7DA7F1</color> + <color name="udfps_enroll_progress_help">#ffEE675C</color> <!-- Global screenshot actions --> <color name="global_screenshot_button_ripple">#1f000000</color> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 475f70fa9fdc..e00b9410a8a7 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -595,17 +595,6 @@ 280 </integer> - <!-- Haptic feedback intensity for ticks used for the udfps dwell time --> - <item name="config_udfpsTickIntensity" translatable="false" format="float" - type="dimen">.5</item> - - <!-- Haptic feedback delay between ticks used for udfps dwell time --> - <integer name="config_udfpsTickDelay" translatable="false">25</integer> - - <!-- Haptic feedback tick type - if true, uses VibrationEffect.Composition.PRIMITIVE_LOW_TICK - else uses VibrationEffect.Composition.PRIMITIVE_TICK --> - <bool name="config_udfpsUseLowTick">true</bool> - <!-- package name of a built-in camera app to use to restrict implicit intent resolution when the double-press power gesture is used. Ignored if empty. --> <string translatable="false" name="config_cameraGesturePackage"></string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1938e48e859a..a67d5c254ebf 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -601,7 +601,7 @@ <!-- When large clock is showing, offset the smartspace by this amount --> <dimen name="keyguard_smartspace_top_offset">12dp</dimen> <!-- With the large clock, move up slightly from the center --> - <dimen name="keyguard_large_clock_top_margin">-52dp</dimen> + <dimen name="keyguard_large_clock_top_margin">-60dp</dimen> <!-- Default line spacing multiplier between hours and minutes of the keyguard clock --> <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 0cfd8d6d0716..051a30ccb524 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -200,9 +200,9 @@ <style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/> - <style name="TextAppearance.AuthCredential" - parent="@android:style/TextAppearance.DeviceDefault"> + <style name="TextAppearance.AuthCredential"> <item name="android:accessibilityLiveRegion">polite</item> + <item name="android:gravity">center_horizontal</item> <item name="android:textAlignment">gravity</item> <item name="android:layout_gravity">top</item> <item name="android:textColor">?android:attr/textColorPrimary</item> @@ -210,57 +210,44 @@ <style name="TextAppearance.AuthCredential.Title"> <item name="android:fontFamily">google-sans</item> - <item name="android:layout_marginTop">20dp</item> - <item name="android:textSize">36sp</item> + <item name="android:paddingTop">12dp</item> + <item name="android:paddingHorizontal">24dp</item> + <item name="android:textSize">24sp</item> </style> <style name="TextAppearance.AuthCredential.Subtitle"> <item name="android:fontFamily">google-sans</item> - <item name="android:layout_marginTop">20dp</item> - <item name="android:textSize">18sp</item> + <item name="android:paddingTop">8dp</item> + <item name="android:paddingHorizontal">24dp</item> + <item name="android:textSize">16sp</item> </style> <style name="TextAppearance.AuthCredential.Description"> <item name="android:fontFamily">google-sans</item> - <item name="android:layout_marginTop">20dp</item> - <item name="android:textSize">16sp</item> + <item name="android:paddingTop">8dp</item> + <item name="android:paddingHorizontal">24dp</item> + <item name="android:textSize">14sp</item> </style> <style name="TextAppearance.AuthCredential.Error"> <item name="android:paddingTop">6dp</item> - <item name="android:paddingBottom">18dp</item> <item name="android:paddingHorizontal">24dp</item> <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/colorError</item> - <item name="android:gravity">center</item> </style> - <style name="TextAppearance.AuthCredential.PasswordEntry"> + <style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault"> <item name="android:gravity">center</item> <item name="android:singleLine">true</item> <item name="android:textColor">?android:attr/colorForeground</item> <item name="android:textSize">24sp</item> </style> - <style name="AuthCredentialHeaderStyle"> - <item name="android:paddingStart">48dp</item> - <item name="android:paddingEnd">24dp</item> - <item name="android:paddingTop">28dp</item> - <item name="android:paddingBottom">20dp</item> - <item name="android:orientation">vertical</item> - <item name="android:layout_gravity">top</item> - </style> - <style name="DeviceManagementDialogTitle"> <item name="android:gravity">center</item> <item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item> </style> - <style name="AuthCredentialPasswordTheme" parent="@style/Theme.MaterialComponents.DayNight"> - <item name="colorPrimary">?android:attr/colorPrimary</item> - <item name="colorPrimaryDark">?android:attr/colorPrimary</item> - </style> - <style name="TextAppearance.DeviceManagementDialog.Content" parent="@*android:style/TextAppearance.DeviceDefault.Subhead"/> <style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI"> @@ -320,8 +307,9 @@ <item name="android:maxWidth">420dp</item> <item name="android:minHeight">0dp</item> <item name="android:minWidth">0dp</item> - <item name="android:paddingHorizontal">60dp</item> - <item name="android:paddingBottom">40dp</item> + <item name="android:paddingBottom">0dp</item> + <item name="android:paddingHorizontal">44dp</item> + <item name="android:paddingTop">0dp</item> </style> <style name="LockPatternStyle"> @@ -462,10 +450,6 @@ <item name="android:background">@drawable/btn_borderless_rect</item> </style> - <style name="MediaOutputRoundedOutlinedButton" parent="@android:style/Widget.Material.Button"> - <item name="android:background">@drawable/media_output_dialog_button_background</item> - </style> - <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings"> <item name="android:windowActionBar">false</item> <item name="preferenceTheme">@style/TunerPreferenceTheme</item> @@ -893,13 +877,18 @@ <item name="android:textAlignment">center</item> </style> - <style name="Widget.Dialog.Button" parent = "Theme.SystemUI.Dialog"> + + <style name="Widget" /> + <style name="Widget.Dialog" /> + <style name="Widget.Dialog.Button"> + <item name="android:buttonCornerRadius">28dp</item> <item name="android:background">@drawable/qs_dialog_btn_filled</item> <item name="android:textColor">?androidprv:attr/textColorOnAccent</item> <item name="android:textSize">14sp</item> <item name="android:lineHeight">20sp</item> <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item> <item name="android:stateListAnimator">@null</item> + <item name="android:minWidth">0dp</item> </style> <style name="Widget.Dialog.Button.BorderButton"> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java index 7d0fb5d38849..567e7aa3d78f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java @@ -53,8 +53,8 @@ public class PipSurfaceTransactionHelper { tx.setMatrix(leash, mTmpTransform, mTmpFloat9) .setPosition(leash, positionX, positionY) .setCornerRadius(leash, cornerRadius); - return new PictureInPictureSurfaceTransaction( - positionX, positionY, mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds); + return newPipSurfaceTransaction(positionX, positionY, + mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds); } public PictureInPictureSurfaceTransaction scale( @@ -70,8 +70,8 @@ public class PipSurfaceTransactionHelper { tx.setMatrix(leash, mTmpTransform, mTmpFloat9) .setPosition(leash, positionX, positionY) .setCornerRadius(leash, cornerRadius); - return new PictureInPictureSurfaceTransaction( - positionX, positionY, mTmpFloat9, degree, cornerRadius, sourceBounds); + return newPipSurfaceTransaction(positionX, positionY, + mTmpFloat9, degree, cornerRadius, sourceBounds); } public PictureInPictureSurfaceTransaction scaleAndCrop( @@ -93,8 +93,8 @@ public class PipSurfaceTransactionHelper { .setWindowCrop(leash, mTmpDestinationRect) .setPosition(leash, left, top) .setCornerRadius(leash, cornerRadius); - return new PictureInPictureSurfaceTransaction( - left, top, mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect); + return newPipSurfaceTransaction(left, top, + mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect); } public PictureInPictureSurfaceTransaction scaleAndRotate( @@ -125,8 +125,7 @@ public class PipSurfaceTransactionHelper { .setWindowCrop(leash, mTmpDestinationRect) .setPosition(leash, adjustedPositionX, adjustedPositionY) .setCornerRadius(leash, cornerRadius); - return new PictureInPictureSurfaceTransaction( - adjustedPositionX, adjustedPositionY, + return newPipSurfaceTransaction(adjustedPositionX, adjustedPositionY, mTmpFloat9, degree, cornerRadius, mTmpDestinationRect); } @@ -137,6 +136,17 @@ public class PipSurfaceTransactionHelper { return mCornerRadius * scale; } + private static PictureInPictureSurfaceTransaction newPipSurfaceTransaction( + float posX, float posY, float[] float9, float rotation, float cornerRadius, + Rect windowCrop) { + return new PictureInPictureSurfaceTransaction.Builder() + .setPosition(posX, posY) + .setTransform(float9, rotation) + .setCornerRadius(cornerRadius) + .setWindowCrop(windowCrop) + .build(); + } + /** @return {@link SurfaceControl.Transaction} instance with vsync-id */ public static SurfaceControl.Transaction newSurfaceControlTransaction() { final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index c628d4401bb1..324fae161afc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -89,7 +89,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final KeyguardBypassController mBypassController; - private int mLargeClockTopMargin = 0; private int mKeyguardClockTopMargin = 0; /** @@ -276,16 +275,13 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } private void updateClockLayout() { - if (mSmartspaceController.isEnabled()) { - RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, - MATCH_PARENT); - mLargeClockTopMargin = getContext().getResources().getDimensionPixelSize( - R.dimen.keyguard_large_clock_top_margin); - lp.topMargin = mLargeClockTopMargin; - mLargeClockFrame.setLayoutParams(lp); - } else { - mLargeClockTopMargin = 0; - } + int largeClockTopMargin = getContext().getResources().getDimensionPixelSize( + R.dimen.keyguard_large_clock_top_margin); + + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, + MATCH_PARENT); + lp.topMargin = largeClockTopMargin; + mLargeClockFrame.setLayoutParams(lp); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index e267c5c7919c..cc452c6f3b79 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -35,12 +35,10 @@ import android.hardware.biometrics.SensorLocationInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.media.AudioAttributes; import android.os.Process; -import android.os.VibrationEffect; import android.os.Vibrator; import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; @@ -68,8 +66,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; import com.android.systemui.util.concurrency.DelayableExecutor; -import com.airbnb.lottie.LottieAnimationView; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Objects; @@ -104,10 +100,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @NonNull private final AccessibilityManager mAccessibilityManager; @NonNull private final ConfigurationController mConfigurationController; @NonNull private final DelayableExecutor mExecutor; - @NonNull private final LayoutInflater mLayoutInflater; private boolean mUdfpsEnrolled; - @Nullable private LottieAnimationView mAodFp; @NonNull private final AnimatedStateListDrawable mIcon; @NonNull private CharSequence mUnlockedLabel; @@ -119,7 +113,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private VelocityTracker mVelocityTracker; // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active. private int mActivePointerId = -1; - private VibrationEffect mTick; private boolean mIsDozing; private boolean mIsBouncerShowing; @@ -143,7 +136,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme // for udfps when strong auth is required or unlocked on AOD private boolean mShowAodLockIcon; - private boolean mShowAODFpIcon; + private boolean mShowAodUnlockedIcon; private final int mMaxBurnInOffsetX; private final int mMaxBurnInOffsetY; private float mInterpolatedDarkAmount; @@ -166,8 +159,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @NonNull @Main DelayableExecutor executor, @Nullable Vibrator vibrator, @Nullable AuthRippleController authRippleController, - @NonNull @Main Resources resources, - @NonNull LayoutInflater inflater + @NonNull @Main Resources resources ) { super(view); mStatusBarStateController = statusBarStateController; @@ -181,7 +173,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mExecutor = executor; mVibrator = vibrator; mAuthRippleController = authRippleController; - mLayoutInflater = inflater; mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); @@ -263,11 +254,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } boolean wasShowingUnlock = mShowUnlockIcon; - boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon; + boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon + && !mShowAodUnlockedIcon && !mShowAodLockIcon; mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen() && (!mUdfpsEnrolled || !mRunningFPS); mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen(); - mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen; + mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen; mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen; final CharSequence prevContentDescription = mView.getContentDescription(); @@ -284,20 +276,11 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mView.updateIcon(ICON_UNLOCK, false); mView.setContentDescription(mUnlockedLabel); mView.setVisibility(View.VISIBLE); - } else if (mShowAODFpIcon) { - // AOD fp icon is special cased as a lottie view (it updates for each burn-in offset), - // this state shows a transparent view - mView.setContentDescription(null); - mAodFp.setVisibility(View.VISIBLE); - mAodFp.setContentDescription(mCanDismissLockScreen ? mUnlockedLabel : mLockedLabel); - - mView.updateIcon(ICON_FINGERPRINT, true); // this shows no icon + } else if (mShowAodUnlockedIcon) { + mView.updateIcon(ICON_UNLOCK, true); + mView.setContentDescription(mUnlockedLabel); mView.setVisibility(View.VISIBLE); } else if (mShowAodLockIcon) { - if (wasShowingUnlock) { - // transition to the unlock icon first - mView.updateIcon(ICON_LOCK, false); - } mView.updateIcon(ICON_LOCK, true); mView.setContentDescription(mLockedLabel); mView.setVisibility(View.VISIBLE); @@ -307,11 +290,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mView.setContentDescription(null); } - if (!mShowAODFpIcon && mAodFp != null) { - mAodFp.setVisibility(View.INVISIBLE); - mAodFp.setContentDescription(null); - } - if (!Objects.equals(prevContentDescription, mView.getContentDescription()) && mView.getContentDescription() != null) { mView.announceForAccessibility(mView.getContentDescription()); @@ -399,7 +377,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme pw.println(); pw.println(" mShowUnlockIcon: " + mShowUnlockIcon); pw.println(" mShowLockIcon: " + mShowLockIcon); - pw.println(" mShowAODFpIcon: " + mShowAODFpIcon); + pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon); pw.println(" mIsDozing: " + mIsDozing); pw.println(" mIsBouncerShowing: " + mIsBouncerShowing); pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric); @@ -428,13 +406,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme - mMaxBurnInOffsetY, mInterpolatedDarkAmount); float progress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount); - if (mAodFp != null) { - mAodFp.setTranslationX(offsetX); - mAodFp.setTranslationY(offsetY); - mAodFp.setProgress(progress); - mAodFp.setAlpha(255 * mInterpolatedDarkAmount); - } - mView.setTranslationX(offsetX); mView.setTranslationY(offsetY); } @@ -447,10 +418,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mView.setUseBackground(mUdfpsSupported); mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled(); - if (!wasUdfpsEnrolled && mUdfpsEnrolled && mAodFp == null) { - mLayoutInflater.inflate(R.layout.udfps_aod_lock_icon, mView); - mAodFp = mView.findViewById(R.id.lock_udfps_aod_fp); - } if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) { updateVisibility(); } @@ -597,15 +564,11 @@ public class LockIconViewController extends ViewController<LockIconView> impleme case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_HOVER_ENTER: if (mVibrator != null && !mDownDetected) { - if (mTick == null) { - mTick = UdfpsController.lowTick(getContext(), true, - LONG_PRESS_TIMEOUT); - } mVibrator.vibrate( Process.myUid(), getContext().getOpPackageName(), - mTick, - "lock-icon-tick", + UdfpsController.EFFECT_CLICK, + "lock-icon-down", VIBRATION_SONIFICATION_ATTRIBUTES); } @@ -718,8 +681,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private boolean inLockIconArea(MotionEvent event) { return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) - && (mView.getVisibility() == View.VISIBLE - || (mAodFp != null && mAodFp.getVisibility() == View.VISIBLE)); + && mView.getVisibility() == View.VISIBLE; } private boolean isActionable() { diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index bb0e79fe23a8..db88569382a7 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -121,7 +121,7 @@ public class SystemUIFactory { .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper()) .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper()) .setRecentTasks(mWMComponent.getRecentTasks()) - .setSizeCompatUI(Optional.of(mWMComponent.getSizeCompatUI())) + .setCompatUI(Optional.of(mWMComponent.getCompatUI())) .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop())); } else { // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option @@ -141,7 +141,7 @@ public class SystemUIFactory { .setStartingSurface(Optional.ofNullable(null)) .setTaskSurfaceHelper(Optional.ofNullable(null)) .setRecentTasks(Optional.ofNullable(null)) - .setSizeCompatUI(Optional.ofNullable(null)) + .setCompatUI(Optional.ofNullable(null)) .setDragAndDrop(Optional.ofNullable(null)); } mSysUIComponent = builder.build(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index f11dc9313852..e35b55841f2f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -636,6 +636,7 @@ public abstract class AuthBiometricView extends LinearLayout { mIndicatorView.setText(message); mIndicatorView.setTextColor(mTextColorError); mIndicatorView.setVisibility(View.VISIBLE); + mIndicatorView.setSelected(true); mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError()); Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java index fb4616a832dc..07aec6994bd0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java @@ -23,6 +23,7 @@ import android.graphics.RectF; import com.android.systemui.Dumpable; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.util.ViewController; @@ -44,6 +45,7 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> extends ViewController<T> implements Dumpable { @NonNull final StatusBarStateController mStatusBarStateController; @NonNull final PanelExpansionStateManager mPanelExpansionStateManager; + @NonNull final SystemUIDialogManager mDialogManager; @NonNull final DumpManager mDumpManger; boolean mNotificationShadeVisible; @@ -52,10 +54,12 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> T view, @NonNull StatusBarStateController statusBarStateController, @NonNull PanelExpansionStateManager panelExpansionStateManager, + @NonNull SystemUIDialogManager dialogManager, @NonNull DumpManager dumpManager) { super(view); mStatusBarStateController = statusBarStateController; mPanelExpansionStateManager = panelExpansionStateManager; + mDialogManager = dialogManager; mDumpManger = dumpManager; } @@ -64,12 +68,14 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> @Override protected void onViewAttached() { mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener); + mDialogManager.registerListener(mDialogListener); mDumpManger.registerDumpable(getDumpTag(), this); } @Override protected void onViewDetached() { mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener); + mDialogManager.registerListener(mDialogListener); mDumpManger.unregisterDumpable(getDumpTag()); } @@ -95,7 +101,8 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> * authentication. */ boolean shouldPauseAuth() { - return mNotificationShadeVisible; + return mNotificationShadeVisible + || mDialogManager.shouldHideAffordance(); } /** @@ -189,4 +196,7 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> updatePauseAuth(); } }; + + private final SystemUIDialogManager.Listener mDialogListener = + (shouldHide) -> updatePauseAuth(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java index 894b29583cc9..3732100a2a06 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; /** @@ -30,8 +31,10 @@ class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> { @NonNull UdfpsBpView view, @NonNull StatusBarStateController statusBarStateController, @NonNull PanelExpansionStateManager panelExpansionStateManager, + @NonNull SystemUIDialogManager systemUIDialogManager, @NonNull DumpManager dumpManager) { - super(view, statusBarStateController, panelExpansionStateManager, dumpManager); + super(view, statusBarStateController, panelExpansionStateManager, + systemUIDialogManager, dumpManager); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 9808045d4ea8..3e9d6b0fa362 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -47,7 +47,6 @@ import android.os.RemoteException; import android.os.Trace; import android.os.VibrationEffect; import android.os.Vibrator; -import android.provider.Settings; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -71,6 +70,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -119,6 +119,7 @@ public class UdfpsController implements DozeReceiver { @NonNull private final KeyguardStateController mKeyguardStateController; @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; @NonNull private final DumpManager mDumpManager; + @NonNull private final SystemUIDialogManager mDialogManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Nullable private final Vibrator mVibrator; @NonNull private final FalsingManager mFalsingManager; @@ -165,9 +166,6 @@ public class UdfpsController implements DozeReceiver { private boolean mAttemptedToDismissKeyguard; private Set<Callback> mCallbacks = new HashSet<>(); - private static final int DEFAULT_TICK = VibrationEffect.Composition.PRIMITIVE_LOW_TICK; - private final VibrationEffect mTick; - @VisibleForTesting public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = new AudioAttributes.Builder() @@ -282,9 +280,6 @@ public class UdfpsController implements DozeReceiver { return; } mGoodCaptureReceived = true; - if (mVibrator != null) { - mVibrator.cancel(); - } mView.stopIllumination(); if (mServerRequest != null) { mServerRequest.onAcquiredGood(); @@ -559,7 +554,8 @@ public class UdfpsController implements DozeReceiver { @Main Handler mainHandler, @NonNull ConfigurationController configurationController, @NonNull SystemClock systemClock, - @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) { + @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, + @NonNull SystemUIDialogManager dialogManager) { mContext = context; mExecution = execution; mVibrator = vibrator; @@ -574,6 +570,7 @@ public class UdfpsController implements DozeReceiver { mKeyguardStateController = keyguardStateController; mKeyguardViewManager = statusBarKeyguardViewManager; mDumpManager = dumpManager; + mDialogManager = dialogManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mFalsingManager = falsingManager; mPowerManager = powerManager; @@ -586,7 +583,6 @@ public class UdfpsController implements DozeReceiver { mConfigurationController = configurationController; mSystemClock = systemClock; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; - mTick = lowTick(context, false /* useShortRampup */, DEFAULT_VIBRATION_DURATION); mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists @@ -622,47 +618,6 @@ public class UdfpsController implements DozeReceiver { } /** - * Returns the continuous low tick effect that starts playing on the udfps finger-down event. - */ - public static VibrationEffect lowTick( - Context context, - boolean useShortRampUp, - long duration - ) { - boolean useLowTickDefault = context.getResources() - .getBoolean(R.bool.config_udfpsUseLowTick); - int primitiveTick = DEFAULT_TICK; - if (Settings.Global.getFloat( - context.getContentResolver(), - "tick-low", useLowTickDefault ? 1 : 0) == 0) { - primitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK; - } - float tickIntensity = Settings.Global.getFloat( - context.getContentResolver(), - "tick-intensity", - context.getResources().getFloat(R.dimen.config_udfpsTickIntensity)); - int tickDelay = Settings.Global.getInt( - context.getContentResolver(), - "tick-delay", - context.getResources().getInteger(R.integer.config_udfpsTickDelay)); - - VibrationEffect.Composition composition = VibrationEffect.startComposition(); - composition.addPrimitive(primitiveTick, tickIntensity, 0); - int primitives = (int) (duration / tickDelay); - float[] rampUp = new float[]{.48f, .58f, .69f, .83f}; - if (useShortRampUp) { - rampUp = new float[]{.5f, .7f}; - } - for (int i = 0; i < rampUp.length; i++) { - composition.addPrimitive(primitiveTick, tickIntensity * rampUp[i], tickDelay); - } - for (int i = rampUp.length; i < primitives; i++) { - composition.addPrimitive(primitiveTick, tickIntensity, tickDelay); - } - return composition.compose(); - } - - /** * Play haptic to signal udfps scanning started. */ @VisibleForTesting @@ -671,8 +626,8 @@ public class UdfpsController implements DozeReceiver { mVibrator.vibrate( Process.myUid(), mContext.getOpPackageName(), - mTick, - "udfps-onStart-tick", + EFFECT_CLICK, + "udfps-onStart-click", VIBRATION_SONIFICATION_ATTRIBUTES); } } @@ -864,6 +819,7 @@ public class UdfpsController implements DozeReceiver { mServerRequest.mEnrollHelper, mStatusBarStateController, mPanelExpansionStateManager, + mDialogManager, mDumpManager ); case BiometricOverlayConstants.REASON_AUTH_KEYGUARD: @@ -882,6 +838,7 @@ public class UdfpsController implements DozeReceiver { mSystemClock, mKeyguardStateController, mUnlockedScreenOffAnimationController, + mDialogManager, this ); case BiometricOverlayConstants.REASON_AUTH_BP: @@ -892,6 +849,7 @@ public class UdfpsController implements DozeReceiver { bpView, mStatusBarStateController, mPanelExpansionStateManager, + mDialogManager, mDumpManager ); case BiometricOverlayConstants.REASON_AUTH_OTHER: @@ -903,6 +861,7 @@ public class UdfpsController implements DozeReceiver { authOtherView, mStatusBarStateController, mPanelExpansionStateManager, + mDialogManager, mDumpManager ); default: @@ -1059,7 +1018,6 @@ public class UdfpsController implements DozeReceiver { } } mOnFingerDown = false; - mVibrator.cancel(); if (mView.isIlluminationRequested()) { mView.stopIllumination(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java index 1f01fc5a4b3d..ac38b13cc4dd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java @@ -20,9 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.content.Context; -import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Rect; @@ -30,7 +28,6 @@ import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; -import android.util.TypedValue; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.LinearInterpolator; @@ -38,7 +35,6 @@ import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.internal.graphics.ColorUtils; import com.android.systemui.R; /** @@ -106,9 +102,8 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { mSensorOutlinePaint = new Paint(0 /* flags */); mSensorOutlinePaint.setAntiAlias(true); - mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon)); - mSensorOutlinePaint.setStyle(Paint.Style.STROKE); - mSensorOutlinePaint.setStrokeWidth(2.f); + mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_moving_target_fill)); + mSensorOutlinePaint.setStyle(Paint.Style.FILL); mBlueFill = new Paint(0 /* flags */); mBlueFill.setAntiAlias(true); @@ -117,12 +112,12 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { mMovingTargetFpIcon = context.getResources() .getDrawable(R.drawable.ic_kg_fingerprint, null); - mMovingTargetFpIcon.setTint(Color.WHITE); + mMovingTargetFpIcon.setTint(mContext.getColor(R.color.udfps_enroll_icon)); mMovingTargetFpIcon.mutate(); mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon)); - mHintColorFaded = getHintColorFaded(context); + mHintColorFaded = context.getColor(R.color.udfps_moving_target_fill); mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress); mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP); mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP); @@ -218,22 +213,6 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { }; } - @ColorInt - private static int getHintColorFaded(@NonNull Context context) { - final TypedValue tv = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true); - final int alpha = (int) (tv.getFloat() * 255f); - - final int[] attrs = new int[] {android.R.attr.colorControlNormal}; - final TypedArray ta = context.obtainStyledAttributes(attrs); - try { - @ColorInt final int color = ta.getColor(0, context.getColor(R.color.white_disabled)); - return ColorUtils.setAlphaComponent(color, alpha); - } finally { - ta.recycle(); - } - } - void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { mEnrollHelper = helper; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java index 79c7e66d40f7..631a461b0627 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java @@ -18,12 +18,10 @@ package com.android.systemui.biometrics; import android.animation.ValueAnimator; import android.content.Context; -import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.drawable.Drawable; -import android.util.TypedValue; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; @@ -80,24 +78,11 @@ public class UdfpsEnrollProgressBarDrawable extends Drawable { mBackgroundPaint = new Paint(); mBackgroundPaint.setStrokeWidth(mStrokeWidthPx); - mBackgroundPaint.setColor(context.getColor(R.color.white_disabled)); + mBackgroundPaint.setColor(context.getColor(R.color.udfps_moving_target_fill)); mBackgroundPaint.setAntiAlias(true); mBackgroundPaint.setStyle(Paint.Style.STROKE); mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND); - // Set background paint color and alpha. - final int[] attrs = new int[] {android.R.attr.colorControlNormal}; - final TypedArray typedArray = context.obtainStyledAttributes(attrs); - try { - @ColorInt final int tintColor = typedArray.getColor(0, mBackgroundPaint.getColor()); - mBackgroundPaint.setColor(tintColor); - } finally { - typedArray.recycle(); - } - TypedValue alpha = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true); - mBackgroundPaint.setAlpha((int) (alpha.getFloat() * 255f)); - // Progress fill should *not* use the extracted system color. mFillPaint = new Paint(); mFillPaint.setStrokeWidth(mStrokeWidthPx); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java index 292a904af96e..ac9e92e22569 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java @@ -22,6 +22,7 @@ import android.graphics.PointF; import com.android.systemui.R; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; /** @@ -54,8 +55,10 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp @NonNull UdfpsEnrollHelper enrollHelper, @NonNull StatusBarStateController statusBarStateController, @NonNull PanelExpansionStateManager panelExpansionStateManager, + @NonNull SystemUIDialogManager systemUIDialogManager, @NonNull DumpManager dumpManager) { - super(view, statusBarStateController, panelExpansionStateManager, dumpManager); + super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager, + dumpManager); mEnrollProgressBarRadius = getContext().getResources() .getInteger(R.integer.config_udfpsEnrollProgressBar); mEnrollHelper = enrollHelper; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java index 619873367ee8..97dca0fbdedf 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; /** @@ -33,8 +34,10 @@ class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmO @NonNull UdfpsFpmOtherView view, @NonNull StatusBarStateController statusBarStateController, @NonNull PanelExpansionStateManager panelExpansionStateManager, + @NonNull SystemUIDialogManager systemUIDialogManager, @NonNull DumpManager dumpManager) { - super(view, statusBarStateController, panelExpansionStateManager, dumpManager); + super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager, + dumpManager); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 8f4d6f6aa973..3e8a568cb803 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -31,6 +31,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; @@ -86,8 +87,10 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @NonNull SystemClock systemClock, @NonNull KeyguardStateController keyguardStateController, @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, + @NonNull SystemUIDialogManager systemUIDialogManager, @NonNull UdfpsController udfpsController) { - super(view, statusBarStateController, panelExpansionStateManager, dumpManager); + super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager, + dumpManager); mKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockScreenShadeTransitionController = transitionController; @@ -217,6 +220,10 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud return false; } + if (mDialogManager.shouldHideAffordance()) { + return true; + } + if (mLaunchTransitionFadingAway) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index d3d6e03c9bc7..6f30ac3901e1 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -18,6 +18,7 @@ package com.android.systemui.controls.ui import android.annotation.MainThread import android.app.Dialog +import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -88,7 +89,7 @@ class ControlActionCoordinatorImpl @Inject constructor( bouncerOrRun(createAction(cvh.cws.ci.controlId, { cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) if (cvh.usePanel()) { - showDetail(cvh, control.getAppIntent().getIntent()) + showDetail(cvh, control.getAppIntent()) } else { cvh.action(CommandAction(templateId)) } @@ -116,7 +117,7 @@ class ControlActionCoordinatorImpl @Inject constructor( // Long press snould only be called when there is valid control state, otherwise ignore cvh.cws.control?.let { cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) - showDetail(cvh, it.getAppIntent().getIntent()) + showDetail(cvh, it.getAppIntent()) } }, false /* blockable */)) } @@ -167,10 +168,10 @@ class ControlActionCoordinatorImpl @Inject constructor( bgExecutor.execute { vibrator.vibrate(effect) } } - private fun showDetail(cvh: ControlViewHolder, intent: Intent) { + private fun showDetail(cvh: ControlViewHolder, pendingIntent: PendingIntent) { bgExecutor.execute { val activities: List<ResolveInfo> = context.packageManager.queryIntentActivities( - intent, + pendingIntent.getIntent(), PackageManager.MATCH_DEFAULT_ONLY ) @@ -178,7 +179,7 @@ class ControlActionCoordinatorImpl @Inject constructor( // make sure the intent is valid before attempting to open the dialog if (activities.isNotEmpty() && taskViewFactory.isPresent) { taskViewFactory.get().create(context, uiExecutor, { - dialog = DetailDialog(activityContext, it, intent, cvh).also { + dialog = DetailDialog(activityContext, it, pendingIntent, cvh).also { it.setOnDismissListener { _ -> dialog = null } it.show() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt index 8a47a36de8c4..4758ab04e2e5 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt @@ -43,7 +43,7 @@ import com.android.wm.shell.TaskView class DetailDialog( val activityContext: Context?, val taskView: TaskView, - val intent: Intent, + val pendingIntent: PendingIntent, val cvh: ControlViewHolder ) : Dialog( activityContext ?: cvh.context, @@ -59,6 +59,14 @@ class DetailDialog( var detailTaskId = INVALID_TASK_ID + private val fillInIntent = Intent().apply { + putExtra(EXTRA_USE_PANEL, true) + + // Apply flags to make behaviour match documentLaunchMode=always. + addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) + addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + } + fun removeDetailTask() { if (detailTaskId == INVALID_TASK_ID) return ActivityTaskManager.getInstance().removeTask(detailTaskId) @@ -67,13 +75,6 @@ class DetailDialog( val stateCallback = object : TaskView.Listener { override fun onInitialized() { - val launchIntent = Intent(intent) - launchIntent.putExtra(EXTRA_USE_PANEL, true) - - // Apply flags to make behaviour match documentLaunchMode=always. - launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) - launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - val options = activityContext?.let { ActivityOptions.makeCustomAnimation( it, @@ -82,9 +83,8 @@ class DetailDialog( ) } ?: ActivityOptions.makeBasic() taskView.startActivity( - PendingIntent.getActivity(context, 0, launchIntent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE), - null /* fillInIntent */, + pendingIntent, + fillInIntent, options, getTaskViewBounds() ) @@ -97,6 +97,9 @@ class DetailDialog( override fun onTaskCreated(taskId: Int, name: ComponentName?) { detailTaskId = taskId + requireViewById<ViewGroup>(R.id.controls_activity_view).apply { + setAlpha(1f) + } } override fun onReleased() { @@ -121,6 +124,7 @@ class DetailDialog( requireViewById<ViewGroup>(R.id.controls_activity_view).apply { addView(taskView) + setAlpha(0f) } requireViewById<ImageView>(R.id.control_detail_close).apply { @@ -134,7 +138,7 @@ class DetailDialog( removeDetailTask() dismiss() context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) - v.context.startActivity(intent) + pendingIntent.send() } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index c5d3a70676d0..d950d6d8c07e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -31,6 +31,7 @@ import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.Bubbles; +import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.draganddrop.DragAndDrop; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; @@ -38,7 +39,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; -import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper; @@ -110,7 +110,7 @@ public interface SysUIComponent { Builder setRecentTasks(Optional<RecentTasks> r); @BindsInstance - Builder setSizeCompatUI(Optional<SizeCompatUI> s); + Builder setCompatUI(Optional<CompatUI> s); @BindsInstance Builder setDragAndDrop(Optional<DragAndDrop> d); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 90a3ad225f51..b815d4e9884b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -25,6 +25,7 @@ import com.android.wm.shell.ShellInit; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.Bubbles; +import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.dagger.TvWMShellModule; import com.android.wm.shell.dagger.WMShellModule; import com.android.wm.shell.dagger.WMSingleton; @@ -35,7 +36,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; -import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper; @@ -119,7 +119,7 @@ public interface WMComponent { Optional<RecentTasks> getRecentTasks(); @WMSingleton - SizeCompatUI getSizeCompatUI(); + CompatUI getCompatUI(); @WMSingleton DragAndDrop getDragAndDrop(); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index ff14064834d7..2ebcd8531128 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -121,6 +121,7 @@ import com.android.systemui.scrim.ScrimDrawable; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.telephony.TelephonyListenerManager; @@ -236,6 +237,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene protected Handler mMainHandler; private int mSmallestScreenWidthDp; private final Optional<StatusBar> mStatusBarOptional; + private final SystemUIDialogManager mDialogManager; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final DialogLaunchAnimator mDialogLaunchAnimator; @@ -346,7 +348,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene PackageManager packageManager, Optional<StatusBar> statusBarOptional, KeyguardUpdateMonitor keyguardUpdateMonitor, - DialogLaunchAnimator dialogLaunchAnimator) { + DialogLaunchAnimator dialogLaunchAnimator, + SystemUIDialogManager dialogManager) { mContext = context; mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; @@ -378,6 +381,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mStatusBarOptional = statusBarOptional; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mDialogLaunchAnimator = dialogLaunchAnimator; + mDialogManager = dialogManager; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -677,7 +681,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger, - mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils); + mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils, + mDialogManager); dialog.setOnDismissListener(this); dialog.setOnShowListener(this); @@ -2219,10 +2224,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing, MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, Optional<StatusBar> statusBarOptional, - KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) { + KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils, + SystemUIDialogManager systemUiDialogManager) { // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to // dismiss this dialog when the device is locked. - super(context, themeRes, false /* dismissOnDeviceLock */); + super(context, themeRes, false /* dismissOnDeviceLock */, + systemUiDialogManager); mContext = context; mAdapter = adapter; mOverflowAdapter = overflowAdapter; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 8d0733645117..89a5d72a3ca4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2090,6 +2090,15 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, private final Runnable mKeyguardGoingAwayRunnable = new Runnable() { @Override public void run() { + // If the keyguard is already going away, or it's about to because we are going to + // trigger the going-away remote animation to show the surface behind, don't do it + // again. That will cause the current animation to be cancelled unnecessarily. + if (mKeyguardStateController.isKeyguardGoingAway() + || mSurfaceBehindRemoteAnimationRequested + || mSurfaceBehindRemoteAnimationRunning) { + return; + } + Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable"); if (DEBUG) Log.d(TAG, "keyguardGoingAway"); mKeyguardViewControllerLazy.get().keyguardGoingAway(); @@ -2451,9 +2460,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, if (mSurfaceBehindRemoteAnimationFinishedCallback != null) { try { - if (!cancelled) { - mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished(); - } + mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished(); mSurfaceBehindRemoteAnimationFinishedCallback = null; } catch (RemoteException e) { e.printStackTrace(); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index c12d48d225ae..f2cb254c3b97 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -44,6 +44,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.systemui.R; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; /** * Base dialog for media output UI @@ -52,6 +53,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements MediaOutputController.Callback, Window.Callback { private static final String TAG = "MediaOutputDialog"; + private static final String EMPTY_TITLE = " "; private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); private final RecyclerView.LayoutManager mLayoutManager; @@ -82,9 +84,12 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements } }; - public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) { - super(context); - mContext = context; + public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController, + SystemUIDialogManager dialogManager) { + super(context, dialogManager); + + // Save the context that is wrapped with our theme. + mContext = getContext(); mMediaOutputController = mediaOutputController; mLayoutManager = new LinearLayoutManager(mContext); mListMaxHeight = context.getResources().getDimensionPixelSize( @@ -105,6 +110,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements lp.setFitInsetsIgnoringVisibility(true); window.setAttributes(lp); window.setContentView(mDialogView); + // Sets window to a blank string to avoid talkback announce app label first when pop up, + // which doesn't make sense. + window.setTitle(EMPTY_TITLE); mHeaderTitle = mDialogView.requireViewById(R.id.header_title); mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index ad6901f40d6b..a1e2c57c5c37 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -56,6 +56,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import java.util.ArrayList; import java.util.Collection; @@ -80,6 +81,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { private final ShadeController mShadeController; private final ActivityStarter mActivityStarter; private final DialogLaunchAnimator mDialogLaunchAnimator; + private final SystemUIDialogManager mDialogManager; private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>(); private final boolean mAboveStatusbar; private final boolean mVolumeAdjustmentForRemoteGroupSessions; @@ -101,7 +103,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager lbm, ShadeController shadeController, ActivityStarter starter, NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger, - DialogLaunchAnimator dialogLaunchAnimator) { + DialogLaunchAnimator dialogLaunchAnimator, SystemUIDialogManager dialogManager) { mContext = context; mPackageName = packageName; mMediaSessionManager = mediaSessionManager; @@ -117,6 +119,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { mDialogLaunchAnimator = dialogLaunchAnimator; mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean( com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions); + mDialogManager = dialogManager; } void start(@NonNull Callback cb) { @@ -478,9 +481,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { // We show the output group dialog from the output dialog. MediaOutputController controller = new MediaOutputController(mContext, mPackageName, mAboveStatusbar, mMediaSessionManager, mLocalBluetoothManager, mShadeController, - mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); + mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, + mDialogManager); MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar, - controller); + controller, mDialogManager); mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog); } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java index eca8ac90427b..7e2558ceb1e8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java @@ -28,6 +28,7 @@ import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; /** * Dialog for media output transferring. @@ -37,8 +38,9 @@ public class MediaOutputDialog extends MediaOutputBaseDialog { final UiEventLogger mUiEventLogger; MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController - mediaOutputController, UiEventLogger uiEventLogger) { - super(context, mediaOutputController); + mediaOutputController, UiEventLogger uiEventLogger, + SystemUIDialogManager dialogManager) { + super(context, mediaOutputController, dialogManager); mUiEventLogger = uiEventLogger; mAdapter = new MediaOutputAdapter(mMediaOutputController, this); if (!aboveStatusbar) { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt index b91901de5af3..a7bc85256fcd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt @@ -25,6 +25,7 @@ import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.phone.ShadeController +import com.android.systemui.statusbar.phone.SystemUIDialogManager import javax.inject.Inject /** @@ -38,7 +39,8 @@ class MediaOutputDialogFactory @Inject constructor( private val starter: ActivityStarter, private val notificationEntryManager: NotificationEntryManager, private val uiEventLogger: UiEventLogger, - private val dialogLaunchAnimator: DialogLaunchAnimator + private val dialogLaunchAnimator: DialogLaunchAnimator, + private val dialogManager: SystemUIDialogManager ) { companion object { var mediaOutputDialog: MediaOutputDialog? = null @@ -51,8 +53,9 @@ class MediaOutputDialogFactory @Inject constructor( val controller = MediaOutputController(context, packageName, aboveStatusBar, mediaSessionManager, lbm, shadeController, starter, notificationEntryManager, - uiEventLogger, dialogLaunchAnimator) - val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger) + uiEventLogger, dialogLaunchAnimator, dialogManager) + val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger, + dialogManager) mediaOutputDialog = dialog // Show the dialog. diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java index b41e8137c1ec..478b890bf92d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java @@ -25,6 +25,7 @@ import android.widget.LinearLayout; import androidx.core.graphics.drawable.IconCompat; import com.android.systemui.R; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; /** * Dialog for media output group. @@ -32,8 +33,8 @@ import com.android.systemui.R; public class MediaOutputGroupDialog extends MediaOutputBaseDialog { MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController - mediaOutputController) { - super(context, mediaOutputController); + mediaOutputController, SystemUIDialogManager dialogManager) { + super(context, mediaOutputController, dialogManager); mMediaOutputController.resetGroupMediaDevices(); mAdapter = new MediaOutputGroupAdapter(mMediaOutputController); if (!aboveStatusbar) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 03bceacdfbdc..7a12ecc097a2 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -1252,6 +1252,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private void onImeSwitcherClick(View v) { mInputMethodManager.showInputMethodPickerFromSystem( true /* showAuxiliarySubtypes */, mDisplayId); + mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP); }; private boolean onLongPressBackHome(View v) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java index debd2ebb51be..d27b71673ce5 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java @@ -96,6 +96,9 @@ public class KeyButtonView extends ImageView implements ButtonInterface { @UiEvent(doc = "The overview button was pressed in the navigation bar.") NAVBAR_OVERVIEW_BUTTON_TAP(535), + @UiEvent(doc = "The ime switcher button was pressed in the navigation bar.") + NAVBAR_IME_SWITCHER_BUTTON_TAP(923), + @UiEvent(doc = "The home button was long-pressed in the navigation bar.") NAVBAR_HOME_BUTTON_LONGPRESS(536), diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java index 7ba9cc22bec9..a2577d6e7f60 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java @@ -98,7 +98,7 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements toggleDataSaver(); Prefs.putBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, true); }); - dialog.setNegativeButton(com.android.internal.R.string.cancel, null); + dialog.setNeutralButton(com.android.internal.R.string.cancel, null); dialog.setShowForAllUsers(true); if (view != null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 20805a141312..a339dcf73f74 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -190,7 +190,6 @@ public class DndTile extends QSTileImpl<BooleanState> { case Settings.Secure.ZEN_DURATION_PROMPT: mUiHandler.post(() -> { Dialog dialog = makeZenModeDialog(); - SystemUIDialog.registerDismissListener(dialog); if (view != null) { mDialogLaunchAnimator.showFromView(dialog, view, false); } else { @@ -211,10 +210,12 @@ public class DndTile extends QSTileImpl<BooleanState> { } private Dialog makeZenModeDialog() { - AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog) - .createDialog(); + AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog, + true /* cancelIsNeutral */).createDialog(); SystemUIDialog.applyFlags(dialog); SystemUIDialog.setShowForAllUsers(dialog, true); + SystemUIDialog.registerDismissListener(dialog); + SystemUIDialog.setDialogSize(dialog); return dialog; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java index ba4257f52892..2a398496af77 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java @@ -159,7 +159,9 @@ public class InternetDialog extends SystemUIDialog implements if (DEBUG) { Log.d(TAG, "Init InternetDialog"); } - mContext = context; + + // Save the context that is wrapped with our theme. + mContext = getContext(); mHandler = handler; mBackgroundExecutor = executor; mInternetDialogFactory = internetDialogFactory; @@ -301,15 +303,11 @@ public class InternetDialog extends SystemUIDialog implements if (DEBUG) { Log.d(TAG, "updateDialog"); } - if (mInternetDialogController.isAirplaneModeEnabled()) { - mInternetDialogSubTitle.setVisibility(View.GONE); - mAirplaneModeLayout.setVisibility(View.VISIBLE); - } else { - mInternetDialogTitle.setText(getDialogTitleText()); - mInternetDialogSubTitle.setVisibility(View.VISIBLE); - mInternetDialogSubTitle.setText(getSubtitleText()); - mAirplaneModeLayout.setVisibility(View.GONE); - } + mInternetDialogTitle.setText(getDialogTitleText()); + mInternetDialogSubTitle.setText(getSubtitleText()); + mAirplaneModeLayout.setVisibility( + mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE); + updateEthernet(); if (shouldUpdateMobileNetwork) { setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular(), diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index 1fee1b430073..4908d4fb214e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -316,15 +316,11 @@ public class InternetDialogController implements AccessPointController.AccessPoi } CharSequence getSubtitleText(boolean isProgressBarVisible) { - if (isAirplaneModeEnabled()) { - return null; - } - if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) { - // When the airplane mode is off and Wi-Fi is disabled. + // When Wi-Fi is disabled. // Sub-Title: Wi-Fi is off if (DEBUG) { - Log.d(TAG, "Airplane mode off + Wi-Fi off."); + Log.d(TAG, "Wi-Fi off."); } return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF); } @@ -882,10 +878,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi if (accessPoints == null || accessPoints.size() == 0) { mConnectedEntry = null; mWifiEntriesCount = 0; - if (mCallback != null) { - mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */, - false /* hasMoreEntry */); - } + mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */, + false /* hasMoreEntry */); return; } @@ -917,9 +911,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi mConnectedEntry = connectedEntry; mWifiEntriesCount = wifiEntries.size(); - if (mCallback != null) { - mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry, hasMoreEntry); - } + mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry, hasMoreEntry); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 3ed7e84af020..e7cd1e2dab3c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -76,6 +76,7 @@ import androidx.annotation.NonNull; import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.ScreenshotHelper; import com.android.systemui.Dumpable; @@ -88,6 +89,7 @@ import com.android.systemui.navigationbar.NavigationBar; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.navigationbar.buttons.KeyButtonView; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.shared.recents.IOverviewProxy; @@ -161,6 +163,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final Optional<StartingSurface> mStartingSurface; private final SmartspaceTransitionController mSmartspaceTransitionController; private final Optional<RecentTasks> mRecentTasks; + private final UiEventLogger mUiEventLogger; private Region mActiveNavBarRegion; @@ -248,6 +251,7 @@ public class OverviewProxyService extends CurrentUserTracker implements mContext.getSystemService(InputMethodManager.class) .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */, DEFAULT_DISPLAY); + mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP); } @Override @@ -560,6 +564,7 @@ public class OverviewProxyService extends CurrentUserTracker implements ShellTransitions shellTransitions, ScreenLifecycle screenLifecycle, SmartspaceTransitionController smartspaceTransitionController, + UiEventLogger uiEventLogger, DumpManager dumpManager) { super(broadcastDispatcher); mContext = context; @@ -581,6 +586,7 @@ public class OverviewProxyService extends CurrentUserTracker implements mOneHandedOptional = oneHandedOptional; mShellTransitions = shellTransitions; mRecentTasks = recentTasks; + mUiEventLogger = uiEventLogger; // Assumes device always starts with back button until launcher tells it that it does not mNavBarButtonAlpha = 1.0f; diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index 85bf98c09f59..7f130cb203c0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -16,6 +16,7 @@ package com.android.systemui.recents; +import static com.android.systemui.shared.recents.utilities.Utilities.isTablet; import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; @@ -248,8 +249,8 @@ public class ScreenPinningRequest implements View.OnClickListener, .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE); View buttons = mLayout.findViewById(R.id.screen_pinning_buttons); WindowManagerWrapper wm = WindowManagerWrapper.getInstance(); - if (!QuickStepContract.isGesturalMode(mNavBarMode) - && wm.hasSoftNavigationBar(mContext.getDisplayId())) { + if (!QuickStepContract.isGesturalMode(mNavBarMode) + && wm.hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) { buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE); swapChildrenIfRtlAndVertical(buttons); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index f43d9c350d62..d7b4738340e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -887,7 +887,13 @@ public class KeyguardIndicationController { mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState); } } else { - showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); + if (mKeyguardUpdateMonitor.isUdfpsSupported() + && mKeyguardUpdateMonitor.getUserCanSkipBouncer( + KeyguardUpdateMonitor.getCurrentUser())) { + showBiometricMessage(mContext.getString(R.string.keyguard_unlock_press)); + } else { + showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java index c4fadff16c09..4551807499ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java @@ -40,7 +40,7 @@ public class UserUtil { super(context); setTitle(R.string.user_remove_user_title); setMessage(context.getString(R.string.user_remove_user_message)); - setButton(DialogInterface.BUTTON_NEGATIVE, + setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(android.R.string.cancel), this); setButton(DialogInterface.BUTTON_POSITIVE, context.getString(R.string.user_remove_user_remove), this); @@ -51,7 +51,7 @@ public class UserUtil { @Override public void onClick(DialogInterface dialog, int which) { - if (which == BUTTON_NEGATIVE) { + if (which == BUTTON_NEUTRAL) { cancel(); } else { dismiss(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt new file mode 100644 index 000000000000..a1d086b5d768 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.charging + +import android.graphics.Color +import android.graphics.PointF +import android.graphics.RuntimeShader +import android.util.MathUtils + +/** + * Shader class that renders a distorted ripple for the UDFPS dwell effect. + * Adjustable shader parameters: + * - progress + * - origin + * - color + * - time + * - maxRadius + * - distortionStrength. + * See per field documentation for more details. + * + * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java. + */ +class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) { + companion object { + private const val SHADER_UNIFORMS = """uniform vec2 in_origin; + uniform float in_time; + uniform float in_radius; + uniform float in_blur; + uniform vec4 in_color; + uniform float in_phase1; + uniform float in_phase2; + uniform float in_distortion_strength;""" + private const val SHADER_LIB = """ + float softCircle(vec2 uv, vec2 xy, float radius, float blur) { + float blurHalf = blur * 0.5; + float d = distance(uv, xy); + return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius); + } + + float softRing(vec2 uv, vec2 xy, float radius, float blur) { + float thickness_half = radius * 0.25; + float circle_outer = softCircle(uv, xy, radius + thickness_half, blur); + float circle_inner = softCircle(uv, xy, radius - thickness_half, blur); + return circle_outer - circle_inner; + } + + vec2 distort(vec2 p, float time, float distort_amount_xy, float frequency) { + return p + vec2(sin(p.x * frequency + in_phase1), + cos(p.y * frequency * 1.23 + in_phase2)) * distort_amount_xy; + } + + vec4 ripple(vec2 p, float distort_xy, float frequency) { + vec2 p_distorted = distort(p, in_time, distort_xy, frequency); + float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur); + float rippleAlpha = max(circle, + softRing(p_distorted, in_origin, in_radius, in_blur)) * 0.25; + return in_color * rippleAlpha; + } + """ + private const val SHADER_MAIN = """vec4 main(vec2 p) { + vec4 color1 = ripple(p, + 12 * in_distortion_strength, // distort_xy + 0.012 // frequency + ); + vec4 color2 = ripple(p, + 17.5 * in_distortion_strength, // distort_xy + 0.018 // frequency + ); + // Alpha blend between two layers. + return vec4(color1.xyz + color2.xyz + * (1 - color1.w), color1.w + color2.w * (1-color1.w)); + }""" + private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN + } + + /** + * Maximum radius of the ripple. + */ + var maxRadius: Float = 0.0f + + /** + * Origin coordinate of the ripple. + */ + var origin: PointF = PointF() + set(value) { + field = value + setUniform("in_origin", floatArrayOf(value.x, value.y)) + } + + /** + * Progress of the ripple. Float value between [0, 1]. + */ + var progress: Float = 0.0f + set(value) { + field = value + setUniform("in_radius", + (1 - (1 - value) * (1 - value) * (1 - value))* maxRadius) + setUniform("in_blur", MathUtils.lerp(1f, 0.7f, value)) + } + + /** + * Distortion strength between [0, 1], with 0 being no distortion and 1 being full distortion. + */ + var distortionStrength: Float = 0.0f + set(value) { + field = value + setUniform("in_distortion_strength", value) + } + + /** + * Play time since the start of the effect in seconds. + */ + var time: Float = 0.0f + set(value) { + field = value * 0.001f + setUniform("in_time", field) + setUniform("in_phase1", field * 2f + 0.367f) + setUniform("in_phase2", field * 5.2f * 1.531f) + } + + /** + * A hex value representing the ripple color, in the format of ARGB + */ + var color: Int = 0xffffff.toInt() + set(value) { + field = value + val color = Color.valueOf(value) + setUniform("in_color", floatArrayOf(color.red(), + color.green(), color.blue(), color.alpha())) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 2eb20654716d..63cb4ae39a58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -579,14 +579,24 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (contentView.hasOverlappingRendering()) { int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE : LAYER_TYPE_HARDWARE; - int currentLayerType = contentView.getLayerType(); - if (currentLayerType != layerType) { - contentView.setLayerType(layerType, null); - } + contentView.setLayerType(layerType, null); } contentView.setAlpha(contentAlpha); + // After updating the current view, reset all views. + if (contentAlpha == 1f) { + resetAllContentAlphas(); + } } + /** + * If a subclass's {@link #getContentView()} returns different views depending on state, + * this method is an opportunity to reset the alpha of ALL content views, not just the + * current one, which may prevent a content view that is temporarily hidden from being reset. + * + * This should setAlpha(1.0f) and setLayerType(LAYER_TYPE_NONE) for all content views. + */ + protected void resetAllContentAlphas() {} + @Override protected void applyRoundness() { super.applyRoundness(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 23a0a750561c..819f5a3f6dc2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -2130,15 +2130,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public void setExpandAnimationRunning(boolean expandAnimationRunning) { - View contentView; - if (mIsSummaryWithChildren) { - contentView = mChildrenContainer; - } else { - contentView = getShowingLayout(); - } - if (mGuts != null && mGuts.isExposed()) { - contentView = mGuts; - } if (expandAnimationRunning) { setAboveShelf(true); mExpandAnimationRunning = true; @@ -2151,9 +2142,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mGuts != null) { mGuts.setAlpha(1.0f); } - if (contentView != null) { - contentView.setAlpha(1.0f); - } + resetAllContentAlphas(); setExtraWidthForClipping(0.0f); if (mNotificationParent != null) { mNotificationParent.setExtraWidthForClipping(0.0f); @@ -2601,10 +2590,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mPrivateLayout.animate().cancel(); if (mChildrenContainer != null) { mChildrenContainer.animate().cancel(); - mChildrenContainer.setAlpha(1f); } - mPublicLayout.setAlpha(1f); - mPrivateLayout.setAlpha(1f); + resetAllContentAlphas(); mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE); updateChildrenVisibility(); } else { @@ -2631,7 +2618,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView .alpha(0f) .setStartDelay(delay) .setDuration(duration) - .withEndAction(() -> hiddenView.setVisibility(View.INVISIBLE)); + .withEndAction(() -> { + hiddenView.setVisibility(View.INVISIBLE); + resetAllContentAlphas(); + }); } for (View showView : shownChildren) { showView.setVisibility(View.VISIBLE); @@ -2754,12 +2744,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (wasAppearing) { // During the animation the visible view might have changed, so let's make sure all // alphas are reset - if (mChildrenContainer != null) { - mChildrenContainer.setAlpha(1.0f); - } - for (NotificationContentView l : mLayouts) { - l.setAlpha(1.0f); - } + resetAllContentAlphas(); if (FADE_LAYER_OPTIMIZATION_ENABLED) { setNotificationFaded(false); } else { @@ -2770,6 +2755,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } + @Override + protected void resetAllContentAlphas() { + mPrivateLayout.setAlpha(1f); + mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null); + mPublicLayout.setAlpha(1f); + mPublicLayout.setLayerType(LAYER_TYPE_NONE, null); + if (mChildrenContainer != null) { + mChildrenContainer.setAlpha(1f); + mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null); + } + } + /** Gets the last value set with {@link #setNotificationFaded(boolean)} */ @Override public boolean isNotificationFaded() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index a9cc3237d719..e65846865ef8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -59,6 +59,7 @@ public class AmbientState { private float mMaxHeadsUpTranslation; private boolean mDismissAllInProgress; private int mLayoutMinHeight; + private int mLayoutMaxHeight; private NotificationShelf mShelf; private int mZDistanceBetweenElements; private int mBaseZHeight; @@ -326,6 +327,14 @@ public class AmbientState { mLayoutHeight = layoutHeight; } + public void setLayoutMaxHeight(int maxLayoutHeight) { + mLayoutMaxHeight = maxLayoutHeight; + } + + public int getLayoutMaxHeight() { + return mLayoutMaxHeight; + } + public float getTopPadding() { return mTopPadding; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 9167b08ecd0c..16c9fa79f24c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -1110,6 +1110,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM) private void updateAlgorithmHeightAndPadding() { mAmbientState.setLayoutHeight(getLayoutHeight()); + mAmbientState.setLayoutMaxHeight(mMaxLayoutHeight); updateAlgorithmLayoutMinHeight(); mAmbientState.setTopPadding(mTopPadding); } @@ -3985,6 +3986,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateChronometers(); requestChildrenUpdate(); updateUseRoundedRectClipping(); + updateDismissBehavior(); } } @@ -4931,6 +4933,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable StringBuilder sb = new StringBuilder("[") .append(this.getClass().getSimpleName()).append(":") .append(" pulsing=").append(mPulsing ? "T" : "f") + .append(" expanded=").append(mIsExpanded ? "T" : "f") + .append(" headsUpPinned=").append(mInHeadsUpPinnedMode ? "T" : "f") + .append(" qsClipping=").append(mShouldUseRoundedRectClipping ? "T" : "f") + .append(" qsClipDismiss=").append(mDismissUsingRowTranslationX ? "T" : "f") .append(" visibility=").append(DumpUtilsKt.visibilityString(getVisibility())) .append(" alpha=").append(getAlpha()) .append(" scrollY=").append(mAmbientState.getScrollY()) @@ -5458,7 +5464,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable // On the split keyguard, dismissing with clipping without a visual boundary looks odd, // so let's use the content dismiss behavior instead. boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade - || mStatusBarState != StatusBarState.KEYGUARD; + || (mStatusBarState != StatusBarState.KEYGUARD && mIsExpanded); if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) { mDismissUsingRowTranslationX = dismissUsingRowTranslationX; for (int i = 0; i < getChildCount(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 015edb8e5541..2c70a5fd9ce7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -29,6 +29,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.R; import com.android.systemui.animation.ShadeInterpolation; +import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -60,6 +61,7 @@ public class StackScrollAlgorithm { @VisibleForTesting float mHeadsUpInset; private int mPinnedZTranslationExtra; private float mNotificationScrimPadding; + private int mCloseHandleUnderlapHeight; public StackScrollAlgorithm( Context context, @@ -85,6 +87,7 @@ public class StackScrollAlgorithm { R.dimen.heads_up_pinned_elevation); mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height); mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings); + mCloseHandleUnderlapHeight = res.getDimensionPixelSize(R.dimen.close_handle_underlap); } /** @@ -459,13 +462,17 @@ public class StackScrollAlgorithm { && !hasOngoingNotifs(algorithmState)); } } else { - if (view != ambientState.getTrackedHeadsUpRow()) { + if (view instanceof EmptyShadeView) { + float fullHeight = ambientState.getLayoutMaxHeight() + mCloseHandleUnderlapHeight + - ambientState.getStackY(); + viewState.yTranslation = (fullHeight - getMaxAllowedChildHeight(view)) / 2f; + } else if (view != ambientState.getTrackedHeadsUpRow()) { if (ambientState.isExpansionChanging()) { // We later update shelf state, then hide views below the shelf. viewState.hidden = false; viewState.inShelf = algorithmState.firstViewInShelf != null && i >= algorithmState.visibleChildren.indexOf( - algorithmState.firstViewInShelf); + algorithmState.firstViewInShelf); } else if (ambientState.getShelf() != null) { // When pulsing (incoming notification on AOD), innerHeight is 0; clamp all // to shelf start, thereby hiding all notifications (except the first one, which diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index aa3b3e12b8f8..ad1c23283912 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -74,7 +74,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000; private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock"; private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl(); - private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3; + private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 2; @IntDef(prefix = { "MODE_" }, value = { MODE_NONE, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 261b5dbac3d1..5eb35ac74732 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -3262,12 +3262,6 @@ public class NotificationPanelViewController extends PanelViewController { mStatusBarStateController.setState(KEYGUARD); } return true; - case StatusBarState.SHADE: - - // This gets called in the middle of the touch handling, where the state is still - // that we are tracking the panel. Collapse the panel after this is done. - mView.post(mPostCollapseRunnable); - return false; default: return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 2823d985102f..2bf16fc9e52c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -928,7 +928,6 @@ public abstract class PanelViewController { private void abortAnimations() { cancelHeightAnimator(); - mView.removeCallbacks(mPostCollapseRunnable); mView.removeCallbacks(mFlingCollapseRunnable); } @@ -1105,13 +1104,6 @@ public abstract class PanelViewController { return onMiddleClicked(); } - protected final Runnable mPostCollapseRunnable = new Runnable() { - @Override - public void run() { - collapse(false /* delayed */, 1.0f /* speedUpFactor */); - } - }; - protected abstract boolean onMiddleClicked(); protected abstract boolean isDozing(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java index 43264b600a0e..93f9892cf5b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java @@ -45,6 +45,10 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; /** * Base class for dialogs that should appear over panels and keyguard. + * + * Optionally provide a {@link SystemUIDialogManager} to its constructor to send signals to + * listeners on whether this dialog is showing. + * * The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast, * and dismisses itself when it receives the broadcast. */ @@ -54,8 +58,9 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh "persist.systemui.flag_tablet_dialog_width"; private final Context mContext; - private final DismissReceiver mDismissReceiver; + @Nullable private final DismissReceiver mDismissReceiver; private final Handler mHandler = new Handler(); + @Nullable private final SystemUIDialogManager mDialogManager; private int mLastWidth = Integer.MIN_VALUE; private int mLastHeight = Integer.MIN_VALUE; @@ -66,11 +71,22 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh this(context, R.style.Theme_SystemUI_Dialog); } + public SystemUIDialog(Context context, SystemUIDialogManager dialogManager) { + this(context, R.style.Theme_SystemUI_Dialog, true, dialogManager); + } + public SystemUIDialog(Context context, int theme) { this(context, theme, true /* dismissOnDeviceLock */); } - public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) { + this(context, theme, dismissOnDeviceLock, null); + } + + /** + * @param udfpsDialogManager If set, UDFPS will hide if this dialog is showing. + */ + public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock, + SystemUIDialogManager dialogManager) { super(context, theme); mContext = context; @@ -80,6 +96,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh getWindow().setAttributes(attrs); mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this) : null; + mDialogManager = dialogManager; } @Override @@ -126,30 +143,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh * the device configuration changes, and the result will be used to resize this dialog window. */ protected int getWidth() { - boolean isOnTablet = - mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600; - if (!isOnTablet) { - return ViewGroup.LayoutParams.MATCH_PARENT; - } - - int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0); - if (flagValue == -1) { - // The width of bottom sheets (624dp). - return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624, - mContext.getResources().getDisplayMetrics())); - } else if (flagValue == -2) { - // The suggested small width for all dialogs (348dp) - return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348, - mContext.getResources().getDisplayMetrics())); - } else if (flagValue > 0) { - // Any given width. - return Math.round( - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue, - mContext.getResources().getDisplayMetrics())); - } else { - // By default we use the same width as the notification shade in portrait mode (504dp). - return mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width); - } + return getDefaultDialogWidth(mContext); } /** @@ -157,7 +151,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh * the device configuration changes, and the result will be used to resize this dialog window. */ protected int getHeight() { - return ViewGroup.LayoutParams.WRAP_CONTENT; + return getDefaultDialogHeight(); } @Override @@ -168,6 +162,10 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh mDismissReceiver.register(); } + if (mDialogManager != null) { + mDialogManager.setShowing(this, true); + } + // Listen for configuration changes to resize this dialog window. This is mostly necessary // for foldables that often go from large <=> small screen when folding/unfolding. ViewRootImpl.addConfigCallback(this); @@ -181,6 +179,10 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh mDismissReceiver.unregister(); } + if (mDialogManager != null) { + mDialogManager.setShowing(this, false); + } + ViewRootImpl.removeConfigCallback(this); } @@ -267,6 +269,45 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh dismissReceiver.register(); } + /** Set an appropriate size to {@code dialog} depending on the current configuration. */ + public static void setDialogSize(Dialog dialog) { + // We need to create the dialog first, otherwise the size will be overridden when it is + // created. + dialog.create(); + dialog.getWindow().setLayout(getDefaultDialogWidth(dialog.getContext()), + getDefaultDialogHeight()); + } + + private static int getDefaultDialogWidth(Context context) { + boolean isOnTablet = context.getResources().getConfiguration().smallestScreenWidthDp >= 600; + if (!isOnTablet) { + return ViewGroup.LayoutParams.MATCH_PARENT; + } + + int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0); + if (flagValue == -1) { + // The width of bottom sheets (624dp). + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624, + context.getResources().getDisplayMetrics())); + } else if (flagValue == -2) { + // The suggested small width for all dialogs (348dp) + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348, + context.getResources().getDisplayMetrics())); + } else if (flagValue > 0) { + // Any given width. + return Math.round( + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue, + context.getResources().getDisplayMetrics())); + } else { + // By default we use the same width as the notification shade in portrait mode (504dp). + return context.getResources().getDimensionPixelSize(R.dimen.large_dialog_width); + } + } + + private static int getDefaultDialogHeight() { + return ViewGroup.LayoutParams.WRAP_CONTENT; + } + private static class DismissReceiver extends BroadcastReceiver { private static final IntentFilter INTENT_FILTER = new IntentFilter(); static { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java new file mode 100644 index 000000000000..204f710b633a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import androidx.annotation.NonNull; + +import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dump.DumpManager; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.HashSet; +import java.util.Set; + +import javax.inject.Inject; + +/** + * Register dialogs to this manager if extraneous affordances (like the UDFPS sensor area) + * should be hidden from the screen when the dialog shows. + * + * Currently, only used if UDFPS is supported on the device; however, can be extended in the future + * for other use cases. + */ +@SysUISingleton +public class SystemUIDialogManager implements Dumpable { + private final StatusBarKeyguardViewManager mKeyguardViewManager; + + private final Set<SystemUIDialog> mDialogsShowing = new HashSet<>(); + private final Set<Listener> mListeners = new HashSet<>(); + + @Inject + public SystemUIDialogManager( + DumpManager dumpManager, + StatusBarKeyguardViewManager statusBarKeyguardViewManager) { + dumpManager.registerDumpable(this); + mKeyguardViewManager = statusBarKeyguardViewManager; + } + + /** + * Whether listeners should hide affordances like the UDFPS sensor icon. + */ + public boolean shouldHideAffordance() { + return !mDialogsShowing.isEmpty(); + } + + /** + * Register a listener to receive callbacks. + */ + public void registerListener(@NonNull Listener listener) { + mListeners.add(listener); + } + + /** + * Unregister a listener from receiving callbacks. + */ + public void unregisterListener(@NonNull Listener listener) { + mListeners.remove(listener); + } + + void setShowing(SystemUIDialog dialog, boolean showing) { + final boolean wasHidingAffordances = shouldHideAffordance(); + if (showing) { + mDialogsShowing.add(dialog); + } else { + mDialogsShowing.remove(dialog); + } + + if (wasHidingAffordances != shouldHideAffordance()) { + updateDialogListeners(); + } + } + + private void updateDialogListeners() { + if (shouldHideAffordance()) { + mKeyguardViewManager.resetAlternateAuth(true); + } + + for (Listener listener : mListeners) { + listener.shouldHideAffordances(shouldHideAffordance()); + } + } + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + pw.println("listeners:"); + for (Listener listener : mListeners) { + pw.println("\t" + listener); + } + pw.println("dialogs tracked:"); + for (SystemUIDialog dialog : mDialogsShowing) { + pw.println("\t" + dialog); + } + } + + /** SystemUIDialogManagerListener */ + public interface Listener { + /** + * Callback where shouldHide=true if listeners should hide their views that may overlap + * a showing dialog. + */ + void shouldHideAffordances(boolean shouldHide); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 36e56f967424..ebf5a6da5d1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -1161,7 +1161,7 @@ public class UserSwitcherController implements Dumpable { ? com.android.settingslib.R.string.guest_reset_guest_dialog_title : R.string.guest_exit_guest_dialog_title); setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); - setButton(DialogInterface.BUTTON_NEGATIVE, + setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(android.R.string.cancel), this); setButton(DialogInterface.BUTTON_POSITIVE, context.getString(mGuestUserAutoCreated @@ -1180,7 +1180,7 @@ public class UserSwitcherController implements Dumpable { if (mFalsingManager.isFalseTap(penalty)) { return; } - if (which == BUTTON_NEGATIVE) { + if (which == BUTTON_NEUTRAL) { cancel(); } else { mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); @@ -1198,7 +1198,7 @@ public class UserSwitcherController implements Dumpable { super(context); setTitle(R.string.user_add_user_title); setMessage(context.getString(R.string.user_add_user_message_short)); - setButton(DialogInterface.BUTTON_NEGATIVE, + setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(android.R.string.cancel), this); setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), this); @@ -1212,7 +1212,7 @@ public class UserSwitcherController implements Dumpable { if (mFalsingManager.isFalseTap(penalty)) { return; } - if (which == BUTTON_NEGATIVE) { + if (which == BUTTON_NEUTRAL) { cancel(); } else { mDialogLaunchAnimator.dismissStack(this); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index b546edf428b8..1fd47a465623 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.ShellCommandHandler; +import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.draganddrop.DragAndDrop; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; @@ -66,7 +67,6 @@ import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.onehanded.OneHandedUiEventLogger; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.protolog.ShellProtoLogImpl; -import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; @@ -114,7 +114,7 @@ public final class WMShell extends SystemUI private final Optional<OneHanded> mOneHandedOptional; private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional; private final Optional<ShellCommandHandler> mShellCommandHandler; - private final Optional<SizeCompatUI> mSizeCompatUIOptional; + private final Optional<CompatUI> mCompatUIOptional; private final Optional<DragAndDrop> mDragAndDropOptional; private final CommandQueue mCommandQueue; @@ -132,7 +132,7 @@ public final class WMShell extends SystemUI private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; private KeyguardUpdateMonitorCallback mPipKeyguardCallback; private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback; - private KeyguardUpdateMonitorCallback mSizeCompatUIKeyguardCallback; + private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback; private WakefulnessLifecycle.Observer mWakefulnessObserver; @Inject @@ -143,7 +143,7 @@ public final class WMShell extends SystemUI Optional<OneHanded> oneHandedOptional, Optional<HideDisplayCutout> hideDisplayCutoutOptional, Optional<ShellCommandHandler> shellCommandHandler, - Optional<SizeCompatUI> sizeCompatUIOptional, + Optional<CompatUI> sizeCompatUIOptional, Optional<DragAndDrop> dragAndDropOptional, CommandQueue commandQueue, ConfigurationController configurationController, @@ -169,7 +169,7 @@ public final class WMShell extends SystemUI mWakefulnessLifecycle = wakefulnessLifecycle; mProtoTracer = protoTracer; mShellCommandHandler = shellCommandHandler; - mSizeCompatUIOptional = sizeCompatUIOptional; + mCompatUIOptional = sizeCompatUIOptional; mDragAndDropOptional = dragAndDropOptional; mSysUiMainExecutor = sysUiMainExecutor; } @@ -185,7 +185,7 @@ public final class WMShell extends SystemUI mSplitScreenOptional.ifPresent(this::initSplitScreen); mOneHandedOptional.ifPresent(this::initOneHanded); mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout); - mSizeCompatUIOptional.ifPresent(this::initSizeCompatUi); + mCompatUIOptional.ifPresent(this::initCompatUi); mDragAndDropOptional.ifPresent(this::initDragAndDrop); } @@ -391,14 +391,14 @@ public final class WMShell extends SystemUI } @VisibleForTesting - void initSizeCompatUi(SizeCompatUI sizeCompatUI) { - mSizeCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() { + void initCompatUi(CompatUI sizeCompatUI) { + mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() { @Override public void onKeyguardOccludedChanged(boolean occluded) { sizeCompatUI.onKeyguardOccludedChanged(occluded); } }; - mKeyguardUpdateMonitor.registerCallback(mSizeCompatUIKeyguardCallback); + mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback); } void initDragAndDrop(DragAndDrop dragAndDrop) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 9e42ff37ea69..ac221a9f7891 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -65,6 +65,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -170,6 +171,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private TypedArray mBrightnessValues; @Mock private TypedArray mBrightnessBacklight; + @Mock + private SystemUIDialogManager mSystemUIDialogManager; // Capture listeners so that they can be used to send events @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor; @@ -179,8 +182,6 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor; private ScreenLifecycle.Observer mScreenObserver; - @Captor private ArgumentCaptor<UdfpsAnimationViewController> mAnimViewControllerCaptor; - @Before public void setUp() { setUpResources(); @@ -238,7 +239,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mHandler, mConfigurationController, mSystemClock, - mUnlockedScreenOffAnimationController); + mUnlockedScreenOffAnimationController, + mSystemUIDialogManager); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); @@ -642,12 +644,12 @@ public class UdfpsControllerTest extends SysuiTestCase { mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); moveEvent.recycle(); - // THEN low-tick haptic is played + // THEN click haptic is played verify(mVibrator).vibrate( anyInt(), anyString(), any(), - eq("udfps-onStart-tick"), + eq("udfps-onStart-click"), eq(UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES)); // THEN make sure vibration attributes has so that it always will play the haptic, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index 1cf21ac40e31..0ae3c39e659b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -42,6 +42,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; @@ -92,6 +93,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Mock + private SystemUIDialogManager mDialogManager; + @Mock private UdfpsController mUdfpsController; private FakeSystemClock mSystemClock = new FakeSystemClock(); @@ -130,6 +133,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { mSystemClock, mKeyguardStateController, mUnlockedScreenOffAnimationController, + mDialogManager, mUdfpsController); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt new file mode 100644 index 000000000000..87b9172dcefc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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.controls.ui + +import android.app.PendingIntent +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.wm.shell.TaskView +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class DetailDialogTest : SysuiTestCase() { + + @Mock + private lateinit var taskView: TaskView + @Mock + private lateinit var controlViewHolder: ControlViewHolder + @Mock + private lateinit var pendingIntent: PendingIntent + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testPendingIntentIsUnModified() { + // GIVEN the dialog is created with a PendingIntent + val dialog = createDialog(pendingIntent) + + // WHEN the TaskView is initialized + dialog.stateCallback.onInitialized() + + // THEN the PendingIntent used to call startActivity is unmodified by systemui + verify(taskView).startActivity(eq(pendingIntent), any(), any(), any()) + } + + private fun createDialog(pendingIntent: PendingIntent): DetailDialog { + return DetailDialog( + mContext, + taskView, + pendingIntent, + controlViewHolder + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index bf5522c50a78..e3a7e3b43b77 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -62,6 +62,7 @@ import com.android.systemui.plugins.GlobalActions; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.telephony.TelephonyListenerManager; @@ -117,6 +118,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private StatusBar mStatusBar; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private DialogLaunchAnimator mDialogLaunchAnimator; + @Mock private SystemUIDialogManager mDialogManager; private TestableLooper mTestableLooper; @@ -162,7 +164,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mPackageManager, Optional.of(mStatusBar), mKeyguardUpdateMonitor, - mDialogLaunchAnimator); + mDialogLaunchAnimator, + mDialogManager); mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting(); ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java index 81bcbfb1f460..d7c00fbe1e85 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,7 +42,6 @@ import android.os.Vibrator; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Pair; -import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; @@ -69,8 +67,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; -import com.airbnb.lottie.LottieAnimationView; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -110,8 +106,6 @@ public class LockIconViewControllerTest extends SysuiTestCase { private @Mock ConfigurationController mConfigurationController; private @Mock Vibrator mVibrator; private @Mock AuthRippleController mAuthRippleController; - private @Mock LottieAnimationView mAodFp; - private @Mock LayoutInflater mLayoutInflater; private FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); private LockIconViewController mLockIconViewController; @@ -149,7 +143,6 @@ public class LockIconViewControllerTest extends SysuiTestCase { when(mLockIconView.getResources()).thenReturn(mResources); when(mLockIconView.getContext()).thenReturn(mContext); - when(mLockIconView.findViewById(R.layout.udfps_aod_lock_icon)).thenReturn(mAodFp); when(mContext.getResources()).thenReturn(mResources); when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager); Rect windowBounds = new Rect(0, 0, 800, 1200); @@ -176,8 +169,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { mDelayableExecutor, mVibrator, mAuthRippleController, - mResources, - mLayoutInflater + mResources ); } @@ -187,35 +179,6 @@ public class LockIconViewControllerTest extends SysuiTestCase { } @Test - public void testIgnoreUdfpsWhenNotSupported() { - // GIVEN Udpfs sensor is NOT available - mLockIconViewController.init(); - captureAttachListener(); - - // WHEN the view is attached - mAttachListener.onViewAttachedToWindow(mLockIconView); - - // THEN lottie animation should NOT be inflated - verify(mLayoutInflater, never()).inflate(eq(R.layout.udfps_aod_lock_icon), any()); - } - - @Test - public void testInflateUdfpsWhenSupported() { - // GIVEN Udpfs sensor is available - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); - - mLockIconViewController.init(); - captureAttachListener(); - - // WHEN the view is attached - mAttachListener.onViewAttachedToWindow(mLockIconView); - - // THEN lottie animation should be inflated - verify(mLayoutInflater).inflate(eq(R.layout.udfps_aod_lock_icon), any()); - } - - @Test public void testUpdateFingerprintLocationOnInit() { // GIVEN fp sensor location is available pre-attached Pair<Integer, PointF> udfps = setupUdfps(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java index 053851ec385d..451291d264b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java @@ -43,6 +43,7 @@ import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import org.junit.Before; import org.junit.Test; @@ -65,6 +66,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { mock(NotificationEntryManager.class); private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); + private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class); private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl; private MediaOutputController mMediaOutputController; @@ -77,7 +79,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { public void setUp() { mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager); mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mMediaOutputController); mMediaOutputBaseDialogImpl.onCreate(new Bundle()); @@ -167,7 +169,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog { MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) { - super(context, mediaOutputController); + super(context, mediaOutputController, mDialogManager); mAdapter = mMediaOutputBaseAdapter; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index 09ec4ca0e1df..cd26e0d960dc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -54,6 +54,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import org.junit.Before; import org.junit.Test; @@ -93,6 +94,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { mock(NotificationEntryManager.class); private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); + private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class); private Context mSpyContext; private MediaOutputController mMediaOutputController; @@ -115,7 +117,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager); mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager); mMediaOutputController.mLocalMediaManager = mLocalMediaManager; MediaDescription.Builder builder = new MediaDescription.Builder(); @@ -159,7 +161,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { public void start_withoutPackageName_verifyMediaControllerInit() { mMediaOutputController = new MediaOutputController(mSpyContext, null, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager); mMediaOutputController.start(mCb); @@ -180,7 +182,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { public void stop_withoutPackageName_verifyMediaControllerDeinit() { mMediaOutputController = new MediaOutputController(mSpyContext, null, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager); mMediaOutputController.start(mCb); @@ -451,7 +453,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { public void getNotificationLargeIcon_withoutPackageName_returnsNull() { mMediaOutputController = new MediaOutputController(mSpyContext, null, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager); assertThat(mMediaOutputController.getNotificationIcon()).isNull(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java index 8a3ea562269d..ada8d3592012 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java @@ -40,6 +40,7 @@ import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import org.junit.After; import org.junit.Before; @@ -67,6 +68,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { mock(NotificationEntryManager.class); private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); + private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class); private MediaOutputDialog mMediaOutputDialog; private MediaOutputController mMediaOutputController; @@ -76,10 +78,10 @@ public class MediaOutputDialogTest extends SysuiTestCase { public void setUp() { mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager); mMediaOutputController.mLocalMediaManager = mLocalMediaManager; mMediaOutputDialog = new MediaOutputDialog(mContext, false, - mMediaOutputController, mUiEventLogger); + mMediaOutputController, mUiEventLogger, mDialogManager); mMediaOutputDialog.show(); when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice); @@ -125,7 +127,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { // and verify if the calling times increases. public void onCreate_ShouldLogVisibility() { MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false, - mMediaOutputController, mUiEventLogger); + mMediaOutputController, mUiEventLogger, mDialogManager); testDialog.show(); testDialog.dismissDialog(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java index e8cd6c88956d..b114452facc3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java @@ -38,6 +38,7 @@ import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; import org.junit.After; import org.junit.Before; @@ -66,6 +67,7 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase { mock(NotificationEntryManager.class); private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); + private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class); private MediaOutputGroupDialog mMediaOutputGroupDialog; private MediaOutputController mMediaOutputController; @@ -75,10 +77,10 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase { public void setUp() { mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager); mMediaOutputController.mLocalMediaManager = mLocalMediaManager; mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false, - mMediaOutputController); + mMediaOutputController, mDialogManager); mMediaOutputGroupDialog.show(); when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java index 6dca2a73b57b..47f6e5c420f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -235,10 +235,18 @@ public class InternetDialogControllerTest extends SysuiTestCase { } @Test - public void getSubtitleText_withAirplaneModeOn_returnNull() { + public void getSubtitleText_withApmOnAndWifiOff_returnWifiIsOff() { fakeAirplaneModeEnabled(true); + when(mWifiManager.isWifiEnabled()).thenReturn(false); - assertThat(mInternetDialogController.getSubtitleText(false)).isNull(); + assertThat(mInternetDialogController.getSubtitleText(false)) + .isEqualTo(getResourcesString("wifi_is_off")); + + // if the Wi-Fi disallow config, then don't return Wi-Fi related string. + mInternetDialogController.mCanConfigWifi = false; + + assertThat(mInternetDialogController.getSubtitleText(false)) + .isNotEqualTo(getResourcesString("wifi_is_off")); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java index 651bcdef9978..89537884903a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java @@ -140,7 +140,7 @@ public class InternetDialogTest extends SysuiTestCase { mInternetDialog.updateDialog(true); - assertThat(mSubTitle.getVisibility()).isEqualTo(View.GONE); + assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index 5b60c9e07342..ea681435132e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -4,6 +4,7 @@ import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.EmptyShadeView import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider @@ -55,4 +56,24 @@ class StackScrollAlgorithmTest : SysuiTestCase() { // top margin presence should decrease heads up translation up to minHeadsUpTranslation assertThat(expandableViewState.yTranslation).isEqualTo(minHeadsUpTranslation) } -}
\ No newline at end of file + + @Test + fun resetViewStates_childIsEmptyShadeView_viewIsCenteredVertically() { + stackScrollAlgorithm.initView(context) + val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply { + layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100) + } + hostView.removeAllViews() + hostView.addView(emptyShadeView) + ambientState.layoutMaxHeight = 1280 + + stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) + + val closeHandleUnderlapHeight = + context.resources.getDimensionPixelSize(R.dimen.close_handle_underlap) + val fullHeight = + ambientState.layoutMaxHeight + closeHandleUnderlapHeight - ambientState.stackY + val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f + assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 07debe68e224..c3349f1d70f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -381,16 +381,15 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { } @Test - public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() { + public void onUdfpsConsecutivelyFailedTwoTimes_showBouncer() { // GIVEN UDFPS is supported when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true); - // WHEN udfps fails twice - then don't show the bouncer - mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); + // WHEN udfps fails once - then don't show the bouncer mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); - // WHEN udfps fails the third time + // WHEN udfps fails the second time mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); // THEN show the bouncer diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 1e15d2ae0bdb..2f2e536322db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.draganddrop.DragAndDrop; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; @@ -42,7 +43,6 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.Pip; -import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; @@ -78,7 +78,7 @@ public class WMShellTest extends SysuiTestCase { @Mock WakefulnessLifecycle mWakefulnessLifecycle; @Mock ProtoTracer mProtoTracer; @Mock ShellCommandHandler mShellCommandHandler; - @Mock SizeCompatUI mSizeCompatUI; + @Mock CompatUI mCompatUI; @Mock ShellExecutor mSysUiMainExecutor; @Mock DragAndDrop mDragAndDrop; @@ -88,7 +88,7 @@ public class WMShellTest extends SysuiTestCase { mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen), Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), - Optional.of(mShellCommandHandler), Optional.of(mSizeCompatUI), + Optional.of(mShellCommandHandler), Optional.of(mCompatUI), Optional.of(mDragAndDrop), mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor, mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer, @@ -136,8 +136,8 @@ public class WMShellTest extends SysuiTestCase { } @Test - public void initSizeCompatUI_registersCallbacks() { - mWMShell.initSizeCompatUi(mSizeCompatUI); + public void initCompatUI_registersCallbacks() { + mWMShell.initCompatUi(mCompatUI); verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class)); } diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java index eaf269415fdc..6744ea8e26a5 100644 --- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java @@ -288,8 +288,6 @@ public class SystemActionPerformer { showGlobalActions(); return true; } - case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: - return toggleSplitScreen(); case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: return lockScreen(); case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: @@ -369,21 +367,6 @@ public class SystemActionPerformer { mWindowManagerService.showGlobalActions(); } - private boolean toggleSplitScreen() { - final long token = Binder.clearCallingIdentity(); - try { - StatusBarManagerInternal statusBarService = LocalServices.getService( - StatusBarManagerInternal.class); - if (statusBarService == null) { - return false; - } - statusBarService.toggleSplitScreen(); - } finally { - Binder.restoreCallingIdentity(token); - } - return true; - } - private boolean lockScreen() { mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 53b1608841b4..ec15af335c78 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4184,7 +4184,7 @@ public class ActivityManagerService extends IActivityManager.Stub didSomething |= mProcessList.killPackageProcessesLSP(packageName, appId, userId, ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit, - evenPersistent, true /* setRemoved */, + evenPersistent, true /* setRemoved */, uninstalling, packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED : ApplicationExitInfo.REASON_USER_REQUESTED, ApplicationExitInfo.SUBREASON_UNKNOWN, @@ -7220,6 +7220,7 @@ public class ActivityManagerService extends IActivityManager.Stub ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */, true /* callerWillRestart */, true /* doit */, true /* evenPersistent */, false /* setRemoved */, + false /* uninstalling */, ApplicationExitInfo.REASON_OTHER, ApplicationExitInfo.SUBREASON_KILL_UID, reason != null ? reason : "kill uid"); @@ -7241,6 +7242,7 @@ public class ActivityManagerService extends IActivityManager.Stub ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */, true /* callerWillRestart */, true /* doit */, true /* evenPersistent */, false /* setRemoved */, + false /* uninstalling */, ApplicationExitInfo.REASON_PERMISSION_CHANGE, ApplicationExitInfo.SUBREASON_UNKNOWN, reason != null ? reason : "kill uid"); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index ff480d1f614c..7673123c6476 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1633,7 +1633,7 @@ public class OomAdjuster { int schedGroup; int procState; int cachedAdjSeq; - int capability = 0; + int capability = cycleReEval ? app.mState.getCurCapability() : 0; boolean foregroundActivities = false; boolean hasVisibleActivities = false; @@ -2018,10 +2018,6 @@ public class OomAdjuster { } if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) { - if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) { - continue; - } - if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) { capability |= cstate.getCurCapability(); } @@ -2042,6 +2038,10 @@ public class OomAdjuster { } } + if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) { + continue; + } + if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) { // If the other app is cached for any reason, for purposes here // we are going to consider it empty. The specific cached state diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index b77270f5963b..1e66ed42ff96 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -374,6 +374,16 @@ public final class ProcessList { private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id. /** + * Native heap allocations in AppZygote process and its descendants will now have a + * non-zero tag in the most significant byte. + * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged + * Pointers</a> + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) + private static final long NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE = 207557677; + + /** * Enable asynchronous (ASYNC) memory tag checking in this process. This * flag will only have an effect on hardware supporting the ARM Memory * Tagging Extension (MTE). @@ -1738,6 +1748,16 @@ public final class ProcessList { return level; } + private int decideTaggingLevelForAppZygote(ProcessRecord app) { + int level = decideTaggingLevel(app); + // TBI ("fake" pointer tagging) in AppZygote is controlled by a separate compat feature. + if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE, app.info) + && level == Zygote.MEMORY_TAG_LEVEL_TBI) { + level = Zygote.MEMORY_TAG_LEVEL_NONE; + } + return level; + } + private int decideGwpAsanLevel(ProcessRecord app) { // Look at the process attribute first. if (app.processInfo != null @@ -2238,7 +2258,8 @@ public final class ProcessList { // not the calling one. appInfo.packageName = app.getHostingRecord().getDefiningPackageName(); appInfo.uid = uid; - appZygote = new AppZygote(appInfo, uid, firstUid, lastUid); + int runtimeFlags = decideTaggingLevelForAppZygote(app); + appZygote = new AppZygote(appInfo, uid, firstUid, lastUid, runtimeFlags); mAppZygotes.put(app.info.processName, uid, appZygote); zygoteProcessList = new ArrayList<ProcessRecord>(); mAppZygoteProcesses.put(appZygote, zygoteProcessList); @@ -2750,8 +2771,8 @@ public final class ProcessList { int reasonCode, int subReason, String reason) { return killPackageProcessesLSP(packageName, appId, userId, minOomAdj, false /* callerWillRestart */, true /* allowRestart */, true /* doit */, - false /* evenPersistent */, false /* setRemoved */, reasonCode, - subReason, reason); + false /* evenPersistent */, false /* setRemoved */, false /* uninstalling */, + reasonCode, subReason, reason); } @GuardedBy("mService") @@ -2784,9 +2805,10 @@ public final class ProcessList { @GuardedBy({"mService", "mProcLock"}) boolean killPackageProcessesLSP(String packageName, int appId, int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart, - boolean doit, boolean evenPersistent, boolean setRemoved, int reasonCode, - int subReason, String reason) { - ArrayList<ProcessRecord> procs = new ArrayList<>(); + boolean doit, boolean evenPersistent, boolean setRemoved, boolean uninstalling, + int reasonCode, int subReason, String reason) { + final PackageManagerInternal pm = mService.getPackageManagerInternal(); + final ArrayList<Pair<ProcessRecord, Boolean>> procs = new ArrayList<>(); // Remove all processes this package may have touched: all with the // same UID (except for the system or root user), and all whose name @@ -2803,7 +2825,18 @@ public final class ProcessList { } if (app.isRemoved()) { if (doit) { - procs.add(app); + boolean shouldAllowRestart = false; + if (!uninstalling && packageName != null) { + // This package has a dependency on the given package being stopped, + // while it's not being frozen nor uninstalled, allow to restart it. + shouldAllowRestart = !app.getPkgList().containsKey(packageName) + && app.getPkgDeps() != null + && app.getPkgDeps().contains(packageName) + && app.info != null + && !pm.isPackageFrozen(app.info.packageName, app.uid, + app.userId); + } + procs.add(new Pair<>(app, shouldAllowRestart)); } continue; } @@ -2818,6 +2851,8 @@ public final class ProcessList { continue; } + boolean shouldAllowRestart = false; + // If no package is specified, we call all processes under the // give user id. if (packageName == null) { @@ -2839,9 +2874,16 @@ public final class ProcessList { if (userId != UserHandle.USER_ALL && app.userId != userId) { continue; } - if (!app.getPkgList().containsKey(packageName) && !isDep) { + final boolean isInPkgList = app.getPkgList().containsKey(packageName); + if (!isInPkgList && !isDep) { continue; } + if (!isInPkgList && isDep && !uninstalling && app.info != null + && !pm.isPackageFrozen(app.info.packageName, app.uid, app.userId)) { + // This package has a dependency on the given package being stopped, + // while it's not being frozen nor uninstalled, allow to restart it. + shouldAllowRestart = true; + } } // Process has passed all conditions, kill it! @@ -2851,14 +2893,15 @@ public final class ProcessList { if (setRemoved) { app.setRemoved(true); } - procs.add(app); + procs.add(new Pair<>(app, shouldAllowRestart)); } } int N = procs.size(); for (int i=0; i<N; i++) { - removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, - reasonCode, subReason, reason); + final Pair<ProcessRecord, Boolean> proc = procs.get(i); + removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second, + reasonCode, subReason, reason); } killAppZygotesLocked(packageName, appId, userId, false /* force */); mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 5ecdfe49cdf7..f053e942fe6b 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -328,7 +328,7 @@ import java.util.concurrent.atomic.AtomicBoolean; } boolean isBtScoRequested = isBluetoothScoRequested(); - if (isBtScoRequested && !wasBtScoRequested) { + if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) { if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) { Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: " + pid); diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 7341e744dea3..358263df916b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -503,10 +503,14 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> protected int getShowOverlayReason() { if (isKeyguard()) { return BiometricOverlayConstants.REASON_AUTH_KEYGUARD; - } else if (isSettings()) { - return BiometricOverlayConstants.REASON_AUTH_SETTINGS; } else if (isBiometricPrompt()) { + // BP reason always takes precedent over settings, since callers from within + // settings can always invoke BP. return BiometricOverlayConstants.REASON_AUTH_BP; + } else if (isSettings()) { + // This is pretty much only for FingerprintManager#authenticate usage from + // FingerprintSettings. + return BiometricOverlayConstants.REASON_AUTH_SETTINGS; } else { return BiometricOverlayConstants.REASON_AUTH_OTHER; } diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 5ce72c2fd080..3120dc58eebd 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -25,7 +25,6 @@ import android.annotation.TestApi; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.compat.CompatChanges; -import android.app.TaskStackListener; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; import android.compat.annotation.Overridable; @@ -35,6 +34,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; import android.content.res.Configuration; import android.hardware.CameraSessionStats; import android.hardware.CameraStreamStats; @@ -84,7 +84,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -309,8 +308,6 @@ public class CameraServiceProxy extends SystemService private final DisplayWindowListener mDisplayWindowListener = new DisplayWindowListener(); - private final TaskStateHandler mTaskStackListener = new TaskStateHandler(); - public static final class TaskInfo { public int frontTaskId; public boolean isResizeable; @@ -320,54 +317,6 @@ public class CameraServiceProxy extends SystemService public int userId; } - private final class TaskStateHandler extends TaskStackListener { - private final Object mMapLock = new Object(); - - // maps the package name to its corresponding current top level task id - @GuardedBy("mMapLock") - private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>(); - - @Override - public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { - synchronized (mMapLock) { - TaskInfo info = new TaskInfo(); - info.frontTaskId = taskInfo.taskId; - info.isResizeable = - (taskInfo.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE); - info.displayId = taskInfo.displayId; - info.userId = taskInfo.userId; - info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape( - taskInfo.topActivityInfo.screenOrientation); - info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait( - taskInfo.topActivityInfo.screenOrientation); - mTaskInfoMap.put(taskInfo.topActivityInfo.packageName, info); - } - } - - @Override - public void onTaskRemoved(int taskId) { - synchronized (mMapLock) { - for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){ - if (entry.getValue().frontTaskId == taskId) { - mTaskInfoMap.remove(entry.getKey()); - break; - } - } - } - } - - public @Nullable TaskInfo getFrontTaskInfo(String packageName) { - synchronized (mMapLock) { - if (mTaskInfoMap.containsKey(packageName)) { - return mTaskInfoMap.get(packageName); - } - } - - Log.e(TAG, "Top task with package name: " + packageName + " not found!"); - return null; - } - } - private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -490,18 +439,53 @@ public class CameraServiceProxy extends SystemService private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() { @Override - public int getRotateAndCropOverride(String packageName, int lensFacing) { + public int getRotateAndCropOverride(String packageName, int lensFacing, int userId) { if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + " camera service UID!"); return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; } + TaskInfo taskInfo = null; + ParceledListSlice<ActivityManager.RecentTaskInfo> recentTasks = null; + + try { + recentTasks = ActivityTaskManager.getService().getRecentTasks(/*maxNum*/1, + /*flags*/ 0, userId); + } catch (RemoteException e) { + Log.e(TAG, "Failed to query recent tasks!"); + return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; + } + + if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) { + ActivityManager.RecentTaskInfo task = recentTasks.getList().get(0); + if (packageName.equals(task.topActivityInfo.packageName)) { + taskInfo = new TaskInfo(); + taskInfo.frontTaskId = task.taskId; + taskInfo.isResizeable = + (task.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE); + taskInfo.displayId = task.displayId; + taskInfo.userId = task.userId; + taskInfo.isFixedOrientationLandscape = + ActivityInfo.isFixedOrientationLandscape( + task.topActivityInfo.screenOrientation); + taskInfo.isFixedOrientationPortrait = + ActivityInfo.isFixedOrientationPortrait( + task.topActivityInfo.screenOrientation); + } else { + Log.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName + + " doesn't match with camera client package name: " + packageName); + return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; + } + } else { + Log.e(TAG, "Recent task list is empty!"); + return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; + } + // TODO: Modify the sensor orientation in camera characteristics along with any 3A // regions in capture requests/results to account for thea physical rotation. The // former is somewhat tricky as it assumes that camera clients always check for the // current value by retrieving the camera characteristics from the camera device. - TaskInfo taskInfo = mTaskStackListener.getFrontTaskInfo(packageName); if ((taskInfo != null) && (CompatChanges.isChangeEnabled( OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName, UserHandle.getUserHandleForUid(taskInfo.userId)))) { @@ -673,12 +657,6 @@ public class CameraServiceProxy extends SystemService CameraStatsJobService.schedule(mContext); try { - ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Log.e(TAG, "Failed to register task stack listener!"); - } - - try { int[] displayIds = WindowManagerGlobal.getWindowManagerService() .registerDisplayWindowListener(mDisplayWindowListener); for (int i = 0; i < displayIds.length; i++) { diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 155b61891d12..737b653318d2 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -177,7 +177,7 @@ public class LocationProviderManager extends protected interface LocationTransport { void deliverOnLocationChanged(LocationResult locationResult, - @Nullable Runnable onCompleteCallback) throws Exception; + @Nullable IRemoteCallback onCompleteCallback) throws Exception; void deliverOnFlushComplete(int requestCode) throws Exception; } @@ -197,9 +197,8 @@ public class LocationProviderManager extends @Override public void deliverOnLocationChanged(LocationResult locationResult, - @Nullable Runnable onCompleteCallback) throws RemoteException { - mListener.onLocationChanged(locationResult.asList(), - SingleUseCallback.wrap(onCompleteCallback)); + @Nullable IRemoteCallback onCompleteCallback) throws RemoteException { + mListener.onLocationChanged(locationResult.asList(), onCompleteCallback); } @Override @@ -227,7 +226,7 @@ public class LocationProviderManager extends @Override public void deliverOnLocationChanged(LocationResult locationResult, - @Nullable Runnable onCompleteCallback) + @Nullable IRemoteCallback onCompleteCallback) throws PendingIntent.CanceledException { BroadcastOptions options = BroadcastOptions.makeBasic(); options.setDontSendToRestrictedApps(true); @@ -243,20 +242,34 @@ public class LocationProviderManager extends intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0])); } + PendingIntent.OnFinished onFinished = null; + // send() SHOULD only run the completion callback if it completes successfully. however, - // b/199464864 (which could not be fixed in the S timeframe) means that it's possible + // b/201299281 (which could not be fixed in the S timeframe) means that it's possible // for send() to throw an exception AND run the completion callback. if this happens, we // would over-release the wakelock... we take matters into our own hands to ensure that // the completion callback can only be run if send() completes successfully. this means // the completion callback may be run inline - but as we've never specified what thread // the callback is run on, this is fine. - GatedCallback gatedCallback = new GatedCallback(onCompleteCallback); + GatedCallback gatedCallback; + if (onCompleteCallback != null) { + gatedCallback = new GatedCallback(() -> { + try { + onCompleteCallback.sendResult(null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + }); + onFinished = (pI, i, rC, rD, rE) -> gatedCallback.run(); + } else { + gatedCallback = new GatedCallback(null); + } mPendingIntent.send( mContext, 0, intent, - (pI, i, rC, rD, rE) -> gatedCallback.run(), + onFinished, null, null, options.toBundle()); @@ -293,7 +306,7 @@ public class LocationProviderManager extends @Override public void deliverOnLocationChanged(@Nullable LocationResult locationResult, - @Nullable Runnable onCompleteCallback) + @Nullable IRemoteCallback onCompleteCallback) throws RemoteException { // ILocationCallback doesn't currently support completion callbacks Preconditions.checkState(onCompleteCallback == null); @@ -714,6 +727,13 @@ public class LocationProviderManager extends final PowerManager.WakeLock mWakeLock; + // b/206340085 - if we allocate a new wakelock releaser object for every delivery we + // increase the risk of resource starvation. if a client stops processing deliveries the + // system server binder allocation pool will be starved as we continue to queue up + // deliveries, each with a new allocation. in order to mitigate this, we use a single + // releaser object per registration rather than per delivery. + final ExternalWakeLockReleaser mWakeLockReleaser; + private volatile ProviderTransport mProviderTransport; private int mNumLocationsDelivered = 0; private long mExpirationRealtimeMs = Long.MAX_VALUE; @@ -727,6 +747,7 @@ public class LocationProviderManager extends .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); mWakeLock.setReferenceCounted(true); mWakeLock.setWorkSource(request.getWorkSource()); + mWakeLockReleaser = new ExternalWakeLockReleaser(identity, mWakeLock); } @Override @@ -943,7 +964,7 @@ public class LocationProviderManager extends } listener.deliverOnLocationChanged(deliverLocationResult, - mUseWakeLock ? mWakeLock::release : null); + mUseWakeLock ? mWakeLockReleaser : null); EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(), getIdentity()); } @@ -2761,7 +2782,7 @@ public class LocationProviderManager extends @GuardedBy("this") private boolean mRun; - GatedCallback(Runnable callback) { + GatedCallback(@Nullable Runnable callback) { mCallback = callback; } @@ -2796,4 +2817,24 @@ public class LocationProviderManager extends } } } + + private static class ExternalWakeLockReleaser extends IRemoteCallback.Stub { + + private final CallerIdentity mIdentity; + private final PowerManager.WakeLock mWakeLock; + + ExternalWakeLockReleaser(CallerIdentity identity, PowerManager.WakeLock wakeLock) { + mIdentity = identity; + mWakeLock = Objects.requireNonNull(wakeLock); + } + + @Override + public void sendResult(Bundle data) { + try { + mWakeLock.release(); + } catch (RuntimeException e) { + Log.e(TAG, "wakelock over-released by " + mIdentity, e); + } + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b469a80a3a2f..393d101e9830 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -160,6 +160,7 @@ import static com.android.server.wm.ActivityRecordProto.NUM_DRAWN_WINDOWS; import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS; import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ENABLED; import static com.android.server.wm.ActivityRecordProto.PROC_ID; +import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS; import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN; import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE; import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED; @@ -261,6 +262,7 @@ import android.content.Intent; import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ConstrainDisplayApisConfig; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -596,6 +598,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private CompatDisplayInsets mCompatDisplayInsets; + private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig; + boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session IVoiceInteractionSession voiceSession; // Voice interaction session for this activity @@ -1162,8 +1166,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (info.configChanges != 0) { pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges)); } - pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis()); - pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis()); + pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis( + sConstrainDisplayApisConfig)); + pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis( + sConstrainDisplayApisConfig)); } if (mLastParentBeforePip != null) { pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId); @@ -1769,6 +1775,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A info.windowLayout.windowLayoutAffinity = uid + ":" + info.windowLayout.windowLayoutAffinity; } + // Initialize once, when we know all system services are available. + if (sConstrainDisplayApisConfig == null) { + sConstrainDisplayApisConfig = new ConstrainDisplayApisConfig(); + } stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0; nonLocalizedLabel = aInfo.nonLocalizedLabel; labelRes = aInfo.labelRes; @@ -7465,8 +7475,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A + "should create compatDisplayInsets = %s", getUid(), mTmpBounds, - info.neverSandboxDisplayApis(), - info.alwaysSandboxDisplayApis(), + info.neverSandboxDisplayApis(sConstrainDisplayApisConfig), + info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig), !matchParentBounds(), mCompatDisplayInsets != null, shouldCreateCompatDisplayInsets()); @@ -8027,11 +8037,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return false; } // Never apply sandboxing to an app that should be explicitly excluded from the config. - if (info != null && info.neverSandboxDisplayApis()) { + if (info.neverSandboxDisplayApis(sConstrainDisplayApisConfig)) { return false; } // Always apply sandboxing to an app that should be explicitly included from the config. - if (info != null && info.alwaysSandboxDisplayApis()) { + if (info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig)) { return true; } // Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it @@ -9047,6 +9057,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled()); proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode()); proto.write(MIN_ASPECT_RATIO, getMinAspectRatio()); + // Only record if max bounds sandboxing is applied, if the caller has the necessary + // permission to access the device configs. + proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds()); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 2cf23c5e6f7f..436a325559e6 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -72,6 +72,8 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; +import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; +import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY; import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; @@ -1275,7 +1277,7 @@ class ActivityStarter { // This is used to block background activity launch even if the app is still // visible to user after user clicking home button. - final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed(); + final int appSwitchState = mService.getBalAppSwitchesState(); // don't abort if the callingUid has a visible window or is a persistent system process final int callingUidProcState = mService.mActiveUids.getUidState(callingUid); @@ -1288,7 +1290,9 @@ class ActivityStarter { // Normal apps with visible app window will be allowed to start activity if app switching // is allowed, or apps like live wallpaper with non app visible window will be allowed. - if (((appSwitchAllowed || mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) + final boolean appSwitchAllowedOrFg = + appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY; + if (((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) && callingUidHasAnyVisibleWindow) || isCallingUidPersistentSystemProcess) { if (DEBUG_ACTIVITY_STARTS) { @@ -1398,7 +1402,7 @@ class ActivityStarter { // don't abort if the callerApp or other processes of that uid are allowed in any way if (callerApp != null) { // first check the original calling process - if (callerApp.areBackgroundActivityStartsAllowed(appSwitchAllowed)) { + if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "Background activity start allowed: callerApp process (pid = " + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed"); @@ -1412,7 +1416,7 @@ class ActivityStarter { for (int i = uidProcesses.size() - 1; i >= 0; i--) { final WindowProcessController proc = uidProcesses.valueAt(i); if (proc != callerApp - && proc.areBackgroundActivityStartsAllowed(appSwitchAllowed)) { + && proc.areBackgroundActivityStartsAllowed(appSwitchState)) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "Background activity start allowed: process " + proc.getPid() @@ -1426,7 +1430,7 @@ class ActivityStarter { // anything that has fallen through would currently be aborted Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage + "; callingUid: " + callingUid - + "; appSwitchAllowed: " + appSwitchAllowed + + "; appSwitchState: " + appSwitchState + "; isCallingUidForeground: " + isCallingUidForeground + "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow + "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class, diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 60a514e4d612..c8227d953009 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -504,7 +504,27 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * Whether normal application switches are allowed; a call to {@link #stopAppSwitches() * disables this. */ - private volatile boolean mAppSwitchesAllowed = true; + private volatile int mAppSwitchesState = APP_SWITCH_ALLOW; + + // The duration of resuming foreground app switch from disallow. + private static final long RESUME_FG_APP_SWITCH_MS = 500; + + /** App switch is not allowed. */ + static final int APP_SWITCH_DISALLOW = 0; + + /** App switch is allowed only if the activity launch was requested by a foreground app. */ + static final int APP_SWITCH_FG_ONLY = 1; + + /** App switch is allowed. */ + static final int APP_SWITCH_ALLOW = 2; + + @IntDef({ + APP_SWITCH_DISALLOW, + APP_SWITCH_FG_ONLY, + APP_SWITCH_ALLOW, + }) + @Retention(RetentionPolicy.SOURCE) + @interface AppSwitchState {} /** * Last stop app switches time, apps finished before this time cannot start background activity @@ -1247,7 +1267,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null && topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid == Binder.getCallingUid()) { - mAppSwitchesAllowed = true; + mAppSwitchesState = APP_SWITCH_ALLOW; } } return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null, @@ -2158,8 +2178,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** * Return true if app switching is allowed. */ - boolean getBalAppSwitchesAllowed() { - return mAppSwitchesAllowed; + @AppSwitchState int getBalAppSwitchesState() { + return mAppSwitchesState; } /** Register an {@link AnrController} to control the ANR dialog behavior */ @@ -3680,8 +3700,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void stopAppSwitches() { mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches"); synchronized (mGlobalLock) { - mAppSwitchesAllowed = false; + mAppSwitchesState = APP_SWITCH_DISALLOW; mLastStopAppSwitchesTime = SystemClock.uptimeMillis(); + mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG); + mH.sendEmptyMessageDelayed(H.RESUME_FG_APP_SWITCH_MSG, RESUME_FG_APP_SWITCH_MS); } } @@ -3689,7 +3711,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void resumeAppSwitches() { mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches"); synchronized (mGlobalLock) { - mAppSwitchesAllowed = true; + mAppSwitchesState = APP_SWITCH_ALLOW; + mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG); } } @@ -5170,6 +5193,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { static final int REPORT_TIME_TRACKER_MSG = 1; static final int UPDATE_PROCESS_ANIMATING_STATE = 2; static final int END_POWER_MODE_UNKNOWN_VISIBILITY_MSG = 3; + static final int RESUME_FG_APP_SWITCH_MSG = 4; static final int FIRST_ACTIVITY_TASK_MSG = 100; static final int FIRST_SUPERVISOR_TASK_MSG = 200; @@ -5207,6 +5231,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } break; + case RESUME_FG_APP_SWITCH_MSG: { + synchronized (mGlobalLock) { + if (mAppSwitchesState == APP_SWITCH_DISALLOW) { + mAppSwitchesState = APP_SWITCH_FG_ONLY; + } + } + } + break; } } } diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java index 71a10df34d30..0afd87282783 100644 --- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java +++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java @@ -20,6 +20,8 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVIT import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS; +import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; +import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY; import android.annotation.NonNull; import android.annotation.Nullable; @@ -70,13 +72,13 @@ class BackgroundLaunchProcessController { } boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName, - boolean appSwitchAllowed, boolean isCheckingForFgsStart, + int appSwitchState, boolean isCheckingForFgsStart, boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges, long lastStopAppSwitchesTime, long lastActivityLaunchTime, long lastActivityFinishTime) { // If app switching is not allowed, we ignore all the start activity grace period // exception so apps cannot start itself in onPause() after pressing home button. - if (appSwitchAllowed) { + if (appSwitchState == APP_SWITCH_ALLOW) { // Allow if any activity in the caller has either started or finished very recently, and // it must be started or finished after last stop app switches time. final long now = SystemClock.uptimeMillis(); @@ -111,7 +113,8 @@ class BackgroundLaunchProcessController { return true; } // Allow if the caller has an activity in any foreground task. - if (appSwitchAllowed && hasActivityInVisibleTask) { + if (hasActivityInVisibleTask + && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: process has activity in foreground task"); diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 94a175caba22..8a2d11636fe3 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -251,15 +251,47 @@ public class LockTaskController { */ boolean activityBlockedFromFinish(ActivityRecord activity) { final Task task = activity.getTask(); - if (activity == task.getRootActivity() - && activity == task.getTopNonFinishingActivity() - && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV - && isRootTask(task)) { - Slog.i(TAG, "Not finishing task in lock task mode"); - showLockTaskToast(); - return true; + if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV || !isRootTask(task)) { + return false; } - return false; + + final ActivityRecord taskTop = task.getTopNonFinishingActivity(); + final ActivityRecord taskRoot = task.getRootActivity(); + // If task has more than one Activity, verify if there's only adjacent TaskFragments that + // should be finish together in the Task. + if (activity != taskRoot || activity != taskTop) { + final TaskFragment taskFragment = activity.getTaskFragment(); + final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment(); + if (taskFragment.asTask() != null + || !taskFragment.isDelayLastActivityRemoval() + || adjacentTaskFragment == null) { + // Don't block activity from finishing if the TaskFragment don't have any adjacent + // TaskFragment, or it won't finish together with its adjacent TaskFragment. + return false; + } + + final boolean hasOtherActivityInTaskFragment = + taskFragment.getActivity(a -> !a.finishing && a != activity) != null; + if (hasOtherActivityInTaskFragment) { + // Don't block activity from finishing if there's other Activity in the same + // TaskFragment. + return false; + } + + final boolean hasOtherActivityInTask = task.getActivity(a -> !a.finishing + && a != activity && a.getTaskFragment() != adjacentTaskFragment) != null; + if (hasOtherActivityInTask) { + // Do not block activity from finishing if there are another running activities + // after the current and adjacent TaskFragments are removed. Note that we don't + // check activities in adjacent TaskFragment because it will be finished together + // with TaskFragment regardless of numbers of activities. + return false; + } + } + + Slog.i(TAG, "Not finishing task in lock task mode"); + showLockTaskToast(); + return true; } /** diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java index b54208d11974..9ad30da1cb54 100644 --- a/services/core/java/com/android/server/wm/PinnedTaskController.java +++ b/services/core/java/com/android/server/wm/PinnedTaskController.java @@ -255,25 +255,27 @@ class PinnedTaskController { mDestRotatedBounds = null; mPipTransaction = null; final Rect areaBounds = taskArea.getBounds(); - if (pipTx != null) { + if (pipTx != null && pipTx.mPosition != null) { // The transaction from recents animation is in old rotation. So the position needs to // be rotated. - float dx = pipTx.mPositionX; - float dy = pipTx.mPositionY; + float dx = pipTx.mPosition.x; + float dy = pipTx.mPosition.y; final Matrix matrix = pipTx.getMatrix(); if (pipTx.mRotation == 90) { - dx = pipTx.mPositionY; - dy = areaBounds.right - pipTx.mPositionX; + dx = pipTx.mPosition.y; + dy = areaBounds.right - pipTx.mPosition.x; matrix.postRotate(-90); } else if (pipTx.mRotation == -90) { - dx = areaBounds.bottom - pipTx.mPositionY; - dy = pipTx.mPositionX; + dx = areaBounds.bottom - pipTx.mPosition.y; + dy = pipTx.mPosition.x; matrix.postRotate(90); } matrix.postTranslate(dx, dy); final SurfaceControl leash = pinnedTask.getSurfaceControl(); - t.setMatrix(leash, matrix, new float[9]) - .setCornerRadius(leash, pipTx.mCornerRadius); + t.setMatrix(leash, matrix, new float[9]); + if (pipTx.hasCornerRadiusSet()) { + t.setCornerRadius(leash, pipTx.mCornerRadius); + } Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy); return; } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index fd4b63e26403..5dd8ef39e8e7 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -264,13 +264,6 @@ public class RecentsAnimationController implements DeathRecipient { "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled); final long token = Binder.clearCallingIdentity(); try { - synchronized (mService.getWindowManagerLock()) { - // Remove all new task targets. - for (int i = mPendingNewTaskTargets.size() - 1; i >= 0; i--) { - removeTaskInternal(mPendingNewTaskTargets.get(i)); - } - } - // Note, the callback will handle its own synchronization, do not lock on WM lock // prior to calling the callback mCallbacks.onAnimationFinished(moveHomeToTop @@ -760,7 +753,7 @@ public class RecentsAnimationController implements DeathRecipient { // the task-id with the leaf id. final Task leafTask = task.getTopLeafTask(); int taskId = leafTask.mTaskId; - TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task, + TaskAnimationAdapter adapter = addAnimation(task, !recentTaskIds.get(taskId), true /* hidden */, finishedCallback); mPendingNewTaskTargets.add(taskId); return adapter.createRemoteAnimationTarget(taskId); @@ -1013,6 +1006,7 @@ public class RecentsAnimationController implements DeathRecipient { taskAdapter.onCleanup(); } // Should already be empty, but clean-up pending task-appears in-case they weren't sent. + mPendingNewTaskTargets.clear(); mPendingTaskAppears.clear(); for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f0a15543a22e..c88dbf719d94 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -321,6 +321,11 @@ class Task extends TaskFragment { */ boolean mInResumeTopActivity = false; + /** + * Used to identify if the activity that is installed from device's system image. + */ + boolean mIsEffectivelySystemApp; + int mCurrentUser; String affinity; // The affinity name for this task, or null; may change identity. @@ -568,13 +573,24 @@ class Task extends TaskFragment { if (r.finishing) return false; - // Set this as the candidate root since it isn't finishing. - mRoot = r; + if (mRoot == null || mRoot.finishing) { + // Set this as the candidate root since it isn't finishing. + mRoot = r; + } + + final int uid = mRoot == r ? effectiveUid : r.info.applicationInfo.uid; + if (ignoreRelinquishIdentity + || (mRoot.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0 + || (mRoot.info.applicationInfo.uid != Process.SYSTEM_UID + && !mRoot.info.applicationInfo.isSystemApp() + && mRoot.info.applicationInfo.uid != uid)) { + // No need to relinquish identity, end search. + return true; + } - // Only end search if we are ignore relinquishing identity or we are not relinquishing. - return ignoreRelinquishIdentity - || mNeverRelinquishIdentity - || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; + // Relinquish to next activity + mRoot = r; + return false; } } @@ -999,7 +1015,15 @@ class Task extends TaskFragment { * @param info The activity info which could be different from {@code r.info} if set. */ void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo info) { - if (this.intent == null || !mNeverRelinquishIdentity) { + boolean updateIdentity = false; + if (this.intent == null) { + updateIdentity = true; + } else if (!mNeverRelinquishIdentity) { + final ActivityInfo activityInfo = info != null ? info : r.info; + updateIdentity = (effectiveUid == Process.SYSTEM_UID || mIsEffectivelySystemApp + || effectiveUid == activityInfo.applicationInfo.uid); + } + if (updateIdentity) { mCallingUid = r.launchedFromUid; mCallingPackage = r.launchedFromPackage; mCallingFeatureId = r.launchedFromFeatureId; @@ -1012,14 +1036,7 @@ class Task extends TaskFragment { private void setIntent(Intent _intent, ActivityInfo info) { if (!isLeafTask()) return; - if (info.applicationInfo.uid == Process.SYSTEM_UID - || info.applicationInfo.isSystemApp()) { - // Only allow the apps that pre-installed on the system image to apply - // relinquishTaskIdentity - mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; - } else { - mNeverRelinquishIdentity = true; - } + mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; affinity = info.taskAffinity; if (intent == null) { // If this task already has an intent associated with it, don't set the root @@ -1028,6 +1045,7 @@ class Task extends TaskFragment { rootAffinity = affinity; } effectiveUid = info.applicationInfo.uid; + mIsEffectivelySystemApp = info.applicationInfo.isSystemApp(); stringName = null; if (info.targetActivity == null) { @@ -3141,14 +3159,6 @@ class Task extends TaskFragment { } @Override - boolean fillsParent() { - // From the perspective of policy, we still want to report that this task fills parent - // in fullscreen windowing mode even it doesn't match parent bounds because there will be - // letterbox around its real content. - return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds(); - } - - @Override void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) { final int count = mChildren.size(); boolean isLeafTask = true; diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 7bcf8a9f274f..916fa5be12d8 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -241,7 +241,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { /** * Whether to delay the last activity of TaskFragment being immediately removed while finishing. * This should only be set on a embedded TaskFragment, where the organizer can have the - * opportunity to perform other actions or animations. + * opportunity to perform animations and finishing the adjacent TaskFragment. */ private boolean mDelayLastActivityRemoval; @@ -2346,6 +2346,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { return true; } + @Override + boolean fillsParent() { + // From the perspective of policy, we still want to report that this task fills parent + // in fullscreen windowing mode even it doesn't match parent bounds because there will be + // letterbox around its real content. + return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds(); + } + boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) { boolean printed = false; diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java index cc527136eb51..7956a112539e 100644 --- a/services/core/java/com/android/server/wm/WindowContextListenerController.java +++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java @@ -173,7 +173,7 @@ class WindowContextListenerController { @VisibleForTesting class WindowContextListenerImpl implements WindowContainerListener { - @NonNull private final IBinder mClientToken; + @NonNull private final IWindowToken mClientToken; private final int mOwnerUid; @NonNull private WindowContainer<?> mContainer; /** @@ -193,7 +193,7 @@ class WindowContextListenerController { private WindowContextListenerImpl(IBinder clientToken, WindowContainer<?> container, int ownerUid, @WindowType int type, @Nullable Bundle options) { - mClientToken = clientToken; + mClientToken = IWindowToken.Stub.asInterface(clientToken); mContainer = Objects.requireNonNull(container); mOwnerUid = ownerUid; mType = type; @@ -205,7 +205,7 @@ class WindowContextListenerController { mDeathRecipient = deathRecipient; } catch (RemoteException e) { ProtoLog.e(WM_ERROR, "Could not register window container listener token=%s, " - + "container=%s", mClientToken, mContainer); + + "container=%s", clientToken, mContainer); } } @@ -228,17 +228,17 @@ class WindowContextListenerController { } private void register() { + final IBinder token = mClientToken.asBinder(); if (mDeathRecipient == null) { - throw new IllegalStateException("Invalid client token: " + mClientToken); + throw new IllegalStateException("Invalid client token: " + token); } - mListeners.putIfAbsent(mClientToken, this); + mListeners.putIfAbsent(token, this); mContainer.registerWindowContainerListener(this); - reportConfigToWindowTokenClient(); } private void unregister() { mContainer.unregisterWindowContainerListener(this); - mListeners.remove(mClientToken); + mListeners.remove(mClientToken.asBinder()); } private void clear() { @@ -258,19 +258,24 @@ class WindowContextListenerController { private void reportConfigToWindowTokenClient() { if (mDeathRecipient == null) { - throw new IllegalStateException("Invalid client token: " + mClientToken); + throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder()); + } + final DisplayContent dc = mContainer.getDisplayContent(); + if (!dc.isReady()) { + // Do not report configuration when booting. The latest configuration will be sent + // when WindowManagerService#displayReady(). + return; } // If the display of window context associated window container is suspended, don't // report the configuration update. Note that we still dispatch the configuration update // to WindowProviderService to make it compatible with Service#onConfigurationChanged. // Service always receives #onConfigurationChanged callback regardless of display state. - if (!isWindowProviderService(mOptions) - && isSuspendedState(mContainer.getDisplayContent().getDisplayInfo().state)) { + if (!isWindowProviderService(mOptions) && isSuspendedState(dc.getDisplayInfo().state)) { mHasPendingConfiguration = true; return; } final Configuration config = mContainer.getConfiguration(); - final int displayId = mContainer.getDisplayContent().getDisplayId(); + final int displayId = dc.getDisplayId(); if (mLastReportedConfig == null) { mLastReportedConfig = new Configuration(); } @@ -282,9 +287,8 @@ class WindowContextListenerController { mLastReportedConfig.setTo(config); mLastReportedDisplay = displayId; - IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken); try { - windowTokenClient.onConfigurationChanged(config, displayId); + mClientToken.onConfigurationChanged(config, displayId); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client."); } @@ -294,7 +298,7 @@ class WindowContextListenerController { @Override public void onRemoved() { if (mDeathRecipient == null) { - throw new IllegalStateException("Invalid client token: " + mClientToken); + throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder()); } final WindowToken windowToken = mContainer.asWindowToken(); if (windowToken != null && windowToken.isFromClient()) { @@ -312,9 +316,8 @@ class WindowContextListenerController { } } mDeathRecipient.unlinkToDeath(); - IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken); try { - windowTokenClient.onWindowTokenRemoved(); + mClientToken.onWindowTokenRemoved(); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client."); } @@ -323,7 +326,7 @@ class WindowContextListenerController { @Override public String toString() { - return "WindowContextListenerImpl{clientToken=" + mClientToken + ", " + return "WindowContextListenerImpl{clientToken=" + mClientToken.asBinder() + ", " + "container=" + mContainer + "}"; } @@ -337,11 +340,11 @@ class WindowContextListenerController { } void linkToDeath() throws RemoteException { - mClientToken.linkToDeath(this, 0); + mClientToken.asBinder().linkToDeath(this, 0); } void unlinkToDeath() { - mClientToken.unlinkToDeath(this, 0); + mClientToken.asBinder().unlinkToDeath(this, 0); } } } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 1cfbe07d3f16..3ccb06ccef15 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -512,19 +512,19 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio */ @HotPath(caller = HotPath.START_SERVICE) public boolean areBackgroundFgsStartsAllowed() { - return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed(), + return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesState(), true /* isCheckingForFgsStart */); } - boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed) { - return areBackgroundActivityStartsAllowed(appSwitchAllowed, + boolean areBackgroundActivityStartsAllowed(int appSwitchState) { + return areBackgroundActivityStartsAllowed(appSwitchState, false /* isCheckingForFgsStart */); } - private boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed, + private boolean areBackgroundActivityStartsAllowed(int appSwitchState, boolean isCheckingForFgsStart) { return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName, - appSwitchAllowed, isCheckingForFgsStart, hasActivityInVisibleTask(), + appSwitchState, isCheckingForFgsStart, hasActivityInVisibleTask(), mInstrumentingWithBackgroundActivityStartPrivileges, mAtm.getLastStopAppSwitchesTime(), mLastActivityLaunchTime, mLastActivityFinishTime); diff --git a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java index 49d5e50e0345..d3353cd6adc7 100644 --- a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java +++ b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java @@ -31,6 +31,7 @@ import android.os.CancellationSignal; import com.android.server.LocalServices; import com.android.server.people.PeopleServiceInternal; +import com.android.server.pm.PackageManagerService; /** * If a {@link ConversationStatus} is added to the system with an expiration time, remove that @@ -50,6 +51,7 @@ public class ConversationStatusExpirationBroadcastReceiver extends BroadcastRece final PendingIntent pi = PendingIntent.getBroadcast(context, REQUEST_CODE, new Intent(ACTION) + .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME) .setData(new Uri.Builder().scheme(SCHEME) .appendPath(getKey(userId, pkg, conversationId, status)) .build()) diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 18f1267b890f..0dd4f5b338b4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; @@ -1854,6 +1855,36 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test + public void testUpdateOomAdj_DoAll_BoundByPersService_Cycle_Branch_Capability() { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + bindService(app, client, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); + ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, + MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); + bindService(client, client2, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); + bindService(client2, app, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); + ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, + MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); + client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ); + bindService(app, client3, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); + ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); + lru.clear(); + lru.add(app); + lru.add(client); + lru.add(client2); + lru.add(client3); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); + + assertEquals(PROCESS_CAPABILITY_ALL, client.mState.getSetCapability()); + assertEquals(PROCESS_CAPABILITY_ALL, client2.mState.getSetCapability()); + assertEquals(PROCESS_CAPABILITY_ALL, app.mState.getSetCapability()); + } + + @SuppressWarnings("GuardedBy") + @Test public void testUpdateOomAdj_DoAll_Provider_Cycle_Branch_2() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml index 4c638d669019..bb3eb81df6ed 100644 --- a/services/tests/servicestests/AndroidTest.xml +++ b/services/tests/servicestests/AndroidTest.xml @@ -16,6 +16,13 @@ <configuration description="Runs Frameworks Services Tests."> <option name="test-suite-tag" value="apct" /> <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push-file" key="SimpleServiceTestApp3.apk" + value="/data/local/tmp/cts/content/SimpleServiceTestApp3.apk" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java index 8b6b7c235c44..1d6ed038b86d 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java @@ -296,14 +296,6 @@ public class SystemActionPerformerTest { } @Test - public void testToggleSplitScreen_legacy() { - setupWithRealContext(); - mSystemActionPerformer.performSystemAction( - AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN); - verify(mMockStatusBarManagerInternal).toggleSplitScreen(); - } - - @Test public void testScreenshot_requestsFromScreenshotHelper_legacy() { setupWithMockContext(); mSystemActionPerformer.performSystemAction( diff --git a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java index 10f4c05eb6d8..e6a8dea973c9 100644 --- a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -26,7 +27,9 @@ import android.app.Instrumentation; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; +import android.os.IBinder; import android.os.SystemClock; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; @@ -42,6 +45,8 @@ import org.junit.runner.RunWith; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Build/Install/Run: @@ -69,6 +74,12 @@ public final class ServiceRestarterTest { private static final int ACTION_STOPPKG = 8; private static final int ACTION_ALL = ACTION_START | ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG; + private static final String LOCAL_APK_BASE_PATH = "/data/local/tmp/cts/content/"; + private static final String TEST_PACKAGE3_APK = "SimpleServiceTestApp3.apk"; + private static final String ACTION_SERVICE_WITH_DEP_PKG = + "com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG"; + private static final String EXTRA_TARGET_PACKAGE = "target_package"; + private Context mContext; private Instrumentation mInstrumentation; private int mTestPackage1Uid; @@ -199,6 +210,83 @@ public final class ServiceRestarterTest { return res; } + @Test + public void testServiceWithDepPkgStopped() throws Exception { + final CountDownLatch[] latchHolder = new CountDownLatch[1]; + final ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + latchHolder[0].countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + latchHolder[0].countDown(); + } + }; + + final long timeout = 5_000; + final long shortTimeout = 2_000; + final Intent intent = new Intent(ACTION_SERVICE_WITH_DEP_PKG); + final String testPkg = TEST_PACKAGE2_NAME; + final String libPkg = TEST_PACKAGE3_NAME; + final String apkPath = LOCAL_APK_BASE_PATH + TEST_PACKAGE3_APK; + final ActivityManager am = mContext.getSystemService(ActivityManager.class); + + intent.setComponent(ComponentName.unflattenFromString(testPkg + "/" + TEST_SERVICE_NAME)); + intent.putExtra(EXTRA_TARGET_PACKAGE, libPkg); + try { + executeShellCmd("am service-restart-backoff disable " + testPkg); + + latchHolder[0] = new CountDownLatch(1); + assertTrue("Unable to bind to test service in " + testPkg, + mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)); + assertTrue("Timed out to bind service in " + testPkg, + latchHolder[0].await(timeout, TimeUnit.MILLISECONDS)); + + Thread.sleep(shortTimeout); + assertTrue(libPkg + " should be a dependency package of " + testPkg, + isPackageDependency(testPkg, libPkg)); + + // Force-stop lib package, the test service should be restarted. + latchHolder[0] = new CountDownLatch(2); + am.forceStopPackage(libPkg); + assertTrue("Test service in didn't restart in " + testPkg, + latchHolder[0].await(timeout, TimeUnit.MILLISECONDS)); + + Thread.sleep(shortTimeout); + + // Re-install the lib package, the test service should be restarted. + latchHolder[0] = new CountDownLatch(2); + assertTrue("Unable to install package " + apkPath, installPackage(apkPath)); + assertTrue("Test service in didn't restart in " + testPkg, + latchHolder[0].await(timeout, TimeUnit.MILLISECONDS)); + + Thread.sleep(shortTimeout); + + // Force-stop the service package, the test service should not be restarted. + latchHolder[0] = new CountDownLatch(2); + am.forceStopPackage(testPkg); + assertFalse("Test service should not be restarted in " + testPkg, + latchHolder[0].await(timeout * 2, TimeUnit.MILLISECONDS)); + } finally { + executeShellCmd("am service-restart-backoff enable " + testPkg); + mContext.unbindService(conn); + am.forceStopPackage(testPkg); + } + } + + private boolean isPackageDependency(String pkgName, String libPackage) throws Exception { + final String output = SystemUtil.runShellCommand("dumpsys activity processes " + pkgName); + final Matcher matcher = Pattern.compile("packageDependencies=\\{.*?\\b" + + libPackage + "\\b.*?\\}").matcher(output); + return matcher.find(); + } + + private boolean installPackage(String apkPath) throws Exception { + return executeShellCmd("pm install -t " + apkPath).equals("Success\n"); + } + private void startServiceAndWait(String pkgName, MyUidImportanceListener uidListener, long timeout) throws Exception { final Intent intent = new Intent(); diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml index 78afb7b72c04..3cc105ebb746 100644 --- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml +++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml @@ -18,6 +18,7 @@ package="com.android.servicestests.apps.simpleservicetestapp"> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <application> <service android:name=".SimpleService" diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java index 4e981b22cd32..b8654d7f4e74 100644 --- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java +++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java @@ -17,8 +17,10 @@ package com.android.servicestests.apps.simpleservicetestapp; import android.app.Service; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.os.Bundle; import android.os.IBinder; import android.os.IRemoteCallback; @@ -33,6 +35,9 @@ public class SimpleService extends Service { private static final String TEST_CLASS = "com.android.servicestests.apps.simpleservicetestapp.SimpleService"; + private static final String ACTION_SERVICE_WITH_DEP_PKG = + "com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG"; + private static final String EXTRA_CALLBACK = "callback"; private static final String EXTRA_COMMAND = "command"; private static final String EXTRA_FLAGS = "flags"; @@ -118,6 +123,21 @@ public class SimpleService extends Service { @Override public IBinder onBind(Intent intent) { + if (ACTION_SERVICE_WITH_DEP_PKG.equals(intent.getAction())) { + final String targetPkg = intent.getStringExtra(EXTRA_TARGET_PACKAGE); + Log.i(TAG, "SimpleService.onBind: " + ACTION_SERVICE_WITH_DEP_PKG + " " + targetPkg); + if (targetPkg != null) { + Context pkgContext = null; + try { + pkgContext = createPackageContext(targetPkg, + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Unable to create package context for " + pkgContext, e); + } + // This effectively loads the target package as a dependency. + pkgContext.getClassLoader(); + } + } return mBinder; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 8c1045d995d6..bfaa8150cb99 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -899,22 +899,21 @@ public class TaskTests extends WindowTestsBase { /** * Test that root activity index is reported correctly when looking for the 'effective root' in - * case when bottom activity is finishing. Ignore the relinquishing task identity if it's not a - * system activity even with the FLAG_RELINQUISH_TASK_IDENTITY. + * case when bottom activities are relinquishing task identity or finishing. */ @Test public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() { - final Task task = getTestTask(); + final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = activity0.getTask(); // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and // one above as finishing. - final ActivityRecord activity0 = task.getBottomMostActivity(); activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); activity1.finishing = true; new ActivityBuilder(mAtm).setTask(task).build(); assertEquals("The first non-finishing activity and non-relinquishing task identity " - + "must be reported.", task.getChildAt(0), task.getRootActivity( + + "must be reported.", task.getChildAt(2), task.getRootActivity( false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); } @@ -934,21 +933,21 @@ public class TaskTests extends WindowTestsBase { } /** - * Test that the root activity index is reported correctly when looking for the - * 'effective root' for the case when all non-system activities have relinquishTaskIdentity set. + * Test that the topmost activity index is reported correctly when looking for the + * 'effective root' for the case when all activities have relinquishTaskIdentity set. */ @Test public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() { - final Task task = getTestTask(); + final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = activity0.getTask(); // Set relinquishTaskIdentity for all activities in the task - final ActivityRecord activity0 = task.getBottomMostActivity(); activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; - assertEquals("The topmost activity in the task must be reported.", task.getChildAt(0), - task.getRootActivity(false /*ignoreRelinquishIdentity*/, - true /*setToBottomIfNone*/)); + assertEquals("The topmost activity in the task must be reported.", + task.getChildAt(task.getChildCount() - 1), task.getRootActivity( + false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); } /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */ @@ -1086,14 +1085,14 @@ public class TaskTests extends WindowTestsBase { } /** - * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with non-system - * activity that relinquishes task identity. + * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that + * relinquishes task identity. */ @Test public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() { - final Task task = getTestTask(); + final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = activity0.getTask(); // Make the current root activity relinquish task identity - final ActivityRecord activity0 = task.getBottomMostActivity(); activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; // Add an extra activity on top - this will be the new root final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); @@ -1102,7 +1101,7 @@ public class TaskTests extends WindowTestsBase { assertEquals(task.mTaskId, ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */)); - assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, + assertEquals(task.mTaskId, ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */)); assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */)); @@ -1189,6 +1188,46 @@ public class TaskTests extends WindowTestsBase { verify(task).setIntent(eq(activity0)); } + /** + * Test {@link Task#updateEffectiveIntent()} when activity with relinquishTaskIdentity but + * another with different uid. This should make the task use the root activity when updating the + * intent. + */ + @Test + public void testUpdateEffectiveIntent_relinquishingWithDifferentUid() { + final ActivityRecord activity0 = new ActivityBuilder(mAtm) + .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build(); + final Task task = activity0.getTask(); + + // Add an extra activity on top + new ActivityBuilder(mAtm).setUid(11).setTask(task).build(); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity0)); + } + + /** + * Test {@link Task#updateEffectiveIntent()} with activities set as relinquishTaskIdentity. + * This should make the task use the topmost activity when updating the intent. + */ + @Test + public void testUpdateEffectiveIntent_relinquishingMultipleActivities() { + final ActivityRecord activity0 = new ActivityBuilder(mAtm) + .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build(); + final Task task = activity0.getTask(); + // Add an extra activity on top + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + + // Add an extra activity on top + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity2)); + } + @Test public void testSaveLaunchingStateWhenConfigurationChanged() { LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 9c9e41b64892..b8a14b8ea72e 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -5794,6 +5794,7 @@ public class CarrierConfigManager { sDefaults.putInt( KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG, 120000); + sDefaults.putAll(ImsServiceEntitlement.getDefaults()); sDefaults.putAll(Gps.getDefaults()); sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY, new int[] { |