diff options
255 files changed, 5357 insertions, 2346 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp index 2bd5aee0cd24..bb6538739c49 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -299,6 +299,7 @@ java_defaults { static_libs: [ // License notices from art module "art-notices-for-framework-stubs-jar", + "framework-res-package-jar", // Export package of framework-res ], errorprone: { javacflags: [ @@ -311,6 +312,15 @@ java_defaults { compile_dex: true, } +java_defaults { + name: "android_stubs_dists_default", + dist: { + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android.jar", + }, +} + java_library_static { name: "android_monolith_stubs_current", srcs: [ ":api-stubs-docs" ], @@ -346,7 +356,21 @@ java_library_static { name: "android_system_monolith_stubs_current", srcs: [ ":system-api-stubs-docs" ], static_libs: [ "private-stub-annotations-jar" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/system", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_system.jar", + }, + ], } java_library_static { @@ -378,14 +402,34 @@ java_library_static { name: "android_test_stubs_current", srcs: [ ":test-api-stubs-docs" ], static_libs: [ "private-stub-annotations-jar" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/test", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_test.jar", + }, + ], } java_library_static { name: "android_module_lib_stubs_current", srcs: [ ":module-lib-api-stubs-docs-non-updatable" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], libs: ["sdk_system_29_android"], + dist: { + dir: "apistubs/android/module-lib", + }, } java_library_static { diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml index c7929af6077f..fbe589248338 100644 --- a/apct-tests/perftests/multiuser/AndroidTest.xml +++ b/apct-tests/perftests/multiuser/AndroidTest.xml @@ -27,6 +27,9 @@ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config_detailed.textproto" value="/sdcard/sample.textproto" /> </target_preparer> <!-- Needed for pulling the collected trace config on to the host --> diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp index 9fa99853ace3..dbfe6ab3210f 100644 --- a/apct-tests/perftests/windowmanager/Android.bp +++ b/apct-tests/perftests/windowmanager/Android.bp @@ -23,6 +23,7 @@ android_test { "platform-test-annotations", ], test_suites: ["device-tests"], + data: [":perfetto_artifacts"], platform_apis: true, certificate: "platform", } diff --git a/apct-tests/perftests/windowmanager/AndroidTest.xml b/apct-tests/perftests/windowmanager/AndroidTest.xml index 0a80cf96fba3..aee02c7ab767 100644 --- a/apct-tests/perftests/windowmanager/AndroidTest.xml +++ b/apct-tests/perftests/windowmanager/AndroidTest.xml @@ -28,14 +28,42 @@ <option name="run-command" value="cmd package compile -m speed com.android.perftests.wm" /> </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + </target_preparer> + + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.perftests.wm" /> <option name="hidden-api-checks" value="false"/> - <option name="device-listeners" value="android.wm.WmPerfRunListener" /> + + <!-- Listener related args for collecting the traces and waiting for the device to stabilize. --> + <option name="device-listeners" value="android.wm.WmPerfRunListener,android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" /> + + <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. --> + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + + <!-- ProcLoadListener related arguments --> + <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run --> + <option name="instrumentation-arg" key="procload-collector:per_run" value="true" /> + <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" /> + <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" /> + <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" /> + + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" /> + <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" /> + + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/data/local/WmPerfTests" /> - <option name="collect-on-run-ended-only" value="true" /> + <option name="directory-keys" value="/data/local/tmp/WmPerfTests" /> + <!-- Needed for pulling the collected trace config on to the host --> + <option name="pull-pattern-keys" value="perfetto_file_path" /> </metrics_collector> </configuration> diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java index dc6245bf2c09..cc0e939a2a80 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java @@ -55,7 +55,7 @@ public class WindowManagerPerfTestBase { * is in /data because while enabling method profling of system server, it cannot write the * trace to external storage. */ - static final File BASE_OUT_PATH = new File("/data/local/WmPerfTests"); + static final File BASE_OUT_PATH = new File("/data/local/tmp/WmPerfTests"); @BeforeClass public static void setUpOnce() { diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 7cd8a628c9fc..17545a469cb8 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -46,6 +46,13 @@ package android.media.session { field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000 } + public final class MediaSessionManager { + method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); + method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + } + } package android.net { diff --git a/api/system-current.txt b/api/system-current.txt index ea16238316ac..3ab164554da6 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -12171,6 +12171,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/api/test-current.txt b/api/test-current.txt index a1d1fa781096..342bd81ac69b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1305,6 +1305,8 @@ package android.hardware.display { method public android.graphics.Point getStableDisplaySize(); method public boolean isMinimalPostProcessingRequested(int); method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); + field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200 + field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400 } } @@ -4618,6 +4620,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 6b335ee1b923..8b2c2da9396d 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -485,6 +485,9 @@ message Atom { NetworkTetheringReported network_tethering_reported = 303 [(module) = "network_tethering"]; ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"]; + UIInteractionFrameInfoReported ui_interaction_frame_info_reported = + 305 [(module) = "framework"]; + UIActionLatencyReported ui_action_latency_reported = 306 [(module) = "framework"]; // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. @@ -5054,6 +5057,54 @@ message BlobOpened{ optional Result result = 4; } +/** + * Event to track Jank for various system interactions. + * + * Logged from: + * frameworks/base/core/java/android/os/aot/FrameTracker.java + */ +message UIInteractionFrameInfoReported { + enum InteractionType { + UNKNOWN = 0; + NOTIFICATION_SHADE_SWIPE = 1; + } + + optional InteractionType interaction_type = 1; + + // Number of frames rendered during the interaction. + optional int64 total_frames = 2; + + // Number of frames that were skipped in rendering during the interaction. + optional int64 missed_frames = 3; + + // Maximum time it took to render a single frame during the interaction. + optional int64 max_frame_time_nanos = 4; +} + +/** + * Event to track various latencies in SystemUI. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/util/LatencyTracker.java + */ +message UIActionLatencyReported { + enum ActionType { + UNKNOWN = 0; + ACTION_EXPAND_PANEL = 1; + ACTION_TOGGLE_RECENTS = 2; + ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 3; + ACTION_CHECK_CREDENTIAL = 4; + ACTION_CHECK_CREDENTIAL_UNLOCKED = 5; + ACTION_TURN_ON_SCREEN = 6; + ACTION_ROTATE_SCREEN = 7; + ACTION_FACE_WAKE_AND_UNLOCK = 8; + } + + optional ActionType action = 1; + + optional int64 latency_millis = 2; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 98a23f2b0075..3cb6293f0706 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -105,7 +105,8 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd public ActivityView( @NonNull Context context, @NonNull AttributeSet attrs, int defStyle, boolean singleTaskInstance, boolean usePublicVirtualDisplay) { - this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, false); + this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, + false /* disableSurfaceViewBackgroundLayer */); } /** @hide */ @@ -113,12 +114,22 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd @NonNull Context context, @NonNull AttributeSet attrs, int defStyle, boolean singleTaskInstance, boolean usePublicVirtualDisplay, boolean disableSurfaceViewBackgroundLayer) { + this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, + disableSurfaceViewBackgroundLayer, false /* useTrustedDisplay */); + } + + // TODO(b/162901735): Refactor ActivityView with Builder + /** @hide */ + public ActivityView( + @NonNull Context context, @NonNull AttributeSet attrs, int defStyle, + boolean singleTaskInstance, boolean usePublicVirtualDisplay, + boolean disableSurfaceViewBackgroundLayer, boolean useTrustedDisplay) { super(context, attrs, defStyle); if (useTaskOrganizer()) { mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this); } else { mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, singleTaskInstance, - usePublicVirtualDisplay); + usePublicVirtualDisplay, useTrustedDisplay); } mSurfaceView = new SurfaceView(context, null, 0, 0, disableSurfaceViewBackgroundLayer); // Since ActivityView#getAlpha has been overridden, we should use parent class's alpha diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 54f3f1026050..6c6c04e4e975 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -413,7 +413,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { public final void disableLocal() { synchronized (mLock) { mDisabled = true; - mCache.clear(); + clear(); } } @@ -463,7 +463,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { cacheName(), mCache.size(), mLastSeenNonce, currentNonce)); } - mCache.clear(); + clear(); mLastSeenNonce = currentNonce; cachedResult = null; } @@ -728,9 +728,13 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * It's better to use explicit cork and uncork pairs that tighly surround big batches of * invalidations, but it's not always practical to tell where these invalidation batches * might occur. AutoCorker's time-based corking is a decent alternative. + * + * The auto-cork delay is configurable but it should not be too long. The purpose of + * the delay is to minimize the number of times a server writes to the system property + * when invalidating the cache. One write every 50ms does not hurt system performance. */ public static final class AutoCorker { - public static final int DEFAULT_AUTO_CORK_DELAY_MS = 2000; + public static final int DEFAULT_AUTO_CORK_DELAY_MS = 50; private final String mPropertyName; private final int mAutoCorkDelayMs; diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index bc418061e1d1..b0437ac7284e 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -101,14 +101,11 @@ public final class ApkAssets { public @interface FormatType {} @GuardedBy("this") - private final long mNativePtr; + private long mNativePtr; // final, except cleared in finalizer. @Nullable @GuardedBy("this") - private final StringBlock mStringBlock; - - @GuardedBy("this") - private boolean mOpen = true; + private final StringBlock mStringBlock; // null or closed if mNativePtr = 0. @PropertyFlags private final int mFlags; @@ -380,12 +377,16 @@ public final class ApkAssets { /** @hide */ @Nullable public OverlayableInfo getOverlayableInfo(String overlayableName) throws IOException { - return nativeGetOverlayableInfo(mNativePtr, overlayableName); + synchronized (this) { + return nativeGetOverlayableInfo(mNativePtr, overlayableName); + } } /** @hide */ public boolean definesOverlayable() throws IOException { - return nativeDefinesOverlayable(mNativePtr); + synchronized (this) { + return nativeDefinesOverlayable(mNativePtr); + } } /** @@ -412,12 +413,12 @@ public final class ApkAssets { */ public void close() { synchronized (this) { - if (mOpen) { - mOpen = false; + if (mNativePtr != 0) { if (mStringBlock != null) { mStringBlock.close(); } nativeDestroy(mNativePtr); + mNativePtr = 0; } } } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index c1ba2094d3cf..392f56212058 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -310,6 +310,7 @@ public final class DisplayManager { * @hide */ // TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors + @TestApi public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 9; /** @@ -320,6 +321,7 @@ public final class DisplayManager { * @see #VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS * @hide */ + @TestApi public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1 << 10; /** @hide */ diff --git a/core/java/android/hardware/face/FaceSensorProperties.java b/core/java/android/hardware/face/FaceSensorProperties.java index e3b2fbb6c614..1015724a485d 100644 --- a/core/java/android/hardware/face/FaceSensorProperties.java +++ b/core/java/android/hardware/face/FaceSensorProperties.java @@ -25,20 +25,36 @@ import android.os.Parcelable; */ public class FaceSensorProperties implements Parcelable { + /** + * A statically configured ID representing this sensor. Sensor IDs must be unique across all + * biometrics across the device, starting at 0, and in increments of 1. + */ public final int sensorId; + /** + * True if the sensor is able to perform generic face detection, without running the + * matching algorithm, and without affecting the lockout counter. + */ public final boolean supportsFaceDetection; + /** + * True if the sensor is able to provide self illumination in dark scenarios, without support + * from above the HAL. + */ + public final boolean supportsSelfIllumination; /** * Initializes SensorProperties with specified values */ - public FaceSensorProperties(int sensorId, boolean supportsFaceDetection) { + public FaceSensorProperties(int sensorId, boolean supportsFaceDetection, + boolean supportsSelfIllumination) { this.sensorId = sensorId; this.supportsFaceDetection = supportsFaceDetection; + this.supportsSelfIllumination = supportsSelfIllumination; } protected FaceSensorProperties(Parcel in) { sensorId = in.readInt(); supportsFaceDetection = in.readBoolean(); + supportsSelfIllumination = in.readBoolean(); } public static final Creator<FaceSensorProperties> CREATOR = @@ -63,5 +79,6 @@ public class FaceSensorProperties implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sensorId); dest.writeBoolean(supportsFaceDetection); + dest.writeBoolean(supportsSelfIllumination); } } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 1eb3fc11df7b..df1f1b21eba3 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -68,7 +68,7 @@ public class GraphicsEnvironment { private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1"; private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time"; private static final String METADATA_DRIVER_BUILD_TIME = - "com.android.graphics.updatabledriver.build_time"; + "com.android.graphics.driver.build_time"; private static final String METADATA_DEVELOPER_DRIVER_ENABLE = "com.android.graphics.developerdriver.enable"; private static final String METADATA_INJECT_LAYERS_ENABLE = @@ -878,9 +878,10 @@ public class GraphicsEnvironment { throw new NullPointerException("apk's meta-data cannot be null"); } - final String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME); - if (driverBuildTime == null || driverBuildTime.isEmpty()) { - Log.v(TAG, "com.android.graphics.updatabledriver.build_time is not set"); + String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME); + if (driverBuildTime == null || driverBuildTime.length() <= 1) { + Log.v(TAG, "com.android.graphics.driver.build_time is not set"); + driverBuildTime = "L0"; } // driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456. // Long.parseLong will throw if the meta-data "driver_build_time" is not set properly. diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 50cc764dd536..58c8efa3a972 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -102,6 +102,8 @@ public final class Trace { /** @hide */ public static final long TRACE_TAG_RRO = 1L << 26; /** @hide */ + public static final long TRACE_TAG_SYSPROP = 1L << 27; + /** @hide */ public static final long TRACE_TAG_APEX_MANAGER = 1L << 18; private static final long TRACE_TAG_NOT_READY = 1L << 63; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 73b33c20d296..6903a995bf7b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9037,6 +9037,13 @@ public final class Settings { "accessibility_magnification_capability"; /** + * Whether the Adaptive connectivity option is enabled. + * + * @hide + */ + public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled"; + + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. * diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java index 914169485979..6eb2a15eec44 100644 --- a/core/java/android/service/autofill/InlinePresentation.java +++ b/core/java/android/service/autofill/InlinePresentation.java @@ -40,6 +40,11 @@ public final class InlinePresentation implements Parcelable { /** * Represents the UI content and the action for the inline suggestion. + * + * <p>The Slice should be constructed using the Content builder provided in the androidx + * autofill library e.g. {@code androidx.autofill.inline.v1.InlineSuggestionUi.Content.Builder} + * and then converted to a Slice with + * {@code androidx.autofill.inline.UiVersions.Content#getSlice()}.</p> */ private final @NonNull Slice mSlice; @@ -90,6 +95,11 @@ public final class InlinePresentation implements Parcelable { * * @param slice * Represents the UI content and the action for the inline suggestion. + * + * <p>The Slice should be constructed using the Content builder provided in the androidx + * autofill library e.g. {@code androidx.autofill.inline.v1.InlineSuggestionUi.Content.Builder} + * and then converted to a Slice with + * {@code androidx.autofill.inline.UiVersions.Content#getSlice()}.</p> * @param inlinePresentationSpec * Specifies the UI specification for the inline suggestion. * @param pinned @@ -118,6 +128,11 @@ public final class InlinePresentation implements Parcelable { /** * Represents the UI content and the action for the inline suggestion. + * + * <p>The Slice should be constructed using the Content builder provided in the androidx + * autofill library e.g. {@code androidx.autofill.inline.v1.InlineSuggestionUi.Content.Builder} + * and then converted to a Slice with + * {@code androidx.autofill.inline.UiVersions.Content#getSlice()}.</p> */ @DataClass.Generated.Member public @NonNull Slice getSlice() { @@ -244,7 +259,7 @@ public final class InlinePresentation implements Parcelable { }; @DataClass.Generated( - time = 1593131904745L, + time = 1596484869201L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java", inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)") diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 7f45c044408a..403ac3ab29c0 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -629,7 +629,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); } - if (!mState.equals(state, true /* excludingCaptionInsets */, + if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */, true /* excludeInvisibleIme */)) { if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState); updateRequestedState(); @@ -1138,15 +1138,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (invokeCallback) { control.cancel(); } + boolean stateChanged = false; for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { RunningAnimation runningAnimation = mRunningAnimations.get(i); if (runningAnimation.runner == control) { mRunningAnimations.remove(i); ArraySet<Integer> types = toInternalType(control.getTypes()); for (int j = types.size() - 1; j >= 0; j--) { - if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) { - mHost.notifyInsetsChanged(); - } + stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished(); } if (invokeCallback && runningAnimation.startDispatched) { dispatchAnimationEnd(runningAnimation.runner.getAnimation()); @@ -1154,6 +1153,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation break; } } + if (stateChanged) { + mHost.notifyInsetsChanged(); + updateRequestedState(); + } } private void applyLocalVisibilityOverride() { diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 6b0b509932a8..593b37af26ad 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -60,6 +60,8 @@ import java.util.StringJoiner; */ public class InsetsState implements Parcelable { + public static final InsetsState EMPTY = new InsetsState(); + /** * Internal representation of inset source types. This is different from the public API in * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 28644858377a..7cc347d25458 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -294,7 +294,7 @@ public final class InputMethodInfo implements Parcelable { */ public InputMethodInfo(String packageName, String className, CharSequence label, String settingsActivity) { - this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */, + this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, false /* isVrOnly */); @@ -344,7 +344,7 @@ public final class InputMethodInfo implements Parcelable { mIsVrOnly = isVrOnly; } - private static ResolveInfo buildDummyResolveInfo(String packageName, String className, + private static ResolveInfo buildFakeResolveInfo(String packageName, String className, CharSequence label) { ResolveInfo ri = new ResolveInfo(); ServiceInfo si = new ServiceInfo(); diff --git a/core/java/android/widget/inline/InlinePresentationSpec.java b/core/java/android/widget/inline/InlinePresentationSpec.java index 5f924c6ae194..e7727fd9ff1d 100644 --- a/core/java/android/widget/inline/InlinePresentationSpec.java +++ b/core/java/android/widget/inline/InlinePresentationSpec.java @@ -42,8 +42,13 @@ public final class InlinePresentationSpec implements Parcelable { private final Size mMaxSize; /** - * The extras encoding the UI style information. Defaults to {@code Bundle.Empty} in which case - * the default system UI style will be used. + * The extras encoding the UI style information. + * + * <p>The style bundles can be created using the relevant Style classes and their builders in + * the androidx autofill library e.g. {@code androidx.autofill.inline.UiVersions.StylesBuilder}. + * </p> + * + * <p>The style must be set for the suggestion to render properly.</p> * * <p>Note: There should be no remote objects in the bundle, all included remote objects will * be removed from the bundle before transmission.</p> @@ -123,8 +128,13 @@ public final class InlinePresentationSpec implements Parcelable { } /** - * The extras encoding the UI style information. Defaults to {@code Bundle.Empty} in which case - * the default system UI style will be used. + * The extras encoding the UI style information. + * + * <p>The style bundles can be created using the relevant Style classes and their builders in + * the androidx autofill library e.g. {@code androidx.autofill.inline.UiVersions.StylesBuilder}. + * </p> + * + * <p>The style must be set for the suggestion to render properly.</p> * * <p>Note: There should be no remote objects in the bundle, all included remote objects will * be removed from the bundle before transmission.</p> @@ -264,8 +274,13 @@ public final class InlinePresentationSpec implements Parcelable { } /** - * The extras encoding the UI style information. Defaults to {@code Bundle.Empty} in which case - * the default system UI style will be used. + * The extras encoding the UI style information. + * + * <p>The style bundles can be created using the relevant Style classes and their builders in + * the androidx autofill library e.g. {@code androidx.autofill.inline.UiVersions.StylesBuilder}. + * </p> + * + * <p>The style must be set for the suggestion to render properly.</p> * * <p>Note: There should be no remote objects in the bundle, all included remote objects will * be removed from the bundle before transmission.</p> @@ -302,7 +317,7 @@ public final class InlinePresentationSpec implements Parcelable { } @DataClass.Generated( - time = 1588109681295L, + time = 1596485189661L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/widget/inline/InlinePresentationSpec.java", inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.NonNull android.os.Bundle mStyle\nprivate static @android.annotation.NonNull android.os.Bundle defaultStyle()\nprivate boolean styleEquals(android.os.Bundle)\npublic void filterContentTypes()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []") diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java index 9ccb4c172158..9013da36007e 100644 --- a/core/java/android/window/VirtualDisplayTaskEmbedder.java +++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java @@ -19,6 +19,7 @@ package android.window; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; import static android.view.Display.INVALID_DISPLAY; import android.app.ActivityManager; @@ -63,6 +64,7 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { private int mDisplayDensityDpi; private final boolean mSingleTaskInstance; private final boolean mUsePublicVirtualDisplay; + private final boolean mUseTrustedDisplay; private VirtualDisplay mVirtualDisplay; private Insets mForwardedInsets; private DisplayMetrics mTmpDisplayMetrics; @@ -77,10 +79,12 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { * only applicable if virtual displays are used */ public VirtualDisplayTaskEmbedder(Context context, VirtualDisplayTaskEmbedder.Host host, - boolean singleTaskInstance, boolean usePublicVirtualDisplay) { + boolean singleTaskInstance, boolean usePublicVirtualDisplay, + boolean useTrustedDisplay) { super(context, host); mSingleTaskInstance = singleTaskInstance; mUsePublicVirtualDisplay = usePublicVirtualDisplay; + mUseTrustedDisplay = useTrustedDisplay; } /** @@ -103,6 +107,9 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { if (mUsePublicVirtualDisplay) { virtualDisplayFlags |= VIRTUAL_DISPLAY_FLAG_PUBLIC; } + if (mUseTrustedDisplay) { + virtualDisplayFlags |= VIRTUAL_DISPLAY_FLAG_TRUSTED; + } mVirtualDisplay = displayManager.createVirtualDisplay( DISPLAY_NAME + "@" + System.identityHashCode(this), mHost.getWidth(), diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 053b06f3d407..2f8c45770eb5 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -347,7 +347,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { super(context); mLayoutInflater = LayoutInflater.from(context); mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(), - DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0; + DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0; } /** diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 541e018d079b..3d12d072eb80 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -181,6 +181,7 @@ message SecureSettingsProto { optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ]; repeated SettingProto completed_categories = 15; optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto adaptive_connectivity_enabled = 84 [ (android.privacy).dest = DEST_AUTOMATIC ]; message Controls { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -614,5 +615,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 84; + // Next tag = 85; } diff --git a/core/res/Android.bp b/core/res/Android.bp index b365de4f4630..f94a2b08e6c3 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -46,6 +46,13 @@ android_app { }, } +java_genrule { + name: "framework-res-package-jar", + srcs: [":framework-res{.export-package.apk}"], + out: ["framework-res-package.jar"], + cmd: "cp $(in) $(out)", +} + // This logic can be removed once robolectric's transition to binary resources is complete filegroup { name: "robolectric_framework_raw_res_files", diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 32c1e4a1411c..b16d4b264e4f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5040,6 +5040,10 @@ <permission android:name="android.permission.RESET_APP_ERRORS" android:protectionLevel="signature" /> + <!-- @hide Allows an application to create/destroy input consumer. --> + <permission android:name="android.permission.INPUT_CONSUMER" + android:protectionLevel="signature" /> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 613111d512be..5f2e4f905b1c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4212,15 +4212,20 @@ </integer-array> <!-- Messages that should not be shown to the user during face authentication, on - BiometricPrompt. This should be used to hide messages that may be too chatty or messages that - the user can't do much about. Entries are defined in - android.hardware.biometrics.face@1.0 types.hal --> + BiometricPrompt. This should be used to hide messages that may be too chatty or messages + that the user can't do much about. Entries are defined in + android.hardware.biometrics.face@1.0 types.hal --> <integer-array name="config_face_acquire_biometricprompt_ignorelist" translatable="false" > </integer-array> <!-- Same as the above, but are defined by vendorCodes --> <integer-array name="config_face_acquire_vendor_biometricprompt_ignorelist" translatable="false" > </integer-array> + <!-- True if the sensor is able to provide self illumination in dark secnarios, without support + from above the HAL. This configuration is only applicable to IBiometricsFace@1.0 and its + minor revisions. --> + <bool name="config_faceAuthSupportsSelfIllumination">true</bool> + <!-- If face auth sends the user directly to home/last open app, or stays on keyguard --> <bool name="config_faceAuthDismissesKeyguard">true</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 781e7cff4470..35ce780d3408 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2514,6 +2514,7 @@ <java-symbol type="array" name="config_face_acquire_vendor_keyguard_ignorelist" /> <java-symbol type="array" name="config_face_acquire_biometricprompt_ignorelist" /> <java-symbol type="array" name="config_face_acquire_vendor_biometricprompt_ignorelist" /> + <java-symbol type="bool" name="config_faceAuthSupportsSelfIllumination" /> <java-symbol type="bool" name="config_faceAuthDismissesKeyguard" /> <!-- Face config --> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 3c5d951f685e..e17c312dbffc 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -129,6 +129,7 @@ <!-- virtual display test permissions --> <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" /> <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" /> + <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" /> <!-- color extraction test permissions --> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java index daf613976358..0f6284d22d10 100644 --- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java +++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java @@ -247,6 +247,25 @@ public class VirtualDisplayTest extends AndroidTestCase { assertDisplayUnregistered(display); } + /** + * Ensures that an application can create a trusted virtual display with the permission + * {@code ADD_TRUSTED_DISPLAY}. + */ + public void testTrustedVirtualDisplay() throws Exception { + VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME, + WIDTH, HEIGHT, DENSITY, mSurface, + DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED); + assertNotNull("virtual display must not be null", virtualDisplay); + + Display display = virtualDisplay.getDisplay(); + try { + assertDisplayRegistered(display, Display.FLAG_PRIVATE | Display.FLAG_TRUSTED); + } finally { + virtualDisplay.release(); + } + assertDisplayUnregistered(display); + } + private void assertDisplayRegistered(Display display, int flags) { assertNotNull("display object must not be null", display); assertTrue("display must be valid", display.isValid()); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index af02b7bdbd90..de128ad6d78e 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -746,6 +746,20 @@ public class InsetsControllerTest { mController.onControlsChanged(createSingletonControl(ITYPE_IME)); assertEquals(newState.getSource(ITYPE_IME), mTestHost.getModifiedState().peekSource(ITYPE_IME)); + + // The modified frames cannot be updated if there is an animation. + mController.onControlsChanged(createSingletonControl(ITYPE_NAVIGATION_BAR)); + mController.hide(navigationBars()); + newState = new InsetsState(mController.getState(), true /* copySource */); + newState.getSource(ITYPE_NAVIGATION_BAR).getFrame().top--; + mController.onStateChanged(newState); + assertNotEquals(newState.getSource(ITYPE_NAVIGATION_BAR), + mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR)); + + // The modified frames can be updated while the animation is done. + mController.cancelExistingAnimations(); + assertEquals(newState.getSource(ITYPE_NAVIGATION_BAR), + mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR)); }); } diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index d0e688d3efc1..e122e0026aac 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -144,13 +144,6 @@ prebuilt_etc { } prebuilt_etc { - name: "allowed_privapp_com.android.car.floatingcardslauncher", - sub_dir: "permissions", - src: "com.android.car.floatingcardslauncher.xml", - filename_from_src: true, -} - -prebuilt_etc { name: "allowed_privapp_com.android.car.ui.paintbooth", sub_dir: "permissions", src: "com.android.car.ui.paintbooth.xml", diff --git a/data/etc/car/com.android.car.floatingcardslauncher.xml b/data/etc/car/com.android.car.floatingcardslauncher.xml deleted file mode 100644 index 2755fee4eb55..000000000000 --- a/data/etc/car/com.android.car.floatingcardslauncher.xml +++ /dev/null @@ -1,25 +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 - --> -<permissions> - <privapp-permissions package="com.android.car.floatingcardslauncher"> - <permission name="android.permission.ACTIVITY_EMBEDDING"/> - <permission name="android.permission.INTERACT_ACROSS_USERS"/> - <permission name="android.permission.MANAGE_USERS"/> - <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> - <permission name="android.permission.MODIFY_PHONE_STATE"/> - </privapp-permissions> -</permissions> diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 28d7911c771f..964640b106b9 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -3111,7 +3111,7 @@ public class Paint { @CriticalNative private static native boolean nGetFillPath(long paintPtr, long src, long dst); @CriticalNative - private static native long nSetShader(long paintPtr, long shader); + private static native void nSetShader(long paintPtr, long shader); @CriticalNative private static native long nSetColorFilter(long paintPtr, long filter); @CriticalNative diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 4d7e5dfea4f7..dfb4009b07e2 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -37,7 +37,6 @@ #include <androidfw/TypeWrappers.h> #include <cutils/atomic.h> #include <utils/ByteOrder.h> -#include <utils/Debug.h> #include <utils/Log.h> #include <utils/String16.h> #include <utils/String8.h> diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 9ae5ad97ed36..0b13754271b9 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -462,6 +462,13 @@ cc_defaults { "RenderNode.cpp", "RenderProperties.cpp", "RootRenderNode.cpp", + "shader/Shader.cpp", + "shader/BitmapShader.cpp", + "shader/ComposeShader.cpp", + "shader/LinearGradientShader.cpp", + "shader/RadialGradientShader.cpp", + "shader/RuntimeShader.cpp", + "shader/SweepGradientShader.cpp", "SkiaCanvas.cpp", "VectorDrawable.cpp", ], diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index 87244427a719..ab9b8b55a4cb 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -56,12 +56,6 @@ class AHBUploader : public RefBase { public: virtual ~AHBUploader() {} - // Called to start creation of the Vulkan and EGL contexts on another thread before we actually - // need to do an upload. - void initialize() { - onInitialize(); - } - void destroy() { std::lock_guard _lock{mLock}; LOG_ALWAYS_FATAL_IF(mPendingUploads, "terminate called while uploads in progress"); @@ -91,7 +85,6 @@ protected: sp<ThreadBase> mUploadThread = nullptr; private: - virtual void onInitialize() = 0; virtual void onIdle() = 0; virtual void onDestroy() = 0; @@ -141,7 +134,6 @@ private: class EGLUploader : public AHBUploader { private: - void onInitialize() override {} void onDestroy() override { mEglManager.destroy(); } @@ -231,62 +223,67 @@ private: class VkUploader : public AHBUploader { private: - void onInitialize() override { - std::lock_guard _lock{mLock}; - if (!mUploadThread) { - mUploadThread = new ThreadBase{}; - } - if (!mUploadThread->isRunning()) { - mUploadThread->start("GrallocUploadThread"); - } - - mUploadThread->queue().post([this]() { - std::lock_guard _lock{mVkLock}; - if (!mVulkanManager.hasVkContext()) { - mVulkanManager.initialize(); - } - }); - } void onDestroy() override { + std::lock_guard _lock{mVkLock}; mGrContext.reset(); - mVulkanManager.destroy(); + mVulkanManagerStrong.clear(); } void onIdle() override { - mGrContext.reset(); + onDestroy(); } - void onBeginUpload() override { - { - std::lock_guard _lock{mVkLock}; - if (!mVulkanManager.hasVkContext()) { - LOG_ALWAYS_FATAL_IF(mGrContext, - "GrContext exists with no VulkanManager for vulkan uploads"); - mUploadThread->queue().runSync([this]() { - mVulkanManager.initialize(); - }); - } - } - if (!mGrContext) { - GrContextOptions options; - mGrContext = mVulkanManager.createContext(options); - LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads"); - this->postIdleTimeoutCheck(); - } - } + void onBeginUpload() override {} bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, AHardwareBuffer* ahb) override { - ATRACE_CALL(); + bool uploadSucceeded = false; + mUploadThread->queue().runSync([this, &uploadSucceeded, bitmap, ahb]() { + ATRACE_CALL(); + std::lock_guard _lock{mVkLock}; + + renderthread::VulkanManager* vkManager = getVulkanManager(); + if (!vkManager->hasVkContext()) { + LOG_ALWAYS_FATAL_IF(mGrContext, + "GrContext exists with no VulkanManager for vulkan uploads"); + vkManager->initialize(); + } + + if (!mGrContext) { + GrContextOptions options; + mGrContext = vkManager->createContext(options, + renderthread::VulkanManager::ContextType::kUploadThread); + LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads"); + this->postIdleTimeoutCheck(); + } + + sk_sp<SkImage> image = + SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb); + mGrContext->submit(true); + + uploadSucceeded = (image.get() != nullptr); + }); + return uploadSucceeded; + } - std::lock_guard _lock{mLock}; + /* must be called on the upload thread after the vkLock has been acquired */ + renderthread::VulkanManager* getVulkanManager() { + if (!mVulkanManagerStrong) { + mVulkanManagerStrong = mVulkanManagerWeak.promote(); + + // create a new manager if we couldn't promote the weak ref + if (!mVulkanManagerStrong) { + mVulkanManagerStrong = renderthread::VulkanManager::getInstance(); + mGrContext.reset(); + mVulkanManagerWeak = mVulkanManagerStrong; + } + } - sk_sp<SkImage> image = - SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb); - return (image.get() != nullptr); + return mVulkanManagerStrong.get(); } sk_sp<GrDirectContext> mGrContext; - renderthread::VulkanManager mVulkanManager; + sp<renderthread::VulkanManager> mVulkanManagerStrong; + wp<renderthread::VulkanManager> mVulkanManagerWeak; std::mutex mVkLock; }; @@ -428,7 +425,6 @@ void HardwareBitmapUploader::initialize() { bool usingGL = uirenderer::Properties::getRenderPipelineType() == uirenderer::RenderPipelineType::SkiaGL; createUploader(usingGL); - sUploader->initialize(); } void HardwareBitmapUploader::terminate() { diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 242b8b0d139e..cfba5d4f6aa2 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -42,6 +42,8 @@ #include <SkTextBlob.h> #include <SkVertices.h> +#include <shader/BitmapShader.h> + #include <memory> #include <optional> #include <utility> @@ -49,6 +51,7 @@ namespace android { using uirenderer::PaintUtils; +using uirenderer::BitmapShader; Canvas* Canvas::create_canvas(const SkBitmap& bitmap) { return new SkiaCanvas(bitmap); @@ -681,7 +684,9 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, if (paint) { pnt = *paint; } - pnt.setShader(bitmap.makeImage()->makeShader()); + + pnt.setShader(sk_ref_sp(new BitmapShader( + bitmap.makeImage(), SkTileMode::kClamp, SkTileMode::kClamp, nullptr))); auto v = builder.detach(); apply_looper(&pnt, [&](const SkPaint& p) { mCanvas->drawVertices(v, SkBlendMode::kModulate, p); diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index cd908354aea5..0f566e4b494a 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -23,7 +23,6 @@ #include "PathParser.h" #include "SkColorFilter.h" #include "SkImageInfo.h" -#include "SkShader.h" #include "hwui/Paint.h" #ifdef __ANDROID__ @@ -159,10 +158,10 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) { // Draw path's fill, if fill color or gradient is valid bool needsFill = false; - SkPaint paint; + Paint paint; if (properties.getFillGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha())); - paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getFillGradient()))); + paint.setShader(sk_sp<Shader>(SkSafeRef(properties.getFillGradient()))); needsFill = true; } else if (properties.getFillColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha())); @@ -179,7 +178,7 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) { bool needsStroke = false; if (properties.getStrokeGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha())); - paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getStrokeGradient()))); + paint.setShader(sk_sp<Shader>(SkSafeRef(properties.getStrokeGradient()))); needsStroke = true; } else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha())); diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index ac7d41e0d600..d4086f1aa622 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -31,8 +31,8 @@ #include <SkPath.h> #include <SkPathMeasure.h> #include <SkRect.h> -#include <SkShader.h> #include <SkSurface.h> +#include <shader/Shader.h> #include <cutils/compiler.h> #include <stddef.h> @@ -227,20 +227,20 @@ public: strokeGradient = prop.strokeGradient; onPropertyChanged(); } - void setFillGradient(SkShader* gradient) { + void setFillGradient(Shader* gradient) { if (fillGradient.get() != gradient) { fillGradient = sk_ref_sp(gradient); onPropertyChanged(); } } - void setStrokeGradient(SkShader* gradient) { + void setStrokeGradient(Shader* gradient) { if (strokeGradient.get() != gradient) { strokeGradient = sk_ref_sp(gradient); onPropertyChanged(); } } - SkShader* getFillGradient() const { return fillGradient.get(); } - SkShader* getStrokeGradient() const { return strokeGradient.get(); } + Shader* getFillGradient() const { return fillGradient.get(); } + Shader* getStrokeGradient() const { return strokeGradient.get(); } float getStrokeWidth() const { return mPrimitiveFields.strokeWidth; } void setStrokeWidth(float strokeWidth) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth); @@ -320,8 +320,8 @@ public: count, }; PrimitiveFields mPrimitiveFields; - sk_sp<SkShader> fillGradient; - sk_sp<SkShader> strokeGradient; + sk_sp<Shader> fillGradient; + sk_sp<Shader> strokeGradient; }; // Called from UI thread diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index c138a32eacc2..2a377bbb83f2 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -73,7 +73,7 @@ void Canvas::drawTextDecorations(float x, float y, float length, const Paint& pa static void simplifyPaint(int color, Paint* paint) { paint->setColor(color); - paint->setShader(nullptr); + paint->setShader((sk_sp<uirenderer::Shader>)nullptr); paint->setColorFilter(nullptr); paint->setLooper(nullptr); paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize()); diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index e75e9e7c6933..0bb689c19079 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -30,6 +30,8 @@ #include <minikin/FamilyVariant.h> #include <minikin/Hyphenator.h> +#include <shader/Shader.h> + namespace android { class Paint : public SkPaint { @@ -149,8 +151,14 @@ public: // The only respected flags are : [ antialias, dither, filterBitmap ] static uint32_t GetSkPaintJavaFlags(const SkPaint&); static void SetSkPaintJavaFlags(SkPaint*, uint32_t flags); + + void setShader(sk_sp<uirenderer::Shader> shader); private: + + using SkPaint::setShader; + using SkPaint::setImageFilter; + SkFont mFont; sk_sp<SkDrawLooper> mLooper; @@ -169,6 +177,7 @@ private: bool mStrikeThru = false; bool mUnderline = false; bool mDevKern = false; + sk_sp<uirenderer::Shader> mShader; }; } // namespace android diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index fa2674fc2f5e..21f60fd7b671 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -24,7 +24,8 @@ Paint::Paint() , mWordSpacing(0) , mFontFeatureSettings() , mMinikinLocaleListId(0) - , mFamilyVariant(minikin::FamilyVariant::DEFAULT) { + , mFamilyVariant(minikin::FamilyVariant::DEFAULT) + , mShader(nullptr) { // SkPaint::antialiasing defaults to false, but // SkFont::edging defaults to kAntiAlias. To keep them // insync, we manually set the font to kAilas. @@ -45,7 +46,8 @@ Paint::Paint(const Paint& paint) , mAlign(paint.mAlign) , mStrikeThru(paint.mStrikeThru) , mUnderline(paint.mUnderline) - , mDevKern(paint.mDevKern) {} + , mDevKern(paint.mDevKern) + , mShader(paint.mShader){} Paint::~Paint() {} @@ -65,9 +67,30 @@ Paint& Paint::operator=(const Paint& other) { mStrikeThru = other.mStrikeThru; mUnderline = other.mUnderline; mDevKern = other.mDevKern; + mShader = other.mShader; return *this; } +void Paint::setShader(sk_sp<uirenderer::Shader> shader) { + if (shader) { + // If there is an SkShader compatible shader, apply it + sk_sp<SkShader> skShader = shader->asSkShader(); + if (skShader.get()) { + SkPaint::setShader(skShader); + SkPaint::setImageFilter(nullptr); + } else { + // ... otherwise the specified shader can only be represented as an ImageFilter + SkPaint::setShader(nullptr); + SkPaint::setImageFilter(shader->asSkImageFilter()); + } + } else { + // No shader is provided at all, clear out both the SkShader and SkImageFilter slots + SkPaint::setShader(nullptr); + SkPaint::setImageFilter(nullptr); + } + mShader = shader; +} + bool operator==(const Paint& a, const Paint& b) { return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && a.mFont == b.mFont && diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index df8635a8fe5a..554674a331cd 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -47,6 +47,7 @@ #include <minikin/LocaleList.h> #include <minikin/Measurement.h> #include <minikin/MinikinPaint.h> +#include <shader/Shader.h> #include <unicode/utf16.h> #include <cassert> @@ -54,6 +55,8 @@ #include <memory> #include <vector> +using namespace android::uirenderer; + namespace android { struct JMetricsID { @@ -782,11 +785,10 @@ namespace PaintGlue { return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE; } - static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) { - Paint* obj = reinterpret_cast<Paint*>(objHandle); - SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle); - obj->setShader(sk_ref_sp(shader)); - return reinterpret_cast<jlong>(obj->getShader()); + static void setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) { + auto* paint = reinterpret_cast<Paint*>(objHandle); + auto* shader = reinterpret_cast<Shader*>(shaderHandle); + paint->setShader(sk_ref_sp(shader)); } static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) { @@ -1097,7 +1099,7 @@ static const JNINativeMethod methods[] = { {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin}, {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin}, {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath}, - {"nSetShader","(JJ)J", (void*) PaintGlue::setShader}, + {"nSetShader","(JJ)V", (void*) PaintGlue::setShader}, {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter}, {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode}, {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect}, diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index e76aace601be..9b1972ed664f 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -5,6 +5,13 @@ #include "SkShader.h" #include "SkBlendMode.h" #include "include/effects/SkRuntimeEffect.h" +#include "shader/Shader.h" +#include "shader/BitmapShader.h" +#include "shader/ComposeShader.h" +#include "shader/LinearGradientShader.h" +#include "shader/RadialGradientShader.h" +#include "shader/RuntimeShader.h" +#include "shader/SweepGradientShader.h" #include <vector> @@ -50,7 +57,7 @@ static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvAr /////////////////////////////////////////////////////////////////////////////////////////////// -static void Shader_safeUnref(SkShader* shader) { +static void Shader_safeUnref(Shader* shader) { SkSafeUnref(shader); } @@ -74,15 +81,15 @@ static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, j SkBitmap bitmap; image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); } - sk_sp<SkShader> shader = image->makeShader( - (SkTileMode)tileModeX, (SkTileMode)tileModeY); - ThrowIAE_IfNull(env, shader.get()); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* shader = new BitmapShader( + image, + static_cast<SkTileMode>(tileModeX), + static_cast<SkTileMode>(tileModeY), + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -118,17 +125,18 @@ static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr, #error Need to convert float array to SkScalar array before calling the following function. #endif - sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0], - GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), - static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr)); - ThrowIAE_IfNull(env, shader); - - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + auto* shader = new LinearGradientShader( + pts, + colors, + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), + pos, + static_cast<SkTileMode>(tileMode), + sGradientShaderFlags, + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -148,17 +156,20 @@ static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat #error Need to convert float array to SkScalar array before calling the following function. #endif - sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0], - GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), - static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr); - ThrowIAE_IfNull(env, shader); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* shader = new RadialGradientShader( + center, + radius, + colors, + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), + pos, + static_cast<SkTileMode>(tileMode), + sGradientShaderFlags, + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////// @@ -174,54 +185,58 @@ static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat #error Need to convert float array to SkScalar array before calling the following function. #endif - sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0], - GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), - sGradientShaderFlags, nullptr); - ThrowIAE_IfNull(env, shader); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* shader = new SweepGradientShader( + x, + y, + colors, + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), + pos, + sGradientShaderFlags, + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr, jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) { - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle); - SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle); - SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle); - sk_sp<SkShader> baseShader(SkShaders::Blend(mode, - sk_ref_sp(shaderA), sk_ref_sp(shaderB))); - - SkShader* shader; - - if (matrix) { - shader = baseShader->makeWithLocalMatrix(*matrix).release(); - } else { - shader = baseShader.release(); - } - return reinterpret_cast<jlong>(shader); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + auto* shaderA = reinterpret_cast<Shader*>(shaderAHandle); + auto* shaderB = reinterpret_cast<Shader*>(shaderBHandle); + + auto mode = static_cast<SkBlendMode>(xfermodeHandle); + + auto* composeShader = new ComposeShader( + *shaderA, + *shaderB, + mode, + matrix + ); + + return reinterpret_cast<jlong>(composeShader); } /////////////////////////////////////////////////////////////////////////////////////////////// static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr, jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) { - SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory); + auto* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory); AutoJavaByteArray arInputs(env, inputs); - sk_sp<SkData> fData; - fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length()); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE); - ThrowIAE_IfNull(env, shader); + auto data = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length()); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - return reinterpret_cast<jlong>(shader.release()); + auto* shader = new RuntimeShader( + *effect, + std::move(data), + isOpaque == JNI_TRUE, + matrix + ); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -239,12 +254,8 @@ static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sks /////////////////////////////////////////////////////////////////////////////////////////////// -static void Effect_safeUnref(SkRuntimeEffect* effect) { - SkSafeUnref(effect); -} - static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) { - return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref)); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref)); } /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index fc594da19708..e817ca744c58 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -184,7 +184,9 @@ static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz, proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); } proxy->setSurface(window, enableTimeout); - ANativeWindow_release(window); + if (window) { + ANativeWindow_release(window); + } } static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz, diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp index 9cffceb308c8..a1adcb30e80d 100644 --- a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp @@ -143,13 +143,13 @@ static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong full static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) { VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr); - SkShader* fillShader = reinterpret_cast<SkShader*>(fillGradientPtr); + auto* fillShader = reinterpret_cast<Shader*>(fillGradientPtr); path->mutateStagingProperties()->setFillGradient(fillShader); } static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) { VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr); - SkShader* strokeShader = reinterpret_cast<SkShader*>(strokeGradientPtr); + auto* strokeShader = reinterpret_cast<Shader*>(strokeGradientPtr); path->mutateStagingProperties()->setStrokeGradient(strokeShader); } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index aad0cca80cdc..b51f6dcfc66f 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -77,10 +77,10 @@ void RenderProxy::setName(const char* name) { } void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { - ANativeWindow_acquire(window); + if (window) { ANativeWindow_acquire(window); } mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { mContext->setSurface(win, enableTimeout); - ANativeWindow_release(win); + if (win) { ANativeWindow_release(win); } }); } diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 565fb61c8994..4dcbc4458e97 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -131,8 +131,7 @@ RenderThread::RenderThread() , mFrameCallbackTaskPending(false) , mRenderState(nullptr) , mEglManager(nullptr) - , mFunctorManager(WebViewFunctorManager::instance()) - , mVkManager(nullptr) { + , mFunctorManager(WebViewFunctorManager::instance()) { Properties::load(); start("RenderThread"); } @@ -166,7 +165,7 @@ void RenderThread::initThreadLocals() { initializeChoreographer(); mEglManager = new EglManager(); mRenderState = new RenderState(*this); - mVkManager = new VulkanManager(); + mVkManager = VulkanManager::getInstance(); mCacheManager = new CacheManager(); } @@ -196,7 +195,8 @@ void RenderThread::requireGlContext() { } void RenderThread::requireVkContext() { - if (mVkManager->hasVkContext()) { + // the getter creates the context in the event it had been destroyed by destroyRenderingContext + if (vulkanManager().hasVkContext()) { return; } mVkManager->initialize(); @@ -222,11 +222,16 @@ void RenderThread::destroyRenderingContext() { mEglManager->destroy(); } } else { - if (vulkanManager().hasVkContext()) { - setGrContext(nullptr); - vulkanManager().destroy(); - } + setGrContext(nullptr); + mVkManager.clear(); + } +} + +VulkanManager& RenderThread::vulkanManager() { + if (!mVkManager.get()) { + mVkManager = VulkanManager::getInstance(); } + return *mVkManager.get(); } void RenderThread::dumpGraphicsMemory(int fd) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index b8ce55650516..d7dc00b8a5c1 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -110,7 +110,7 @@ public: void setGrContext(sk_sp<GrDirectContext> cxt); CacheManager& cacheManager() { return *mCacheManager; } - VulkanManager& vulkanManager() { return *mVkManager; } + VulkanManager& vulkanManager(); sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap); void dumpGraphicsMemory(int fd); @@ -188,7 +188,7 @@ private: sk_sp<GrDirectContext> mGrContext; CacheManager* mCacheManager; - VulkanManager* mVkManager; + sp<VulkanManager> mVkManager; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 249936eb485e..0c5cf682e566 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -57,12 +57,22 @@ static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& fe #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) -void VulkanManager::destroy() { - if (VK_NULL_HANDLE != mCommandPool) { - mDestroyCommandPool(mDevice, mCommandPool, nullptr); - mCommandPool = VK_NULL_HANDLE; +sp<VulkanManager> VulkanManager::getInstance() { + // cache a weakptr to the context to enable a second thread to share the same vulkan state + static wp<VulkanManager> sWeakInstance = nullptr; + static std::mutex sLock; + + std::lock_guard _lock{sLock}; + sp<VulkanManager> vulkanManager = sWeakInstance.promote(); + if (!vulkanManager.get()) { + vulkanManager = new VulkanManager(); + sWeakInstance = vulkanManager; } + return vulkanManager; +} + +VulkanManager::~VulkanManager() { if (mDevice != VK_NULL_HANDLE) { mDeviceWaitIdle(mDevice); mDestroyDevice(mDevice, nullptr); @@ -73,6 +83,7 @@ void VulkanManager::destroy() { } mGraphicsQueue = VK_NULL_HANDLE; + mAHBUploadQueue = VK_NULL_HANDLE; mPresentQueue = VK_NULL_HANDLE; mDevice = VK_NULL_HANDLE; mPhysicalDevice = VK_NULL_HANDLE; @@ -175,6 +186,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe for (uint32_t i = 0; i < queueCount; i++) { if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { mGraphicsQueueIndex = i; + LOG_ALWAYS_FATAL_IF(queueProps[i].queueCount < 2); break; } } @@ -283,7 +295,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe queueNextPtr, // pNext 0, // VkDeviceQueueCreateFlags mGraphicsQueueIndex, // queueFamilyIndex - 1, // queueCount + 2, // queueCount queuePriorities, // pQueuePriorities }, { @@ -347,6 +359,7 @@ void VulkanManager::initialize() { this->setupDevice(mExtensions, mPhysicalDeviceFeatures2); mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); + mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue); // create the command pool for the command buffers if (VK_NULL_HANDLE == mCommandPool) { @@ -369,7 +382,8 @@ void VulkanManager::initialize() { } } -sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options) { +sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options, + ContextType contextType) { auto getProc = [](const char* proc_name, VkInstance instance, VkDevice device) { if (device != VK_NULL_HANDLE) { return vkGetDeviceProcAddr(device, proc_name); @@ -381,7 +395,8 @@ sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& opti backendContext.fInstance = mInstance; backendContext.fPhysicalDevice = mPhysicalDevice; backendContext.fDevice = mDevice; - backendContext.fQueue = mGraphicsQueue; + backendContext.fQueue = (contextType == ContextType::kRenderThread) ? mGraphicsQueue + : mAHBUploadQueue; backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; backendContext.fMaxAPIVersion = mAPIVersion; backendContext.fVkExtensions = &mExtensions; diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 3f2df8d75d89..13335f32ef06 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -43,10 +43,9 @@ class RenderThread; // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue, // which are re-used by CanvasContext. This class is created once and should be used by all vulkan // windowing contexts. The VulkanManager must be initialized before use. -class VulkanManager { +class VulkanManager final : public RefBase { public: - explicit VulkanManager() {} - ~VulkanManager() { destroy(); } + static sp<VulkanManager> getInstance(); // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must // be call once before use of the VulkanManager. Multiple calls after the first will simiply @@ -68,9 +67,6 @@ public: Frame dequeueNextBuffer(VulkanSurface* surface); void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect); - // Cleans up all the global state in the VulkanManger. - void destroy(); - // Inserts a wait on fence command into the Vulkan command buffer. status_t fenceWait(int fence, GrDirectContext* grContext); @@ -83,12 +79,24 @@ public: // the internal state of VulkanManager: VulkanManager must be alive to use the returned value. VkFunctorInitParams getVkFunctorInitParams() const; - sk_sp<GrDirectContext> createContext(const GrContextOptions& options); + + enum class ContextType { + kRenderThread, + kUploadThread + }; + + // returns a Skia graphic context used to draw content on the specified thread + sk_sp<GrDirectContext> createContext(const GrContextOptions& options, + ContextType contextType = ContextType::kRenderThread); uint32_t getDriverVersion() const { return mDriverVersion; } private: friend class VulkanSurface; + + explicit VulkanManager() {} + ~VulkanManager(); + // Sets up the VkInstance and VkDevice objects. Also fills out the passed in // VkPhysicalDeviceFeatures struct. void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&); @@ -154,6 +162,7 @@ private: uint32_t mGraphicsQueueIndex; VkQueue mGraphicsQueue = VK_NULL_HANDLE; + VkQueue mAHBUploadQueue = VK_NULL_HANDLE; uint32_t mPresentQueueIndex; VkQueue mPresentQueue = VK_NULL_HANDLE; VkCommandPool mCommandPool = VK_NULL_HANDLE; diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/dagger/OneHandedModule.java b/libs/hwui/shader/BitmapShader.cpp index fe5fa2b5fccd..fe653e85a021 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/dagger/OneHandedModule.java +++ b/libs/hwui/shader/BitmapShader.cpp @@ -14,23 +14,18 @@ * limitations under the License. */ -package com.android.systemui.onehanded.dagger; +#include "BitmapShader.h" -import com.android.systemui.onehanded.OneHandedManager; -import com.android.systemui.onehanded.OneHandedManagerImpl; +#include "SkImagePriv.h" -import dagger.Binds; -import dagger.Module; - -/** - * Dagger Module for One handed. - */ -@Module -public abstract class OneHandedModule { - - /** Binds OneHandedManager as the default. */ - @Binds - public abstract OneHandedManager provideOneHandedManager( - OneHandedManagerImpl oneHandedManagerImpl); +namespace android::uirenderer { +BitmapShader::BitmapShader(const sk_sp<SkImage>& image, const SkTileMode tileModeX, + const SkTileMode tileModeY, const SkMatrix* matrix) + : Shader(matrix), skShader(image->makeShader(tileModeX, tileModeY)) {} +sk_sp<SkShader> BitmapShader::makeSkShader() { + return skShader; } + +BitmapShader::~BitmapShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/BitmapShader.h b/libs/hwui/shader/BitmapShader.h new file mode 100644 index 000000000000..ed6a6e6802d1 --- /dev/null +++ b/libs/hwui/shader/BitmapShader.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a Bitmap as either a SkShader or SkImageFilter + */ +class BitmapShader : public Shader { +public: + BitmapShader(const sk_sp<SkImage>& image, const SkTileMode tileModeX, + const SkTileMode tileModeY, const SkMatrix* matrix); + ~BitmapShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/ComposeShader.cpp b/libs/hwui/shader/ComposeShader.cpp new file mode 100644 index 000000000000..3765489e7431 --- /dev/null +++ b/libs/hwui/shader/ComposeShader.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ComposeShader.h" + +#include "SkImageFilters.h" +#include "SkShader.h" + +namespace android::uirenderer { + +ComposeShader::ComposeShader(Shader& shaderA, Shader& shaderB, const SkBlendMode blendMode, + const SkMatrix* matrix) + : Shader(matrix) { + // If both Shaders can be represented as SkShaders then use those, if not + // create an SkImageFilter from both Shaders and create the equivalent SkImageFilter + sk_sp<SkShader> skShaderA = shaderA.asSkShader(); + sk_sp<SkShader> skShaderB = shaderB.asSkShader(); + if (skShaderA.get() && skShaderB.get()) { + skShader = SkShaders::Blend(blendMode, skShaderA, skShaderB); + skImageFilter = nullptr; + } else { + sk_sp<SkImageFilter> skImageFilterA = shaderA.asSkImageFilter(); + sk_sp<SkImageFilter> skImageFilterB = shaderB.asSkImageFilter(); + skShader = nullptr; + skImageFilter = SkImageFilters::Xfermode(blendMode, skImageFilterA, skImageFilterB); + } +} + +sk_sp<SkShader> ComposeShader::makeSkShader() { + return skShader; +} + +sk_sp<SkImageFilter> ComposeShader::makeSkImageFilter() { + return skImageFilter; +} + +ComposeShader::~ComposeShader() {} +} // namespace android::uirenderer diff --git a/libs/hwui/shader/ComposeShader.h b/libs/hwui/shader/ComposeShader.h new file mode 100644 index 000000000000..a246b520d46a --- /dev/null +++ b/libs/hwui/shader/ComposeShader.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that can composite 2 Shaders together with the specified blend mode. + * This implementation can appropriately convert the composed result to either an SkShader or + * SkImageFilter depending on the inputs + */ +class ComposeShader : public Shader { +public: + ComposeShader(Shader& shaderA, Shader& shaderB, const SkBlendMode blendMode, + const SkMatrix* matrix); + ~ComposeShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + sk_sp<SkImageFilter> makeSkImageFilter() override; + +private: + sk_sp<SkShader> skShader; + sk_sp<SkImageFilter> skImageFilter; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/LinearGradientShader.cpp b/libs/hwui/shader/LinearGradientShader.cpp new file mode 100644 index 000000000000..868fa44fb4b7 --- /dev/null +++ b/libs/hwui/shader/LinearGradientShader.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LinearGradientShader.h" + +#include <vector> + +#include "SkGradientShader.h" + +namespace android::uirenderer { + +LinearGradientShader::LinearGradientShader(const SkPoint pts[2], + const std::vector<SkColor4f>& colors, + sk_sp<SkColorSpace> colorspace, const SkScalar pos[], + const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix) + : Shader(matrix) + , skShader(SkGradientShader::MakeLinear(pts, colors.data(), colorspace, pos, colors.size(), + tileMode, shaderFlags, nullptr)) {} + +sk_sp<SkShader> LinearGradientShader::makeSkShader() { + return skShader; +} + +LinearGradientShader::~LinearGradientShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/LinearGradientShader.h b/libs/hwui/shader/LinearGradientShader.h new file mode 100644 index 000000000000..596f4e009448 --- /dev/null +++ b/libs/hwui/shader/LinearGradientShader.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a color ramp of colors to either as either SkShader or + * SkImageFilter + */ +class LinearGradientShader : public Shader { +public: + LinearGradientShader(const SkPoint pts[2], const std::vector<SkColor4f>& colors, + sk_sp<SkColorSpace> colorspace, const SkScalar pos[], + const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix); + ~LinearGradientShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/RadialGradientShader.cpp b/libs/hwui/shader/RadialGradientShader.cpp new file mode 100644 index 000000000000..21ff56fee2f8 --- /dev/null +++ b/libs/hwui/shader/RadialGradientShader.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "RadialGradientShader.h" + +#include <vector> + +#include "SkGradientShader.h" + +namespace android::uirenderer { + +RadialGradientShader::RadialGradientShader(const SkPoint& center, const float radius, + const std::vector<SkColor4f>& colors, + sk_sp<SkColorSpace> colorspace, const SkScalar pos[], + const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix) + : Shader(matrix) + , skShader(SkGradientShader::MakeRadial(center, radius, colors.data(), colorspace, pos, + colors.size(), tileMode, shaderFlags, nullptr)) {} + +sk_sp<SkShader> RadialGradientShader::makeSkShader() { + return skShader; +} + +RadialGradientShader::~RadialGradientShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/RadialGradientShader.h b/libs/hwui/shader/RadialGradientShader.h new file mode 100644 index 000000000000..9a2ff139aedb --- /dev/null +++ b/libs/hwui/shader/RadialGradientShader.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a color ramp from the center outward to either as either + * a SkShader or SkImageFilter + */ +class RadialGradientShader : public Shader { +public: + RadialGradientShader(const SkPoint& center, const float radius, + const std::vector<SkColor4f>& colors, sk_sp<SkColorSpace> colorSpace, + const SkScalar pos[], const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix); + ~RadialGradientShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/RuntimeShader.cpp b/libs/hwui/shader/RuntimeShader.cpp new file mode 100644 index 000000000000..dd0b6980841a --- /dev/null +++ b/libs/hwui/shader/RuntimeShader.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RuntimeShader.h" + +#include "SkShader.h" +#include "include/effects/SkRuntimeEffect.h" + +namespace android::uirenderer { + +RuntimeShader::RuntimeShader(SkRuntimeEffect& effect, sk_sp<SkData> data, bool isOpaque, + const SkMatrix* matrix) + : Shader(nullptr) + , // Explicitly passing null as RuntimeShader is created with the + // matrix directly + skShader(effect.makeShader(std::move(data), nullptr, 0, matrix, isOpaque)) {} + +sk_sp<SkShader> RuntimeShader::makeSkShader() { + return skShader; +} + +RuntimeShader::~RuntimeShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/RuntimeShader.h b/libs/hwui/shader/RuntimeShader.h new file mode 100644 index 000000000000..7fe0b0206467 --- /dev/null +++ b/libs/hwui/shader/RuntimeShader.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" +#include "include/effects/SkRuntimeEffect.h" + +namespace android::uirenderer { + +/** + * RuntimeShader implementation that can map to either a SkShader or SkImageFilter + */ +class RuntimeShader : public Shader { +public: + RuntimeShader(SkRuntimeEffect& effect, sk_sp<SkData> data, bool isOpaque, + const SkMatrix* matrix); + ~RuntimeShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/Shader.cpp b/libs/hwui/shader/Shader.cpp new file mode 100644 index 000000000000..45123dd55002 --- /dev/null +++ b/libs/hwui/shader/Shader.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Shader.h" + +#include "SkImageFilters.h" +#include "SkPaint.h" +#include "SkRefCnt.h" + +namespace android::uirenderer { + +Shader::Shader(const SkMatrix* matrix) + : localMatrix(matrix ? *matrix : SkMatrix::I()) + , skShader(nullptr) + , skImageFilter(nullptr) {} + +Shader::~Shader() {} + +sk_sp<SkShader> Shader::asSkShader() { + // If we already have created a shader with these parameters just return the existing + // shader we have already created + if (!this->skShader.get()) { + this->skShader = makeSkShader(); + if (this->skShader.get()) { + if (!localMatrix.isIdentity()) { + this->skShader = this->skShader->makeWithLocalMatrix(localMatrix); + } + } + } + return this->skShader; +} + +/** + * By default return null as we cannot convert all visual effects to SkShader instances + */ +sk_sp<SkShader> Shader::makeSkShader() { + return nullptr; +} + +sk_sp<SkImageFilter> Shader::asSkImageFilter() { + // If we already have created an ImageFilter with these parameters just return the existing + // ImageFilter we have already created + if (!this->skImageFilter.get()) { + // Attempt to create an SkImageFilter from the current Shader implementation + this->skImageFilter = makeSkImageFilter(); + if (this->skImageFilter) { + if (!localMatrix.isIdentity()) { + // If we have created an SkImageFilter and we have a transformation, wrap + // the created SkImageFilter to apply the given matrix + this->skImageFilter = SkImageFilters::MatrixTransform( + localMatrix, kMedium_SkFilterQuality, this->skImageFilter); + } + } else { + // Otherwise if no SkImageFilter implementation is provided, create one from + // the result of asSkShader. Note the matrix is already applied to the shader in + // this case so just convert it to an SkImageFilter using SkImageFilters::Paint + SkPaint paint; + paint.setShader(asSkShader()); + sk_sp<SkImageFilter> paintFilter = SkImageFilters::Paint(paint); + this->skImageFilter = SkImageFilters::Xfermode(SkBlendMode::kDstIn, + std::move(paintFilter)); + } + } + return this->skImageFilter; +} + +/** + * By default return null for subclasses to implement. If there is not a direct SkImageFilter + * conversion + */ +sk_sp<SkImageFilter> Shader::makeSkImageFilter() { + return nullptr; +} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/Shader.h b/libs/hwui/shader/Shader.h new file mode 100644 index 000000000000..3c0cdaae8253 --- /dev/null +++ b/libs/hwui/shader/Shader.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "SkImageFilter.h" +#include "SkShader.h" +#include "SkPaint.h" +#include "SkRefCnt.h" + +class SkMatrix; + +namespace android::uirenderer { + +/** + * Shader class that can optionally wrap an SkShader or SkImageFilter depending + * on the implementation + */ +class Shader: public SkRefCnt { +public: + /** + * Creates a Shader instance with an optional transformation matrix + * @param matrix Optional matrix to transform the underlying SkShader or SkImageFilter + */ + Shader(const SkMatrix* matrix); + virtual ~Shader(); + + /** + * Create an SkShader from the current Shader instance or return a previously + * created instance. This can be null if no SkShader could be created from this + * Shader instance. + */ + sk_sp<SkShader> asSkShader(); + + /** + * Create an SkImageFilter from the current Shader instance or return a previously + * created instance. Unlike asSkShader, this method cannot return null. + */ + sk_sp<SkImageFilter> asSkImageFilter(); + +protected: + /** + * Create a new SkShader instance based on this Shader instance + */ + virtual sk_sp<SkShader> makeSkShader(); + + /** + * Create a new SkImageFilter instance based on this Shader instance. If no SkImageFilter + * can be created then return nullptr + */ + virtual sk_sp<SkImageFilter> makeSkImageFilter(); + +private: + /** + * Optional matrix transform + */ + const SkMatrix localMatrix; + + /** + * Cached SkShader instance to be returned on subsequent queries + */ + sk_sp<SkShader> skShader; + + /** + * Cached SkImageFilter instance to be returned on subsequent queries + */ + sk_sp<SkImageFilter> skImageFilter; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/SweepGradientShader.cpp b/libs/hwui/shader/SweepGradientShader.cpp new file mode 100644 index 000000000000..3b1f37f8b051 --- /dev/null +++ b/libs/hwui/shader/SweepGradientShader.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SweepGradientShader.h" + +#include <vector> + +#include "SkGradientShader.h" +#include "SkImageFilters.h" + +namespace android::uirenderer { + +SweepGradientShader::SweepGradientShader(float x, float y, const std::vector<SkColor4f>& colors, + const sk_sp<SkColorSpace>& colorspace, const SkScalar pos[], + const uint32_t shaderFlags, const SkMatrix* matrix) + : Shader(matrix) + , skShader(SkGradientShader::MakeSweep(x, y, colors.data(), colorspace, pos, colors.size(), + shaderFlags, nullptr)) {} + +sk_sp<SkShader> SweepGradientShader::makeSkShader() { + return skShader; +} + +SweepGradientShader::~SweepGradientShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/SweepGradientShader.h b/libs/hwui/shader/SweepGradientShader.h new file mode 100644 index 000000000000..dad3ef0ffad4 --- /dev/null +++ b/libs/hwui/shader/SweepGradientShader.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a color ramp clockwise such that the start and end colors + * are visible at 3 o'clock. This handles converting to either an SkShader or SkImageFilter + */ +class SweepGradientShader : public Shader { +public: + SweepGradientShader(float x, float y, const std::vector<SkColor4f>& colors, + const sk_sp<SkColorSpace>& colorspace, const SkScalar pos[], + const uint32_t shaderFlags, const SkMatrix* matrix); + virtual ~SweepGradientShader() override; + +protected: + virtual sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index c4067af388e3..e2c1651d823a 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -18,6 +18,7 @@ #include "hwui/Paint.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" +#include <shader/BitmapShader.h> #include "utils/Color.h" class BitmapShaders; @@ -45,15 +46,24 @@ public: }); Paint paint; + sk_sp<BitmapShader> bitmapShader = sk_make_sp<BitmapShader>( + hwuiBitmap->makeImage(), + SkTileMode::kRepeat, + SkTileMode::kRepeat, + nullptr + ); + sk_sp<SkImage> image = hwuiBitmap->makeImage(); - sk_sp<SkShader> repeatShader = - image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat); - paint.setShader(std::move(repeatShader)); + paint.setShader(std::move(bitmapShader)); canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint); - sk_sp<SkShader> mirrorShader = - image->makeShader(SkTileMode::kMirror, SkTileMode::kMirror); - paint.setShader(std::move(mirrorShader)); + sk_sp<BitmapShader> mirrorBitmapShader = sk_make_sp<BitmapShader>( + image, + SkTileMode::kMirror, + SkTileMode::kMirror, + nullptr + ); + paint.setShader(std::move(mirrorBitmapShader)); canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint); } diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 5886ea39acce..d37bc3c7d37c 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -20,6 +20,10 @@ #include <SkGradientShader.h> #include <SkImagePriv.h> #include <ui/PixelFormat.h> +#include <shader/BitmapShader.h> +#include <shader/LinearGradientShader.h> +#include <shader/RadialGradientShader.h> +#include <shader/ComposeShader.h> class HwBitmapInCompositeShader; @@ -50,20 +54,41 @@ public: pixels[4000 + 4 * i + 3] = 255; } buffer->unlock(); - sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer->toAHardwareBuffer(), - SkColorSpace::MakeSRGB())); - sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap)); + + sk_sp<BitmapShader> bitmapShader = sk_make_sp<BitmapShader>( + Bitmap::createFrom( + buffer->toAHardwareBuffer(), + SkColorSpace::MakeSRGB() + )->makeImage(), + SkTileMode::kClamp, + SkTileMode::kClamp, + nullptr + ); SkPoint center; center.set(50, 50); - SkColor colors[2]; - colors[0] = Color::Black; - colors[1] = Color::White; - sk_sp<SkShader> gradientShader = SkGradientShader::MakeRadial( - center, 50, colors, nullptr, 2, SkTileMode::kRepeat); - - sk_sp<SkShader> compositeShader( - SkShaders::Blend(SkBlendMode::kDstATop, hardwareShader, gradientShader)); + + std::vector<SkColor4f> vColors(2); + vColors[0] = SkColors::kBlack; + vColors[1] = SkColors::kWhite; + + sk_sp<RadialGradientShader> radialShader = sk_make_sp<RadialGradientShader>( + center, + 50, + vColors, + SkColorSpace::MakeSRGB(), + nullptr, + SkTileMode::kRepeat, + 0, + nullptr + ); + + sk_sp<ComposeShader> compositeShader = sk_make_sp<ComposeShader>( + *bitmapShader.get(), + *radialShader.get(), + SkBlendMode::kDstATop, + nullptr + ); Paint paint; paint.setShader(std::move(compositeShader)); diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index a9449b62a1f8..76e39deedd9a 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -17,7 +17,8 @@ #include "TestSceneBase.h" #include "tests/common/TestListViewSceneBase.h" #include "hwui/Paint.h" -#include <SkGradientShader.h> +#include "SkColor.h" +#include <shader/LinearGradientShader.h> class ListOfFadedTextAnimation; @@ -42,15 +43,26 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { pts[0].set(0, 0); pts[1].set(0, 1); - SkColor colors[2] = {Color::Black, Color::Transparent}; - sk_sp<SkShader> s( - SkGradientShader::MakeLinear(pts, colors, NULL, 2, SkTileMode::kClamp)); - SkMatrix matrix; matrix.setScale(1, length); matrix.postRotate(-90); + + std::vector<SkColor4f> vColors(2); + vColors[0] = SkColors::kBlack; + vColors[1] = SkColors::kTransparent; + + sk_sp<LinearGradientShader> linearGradientShader = sk_make_sp<LinearGradientShader>( + pts, + vColors, + SkColorSpace::MakeSRGB(), + nullptr, + SkTileMode::kClamp, + 0, + &matrix + ); + Paint fadingPaint; - fadingPaint.setShader(s->makeWithLocalMatrix(matrix)); + fadingPaint.setShader(linearGradientShader); fadingPaint.setBlendMode(SkBlendMode::kDstOut); canvas.drawRect(0, 0, length, itemHeight, fadingPaint); canvas.restore(); diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index a0bc5aa245d5..bdc157f85264 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -17,7 +17,7 @@ #include "TestSceneBase.h" #include <SkColorMatrixFilter.h> -#include <SkGradientShader.h> +#include <shader/LinearGradientShader.h> class SimpleColorMatrixAnimation; @@ -65,9 +65,12 @@ private: // enough renderer might apply it directly to the paint color) float pos[] = {0, 1}; SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; - SkColor colors[2] = {Color::DeepPurple_500, Color::DeepOrange_500}; - paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, - SkTileMode::kClamp)); + std::vector<SkColor4f> colors(2); + colors[0] = SkColor4f::FromColor(Color::DeepPurple_500); + colors[1] = SkColor4f::FromColor(Color::DeepOrange_500); + paint.setShader(sk_make_sp<LinearGradientShader>( + pts, colors, SkColorSpace::MakeSRGB(), pos, SkTileMode::kClamp, + 0, nullptr)); // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp index 57a260c8d234..9a15c9d370a4 100644 --- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp @@ -17,6 +17,7 @@ #include "TestSceneBase.h" #include <SkGradientShader.h> +#include <shader/LinearGradientShader.h> class SimpleGradientAnimation; @@ -55,9 +56,24 @@ private: // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { // use i%2 start position to pick 2 color combo with black in it - SkColor colors[3] = {Color::Transparent, Color::Black, Color::Cyan_500}; - paint.setShader(SkGradientShader::MakeLinear(pts, colors + (i % 2), pos, 2, - SkTileMode::kClamp)); + std::vector<SkColor4f> vColors(2); + vColors[0] = ((i % 2) == 0) ? + SkColor4f::FromColor(Color::Transparent) : + SkColor4f::FromColor(Color::Black); + vColors[1] = (((i + 1) % 2) == 0) ? + SkColor4f::FromColor(Color::Black) : + SkColor4f::FromColor(Color::Cyan_500); + + sk_sp<LinearGradientShader> gradient = sk_make_sp<LinearGradientShader>( + pts, + vColors, + SkColorSpace::MakeSRGB(), + pos, + SkTileMode::kClamp, + 0, + nullptr + ); + paint.setShader(gradient); canvas.drawRect(i, i, width, height, paint); } }); diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 6d4c57413f00..5e56b26f46f0 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -17,9 +17,14 @@ #include <gtest/gtest.h> #include "PathParser.h" +#include "GraphicsJNI.h" +#include "SkGradientShader.h" +#include "SkShader.h" #include "VectorDrawable.h" #include "utils/MathUtils.h" #include "utils/VectorDrawableUtils.h" +#include <shader/Shader.h> +#include <shader/LinearGradientShader.h> #include <functional> @@ -395,7 +400,21 @@ TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) { bitmap.allocN32Pixels(5, 5, false); SkCanvas canvas(bitmap); - sk_sp<SkShader> shader = SkShaders::Color(SK_ColorBLACK); + SkPoint pts[2]; + pts[0].set(0, 0); + pts[1].set(0, 0); + + std::vector<SkColor4f> colors(2); + colors[0] = SkColors::kBlack; + colors[1] = SkColors::kBlack; + + sk_sp<LinearGradientShader> shader = sk_sp(new LinearGradientShader(pts, + colors, + SkColorSpace::MakeSRGB(), + nullptr, + SkTileMode::kClamp, + SkGradientShader::kInterpolateColorsInPremul_Flag, + nullptr)); // Initial ref count is 1 EXPECT_TRUE(shader->unique()); diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index 21378c80fd56..77f7b54368f8 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -41,7 +41,8 @@ interface ISession { // These commands are for the TransportPerformer void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription); void setPlaybackState(in PlaybackState state); - void setQueue(in ParceledListSlice queue); + void resetQueue(); + IBinder getBinderForSetQueue(); void setQueueTitle(CharSequence title); void setExtras(in Bundle extras); void setRatingType(int type); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 70bd1609ddbd..6c41f7bcd0bc 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -25,7 +25,6 @@ import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; -import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.MediaDescription; import android.media.MediaMetadata; @@ -36,6 +35,7 @@ import android.net.Uri; import android.os.BadParcelableException; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; @@ -491,7 +491,12 @@ public final class MediaSession { */ public void setQueue(@Nullable List<QueueItem> queue) { try { - mBinder.setQueue(queue == null ? null : new ParceledListSlice(queue)); + if (queue == null) { + mBinder.resetQueue(); + } else { + IBinder binder = mBinder.getBinderForSetQueue(); + ParcelableListBinder.send(binder, queue); + } } catch (RemoteException e) { Log.wtf("Dead object in setQueue.", e); } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 6976a3569655..20006934ee46 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -523,7 +523,8 @@ public final class MediaSessionManager { * @param keyEvent The KeyEvent to send. * @hide */ - public void dispatchMediaKeyEventAsSystemService(KeyEvent keyEvent) { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public void dispatchMediaKeyEventAsSystemService(@NonNull KeyEvent keyEvent) { dispatchMediaKeyEventInternal(true, keyEvent, false); } @@ -548,6 +549,7 @@ public final class MediaSessionManager { * @return {@code true} if the event was sent to the session, {@code false} otherwise * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public boolean dispatchMediaKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken, @NonNull KeyEvent keyEvent) { if (sessionToken == null) { @@ -586,10 +588,15 @@ public final class MediaSessionManager { * Should be only called by the {@link com.android.internal.policy.PhoneWindow} or * {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key * from the hardware devices. + * <p> + * Valid stream types include {@link AudioManager.PublicStreamTypes} and + * {@link AudioManager#USE_DEFAULT_STREAM_TYPE}. * - * @param keyEvent The KeyEvent to send. + * @param keyEvent volume key event + * @param streamType type of stream * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchVolumeKeyEventAsSystemService(@NonNull KeyEvent keyEvent, int streamType) { dispatchVolumeKeyEventInternal(true, keyEvent, streamType, false); } @@ -614,6 +621,7 @@ public final class MediaSessionManager { * @param keyEvent volume key event * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchVolumeKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken, @NonNull KeyEvent keyEvent) { if (sessionToken == null) { diff --git a/media/java/android/media/session/ParcelableListBinder.java b/media/java/android/media/session/ParcelableListBinder.java new file mode 100644 index 000000000000..a7aacf2d8fca --- /dev/null +++ b/media/java/android/media/session/ParcelableListBinder.java @@ -0,0 +1,131 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.annotation.NonNull; +import android.os.Binder; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * Binder to receive a list that has a large number of {@link Parcelable} items. + * + * It's similar to {@link android.content.pm.ParceledListSlice}, but transactions are performed in + * the opposite direction. + * + * @param <T> the type of {@link Parcelable} + * @hide + */ +public class ParcelableListBinder<T extends Parcelable> extends Binder { + + private static final int SUGGESTED_MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes(); + + private static final int END_OF_PARCEL = 0; + private static final int ITEM_CONTINUED = 1; + + private final Consumer<List<T>> mConsumer; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final List<T> mList = new ArrayList<>(); + + @GuardedBy("mLock") + private int mCount; + + @GuardedBy("mLock") + private boolean mConsumed; + + /** + * Creates an instance. + * + * @param consumer a consumer that consumes the list received + */ + public ParcelableListBinder(@NonNull Consumer<List<T>> consumer) { + mConsumer = consumer; + } + + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + if (code != FIRST_CALL_TRANSACTION) { + return super.onTransact(code, data, reply, flags); + } + List<T> listToBeConsumed; + synchronized (mLock) { + if (mConsumed) { + return false; + } + int i = mList.size(); + if (i == 0) { + mCount = data.readInt(); + } + while (i < mCount && data.readInt() != END_OF_PARCEL) { + mList.add(data.readParcelable(null)); + i++; + } + if (i >= mCount) { + listToBeConsumed = mList; + mConsumed = true; + } else { + listToBeConsumed = null; + } + } + if (listToBeConsumed != null) { + mConsumer.accept(listToBeConsumed); + } + return true; + } + + /** + * Sends a list of {@link Parcelable} to a binder. + * + * @param binder a binder interface backed by {@link ParcelableListBinder} + * @param list a list to send + */ + public static <T extends Parcelable> void send(@NonNull IBinder binder, @NonNull List<T> list) + throws RemoteException { + int count = list.size(); + int i = 0; + while (i < count) { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + if (i == 0) { + data.writeInt(count); + } + while (i < count && data.dataSize() < SUGGESTED_MAX_IPC_SIZE) { + data.writeInt(ITEM_CONTINUED); + data.writeParcelable(list.get(i), 0); + i++; + } + if (i < count) { + data.writeInt(END_OF_PARCEL); + } + binder.transact(FIRST_CALL_TRANSACTION, data, reply, 0); + reply.recycle(); + data.recycle(); + } + } +} diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index 617a83f05791..8892a2954afb 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -46,6 +46,13 @@ package android.media.session { field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000 } + public final class MediaSessionManager { + method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); + method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + } + } package android.os { diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index dbf9e9fb4ae5..3fd0ee1c8011 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -11037,6 +11037,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml index b8e1edc46da7..20aa5f79c5cc 100644 --- a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml +++ b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml @@ -22,7 +22,8 @@ <item target="attr/icon" value="@attr/icon"/> <item target="attr/selectedIcon" value="@attr/selectedIcon"/> - <item target="attr/intent" value="@attr/longIntent"/> + <item target="attr/intent" value="@attr/intent"/> + <item target="attr/longIntent" value="@attr/longIntent"/> <item target="attr/componentNames" value="@attr/componentNames"/> <item target="attr/highlightWhenSelected" value="@attr/highlightWhenSelected"/> <item target="attr/categories" value="@attr/categories"/> diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java index b2f98ecde513..24d9d09d2ca9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java @@ -22,7 +22,6 @@ import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SystemServicesModule; import com.android.systemui.dagger.SystemUIModule; -import com.android.systemui.onehanded.dagger.OneHandedModule; import com.android.systemui.pip.phone.dagger.PipModule; import dagger.Subcomponent; @@ -36,7 +35,6 @@ import dagger.Subcomponent; DependencyProvider.class, DependencyBinder.class, PipModule.class, - OneHandedModule.class, SystemServicesModule.class, SystemUIModule.class, CarSystemUIModule.class, diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index adc8b4d24cd8..290700fad147 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -22,7 +22,6 @@ import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import android.content.Context; import android.os.Handler; import android.os.PowerManager; -import android.view.IWindowManager; import com.android.keyguard.KeyguardViewController; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -40,8 +39,6 @@ import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; -import com.android.systemui.pip.phone.PipMenuActivity; -import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.EnhancedEstimates; @@ -68,11 +65,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.volume.VolumeDialogComponent; -import com.android.systemui.wm.DisplaySystemBarsController; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.SystemWindows; -import com.android.wm.shell.common.TransactionPool; +import com.android.systemui.wmshell.CarWMShellModule; import javax.inject.Named; @@ -83,7 +76,8 @@ import dagger.Provides; @Module( includes = { DividerModule.class, - QSModule.class + QSModule.class, + CarWMShellModule.class }) abstract class CarSystemUIModule { @@ -120,42 +114,6 @@ abstract class CarSystemUIModule { return new Recents(context, recentsImplementation, commandQueue); } - @SysUISingleton - @Provides - static TransactionPool provideTransactionPool() { - return new TransactionPool(); - } - - @SysUISingleton - @Provides - static DisplayController providerDisplayController(Context context, @Main Handler handler, - IWindowManager wmService) { - return new DisplayController(context, handler, wmService); - } - - @SysUISingleton - @Provides - static SystemWindows provideSystemWindows(DisplayController displayController, - IWindowManager wmService) { - return new SystemWindows(displayController, wmService); - } - - @SysUISingleton - @Provides - static DisplayImeController provideDisplayImeController(Context context, - IWindowManager wmService, DisplayController displayController, - @Main Handler mainHandler, TransactionPool transactionPool) { - return new DisplaySystemBarsController.Builder(context, wmService, displayController, - mainHandler, transactionPool).build(); - } - - @SysUISingleton - @PipMenuActivityClass - @Provides - static Class<?> providePipMenuActivityClass() { - return PipMenuActivity.class; - } - @Binds abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone); diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java new file mode 100644 index 000000000000..2324c3d59155 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 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.wmshell; + +import android.content.Context; +import android.os.Handler; +import android.view.IWindowManager; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.pip.phone.PipMenuActivity; +import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; +import com.android.systemui.wm.DisplaySystemBarsController; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.TransactionPool; + +import dagger.Module; +import dagger.Provides; + +/** Provides dependencies from {@link com.android.wm.shell} for CarSystemUI. */ +@Module(includes = WMShellBaseModule.class) +public class CarWMShellModule { + @SysUISingleton + @Provides + DisplayImeController provideDisplayImeController(Context context, + IWindowManager wmService, DisplayController displayController, + @Main Handler mainHandler, TransactionPool transactionPool) { + return new DisplaySystemBarsController.Builder(context, wmService, displayController, + mainHandler, transactionPool).build(); + } + + /** TODO(b/150319024): PipMenuActivity will move to a Window */ + @SysUISingleton + @PipMenuActivityClass + @Provides + Class<?> providePipMenuActivityClass() { + return PipMenuActivity.class; + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 9d06c8467e41..72a6074ff89c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -465,7 +465,16 @@ public class LocalMediaManager implements BluetoothCallback { synchronized (mMediaDevicesLock) { mMediaDevices.clear(); mMediaDevices.addAll(devices); - mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + // Add disconnected bluetooth devices only when phone output device is available. + for (MediaDevice device : devices) { + final int type = device.getDeviceType(); + if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) { + mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + break; + } + } } final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index a654fd47ea12..8e850b25159c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -585,6 +585,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); @@ -683,6 +684,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index bdf00e38ce94..bcd2ff71b57f 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -175,5 +175,6 @@ public class SecureSettings { Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, Settings.Secure.PANIC_GESTURE_ENABLED, Settings.Secure.PANIC_SOUND_ENABLED, + Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 4ea7f3a23c10..3630f257f583 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -263,5 +263,6 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PANIC_GESTURE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PANIC_SOUND_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 47cc6d0fd752..66be8dd68283 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1976,6 +1976,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS); + dumpSetting(s, p, + Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, + SecureSettingsProto.ADAPTIVE_CONNECTIVITY_ENABLED); final long controlsToken = p.start(SecureSettingsProto.CONTROLS); dumpSetting(s, p, diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 4ce9f5a9edc6..af008b996172 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -113,6 +113,7 @@ <uses-permission android:name="android.permission.SET_ORIENTATION" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.MONITOR_INPUT" /> + <uses-permission android:name="android.permission.INPUT_CONSUMER" /> <!-- DreamManager --> <uses-permission android:name="android.permission.READ_DREAM_STATE" /> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java index 0d960f0c21be..6c5c4ef94921 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java @@ -39,6 +39,10 @@ public interface StatusBarStateController { */ boolean isDozing(); + /** + * Is the status bar panel expanded. + */ + boolean isExpanded(); /** * Is device pulsing. @@ -113,5 +117,10 @@ public interface StatusBarStateController { * Callback to be notified when the pulsing state changes */ default void onPulsingChanged(boolean pulsing) {} + + /** + * Callback to be notified when the expanded state of the status bar changes + */ + default void onExpandedChanged(boolean isExpanded) {} } } diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index b42d71abaa6d..df66bf5a1051 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -45,4 +45,5 @@ -keep class com.android.systemui.dagger.GlobalRootComponent { *; } -keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; } --keep class com.android.systemui.dagger.Dagger** { *; }
\ No newline at end of file +-keep class com.android.systemui.dagger.Dagger** { *; } +-keep class com.android.systemui.tv.Dagger** { *; }
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/bubble_stack_user_education.xml b/packages/SystemUI/res/layout/bubble_stack_user_education.xml index 616403219bc6..fe1ed4b6f726 100644 --- a/packages/SystemUI/res/layout/bubble_stack_user_education.xml +++ b/packages/SystemUI/res/layout/bubble_stack_user_education.xml @@ -15,8 +15,8 @@ ~ limitations under the License. --> <LinearLayout + android:id="@+id/stack_education_layout" xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/user_education_view" android:layout_height="wrap_content" android:layout_width="wrap_content" android:paddingTop="48dp" @@ -25,26 +25,29 @@ android:paddingEnd="16dp" android:layout_marginEnd="24dp" android:orientation="vertical" - android:background="@drawable/bubble_stack_user_education_bg"> - + android:background="@drawable/bubble_stack_user_education_bg" + > <TextView - android:id="@+id/user_education_title" + android:id="@+id/stack_education_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="16dp" android:fontFamily="@*android:string/config_bodyFontFamilyMedium" android:maxLines="2" android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" android:text="@string/bubbles_user_education_title" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/> <TextView - android:id="@+id/user_education_description" + android:id="@+id/stack_education_description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" android:text="@string/bubbles_user_education_description" android:fontFamily="@*android:string/config_bodyFontFamily" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/> - </LinearLayout> diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml index 213bb923db65..b51dc93dc373 100644 --- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml +++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml @@ -14,77 +14,77 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.bubbles.ManageEducationView + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" + android:id="@+id/manage_education_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="true" + android:paddingTop="28dp" + android:paddingBottom="16dp" + android:paddingStart="@dimen/bubble_expanded_view_padding" + android:paddingEnd="48dp" + android:layout_marginEnd="24dp" + android:orientation="vertical" + android:background="@drawable/bubble_stack_user_education_bg" > - <LinearLayout + + <TextView + android:id="@+id/user_education_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:id="@+id/manage_education_view" - android:clickable="true" - android:paddingTop="28dp" + android:paddingStart="16dp" android:paddingBottom="16dp" - android:paddingStart="@dimen/bubble_expanded_view_padding" - android:paddingEnd="48dp" - android:layout_marginEnd="24dp" - android:orientation="vertical" - android:background="@drawable/bubble_stack_user_education_bg" - > + android:fontFamily="@*android:string/config_bodyFontFamilyMedium" + android:maxLines="2" + android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" + android:text="@string/bubbles_user_education_manage_title" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/> - <TextView - android:id="@+id/user_education_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingBottom="16dp" - android:fontFamily="@*android:string/config_bodyFontFamilyMedium" - android:maxLines="2" - android:ellipsize="end" - android:text="@string/bubbles_user_education_manage_title" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/> + <TextView + android:id="@+id/user_education_description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingStart="16dp" + android:paddingBottom="24dp" + android:text="@string/bubbles_user_education_manage" + android:maxLines="2" + android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/> - <TextView - android:id="@+id/user_education_description" + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:id="@+id/button_layout" + android:orientation="horizontal" > + + <com.android.systemui.statusbar.AlphaOptimizedButton + style="@android:style/Widget.Material.Button.Borderless" + android:id="@+id/manage" + android:layout_gravity="start" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingBottom="24dp" - android:text="@string/bubbles_user_education_manage" - android:maxLines="2" - android:ellipsize="end" - android:fontFamily="@*android:string/config_bodyFontFamily" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/> + android:focusable="true" + android:clickable="false" + android:text="@string/manage_bubbles_text" + android:textColor="?attr/wallpaperTextColor" + /> - <LinearLayout - android:layout_height="wrap_content" + <com.android.systemui.statusbar.AlphaOptimizedButton + style="@android:style/Widget.Material.Button.Borderless" + android:id="@+id/got_it" + android:layout_gravity="start" android:layout_width="wrap_content" - android:id="@+id/button_layout" - android:orientation="horizontal" > - - <com.android.systemui.statusbar.AlphaOptimizedButton - style="@android:style/Widget.Material.Button.Borderless" - android:id="@+id/manage" - android:layout_gravity="start" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:focusable="true" - android:clickable="false" - android:text="@string/manage_bubbles_text" - android:textColor="?attr/wallpaperTextColor" - /> - - <com.android.systemui.statusbar.AlphaOptimizedButton - style="@android:style/Widget.Material.Button.Borderless" - android:id="@+id/got_it" - android:layout_gravity="start" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:focusable="true" - android:text="@string/bubbles_user_education_got_it" - android:textColor="?attr/wallpaperTextColor" - /> - </LinearLayout> + android:layout_height="wrap_content" + android:focusable="true" + android:text="@string/bubbles_user_education_got_it" + android:textColor="?attr/wallpaperTextColor" + /> </LinearLayout> -</com.android.systemui.bubbles.ManageEducationView> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index 732758a2ded2..31a33fbfc308 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -5,6 +5,6 @@ android:id="@+id/udfps_view" android:layout_width="match_parent" android:layout_height="match_parent" - systemui:sensorRadius="140px" - systemui:sensorMarginBottom="630px" + systemui:sensorRadius="130px" + systemui:sensorCenterY="1636px" systemui:sensorTouchAreaCoefficient="0.5"/> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 84dbd60b799d..a62502965dd2 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -159,7 +159,7 @@ <declare-styleable name="UdfpsView"> <attr name="sensorRadius" format="dimension"/> - <attr name="sensorMarginBottom" format="dimension"/> + <attr name="sensorCenterY" format="dimension"/> <attr name="sensorPressureCoefficient" format="float"/> <attr name="sensorTouchAreaCoefficient" format="float"/> </declare-styleable> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 765a9422585a..d12f0103238d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1342,7 +1342,7 @@ <dimen name="controls_app_divider_height">2dp</dimen> <dimen name="controls_app_divider_side_margin">32dp</dimen> - <dimen name="controls_card_margin">2dp</dimen> + <dimen name="controls_card_margin">@dimen/control_base_item_margin</dimen> <item name="control_card_elevation" type="dimen" format="float">15</item> <dimen name="controls_dialog_padding">32dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index de47c68166ff..748a9c9448c5 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -78,7 +78,7 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment; import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index 59af458e2402..17b840cc7a20 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -162,8 +162,8 @@ public class ExpandHelper implements Gefingerpoken { * * @param context application context * @param callback the container that holds the items to be manipulated - * @param small the smallest allowable size for the manuipulated items. - * @param large the largest allowable size for the manuipulated items. + * @param small the smallest allowable size for the manipulated items. + * @param large the largest allowable size for the manipulated items. */ public ExpandHelper(Context context, Callback callback, int small, int large) { mSmallSize = small; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 36353fa96956..d7e91384f049 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -53,12 +53,10 @@ public class UdfpsView extends View implements DozeReceiver, private final Paint mScrimPaint; private final Paint mDebugTextPaint; - private float mSensorX; - private float mSensorY; private final RectF mSensorRect; private final Paint mSensorPaint; private final float mSensorRadius; - private final float mSensorMarginBottom; + private final float mSensorCenterY; private final float mSensorTouchAreaCoefficient; private final int mMaxBurnInOffsetX; private final int mMaxBurnInOffsetY; @@ -66,6 +64,10 @@ public class UdfpsView extends View implements DozeReceiver, private final Rect mTouchableRegion; private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener; + // This is calculated from the screen's dimensions at runtime, as opposed to mSensorCenterY, + // which is defined in layout.xml + private float mSensorCenterX; + // AOD anti-burn-in offsets private float mInterpolatedDarkAmount; private float mBurnInOffsetX; @@ -84,7 +86,7 @@ public class UdfpsView extends View implements DozeReceiver, if (!a.hasValue(R.styleable.UdfpsView_sensorRadius)) { throw new IllegalArgumentException("UdfpsView must contain sensorRadius"); } - if (!a.hasValue(R.styleable.UdfpsView_sensorMarginBottom)) { + if (!a.hasValue(R.styleable.UdfpsView_sensorCenterY)) { throw new IllegalArgumentException("UdfpsView must contain sensorMarginBottom"); } if (!a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) { @@ -92,7 +94,7 @@ public class UdfpsView extends View implements DozeReceiver, "UdfpsView must contain sensorTouchAreaCoefficient"); } mSensorRadius = a.getDimension(R.styleable.UdfpsView_sensorRadius, 0f); - mSensorMarginBottom = a.getDimension(R.styleable.UdfpsView_sensorMarginBottom, 0f); + mSensorCenterY = a.getDimension(R.styleable.UdfpsView_sensorCenterY, 0f); mSensorTouchAreaCoefficient = a.getFloat( R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f); } finally { @@ -163,10 +165,9 @@ public class UdfpsView extends View implements DozeReceiver, final int h = getLayoutParams().height; final int w = getLayoutParams().width; mScrimRect.set(0 /* left */, 0 /* top */, w, h); - mSensorX = w / 2f; - mSensorY = h - mSensorMarginBottom - mSensorRadius; - mSensorRect.set(mSensorX - mSensorRadius, mSensorY - mSensorRadius, - mSensorX + mSensorRadius, mSensorY + mSensorRadius); + mSensorCenterX = w / 2f; + mSensorRect.set(mSensorCenterX - mSensorRadius, mSensorCenterY - mSensorRadius, + mSensorCenterX + mSensorRadius, mSensorCenterY + mSensorRadius); // Sets mTouchableRegion with rounded up values from mSensorRect. mSensorRect.roundOut(mTouchableRegion); @@ -210,10 +211,10 @@ public class UdfpsView extends View implements DozeReceiver, } boolean isValidTouch(float x, float y, float pressure) { - return x > (mSensorX - mSensorRadius * mSensorTouchAreaCoefficient) - && x < (mSensorX + mSensorRadius * mSensorTouchAreaCoefficient) - && y > (mSensorY - mSensorRadius * mSensorTouchAreaCoefficient) - && y < (mSensorY + mSensorRadius * mSensorTouchAreaCoefficient); + return x > (mSensorCenterX - mSensorRadius * mSensorTouchAreaCoefficient) + && x < (mSensorCenterX + mSensorRadius * mSensorTouchAreaCoefficient) + && y > (mSensorCenterY - mSensorRadius * mSensorTouchAreaCoefficient) + && y < (mSensorCenterY + mSensorRadius * mSensorTouchAreaCoefficient); } void setScrimAlpha(int alpha) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index fa0d2ba84b4e..80150c9b7e32 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -305,7 +305,7 @@ public class BubbleExpandedView extends LinearLayout { mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */, true /* singleTaskInstance */, false /* usePublicVirtualDisplay*/, - true /* disableSurfaceViewBackgroundLayer */); + true /* disableSurfaceViewBackgroundLayer */, true /* useTrustedDisplay */); // Set ActivityView's alpha value as zero, since there is no view content to be shown. setContentVisibility(false); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index ec9644af7013..64df2b99ee22 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -19,11 +19,7 @@ package com.android.systemui.bubbles; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION; -import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW; -import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; @@ -38,7 +34,6 @@ import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; -import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Outline; @@ -54,7 +49,6 @@ import android.provider.Settings; import android.util.Log; import android.view.Choreographer; import android.view.DisplayCutout; -import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -70,10 +64,8 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TextView; -import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.DynamicAnimation; @@ -82,7 +74,6 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Interpolators; import com.android.systemui.Prefs; import com.android.systemui.R; @@ -115,10 +106,6 @@ public class BubbleStackView extends FrameLayout implements ViewTreeObserver.OnComputeInternalInsetsListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES; - /** Animation durations for bubble stack user education views. **/ - static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200; - private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40; - /** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */ static final float FLYOUT_DRAG_PERCENT_DISMISS = 0.25f; @@ -556,7 +543,7 @@ public class BubbleStackView extends FrameLayout // Otherwise, we either tapped the stack (which means we're collapsed // and should expand) or the currently selected bubble (we're expanded // and should collapse). - if (!maybeShowStackUserEducation()) { + if (!maybeShowStackEdu()) { mBubbleData.setExpanded(!mBubbleData.isExpanded()); } } @@ -582,7 +569,9 @@ public class BubbleStackView extends FrameLayout } if (mBubbleData.isExpanded()) { - maybeShowManageEducation(false /* show */); + if (mManageEduView != null) { + mManageEduView.hide(false /* show */); + } // If we're expanded, tell the animation controller to prepare to drag this bubble, // dispatching to the individual bubble magnet listener. @@ -637,7 +626,9 @@ public class BubbleStackView extends FrameLayout mExpandedAnimationController.dragBubbleOut( v, viewInitialX + dx, viewInitialY + dy); } else { - hideStackUserEducation(false /* fromExpansion */); + if (mStackEduView != null) { + mStackEduView.hide(false /* fromExpansion */); + } mStackAnimationController.moveStackFromTouch( viewInitialX + dx, viewInitialY + dy); } @@ -684,7 +675,7 @@ public class BubbleStackView extends FrameLayout private OnClickListener mFlyoutClickListener = new OnClickListener() { @Override public void onClick(View view) { - if (maybeShowStackUserEducation()) { + if (maybeShowStackEdu()) { // If we're showing user education, don't open the bubble show the education first mBubbleToExpandAfterFlyoutCollapse = null; } else { @@ -728,7 +719,7 @@ public class BubbleStackView extends FrameLayout mFlyout.removeCallbacks(mHideFlyout); animateFlyoutCollapsed(shouldDismiss, velX); - maybeShowStackUserEducation(); + maybeShowStackEdu(); } }; @@ -737,14 +728,8 @@ public class BubbleStackView extends FrameLayout @Nullable private BubbleOverflow mBubbleOverflow; - - private boolean mShouldShowUserEducation; - private boolean mAnimatingEducationAway; - private View mUserEducationView; - - private boolean mShouldShowManageEducation; - private ManageEducationView mManageEducationView; - private boolean mAnimatingManageEducationAway; + private StackEducationView mStackEduView; + private ManageEducationView mManageEduView; private ViewGroup mManageMenu; private ImageView mManageSettingsIcon; @@ -805,8 +790,6 @@ public class BubbleStackView extends FrameLayout onBubbleAnimatedOut); mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; - setUpUserEducation(); - // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or // is centered. It greatly simplifies translation positioning/animations. Views that will // actually lay out differently in RTL, such as the flyout and expanded view, will set their @@ -819,6 +802,8 @@ public class BubbleStackView extends FrameLayout mBubbleContainer.setClipChildren(false); addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + updateUserEdu(); + mExpandedViewContainer = new FrameLayout(context); mExpandedViewContainer.setElevation(elevation); mExpandedViewContainer.setClipChildren(false); @@ -1092,48 +1077,66 @@ public class BubbleStackView extends FrameLayout addView(mManageMenu); } - private void setUpUserEducation() { - if (mUserEducationView != null) { - removeView(mUserEducationView); - } - mShouldShowUserEducation = shouldShowBubblesEducation(); - if (DEBUG_USER_EDUCATION) { - Log.d(TAG, "shouldShowUserEducation: " + mShouldShowUserEducation); + /** + * Whether the educational view should show for the expanded view "manage" menu. + */ + private boolean shouldShowManageEdu() { + final boolean seen = Prefs.getBoolean(mContext, + Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false /* default */); + final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext)) + && mExpandedBubble != null; + if (BubbleDebugConfig.DEBUG_USER_EDUCATION) { + Log.d(TAG, "Show manage edu: " + shouldShow); } - if (mShouldShowUserEducation) { - mUserEducationView = mInflater.inflate(R.layout.bubble_stack_user_education, this, - false /* attachToRoot */); - mUserEducationView.setVisibility(GONE); - - final TypedArray ta = mContext.obtainStyledAttributes( - new int[] {android.R.attr.colorAccent, - android.R.attr.textColorPrimaryInverse}); - final int bgColor = ta.getColor(0, Color.BLACK); - int textColor = ta.getColor(1, Color.WHITE); - ta.recycle(); - textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true); + return shouldShow; + } - TextView title = mUserEducationView.findViewById(R.id.user_education_title); - TextView description = mUserEducationView.findViewById(R.id.user_education_description); - title.setTextColor(textColor); - description.setTextColor(textColor); + private void maybeShowManageEdu() { + if (!shouldShowManageEdu()) { + return; + } + if (mManageEduView == null) { + mManageEduView = new ManageEducationView(mContext); + addView(mManageEduView); + } + mManageEduView.show(mExpandedBubble.getExpandedView(), mTempRect); + } - updateUserEducationForLayoutDirection(); - addView(mUserEducationView); + /** + * Whether education view should show for the collapsed stack. + */ + private boolean shouldShowStackEdu() { + final boolean seen = Prefs.getBoolean(getContext(), + Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION, false /* default */); + final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext); + if (BubbleDebugConfig.DEBUG_USER_EDUCATION) { + Log.d(TAG, "Show stack edu: " + shouldShow); } + return shouldShow; + } - if (mManageEducationView != null) { - removeView(mManageEducationView); + /** + * @return true if education view for collapsed stack should show and was not showing before. + */ + private boolean maybeShowStackEdu() { + if (!shouldShowStackEdu()) { + return false; + } + if (mStackEduView == null) { + mStackEduView = new StackEducationView(mContext); + addView(mStackEduView); } - mShouldShowManageEducation = shouldShowManageEducation(); - if (DEBUG_USER_EDUCATION) { - Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation); + return mStackEduView.show(mStackAnimationController.getStartPosition()); + } + + private void updateUserEdu() { + maybeShowStackEdu(); + if (mManageEduView != null) { + mManageEduView.invalidate(); } - if (mShouldShowManageEducation) { - mManageEducationView = (ManageEducationView) - mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */, - false /* attachToRoot */); - addView(mManageEducationView); + maybeShowManageEdu(); + if (mStackEduView != null) { + mStackEduView.invalidate(); } } @@ -1164,9 +1167,9 @@ public class BubbleStackView extends FrameLayout */ public void onThemeChanged() { setUpFlyout(); - setUpUserEducation(); setUpManageMenu(); updateOverflow(); + updateUserEdu(); updateExpandedViewTheme(); } @@ -1197,12 +1200,11 @@ public class BubbleStackView extends FrameLayout public void onLayoutDirectionChanged(int direction) { mManageMenu.setLayoutDirection(direction); mFlyout.setLayoutDirection(direction); - if (mUserEducationView != null) { - mUserEducationView.setLayoutDirection(direction); - updateUserEducationForLayoutDirection(); + if (mStackEduView != null) { + mStackEduView.setLayoutDirection(direction); } - if (mManageEducationView != null) { - mManageEducationView.setLayoutDirection(direction); + if (mManageEduView != null) { + mManageEduView.setLayoutDirection(direction); } updateExpandedViewDirection(direction); } @@ -1446,7 +1448,7 @@ public class BubbleStackView extends FrameLayout Log.d(TAG, "addBubble: " + bubble); } - if (getBubbleCount() == 0 && mShouldShowUserEducation) { + if (getBubbleCount() == 0 && shouldShowStackEdu()) { // Override the default stack position if we're showing user education. mStackAnimationController.setStackPosition( mStackAnimationController.getStartPosition()); @@ -1649,115 +1651,6 @@ public class BubbleStackView extends FrameLayout notifyExpansionChanged(mExpandedBubble, mIsExpanded); } - /** - * If necessary, shows the user education view for the bubble stack. This appears the first - * time a user taps on a bubble. - * - * @return true if user education was shown, false otherwise. - */ - private boolean maybeShowStackUserEducation() { - if (mShouldShowUserEducation && mUserEducationView.getVisibility() != VISIBLE) { - mUserEducationView.setAlpha(0); - mUserEducationView.setVisibility(VISIBLE); - updateUserEducationForLayoutDirection(); - - // Post so we have height of mUserEducationView - mUserEducationView.post(() -> { - final int viewHeight = mUserEducationView.getHeight(); - PointF stackPosition = mStackAnimationController.getStartPosition(); - final float translationY = stackPosition.y + (mBubbleSize / 2) - (viewHeight / 2); - mUserEducationView.setTranslationY(translationY); - mUserEducationView.animate() - .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION) - .setInterpolator(FAST_OUT_SLOW_IN) - .alpha(1); - }); - Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, true); - return true; - } - return false; - } - - private void updateUserEducationForLayoutDirection() { - if (mUserEducationView == null) { - return; - } - LinearLayout textLayout = mUserEducationView.findViewById(R.id.user_education_view); - TextView title = mUserEducationView.findViewById(R.id.user_education_title); - TextView description = mUserEducationView.findViewById(R.id.user_education_description); - boolean isLtr = - getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR; - if (isLtr) { - mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_LTR); - textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg); - title.setGravity(Gravity.LEFT); - description.setGravity(Gravity.LEFT); - } else { - mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_RTL); - textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl); - title.setGravity(Gravity.RIGHT); - description.setGravity(Gravity.RIGHT); - } - } - - /** - * If necessary, hides the user education view for the bubble stack. - * - * @param fromExpansion if true this indicates the hide is happening due to the bubble being - * expanded, false if due to a touch outside of the bubble stack. - */ - void hideStackUserEducation(boolean fromExpansion) { - if (mShouldShowUserEducation - && mUserEducationView.getVisibility() == VISIBLE - && !mAnimatingEducationAway) { - mAnimatingEducationAway = true; - mUserEducationView.animate() - .alpha(0) - .setDuration(fromExpansion - ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT - : ANIMATE_STACK_USER_EDUCATION_DURATION) - .withEndAction(() -> { - mAnimatingEducationAway = false; - mShouldShowUserEducation = shouldShowBubblesEducation(); - mUserEducationView.setVisibility(GONE); - }); - } - } - - /** - * If necessary, toggles the user education view for the manage button. This is shown when the - * bubble stack is expanded for the first time. - * - * @param show whether the user education view should show or not. - */ - void maybeShowManageEducation(boolean show) { - if (mManageEducationView == null) { - return; - } - if (show - && mShouldShowManageEducation - && mManageEducationView.getVisibility() != VISIBLE - && mIsExpanded - && mExpandedBubble.getExpandedView() != null) { - mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect, - () -> maybeShowManageEducation(false) /* run on click */); - Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true); - } else if (!show - && mManageEducationView.getVisibility() == VISIBLE - && !mAnimatingManageEducationAway) { - mManageEducationView.animate() - .alpha(0) - .setDuration(mIsExpansionAnimating - ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT - : ANIMATE_STACK_USER_EDUCATION_DURATION) - .withEndAction(() -> { - mAnimatingManageEducationAway = false; - mShouldShowManageEducation = shouldShowManageEducation(); - mManageEducationView.setVisibility(GONE); - }); - } - } - void showExpandedViewContents(int displayId) { if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null @@ -1791,7 +1684,9 @@ public class BubbleStackView extends FrameLayout cancelDelayedExpandCollapseSwitchAnimations(); mIsExpanded = true; - hideStackUserEducation(true /* fromExpansion */); + if (mStackEduView != null) { + mStackEduView.hide(true /* fromExpansion */); + } beforeExpandedViewAnimation(); mBubbleContainer.setActiveController(mExpandedAnimationController); @@ -1799,7 +1694,9 @@ public class BubbleStackView extends FrameLayout updatePointerPosition(); mExpandedAnimationController.expandFromStack(() -> { afterExpandedViewAnimation(); - maybeShowManageEducation(true); + if (mIsExpanded && mExpandedBubble.getExpandedView() != null) { + maybeShowManageEdu(); + } } /* after */); mExpandedViewContainer.setTranslationX(0); @@ -1936,7 +1833,9 @@ public class BubbleStackView extends FrameLayout .withEndActions(() -> { final BubbleViewProvider previouslySelected = mExpandedBubble; beforeExpandedViewAnimation(); - maybeShowManageEducation(false); + if (mManageEduView != null) { + mManageEduView.hide(false /* fromExpansion */); + } if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "animateCollapse"); @@ -2104,8 +2003,8 @@ public class BubbleStackView extends FrameLayout // from any location. if (!mIsExpanded || mShowingManage - || (mManageEducationView != null - && mManageEducationView.getVisibility() == VISIBLE)) { + || (mManageEduView != null + && mManageEduView.getVisibility() == VISIBLE)) { touchableRegion.setEmpty(); } } @@ -2289,7 +2188,7 @@ public class BubbleStackView extends FrameLayout if (flyoutMessage == null || flyoutMessage.message == null || !bubble.showFlyout() - || (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) + || (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) || isExpanded() || mIsExpansionAnimating || mIsGestureInProgress @@ -2398,7 +2297,7 @@ public class BubbleStackView extends FrameLayout * them. */ public void getTouchableRegion(Rect outRect) { - if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) { + if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) { // When user education shows then capture all touches outRect.set(0, 0, getWidth(), getHeight()); return; @@ -2770,18 +2669,6 @@ public class BubbleStackView extends FrameLayout return mExpandedBubble.getExpandedView().performBackPressIfNeeded(); } - /** Whether the educational view should appear for bubbles. **/ - private boolean shouldShowBubblesEducation() { - return BubbleDebugConfig.forceShowUserEducation(getContext()) - || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, false); - } - - /** Whether the educational view should appear for the expanded view "manage" button. **/ - private boolean shouldShowManageEducation() { - return BubbleDebugConfig.forceShowUserEducation(getContext()) - || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false); - } - /** For debugging only */ List<Bubble> getBubblesOnScreen() { List<Bubble> bubbles = new ArrayList<>(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt index c58ab31c4561..26a9773f9bb8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt @@ -18,52 +18,56 @@ package com.android.systemui.bubbles import android.content.Context import android.graphics.Color import android.graphics.Rect -import android.util.AttributeSet -import android.view.Gravity +import android.view.LayoutInflater import android.view.View import android.widget.Button import android.widget.LinearLayout import android.widget.TextView import com.android.internal.util.ContrastColorUtil import com.android.systemui.Interpolators +import com.android.systemui.Prefs +import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION import com.android.systemui.R /** - * Educational view to highlight the manage button that allows a user to configure the settings + * User education view to highlight the manage button that allows a user to configure the settings * for the bubble. Shown only the first time a user expands a bubble. */ -class ManageEducationView @JvmOverloads constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, - defStyleRes: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) { +class ManageEducationView constructor(context: Context) : LinearLayout(context) { + + private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView" + else BubbleDebugConfig.TAG_BUBBLES + + private val ANIMATE_DURATION : Long = 200 + private val ANIMATE_DURATION_SHORT : Long = 40 private val manageView by lazy { findViewById<View>(R.id.manage_education_view) } private val manageButton by lazy { findViewById<Button>(R.id.manage) } private val gotItButton by lazy { findViewById<Button>(R.id.got_it) } private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) } private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) } - private var isInflated = false + + private var isHiding = false init { - this.visibility = View.GONE - this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat() - this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE + LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this); + visibility = View.GONE + elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat() + + // BubbleStackView forces LTR by default + // since most of Bubble UI direction depends on positioning by the user. + // This view actually lays out differently in RTL, so we set layout LOCALE here. + layoutDirection = View.LAYOUT_DIRECTION_LOCALE } - override fun setLayoutDirection(direction: Int) { - super.setLayoutDirection(direction) - // setLayoutDirection runs before onFinishInflate - // so skip if views haven't inflated; otherwise we'll get NPEs - if (!isInflated) return - setDirection() + override fun setLayoutDirection(layoutDirection: Int) { + super.setLayoutDirection(layoutDirection) + setDrawableDirection() } override fun onFinishInflate() { super.onFinishInflate() - isInflated = true - setDirection() + layoutDirection = resources.configuration.layoutDirection setTextColor() } @@ -78,29 +82,35 @@ class ManageEducationView @JvmOverloads constructor( descTextView.setTextColor(textColor) } - fun setDirection() { + private fun setDrawableDirection() { manageView.setBackgroundResource( if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL) R.drawable.bubble_stack_user_education_bg_rtl else R.drawable.bubble_stack_user_education_bg) - titleTextView.gravity = Gravity.START - descTextView.gravity = Gravity.START } - fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) { + /** + * If necessary, toggles the user education view for the manage button. This is shown when the + * bubble stack is expanded for the first time. + * + * @param show whether the user education view should show or not. + */ + fun show(expandedView: BubbleExpandedView, rect : Rect) { + if (visibility == VISIBLE) return + alpha = 0f visibility = View.VISIBLE post { expandedView.getManageButtonBoundsOnScreen(rect) - with(hideMenu) { - manageButton - .setOnClickListener { - expandedView.findViewById<View>(R.id.settings_button).performClick() - this.run() - } - gotItButton.setOnClickListener { this.run() } - setOnClickListener { this.run() } - } + + manageButton + .setOnClickListener { + expandedView.findViewById<View>(R.id.settings_button).performClick() + hide(true /* isStackExpanding */) + } + gotItButton.setOnClickListener { hide(true /* isStackExpanding */) } + setOnClickListener { hide(true /* isStackExpanding */) } + with(manageView) { translationX = 0f val inset = resources.getDimensionPixelSize( @@ -109,9 +119,27 @@ class ManageEducationView @JvmOverloads constructor( } bringToFront() animate() - .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong()) + .setDuration(ANIMATE_DURATION) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .alpha(1f) } + setShouldShow(false) + } + + fun hide(isStackExpanding: Boolean) { + if (visibility != VISIBLE || isHiding) return + + animate() + .withStartAction { isHiding = true } + .alpha(0f) + .setDuration(if (isStackExpanding) ANIMATE_DURATION_SHORT else ANIMATE_DURATION) + .withEndAction { + isHiding = false + visibility = GONE + }; + } + + private fun setShouldShow(shouldShow: Boolean) { + Prefs.putBoolean(context, HAS_SEEN_BUBBLES_MANAGE_EDUCATION, !shouldShow) } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt new file mode 100644 index 000000000000..3e4c729d8315 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2020 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.bubbles + +import android.content.Context +import android.graphics.Color +import android.graphics.PointF +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.android.internal.util.ContrastColorUtil +import com.android.systemui.Interpolators +import com.android.systemui.Prefs +import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION +import com.android.systemui.R + +/** + * User education view to highlight the collapsed stack of bubbles. + * Shown only the first time a user taps the stack. + */ +class StackEducationView constructor(context: Context) : LinearLayout(context){ + + private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView" + else BubbleDebugConfig.TAG_BUBBLES + + private val ANIMATE_DURATION : Long = 200 + private val ANIMATE_DURATION_SHORT : Long = 40 + + private val view by lazy { findViewById<View>(R.id.stack_education_layout) } + private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) } + private val descTextView by lazy { findViewById<TextView>(R.id.stack_education_description) } + + private var isHiding = false + + init { + LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this); + + visibility = View.GONE + elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat() + + // BubbleStackView forces LTR by default + // since most of Bubble UI direction depends on positioning by the user. + // This view actually lays out differently in RTL, so we set layout LOCALE here. + layoutDirection = View.LAYOUT_DIRECTION_LOCALE + } + + override fun setLayoutDirection(layoutDirection: Int) { + super.setLayoutDirection(layoutDirection) + setDrawableDirection() + } + + override fun onFinishInflate() { + super.onFinishInflate() + layoutDirection = resources.configuration.layoutDirection + setTextColor() + } + + private fun setTextColor() { + val ta = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent, + android.R.attr.textColorPrimaryInverse)) + val bgColor = ta.getColor(0 /* index */, Color.BLACK) + var textColor = ta.getColor(1 /* index */, Color.WHITE) + ta.recycle() + textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true) + titleTextView.setTextColor(textColor) + descTextView.setTextColor(textColor) + } + + private fun setDrawableDirection() { + view.setBackgroundResource( + if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) + R.drawable.bubble_stack_user_education_bg + else R.drawable.bubble_stack_user_education_bg_rtl) + } + + /** + * If necessary, shows the user education view for the bubble stack. This appears the first + * time a user taps on a bubble. + * + * @return true if user education was shown, false otherwise. + */ + fun show(stackPosition: PointF) : Boolean{ + if (visibility == VISIBLE) return false + + setAlpha(0f) + setVisibility(View.VISIBLE) + post { + with(view) { + val bubbleSize = context.resources.getDimensionPixelSize( + R.dimen.individual_bubble_size) + translationY = stackPosition.y + bubbleSize / 2 - getHeight() / 2 + } + animate() + .setDuration(ANIMATE_DURATION) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .alpha(1f) + } + setShouldShow(false) + return true + } + + /** + * If necessary, hides the stack education view. + * + * @param fromExpansion if true this indicates the hide is happening due to the bubble being + * expanded, false if due to a touch outside of the bubble stack. + */ + fun hide(fromExpansion: Boolean) { + if (visibility != VISIBLE || isHiding) return + + animate() + .alpha(0f) + .setDuration(if (fromExpansion) ANIMATE_DURATION_SHORT else ANIMATE_DURATION) + .withEndAction { visibility = GONE } + } + + private fun setShouldShow(shouldShow: Boolean) { + Prefs.putBoolean(context, HAS_SEEN_BUBBLES_EDUCATION, !shouldShow) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index c683a87d6282..31830b94e8e4 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -72,8 +72,13 @@ class ControlAdapter( TYPE_CONTROL -> { ControlHolder( layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply { - layoutParams.apply { + (layoutParams as ViewGroup.MarginLayoutParams).apply { width = ViewGroup.LayoutParams.MATCH_PARENT + // Reset margins as they will be set through the decoration + topMargin = 0 + bottomMargin = 0 + leftMargin = 0 + rightMargin = 0 } elevation = this@ControlAdapter.elevation background = parent.context.getDrawable( @@ -386,7 +391,7 @@ class MarginItemDecorator( val type = parent.adapter?.getItemViewType(position) if (type == ControlAdapter.TYPE_CONTROL) { outRect.apply { - top = topMargin + top = topMargin * 2 // Use double margin, as we are not setting bottom left = sideMargins right = sideMargins bottom = 0 diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 7281faf1a2c4..b606201cc803 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -22,7 +22,6 @@ import com.android.systemui.InitController; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; -import com.android.systemui.onehanded.dagger.OneHandedModule; import com.android.systemui.pip.phone.dagger.PipModule; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; @@ -37,7 +36,6 @@ import dagger.Subcomponent; DefaultComponentBinder.class, DependencyProvider.class, DependencyBinder.class, - OneHandedModule.class, PipModule.class, SystemServicesModule.class, SystemUIBinder.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 43d7d7f22e6f..3b225d5313c1 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -62,7 +62,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.wmshell.WindowManagerShellModule; +import com.android.systemui.wmshell.WMShellModule; import javax.inject.Named; @@ -77,7 +77,7 @@ import dagger.Provides; @Module(includes = { DividerModule.class, QSModule.class, - WindowManagerShellModule.class + WMShellModule.class }) public abstract class SystemUIDefaultModule { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 1a1cc072c6bf..19b0ea1db04e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -33,9 +33,6 @@ import java.io.PrintWriter; import javax.inject.Inject; -import dagger.Reusable; - -@Reusable // Don't create multiple DozeServices. public class DozeService extends DreamService implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> { private static final String TAG = "DozeService"; @@ -60,7 +57,7 @@ public class DozeService extends DreamService setWindowless(true); mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */); - DozeComponent dozeComponent = mDozeComponentBuilder.build(); + DozeComponent dozeComponent = mDozeComponentBuilder.build(this); mDozeMachine = dozeComponent.getDozeMachine(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java index 247285434df9..05050f905e60 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.doze.dagger; import com.android.systemui.doze.DozeMachine; import com.android.systemui.doze.DozeService; +import dagger.BindsInstance; import dagger.Subcomponent; /** @@ -30,7 +31,7 @@ public interface DozeComponent { /** Simple Builder for {@link DozeComponent}. */ @Subcomponent.Factory interface Builder { - DozeComponent build(); + DozeComponent build(@BindsInstance DozeMachine.Service dozeMachineService); } /** Supply a {@link DozeMachine}. */ diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java index a12e280fcca6..04f7c368fdc4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java @@ -33,7 +33,6 @@ import com.android.systemui.doze.DozeScreenBrightness; import com.android.systemui.doze.DozeScreenState; import com.android.systemui.doze.DozeScreenStatePreventingAdapter; import com.android.systemui.doze.DozeSensors; -import com.android.systemui.doze.DozeService; import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter; import com.android.systemui.doze.DozeTriggers; import com.android.systemui.doze.DozeUi; @@ -52,9 +51,9 @@ public abstract class DozeModule { @Provides @DozeScope @WrappedService - static DozeMachine.Service providesWrappedService(DozeService dozeService, DozeHost dozeHost, - DozeParameters dozeParameters) { - DozeMachine.Service wrappedService = dozeService; + static DozeMachine.Service providesWrappedService(DozeMachine.Service dozeMachineService, + DozeHost dozeHost, DozeParameters dozeParameters) { + DozeMachine.Service wrappedService = dozeMachineService; wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost); wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded( wrappedService, dozeParameters); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index b2e8cb04c739..a003d8365810 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -17,7 +17,7 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.PageIndicator -import com.android.systemui.statusbar.notification.VisualStabilityManager +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.Utils import com.android.systemui.util.animation.UniqueObjectHostView @@ -155,6 +155,7 @@ class MediaCarouselController @Inject constructor( inflateSettingsButton() mediaContent = mediaCarousel.requireViewById(R.id.media_carousel) configurationController.addCallback(configListener) + // TODO (b/162832756): remove visual stability manager when migrating to new pipeline visualStabilityCallback = VisualStabilityManager.Callback { if (needsReordering) { needsReordering = false diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java index 90e7e12b2b47..bb59449d114d 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java @@ -49,7 +49,7 @@ import javax.inject.Inject; * Manages and manipulates the one handed states, transitions, and gesture for phones. */ @SysUISingleton -public class OneHandedManagerImpl implements OneHandedManager, Dumpable { +public class OneHandedController implements Dumpable { private static final String TAG = "OneHandedManager"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = "persist.debug.one_handed_offset_percentage"; @@ -106,7 +106,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { * Constructor of OneHandedManager */ @Inject - public OneHandedManagerImpl(Context context, + public OneHandedController(Context context, CommandQueue commandQueue, DisplayController displayController, NavigationModeController navigationModeController, @@ -137,7 +137,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { */ // TODO(b/161980408): Should remove extra constructor. @VisibleForTesting - OneHandedManagerImpl(Context context, + OneHandedController(Context context, CommandQueue commandQueue, DisplayController displayController, OneHandedDisplayAreaOrganizer displayAreaOrganizer, @@ -194,7 +194,6 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { /** * Enters one handed mode. */ - @Override public void startOneHanded() { if (!mDisplayAreaOrganizer.isInOneHanded()) { final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction); @@ -206,7 +205,6 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { /** * Exits one handed mode. */ - @Override public void stopOneHanded() { if (mDisplayAreaOrganizer.isInOneHanded()) { mDisplayAreaOrganizer.scheduleOffset(0, 0); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java index 1420811b9b30..f3be699ab821 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java @@ -103,7 +103,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, } /** - * Notified by {@link OneHandedManager}, when user update settings of Enabled or Disabled + * Notified by {@link OneHandedController}, when user update settings of Enabled or Disabled * * @param isEnabled is one handed settings enabled or not */ @@ -264,7 +264,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, } /** - * The touch(gesture) events to notify {@link OneHandedManager} start or stop one handed + * The touch(gesture) events to notify {@link OneHandedController} start or stop one handed */ public interface OneHandedGestureEventCallback { /** diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java index 0a7eb1bdada0..8265da6a5f14 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java @@ -63,7 +63,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa } /** - * Notified by {@link OneHandedManagerImpl}, when user update settings of Enabled or Disabled + * Notified by {@link OneHandedController}, when user update settings of Enabled or Disabled * * @param isEnabled is one handed settings enabled or not */ @@ -166,7 +166,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa } /** - * The touch(gesture) events to notify {@link OneHandedManager} start or stop one handed + * The touch(gesture) events to notify {@link OneHandedController} start or stop one handed */ public interface OneHandedTouchEventCallback { /** diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java index cebcd4ceabe9..3348a06d5cac 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java @@ -59,7 +59,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum "com.android.internal.systemui.onehanded.gestural"; private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; - private final OneHandedManagerImpl mOneHandedManager; + private final OneHandedController mOneHandedController; private final CommandQueue mCommandQueue; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); private final IOverlayManager mOverlayManager; @@ -74,8 +74,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum OneHandedEvents.writeEvent(enabled ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF); - if (mOneHandedManager != null) { - mOneHandedManager.setOneHandedEnabled(enabled); + if (mOneHandedController != null) { + mOneHandedController.setOneHandedEnabled(enabled); } // Also checks swipe to notification settings since they all need gesture overlay. @@ -125,8 +125,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF); - if (mOneHandedManager != null) { - mOneHandedManager.setTaskChangeToExit(enabled); + if (mOneHandedController != null) { + mOneHandedController.setTaskChangeToExit(enabled); } } }; @@ -138,8 +138,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum final boolean enabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver()); - if (mOneHandedManager != null) { - mOneHandedManager.setSwipeToNotificationEnabled(enabled); + if (mOneHandedController != null) { + mOneHandedController.setSwipeToNotificationEnabled(enabled); } // Also checks one handed mode settings since they all need gesture overlay. @@ -152,14 +152,14 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum @Inject public OneHandedUI(Context context, CommandQueue commandQueue, - OneHandedManagerImpl oneHandedManager, + OneHandedController oneHandedController, ScreenLifecycle screenLifecycle) { super(context); if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) { Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off"); mCommandQueue = null; - mOneHandedManager = null; + mOneHandedController = null; mOverlayManager = null; mTimeoutHandler = null; mScreenLifecycle = null; @@ -167,7 +167,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum } mCommandQueue = commandQueue; - mOneHandedManager = oneHandedManager; + mOneHandedController = oneHandedController; mTimeoutHandler = OneHandedTimeoutHandler.get(); mScreenLifecycle = screenLifecycle; mOverlayManager = IOverlayManager.Stub.asInterface( @@ -260,13 +260,13 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum } private void updateSettings() { - mOneHandedManager.setOneHandedEnabled(OneHandedSettingsUtil + mOneHandedController.setOneHandedEnabled(OneHandedSettingsUtil .getSettingsOneHandedModeEnabled(mContext.getContentResolver())); mTimeoutHandler.setTimeout(OneHandedSettingsUtil .getSettingsOneHandedModeTimeout(mContext.getContentResolver())); - mOneHandedManager.setTaskChangeToExit(OneHandedSettingsUtil + mOneHandedController.setTaskChangeToExit(OneHandedSettingsUtil .getSettingsTapsAppToExit(mContext.getContentResolver())); - mOneHandedManager.setSwipeToNotificationEnabled(OneHandedSettingsUtil + mOneHandedController.setSwipeToNotificationEnabled(OneHandedSettingsUtil .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver())); } @@ -295,7 +295,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum * Trigger one handed more */ public void startOneHanded() { - mOneHandedManager.startOneHanded(); + mOneHandedController.startOneHanded(); OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); } @@ -303,7 +303,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum * Dismiss one handed more */ public void stopOneHanded() { - mOneHandedManager.stopOneHanded(); + mOneHandedController.stopOneHanded(); OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); } @@ -314,8 +314,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum final String innerPrefix = " "; pw.println(TAG + "one handed states: "); - if (mOneHandedManager != null) { - ((OneHandedManagerImpl) mOneHandedManager).dump(fd, pw, args); + if (mOneHandedController != null) { + mOneHandedController.dump(fd, pw, args); } if (mTimeoutHandler != null) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index 4931388fe362..fd8ca8044acf 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -31,8 +31,6 @@ import com.android.systemui.Interpolators; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import javax.inject.Inject; - /** * Controller class of PiP animations (both from and to PiP mode). */ @@ -88,7 +86,6 @@ public class PipAnimationController { return handler; }); - @Inject PipAnimationController(PipSurfaceTransactionHelper helper) { mSurfaceTransactionHelper = helper; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index d6aa61b0c767..b464e8adea59 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -40,13 +40,10 @@ import android.view.Gravity; import android.window.WindowContainerTransaction; import com.android.systemui.dagger.SysUISingleton; -import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import java.io.PrintWriter; -import javax.inject.Inject; - /** * Handles bounds calculation for PIP on Phone and other form factors, it keeps tracking variant * state changes originated from Window Manager and is the source of truth for PiP window bounds. @@ -57,10 +54,8 @@ public class PipBoundsHandler { private static final String TAG = PipBoundsHandler.class.getSimpleName(); private static final float INVALID_SNAP_FRACTION = -1f; - private final Context mContext; private final PipSnapAlgorithm mSnapAlgorithm; private final DisplayInfo mDisplayInfo = new DisplayInfo(); - private final DisplayController mDisplayController; private DisplayLayout mDisplayLayout; private ComponentName mLastPipComponentName; @@ -82,25 +77,10 @@ public class PipBoundsHandler { private boolean mIsShelfShowing; private int mShelfHeight; - private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener = - new DisplayController.OnDisplaysChangedListener() { - @Override - public void onDisplayAdded(int displayId) { - if (displayId == mContext.getDisplayId()) { - mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId)); - } - } - }; - - @Inject - public PipBoundsHandler(Context context, PipSnapAlgorithm pipSnapAlgorithm, - DisplayController displayController) { - mContext = context; - mSnapAlgorithm = pipSnapAlgorithm; + public PipBoundsHandler(Context context) { + mSnapAlgorithm = new PipSnapAlgorithm(context); mDisplayLayout = new DisplayLayout(); - mDisplayController = displayController; - mDisplayController.addDisplayWindowListener(mDisplaysChangedListener); - reloadResources(); + reloadResources(context); // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload // resources as it would clobber mAspectRatio when entering PiP from fullscreen which // triggers a configuration change and the resources to be reloaded. @@ -110,8 +90,8 @@ public class PipBoundsHandler { /** * TODO: move the resources to SysUI package. */ - private void reloadResources() { - final Resources res = mContext.getResources(); + private void reloadResources(Context context) { + final Resources res = context.getResources(); mDefaultAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); mDefaultStackGravity = res.getInteger( @@ -133,6 +113,19 @@ public class PipBoundsHandler { com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); } + /** + * Sets or update latest {@link DisplayLayout} when new display added or rotation callbacks + * from {@link DisplayController.OnDisplaysChangedListener} + * @param newDisplayLayout latest {@link DisplayLayout} + */ + public void setDisplayLayout(DisplayLayout newDisplayLayout) { + mDisplayLayout.set(newDisplayLayout); + } + + /** + * Update the Min edge size for {@link PipSnapAlgorithm} to calculate corresponding bounds + * @param minEdgeSize + */ public void setMinEdgeSize(int minEdgeSize) { mCurrentMinSize = minEdgeSize; } @@ -217,6 +210,14 @@ public class PipBoundsHandler { return mReentrySnapFraction != INVALID_SNAP_FRACTION; } + /** + * The {@link PipSnapAlgorithm} is couple on display bounds + * @return {@link PipSnapAlgorithm}. + */ + public PipSnapAlgorithm getSnapAlgorithm() { + return mSnapAlgorithm; + } + public Rect getDisplayBounds() { return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); } @@ -237,8 +238,8 @@ public class PipBoundsHandler { /** * Responds to IPinnedStackListener on configuration change. */ - public void onConfigurationChanged() { - reloadResources(); + public void onConfigurationChanged(Context context) { + reloadResources(context); } /** @@ -300,10 +301,10 @@ public class PipBoundsHandler { * aren't in PIP because the rotation layout is used to calculate the proper insets for the * next enter animation into PIP. */ - public void onDisplayRotationChangedNotInPip(int toRotation) { + public void onDisplayRotationChangedNotInPip(Context context, int toRotation) { // Update the display layout, note that we have to do this on every rotation even if we // aren't in PIP since we need to update the display layout to get the right resources - mDisplayLayout.rotateTo(mContext.getResources(), toRotation); + mDisplayLayout.rotateTo(context.getResources(), toRotation); // Populate the new {@link #mDisplayInfo}. // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation, @@ -319,7 +320,8 @@ public class PipBoundsHandler { * * @return {@code true} if internal {@link DisplayInfo} is rotated, {@code false} otherwise. */ - public boolean onDisplayRotationChanged(Rect outBounds, Rect oldBounds, Rect outInsetBounds, + public boolean onDisplayRotationChanged(Context context, Rect outBounds, Rect oldBounds, + Rect outInsetBounds, int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) { // Bail early if the event is not sent to current {@link #mDisplayInfo} if ((displayId != mDisplayInfo.displayId) || (fromRotation == toRotation)) { @@ -342,7 +344,7 @@ public class PipBoundsHandler { final float snapFraction = getSnapFraction(postChangeStackBounds); // Update the display layout - mDisplayLayout.rotateTo(mContext.getResources(), toRotation); + mDisplayLayout.rotateTo(context.getResources(), toRotation); // Populate the new {@link #mDisplayInfo}. // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation, @@ -546,5 +548,6 @@ public class PipBoundsHandler { pw.println(innerPrefix + "mImeHeight=" + mImeHeight); pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight); + pw.println(innerPrefix + "mSnapAlgorithm" + mSnapAlgorithm); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java index a9b32d917d85..5d23e4207c33 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java @@ -22,24 +22,18 @@ import android.graphics.PointF; import android.graphics.Rect; import android.util.Size; -import javax.inject.Inject; - /** * Calculates the snap targets and the snap position for the PIP given a position and a velocity. * All bounds are relative to the display top/left. */ public class PipSnapAlgorithm { - private final Context mContext; - private final float mDefaultSizePercent; private final float mMinAspectRatioForMinSize; private final float mMaxAspectRatioForMinSize; - @Inject public PipSnapAlgorithm(Context context) { Resources res = context.getResources(); - mContext = context; mDefaultSizePercent = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); mMaxAspectRatioForMinSize = res.getFloat( diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java index e88451ca00b5..3e98169c5b2b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java @@ -27,8 +27,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.wm.shell.R; -import javax.inject.Inject; - /** * Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition. */ @@ -46,7 +44,6 @@ public class PipSurfaceTransactionHelper implements ConfigurationController.Conf private final RectF mTmpDestinationRectF = new RectF(); private final Rect mTmpDestinationRect = new Rect(); - @Inject public PipSurfaceTransactionHelper(Context context, ConfigurationController configController) { final Resources res = context.getResources(); mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 0e60c83b8392..cfc544709725 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -70,8 +70,6 @@ import java.util.Map; import java.util.Objects; import java.util.function.Consumer; -import javax.inject.Inject; - /** * Manages PiP tasks such as resize and offset. * @@ -205,12 +203,10 @@ public class PipTaskOrganizer extends TaskOrganizer implements */ private boolean mShouldDeferEnteringPip; - @Inject public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @Nullable Divider divider, @NonNull DisplayController displayController, - @NonNull PipAnimationController pipAnimationController, @NonNull PipUiEventLogger pipUiEventLogger) { mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); @@ -218,7 +214,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; - mPipAnimationController = pipAnimationController; + mPipAnimationController = new PipAnimationController(mSurfaceTransactionHelper); mPipUiEventLoggerLogger = pipUiEventLogger; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitDivider = divider; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java index 7ce2028b5f1b..8bcaa8ab5404 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java @@ -22,9 +22,6 @@ import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.systemui.dagger.SysUISingleton; -import javax.inject.Inject; - - /** * Helper class that ends PiP log to UiEvent, see also go/uievent */ @@ -35,7 +32,6 @@ public class PipUiEventLogger { private TaskInfo mTaskInfo; - @Inject public PipUiEventLogger(UiEventLogger uiEventLogger) { mUiEventLogger = uiEventLogger; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index e2aa09c862f3..ac076415cd1a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -46,7 +46,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; -import com.android.systemui.pip.PipSnapAlgorithm; +import com.android.systemui.pip.PipSurfaceTransactionHelper; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; @@ -56,6 +56,7 @@ import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.FloatingContentCoordinator; @@ -82,15 +83,17 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private final Rect mTmpNormalBounds = new Rect(); protected final Rect mReentryBounds = new Rect(); - private PipBoundsHandler mPipBoundsHandler; + private DisplayController mDisplayController; private InputConsumerController mInputConsumerController; + private PipAppOpsListener mAppOpsListener; private PipMediaController mMediaController; private PipTouchHandler mTouchHandler; private PipTaskOrganizer mPipTaskOrganizer; - private PipAppOpsListener mAppOpsListener; + private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper; private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener; private boolean mIsInFixedRotation; + protected PipBoundsHandler mPipBoundsHandler; protected PipMenuActivityController mMenuController; /** @@ -101,15 +104,16 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) { // Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update // the display layout in the bounds handler in this case. - mPipBoundsHandler.onDisplayRotationChangedNotInPip(toRotation); + mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, toRotation); return; } // If there is an animation running (ie. from a shelf offset), then ensure that we calculate // the bounds for the next orientation using the destination bounds of the animation // TODO: Techincally this should account for movement animation bounds as well Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds(); - final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mTmpNormalBounds, - currentBounds, mTmpInsetBounds, displayId, fromRotation, toRotation, t); + final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mContext, + mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation, + toRotation, t); if (changed) { // If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the // movement bounds @@ -135,16 +139,22 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private DisplayController.OnDisplaysChangedListener mFixedRotationListener = new DisplayController.OnDisplaysChangedListener() { - @Override - public void onFixedRotationStarted(int displayId, int newRotation) { - mIsInFixedRotation = true; - } - - @Override - public void onFixedRotationFinished(int displayId) { - mIsInFixedRotation = false; - } - }; + @Override + public void onFixedRotationStarted(int displayId, int newRotation) { + mIsInFixedRotation = true; + } + + @Override + public void onFixedRotationFinished(int displayId) { + mIsInFixedRotation = false; + } + + @Override + public void onDisplayAdded(int displayId) { + mPipBoundsHandler.setDisplayLayout( + mDisplayController.getDisplayLayout(displayId)); + } + }; /** * Handler for system task stack changes. @@ -228,12 +238,15 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Override public void onConfigurationChanged() { - mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged()); + mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged(mContext)); } @Override public void onAspectRatioChanged(float aspectRatio) { - mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio)); + mHandler.post(() -> { + mPipBoundsHandler.onAspectRatioChanged(aspectRatio); + mTouchHandler.onAspectRatioChanged(); + }); } } @@ -253,14 +266,12 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Inject public PipManager(Context context, BroadcastDispatcher broadcastDispatcher, @PipMenuActivityClass Class<?> pipMenuActivityClass, + ConfigurationController configController, + DeviceConfigProxy deviceConfig, DisplayController displayController, + Divider divider, FloatingContentCoordinator floatingContentCoordinator, - DeviceConfigProxy deviceConfig, - PipBoundsHandler pipBoundsHandler, - PipSnapAlgorithm pipSnapAlgorithm, - PipTaskOrganizer pipTaskOrganizer, SysUiState sysUiState, - ConfigurationController configController, PipUiEventLogger pipUiEventLogger) { mContext = context; mActivityManager = ActivityManager.getService(); @@ -273,8 +284,11 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio } ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); - mPipBoundsHandler = pipBoundsHandler; - mPipTaskOrganizer = pipTaskOrganizer; + mDisplayController = displayController; + mPipBoundsHandler = new PipBoundsHandler(mContext); + mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler, + mPipSurfaceTransactionHelper, divider, mDisplayController, pipUiEventLogger); mPipTaskOrganizer.registerPipTransitionCallback(this); mInputConsumerController = InputConsumerController.getPipInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); @@ -282,8 +296,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMediaController, mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, - floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState, - pipUiEventLogger); + floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); displayController.addDisplayChangingController(mRotationController); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index 9c42f8bff378..2800bb938149 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -364,6 +364,10 @@ public class PipResizeGestureHandler { mUserResizeBounds.set(bounds); } + void invalidateUserResizeBounds() { + mUserResizeBounds.setEmpty(); + } + Rect getUserResizeBounds() { return mUserResizeBounds; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index b20ea4e5c836..1b84c1417c51 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -58,7 +58,6 @@ import com.android.systemui.R; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; -import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.InputConsumerController; @@ -99,7 +98,6 @@ public class PipTouchHandler { private IPinnedStackController mPinnedStackController; private final PipMenuActivityController mMenuController; - private final PipSnapAlgorithm mSnapAlgorithm; private final AccessibilityManager mAccessibilityManager; private boolean mShowPipMenuOnAnimationEnd = false; @@ -216,20 +214,19 @@ public class PipTouchHandler { PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator, DeviceConfigProxy deviceConfig, - PipSnapAlgorithm pipSnapAlgorithm, SysUiState sysUiState, PipUiEventLogger pipUiEventLogger) { // Initialize the Pip input consumer mContext = context; mActivityManager = activityManager; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); + mPipBoundsHandler = pipBoundsHandler; mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mMenuController = menuController; mMenuController.addListener(new PipMenuListener()); - mSnapAlgorithm = pipSnapAlgorithm; mGesture = new DefaultPipTouchGesture(); mMotionHelper = new PipMotionHelper(mContext, pipTaskOrganizer, mMenuController, - mSnapAlgorithm, floatingContentCoordinator); + mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, deviceConfig, pipTaskOrganizer, this::getMovementBounds, @@ -248,11 +245,10 @@ public class PipTouchHandler { inputConsumerController.setInputListener(this::handleTouchEvent); inputConsumerController.setRegistrationListener(this::onRegistrationChanged); - mPipBoundsHandler = pipBoundsHandler; mFloatingContentCoordinator = floatingContentCoordinator; mConnection = new PipAccessibilityInteractionConnection(mContext, mMotionHelper, - pipTaskOrganizer, pipSnapAlgorithm, this::onAccessibilityShowMenu, - this::updateMovementBounds, mHandler); + pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(), + this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler); mPipUiEventLogger = pipUiEventLogger; @@ -419,15 +415,29 @@ public class PipTouchHandler { public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) { final Rect toMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds, + toMovementBounds, 0); final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets; if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) { outBounds.offsetTo(outBounds.left, toMovementBounds.bottom); } } + /** + * Responds to IPinnedStackListener on resetting aspect ratio for the pinned window. + */ + public void onAspectRatioChanged() { + mPipResizeGestureHandler.invalidateUserResizeBounds(); + } + public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect curBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) { + // Set the user resized bounds equal to the new normal bounds in case they were + // invalidated (e.g. by an aspect ratio change). + if (mPipResizeGestureHandler.getUserResizeBounds().isEmpty()) { + mPipResizeGestureHandler.setUserResizeBounds(normalBounds); + } + final int bottomOffset = mIsImeShowing ? mImeHeight : 0; final boolean fromDisplayRotationChanged = (mDisplayRotation != displayRotation); if (fromDisplayRotationChanged) { @@ -437,26 +447,26 @@ public class PipTouchHandler { // Re-calculate the expanded bounds mNormalBounds.set(normalBounds); Rect normalMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds, - bottomOffset); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mNormalBounds, insetBounds, + normalMovementBounds, bottomOffset); if (mMovementBounds.isEmpty()) { // mMovementBounds is not initialized yet and a clean movement bounds without // bottom offset shall be used later in this function. - mSnapAlgorithm.getMovementBounds(curBounds, insetBounds, mMovementBounds, - 0 /* bottomOffset */); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, + mMovementBounds, 0 /* bottomOffset */); } // Calculate the expanded size float aspectRatio = (float) normalBounds.width() / normalBounds.height(); Point displaySize = new Point(); mContext.getDisplay().getRealSize(displaySize); - Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, + Size expandedSize = mPipBoundsHandler.getSnapAlgorithm().getSizeForAspectRatio(aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y); mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight()); Rect expandedMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds, - bottomOffset); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mExpandedBounds, insetBounds, + expandedMovementBounds, bottomOffset); mPipResizeGestureHandler.updateMinSize(mNormalBounds.width(), mNormalBounds.height()); mPipResizeGestureHandler.updateMaxSize(mExpandedBounds.width(), mExpandedBounds.height()); @@ -476,7 +486,7 @@ public class PipTouchHandler { } else { final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu(); final Rect toMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(curBounds, insetBounds, + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, toMovementBounds, mIsImeShowing ? mImeHeight : 0); final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets; // This is to handle landscape fullscreen IMEs, don't apply the extra offset in this @@ -487,8 +497,8 @@ public class PipTouchHandler { if (isExpanded) { curBounds.set(mExpandedBounds); - mSnapAlgorithm.applySnapFraction(curBounds, toMovementBounds, - mSavedSnapFraction); + mPipBoundsHandler.getSnapAlgorithm().applySnapFraction(curBounds, + toMovementBounds, mSavedSnapFraction); } if (prevBottom < toBottom) { @@ -595,7 +605,7 @@ public class PipTouchHandler { .spring(DynamicAnimation.TRANSLATION_Y, mTargetViewContainer.getHeight(), mTargetSpringConfig) - .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE)) + .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE)) .start(); ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition( @@ -831,8 +841,8 @@ public class PipTouchHandler { if (mDeferResizeToNormalBoundsUntilRotation == -1) { Rect restoreBounds = new Rect(getUserResizeBounds()); Rect restoredMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds, - restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(restoreBounds, + mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction, restoredMovementBounds, mMovementBounds, false /* immediate */); mSavedSnapFraction = -1f; @@ -1012,25 +1022,25 @@ public class PipTouchHandler { mMenuController.hideMenu(); } } - }; + } /** * Updates the current movement bounds based on whether the menu is currently visible and * resized. */ private void updateMovementBounds() { - mSnapAlgorithm.getMovementBounds(mMotionHelper.getBounds(), mInsetBounds, - mMovementBounds, mIsImeShowing ? mImeHeight : 0); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mMotionHelper.getBounds(), + mInsetBounds, mMovementBounds, mIsImeShowing ? mImeHeight : 0); mMotionHelper.setCurrentMovementBounds(mMovementBounds); boolean isMenuExpanded = mMenuState == MENU_STATE_FULL; mPipBoundsHandler.setMinEdgeSize( - isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0); + isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0); } private Rect getMovementBounds(Rect curBounds) { Rect movementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(curBounds, mInsetBounds, + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, mInsetBounds, movementBounds, mIsImeShowing ? mImeHeight : 0); return movementBounds; } @@ -1062,6 +1072,7 @@ public class PipTouchHandler { pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction); pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge); pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets); + mPipBoundsHandler.dump(pw, innerPrefix); mTouchState.dump(pw, innerPrefix); mMotionHelper.dump(pw, innerPrefix); if (mPipResizeGestureHandler != null) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 74dc003bd8b6..3aef0dacad72 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import android.annotation.NonNull; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; import android.app.ActivityTaskManager; @@ -54,11 +55,14 @@ import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSurfaceTransactionHelper; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.stackdivider.Divider; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.wm.shell.common.DisplayController; import java.util.ArrayList; import java.util.List; @@ -111,6 +115,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private Context mContext; private PipBoundsHandler mPipBoundsHandler; private PipTaskOrganizer mPipTaskOrganizer; + private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper; private IActivityTaskManager mActivityTaskManager; private MediaSessionManager mMediaSessionManager; private int mState = STATE_NO_PIP; @@ -229,17 +234,17 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Inject public PipManager(Context context, BroadcastDispatcher broadcastDispatcher, - PipBoundsHandler pipBoundsHandler, - PipTaskOrganizer pipTaskOrganizer, - PipSurfaceTransactionHelper surfaceTransactionHelper, - Divider divider) { + ConfigurationController configController, + DisplayController displayController, + Divider divider, + @NonNull PipUiEventLogger pipUiEventLogger) { if (mInitialized) { return; } mInitialized = true; mContext = context; - mPipBoundsHandler = pipBoundsHandler; + mPipBoundsHandler = new PipBoundsHandler(mContext); // Ensure that we have the display info in case we get calls to update the bounds before the // listener calls back final DisplayInfo displayInfo = new DisplayInfo(); @@ -248,7 +253,9 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mResizeAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); - mPipTaskOrganizer = pipTaskOrganizer; + mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler, + mPipSurfaceTransactionHelper, divider, displayController, pipUiEventLogger); mPipTaskOrganizer.registerPipTransitionCallback(this); mActivityTaskManager = ActivityTaskManager.getService(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index f272be477f51..1cd1b60ac1ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -21,7 +21,6 @@ import android.content.res.Resources; import android.os.Handler; import android.os.Trace; import android.os.UserHandle; -import android.service.notification.NotificationListenerService.Ranking; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -35,9 +34,9 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index e0352671e204..e9442499a8ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -103,6 +103,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll private boolean mIsDozing; /** + * If the status bar is currently expanded or not. + */ + private boolean mIsExpanded; + + /** * Current {@link #mDozeAmount} animator. */ private ValueAnimator mDarkAnimator; @@ -190,6 +195,26 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll } @Override + public boolean isExpanded() { + return mIsExpanded; + } + + @Override + public boolean setPanelExpanded(boolean expanded) { + if (mIsExpanded == expanded) { + return false; + } + mIsExpanded = expanded; + String tag = getClass().getSimpleName() + "#setIsExpanded"; + DejankUtils.startDetectingBlockingIpcs(tag); + for (RankedListener rl : new ArrayList<>(mListeners)) { + rl.mListener.onExpandedChanged(mIsExpanded); + } + DejankUtils.stopDetectingBlockingIpcs(tag); + return true; + } + + @Override public float getInterpolatedDozeAmount() { return mDozeInterpolator.getInterpolation(mDozeAmount); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java index 07b35502478f..9f8fe35dfbc9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -75,6 +75,14 @@ public interface SysuiStatusBarStateController extends StatusBarStateController */ void setDozeAmount(float dozeAmount, boolean animated); + + /** + * Update the expanded state from {@link StatusBar}'s perspective + * @param expanded are we expanded? + * @return {@code true} if the state changed, else {@code false} + */ + boolean setPanelExpanded(boolean expanded); + /** * Sets whether to leave status bar open when hiding keyguard */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index 5b073cec9764..44550b72e521 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -41,8 +41,8 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index e1ff872456eb..b5f1c7ff9b62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -48,6 +48,7 @@ import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt index 57f8a6a3abef..3bfdf5caa9b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt @@ -16,9 +16,9 @@ package com.android.systemui.statusbar.notification.collection +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection /** * Stores the state that [ShadeListBuilder] assigns to this [ListEntry] @@ -35,7 +35,6 @@ data class ListAttachState private constructor( * parent's section. Null if not attached to the list. */ var section: NotifSection?, - var sectionIndex: Int, /** * If a [NotifFilter] is excluding this entry from the list, then that filter. Always null for @@ -46,25 +45,32 @@ data class ListAttachState private constructor( /** * The [NotifPromoter] promoting this entry to top-level, if any. Always null for [GroupEntry]s. */ - var promoter: NotifPromoter? + var promoter: NotifPromoter?, + + /** + * If the [VisualStabilityManager] is suppressing group or section changes for this entry, + * suppressedChanges will contain the new parent or section that we would have assigned to + * the entry had it not been suppressed by the VisualStabilityManager. + */ + var suppressedChanges: SuppressedAttachState ) { /** Copies the state of another instance. */ fun clone(other: ListAttachState) { parent = other.parent section = other.section - sectionIndex = other.sectionIndex excludingFilter = other.excludingFilter promoter = other.promoter + suppressedChanges.clone(other.suppressedChanges) } /** Resets back to a "clean" state (the same as created by the factory method) */ fun reset() { parent = null section = null - sectionIndex = -1 excludingFilter = null promoter = null + suppressedChanges.reset() } companion object { @@ -73,9 +79,9 @@ data class ListAttachState private constructor( return ListAttachState( null, null, - -1, null, - null) + null, + SuppressedAttachState.create()) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java index 3a0520115d67..52c5c3e08118 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java @@ -112,11 +112,9 @@ public class ListDumper { .append(")"); } - if (entry.getNotifSection() != null) { - sb.append(" sectionIndex=") - .append(entry.getSection()) - .append(" sectionName=") - .append(entry.getNotifSection().getName()); + if (entry.getSection() != null) { + sb.append(" section=") + .append(entry.getSection().getLabel()); } if (includeRecordKeeping) { @@ -167,6 +165,20 @@ public class ListDumper { .append(" "); } + if (notifEntry.getAttachState().getSuppressedChanges().getParent() != null) { + rksb.append("suppressedParent=") + .append(notifEntry.getAttachState().getSuppressedChanges() + .getParent().getKey()) + .append(" "); + } + + if (notifEntry.getAttachState().getSuppressedChanges().getSection() != null) { + rksb.append("suppressedSection=") + .append(notifEntry.getAttachState().getSuppressedChanges() + .getSection()) + .append(" "); + } + if (hasBeenInteractedWith) { rksb.append("interacted=yes "); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java index 65f5dc4e5f7c..82c1f243dcdb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java @@ -21,7 +21,7 @@ import android.annotation.UptimeMillisLong; import androidx.annotation.Nullable; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; /** * Abstract superclass for top-level entries, i.e. things that can appear in the final notification @@ -78,13 +78,12 @@ public abstract class ListEntry { return mPreviousAttachState.getParent(); } - /** The section this notification was assigned to (0 to N-1, where N is number of sections). */ - public int getSection() { - return mAttachState.getSectionIndex(); + @Nullable public NotifSection getSection() { + return mAttachState.getSection(); } - @Nullable public NotifSection getNotifSection() { - return mAttachState.getSection(); + public int getSectionIndex() { + return mAttachState.getSection() != null ? mAttachState.getSection().getIndex() : -1; } ListAttachState getAttachState() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java index 7d269177a961..a1844ff5d221 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java @@ -24,7 +24,8 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; @@ -154,10 +155,18 @@ public class NotifPipeline implements CommonNotifCollection { * Sections that are used to sort top-level entries. If two entries have the same section, * NotifComparators are consulted. Sections from this list are called in order for each * notification passed through the pipeline. The first NotifSection to return true for - * {@link NotifSection#isInSection(ListEntry)} sets the entry as part of its Section. + * {@link NotifSectioner#isInSection(ListEntry)} sets the entry as part of its Section. */ - public void setSections(List<NotifSection> sections) { - mShadeListBuilder.setSections(sections); + public void setSections(List<NotifSectioner> sections) { + mShadeListBuilder.setSectioners(sections); + } + + /** + * StabilityManager that is used to determine whether to suppress group and section changes. + * This should only be set once. + */ + public void setVisualStabilityManager(NotifStabilityManager notifStabilityManager) { + mShadeListBuilder.setNotifStabilityManager(notifStabilityManager); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index c3ed5b9d77fc..2b545c56c8bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -21,16 +21,18 @@ import static com.android.systemui.statusbar.notification.collection.listbuilder import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FINALIZE_FILTERING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FINALIZING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_GROUPING; +import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_GROUP_STABILIZING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_IDLE; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_PRE_GROUP_FILTERING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_RESETTING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING; +import static java.util.Objects.requireNonNull; + import android.annotation.MainThread; import android.annotation.Nullable; import android.util.ArrayMap; -import android.util.Pair; import androidx.annotation.NonNull; @@ -38,6 +40,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationInteractionTracker; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; @@ -47,7 +50,8 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeL import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; import com.android.systemui.util.Assert; @@ -90,6 +94,7 @@ public class ShadeListBuilder implements Dumpable { private final List<NotifFilter> mNotifFinalizeFilters = new ArrayList<>(); private final List<NotifComparator> mNotifComparators = new ArrayList<>(); private final List<NotifSection> mNotifSections = new ArrayList<>(); + @Nullable private NotifStabilityManager mNotifStabilityManager; private final List<OnBeforeTransformGroupsListener> mOnBeforeTransformGroupsListeners = new ArrayList<>(); @@ -109,12 +114,15 @@ public class ShadeListBuilder implements Dumpable { SystemClock systemClock, ShadeListBuilderLogger logger, DumpManager dumpManager, - NotificationInteractionTracker interactionTracker) { + NotificationInteractionTracker interactionTracker + ) { Assert.isMainThread(); mSystemClock = systemClock; mLogger = logger; mInteractionTracker = interactionTracker; dumpManager.registerDumpable(TAG, this); + + setSectioners(Collections.emptyList()); } /** @@ -189,15 +197,33 @@ public class ShadeListBuilder implements Dumpable { promoter.setInvalidationListener(this::onPromoterInvalidated); } - void setSections(List<NotifSection> sections) { + void setSectioners(List<NotifSectioner> sectioners) { Assert.isMainThread(); mPipelineState.requireState(STATE_IDLE); mNotifSections.clear(); - for (NotifSection section : sections) { - mNotifSections.add(section); - section.setInvalidationListener(this::onNotifSectionInvalidated); + for (NotifSectioner sectioner : sectioners) { + mNotifSections.add(new NotifSection(sectioner, mNotifSections.size())); + sectioner.setInvalidationListener(this::onNotifSectionInvalidated); + } + + mNotifSections.add(new NotifSection(DEFAULT_SECTIONER, mNotifSections.size())); + } + + void setNotifStabilityManager(NotifStabilityManager notifStabilityManager) { + Assert.isMainThread(); + mPipelineState.requireState(STATE_IDLE); + + if (mNotifStabilityManager != null) { + throw new IllegalStateException( + "Attempting to set the NotifStabilityManager more than once. There should " + + "only be one visual stability manager. Manager is being set by " + + mNotifStabilityManager.getName() + " and " + + notifStabilityManager.getName()); } + + mNotifStabilityManager = notifStabilityManager; + mNotifStabilityManager.setInvalidationListener(this::onReorderingAllowedInvalidated); } void setComparators(List<NotifComparator> comparators) { @@ -237,6 +263,16 @@ public class ShadeListBuilder implements Dumpable { rebuildListIfBefore(STATE_PRE_GROUP_FILTERING); } + private void onReorderingAllowedInvalidated(NotifStabilityManager stabilityManager) { + Assert.isMainThread(); + + mLogger.logReorderingAllowedInvalidated( + stabilityManager.getName(), + mPipelineState.getState()); + + rebuildListIfBefore(STATE_GROUPING); + } + private void onPromoterInvalidated(NotifPromoter promoter) { Assert.isMainThread(); @@ -245,7 +281,7 @@ public class ShadeListBuilder implements Dumpable { rebuildListIfBefore(STATE_TRANSFORMING); } - private void onNotifSectionInvalidated(NotifSection section) { + private void onNotifSectionInvalidated(NotifSectioner section) { Assert.isMainThread(); mLogger.logNotifSectionInvalidated(section.getName(), mPipelineState.getState()); @@ -285,6 +321,7 @@ public class ShadeListBuilder implements Dumpable { // Step 1: Reset notification states mPipelineState.incrementTo(STATE_RESETTING); resetNotifs(); + onBeginRun(); // Step 2: Filter out any notifications that shouldn't be shown right now mPipelineState.incrementTo(STATE_PRE_GROUP_FILTERING); @@ -303,6 +340,10 @@ public class ShadeListBuilder implements Dumpable { promoteNotifs(mNotifList); pruneIncompleteGroups(mNotifList); + // Step 4.5: Reassign/revert any groups to maintain visual stability + mPipelineState.incrementTo(STATE_GROUP_STABILIZING); + stabilizeGroupingNotifs(mNotifList); + // Step 5: Sort // Assign each top-level entry a section, then sort the list by section and then within // section by our list of custom comparators @@ -472,6 +513,28 @@ public class ShadeListBuilder implements Dumpable { } } + private void stabilizeGroupingNotifs(List<ListEntry> list) { + if (mNotifStabilityManager == null) { + return; + } + + for (int i = 0; i < list.size(); i++) { + final ListEntry tle = list.get(i); + if (tle.getPreviousAttachState().getParent() == null) { + continue; // new entries are allowed + } + + final GroupEntry prevParent = tle.getPreviousAttachState().getParent(); + final GroupEntry assignedParent = tle.getParent(); + if (prevParent != assignedParent) { + if (!mNotifStabilityManager.isGroupChangeAllowed(tle.getRepresentativeEntry())) { + tle.getAttachState().getSuppressedChanges().setParent(assignedParent); + tle.setParent(prevParent); + } + } + } + } + private void promoteNotifs(List<ListEntry> list) { for (int i = 0; i < list.size(); i++) { final ListEntry tle = list.get(i); @@ -595,7 +658,6 @@ public class ShadeListBuilder implements Dumpable { */ private void annulAddition(ListEntry entry) { entry.setParent(null); - entry.getAttachState().setSectionIndex(-1); entry.getAttachState().setSection(null); entry.getAttachState().setPromoter(null); if (entry.mFirstAddedIteration == mIterationCount) { @@ -606,12 +668,12 @@ public class ShadeListBuilder implements Dumpable { private void sortList() { // Assign sections to top-level elements and sort their children for (ListEntry entry : mNotifList) { - Pair<NotifSection, Integer> sectionWithIndex = applySections(entry); + NotifSection section = applySections(entry); if (entry instanceof GroupEntry) { GroupEntry parent = (GroupEntry) entry; for (NotificationEntry child : parent.getChildren()) { - child.getAttachState().setSection(sectionWithIndex.first); - child.getAttachState().setSectionIndex(sectionWithIndex.second); + child.getAttachState().setSection(section); + child.getAttachState().setSection(section); } parent.sortChildren(sChildComparator); } @@ -650,9 +712,18 @@ public class ShadeListBuilder implements Dumpable { mLogger.logParentChanged(mIterationCount, prev.getParent(), curr.getParent()); } + if (curr.getSuppressedChanges().getParent() != null) { + mLogger.logParentChangeSuppressed( + mIterationCount, + curr.getSuppressedChanges().getParent(), + curr.getParent()); + } + if (curr.getExcludingFilter() != prev.getExcludingFilter()) { mLogger.logFilterChanged( - mIterationCount, prev.getExcludingFilter(), curr.getExcludingFilter()); + mIterationCount, + prev.getExcludingFilter(), + curr.getExcludingFilter()); } // When something gets detached, its promoter and section are always set to null, so @@ -661,26 +732,46 @@ public class ShadeListBuilder implements Dumpable { if (!wasDetached && curr.getPromoter() != prev.getPromoter()) { mLogger.logPromoterChanged( - mIterationCount, prev.getPromoter(), curr.getPromoter()); + mIterationCount, + prev.getPromoter(), + curr.getPromoter()); } if (!wasDetached && curr.getSection() != prev.getSection()) { mLogger.logSectionChanged( mIterationCount, prev.getSection(), - prev.getSectionIndex(), - curr.getSection(), - curr.getSectionIndex()); + curr.getSection()); + } + + if (curr.getSuppressedChanges().getSection() != null) { + mLogger.logSectionChangeSuppressed( + mIterationCount, + curr.getSuppressedChanges().getSection(), + curr.getSection()); } } } + private void onBeginRun() { + if (mNotifStabilityManager != null) { + mNotifStabilityManager.onBeginRun(); + } + } + private void cleanupPluggables() { callOnCleanup(mNotifPreGroupFilters); callOnCleanup(mNotifPromoters); callOnCleanup(mNotifFinalizeFilters); callOnCleanup(mNotifComparators); - callOnCleanup(mNotifSections); + + for (int i = 0; i < mNotifSections.size(); i++) { + mNotifSections.get(i).getSectioner().onCleanup(); + } + + if (mNotifStabilityManager != null) { + callOnCleanup(List.of(mNotifStabilityManager)); + } } private void callOnCleanup(List<? extends Pluggable<?>> pluggables) { @@ -691,7 +782,9 @@ public class ShadeListBuilder implements Dumpable { private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> { - int cmp = Integer.compare(o1.getSection(), o2.getSection()); + int cmp = Integer.compare( + requireNonNull(o1.getSection()).getIndex(), + requireNonNull(o2.getSection()).getIndex()); if (cmp == 0) { for (int i = 0; i < mNotifComparators.size(); i++) { @@ -769,25 +862,41 @@ public class ShadeListBuilder implements Dumpable { return null; } - private Pair<NotifSection, Integer> applySections(ListEntry entry) { - final Pair<NotifSection, Integer> sectionWithIndex = findSection(entry); - final NotifSection section = sectionWithIndex.first; - final Integer sectionIndex = sectionWithIndex.second; + private NotifSection applySections(ListEntry entry) { + final NotifSection newSection = findSection(entry); + final ListAttachState prevAttachState = entry.getPreviousAttachState(); + + NotifSection finalSection = newSection; - entry.getAttachState().setSection(section); - entry.getAttachState().setSectionIndex(sectionIndex); + // are we changing sections of this entry? + if (mNotifStabilityManager != null + && prevAttachState.getParent() != null + && newSection != prevAttachState.getSection()) { - return sectionWithIndex; + // are section changes allowed? + if (!mNotifStabilityManager.isSectionChangeAllowed(entry.getRepresentativeEntry())) { + // record the section that we wanted to change to + entry.getAttachState().getSuppressedChanges().setSection(newSection); + + // keep the previous section + finalSection = prevAttachState.getSection(); + } + } + + entry.getAttachState().setSection(finalSection); + + return finalSection; } - private Pair<NotifSection, Integer> findSection(ListEntry entry) { + @NonNull + private NotifSection findSection(ListEntry entry) { for (int i = 0; i < mNotifSections.size(); i++) { - NotifSection sectioner = mNotifSections.get(i); - if (sectioner.isInSection(entry)) { - return new Pair<>(sectioner, i); + NotifSection section = mNotifSections.get(i); + if (section.getSectioner().isInSection(entry)) { + return section; } } - return new Pair<>(sDefaultSection, mNotifSections.size()); + throw new RuntimeException("Missing default sectioner!"); } private void rebuildListIfBefore(@PipelineState.StateName int state) { @@ -857,15 +966,15 @@ public class ShadeListBuilder implements Dumpable { void onRenderList(@NonNull List<ListEntry> entries); } - private static final NotifSection sDefaultSection = - new NotifSection("UnknownSection") { + private static final NotifSectioner DEFAULT_SECTIONER = + new NotifSectioner("UnknownSection") { @Override public boolean isInSection(ListEntry entry) { return true; } }; - private static final String TAG = "ShadeListBuilder"; - private static final int MIN_CHILDREN_FOR_GROUP = 2; + + private static final String TAG = "ShadeListBuilder"; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt new file mode 100644 index 000000000000..3eb2e610f329 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 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.notification.collection + +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection + +/** + * Stores the suppressed state that [ShadeListBuilder] assigned to this [ListEntry] before the + * VisualStabilityManager suppressed group and section changes. + */ +data class SuppressedAttachState private constructor( + /** + * Null if not attached to the current shade list. If top-level, then the shade list root. If + * part of a group, then that group's GroupEntry. + */ + var parent: GroupEntry?, + + /** + * The assigned section for this ListEntry. If the child of the group, this will be the + * parent's section. Null if not attached to the list. + */ + var section: NotifSection? +) { + + /** Copies the state of another instance. */ + fun clone(other: SuppressedAttachState) { + parent = other.parent + section = other.section + } + + /** Resets back to a "clean" state (the same as created by the factory method) */ + fun reset() { + parent = null + section = null + } + + companion object { + @JvmStatic + fun create(): SuppressedAttachState { + return SuppressedAttachState( + null, + null) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java index 0b9bded5ef58..c7ac40346ce1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java @@ -29,7 +29,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -83,8 +83,8 @@ public class AppOpsCoordinator implements Coordinator { } - public NotifSection getSection() { - return mNotifSection; + public NotifSectioner getSectioner() { + return mNotifSectioner; } /** @@ -179,7 +179,7 @@ public class AppOpsCoordinator implements Coordinator { /** * Puts foreground service notifications into its own section. */ - private final NotifSection mNotifSection = new NotifSection("ForegroundService") { + private final NotifSectioner mNotifSectioner = new NotifSectioner("ForegroundService") { @Override public boolean isInSection(ListEntry entry) { NotificationEntry notificationEntry = entry.getRepresentativeEntry(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt index c8e859f27a5c..dea11626a3f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -21,7 +21,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import javax.inject.Inject @@ -42,7 +42,7 @@ class ConversationCoordinator @Inject constructor( } } - private val mNotifSection: NotifSection = object : NotifSection("People") { + val sectioner = object : NotifSectioner("People") { override fun isInSection(entry: ListEntry): Boolean { return isConversation(entry.representativeEntry!!) } @@ -52,10 +52,6 @@ class ConversationCoordinator @Inject constructor( pipeline.addPromoter(notificationPromoter) } - fun getSection(): NotifSection { - return mNotifSection - } - private fun isConversation(entry: NotificationEntry): Boolean = peopleNotificationIdentifier.getPeopleNotificationType(entry.sbn, entry.ranking) != TYPE_NON_PERSON diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java index 6e6cecaf62fa..c023400ca9ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java @@ -27,7 +27,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder; @@ -88,8 +88,8 @@ public class HeadsUpCoordinator implements Coordinator { pipeline.addNotificationLifetimeExtender(mLifetimeExtender); } - public NotifSection getSection() { - return mNotifSection; + public NotifSectioner getSectioner() { + return mNotifSectioner; } private void onHeadsUpViewBound(NotificationEntry entry) { @@ -191,7 +191,7 @@ public class HeadsUpCoordinator implements Coordinator { } }; - private final NotifSection mNotifSection = new NotifSection("HeadsUp") { + private final NotifSectioner mNotifSectioner = new NotifSectioner("HeadsUp") { @Override public boolean isInSection(ListEntry entry) { return isCurrentlyShowingHun(entry); @@ -207,7 +207,7 @@ public class HeadsUpCoordinator implements Coordinator { endNotifLifetimeExtension(); mCurrentHun = newHUN; mNotifPromoter.invalidateList(); - mNotifSection.invalidateList(); + mNotifSectioner.invalidateList(); } if (!isHeadsUp) { mHeadsUpViewBinder.unbindHeadsUpView(entry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index f2444d5f1cb3..ded5e46593f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -21,7 +21,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; @@ -41,7 +41,8 @@ import javax.inject.Inject; public class NotifCoordinators implements Dumpable { private static final String TAG = "NotifCoordinators"; private final List<Coordinator> mCoordinators = new ArrayList<>(); - private final List<NotifSection> mOrderedSections = new ArrayList<>(); + private final List<NotifSectioner> mOrderedSections = new ArrayList<>(); + /** * Creates all the coordinators. */ @@ -58,7 +59,8 @@ public class NotifCoordinators implements Dumpable { HeadsUpCoordinator headsUpCoordinator, ConversationCoordinator conversationCoordinator, PreparationCoordinator preparationCoordinator, - MediaCoordinator mediaCoordinator) { + MediaCoordinator mediaCoordinator, + VisualStabilityCoordinator visualStabilityCoordinator) { dumpManager.registerDumpable(TAG, this); mCoordinators.add(new HideLocallyDismissedNotifsCoordinator()); @@ -70,6 +72,7 @@ public class NotifCoordinators implements Dumpable { mCoordinators.add(bubbleCoordinator); mCoordinators.add(mediaCoordinator); mCoordinators.add(conversationCoordinator); + mCoordinators.add(visualStabilityCoordinator); if (featureFlags.isNewNotifPipelineRenderingEnabled()) { mCoordinators.add(headsUpCoordinator); mCoordinators.add(preparationCoordinator); @@ -78,12 +81,12 @@ public class NotifCoordinators implements Dumpable { // Manually add Ordered Sections // HeadsUp > FGS > People > Alerting > Silent > Unknown/Default if (featureFlags.isNewNotifPipelineRenderingEnabled()) { - mOrderedSections.add(headsUpCoordinator.getSection()); // HeadsUp + mOrderedSections.add(headsUpCoordinator.getSectioner()); // HeadsUp } - mOrderedSections.add(appOpsCoordinator.getSection()); // ForegroundService - mOrderedSections.add(conversationCoordinator.getSection()); // People - mOrderedSections.add(rankingCoordinator.getAlertingSection()); // Alerting - mOrderedSections.add(rankingCoordinator.getSilentSection()); // Silent + mOrderedSections.add(appOpsCoordinator.getSectioner()); // ForegroundService + mOrderedSections.add(conversationCoordinator.getSectioner()); // People + mOrderedSections.add(rankingCoordinator.getAlertingSectioner()); // Alerting + mOrderedSections.add(rankingCoordinator.getSilentSectioner()); // Silent } /** @@ -106,7 +109,7 @@ public class NotifCoordinators implements Dumpable { pw.println("\t" + c.getClass()); } - for (NotifSection s : mOrderedSections) { + for (NotifSectioner s : mOrderedSections) { pw.println("\t" + s.getName()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index a32b1636057b..0f08e0ff491c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -22,7 +22,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import javax.inject.Inject; @@ -57,22 +57,22 @@ public class RankingCoordinator implements Coordinator { pipeline.addPreGroupFilter(mDozingFilter); } - public NotifSection getAlertingSection() { - return mAlertingNotifSection; + public NotifSectioner getAlertingSectioner() { + return mAlertingNotifSectioner; } - public NotifSection getSilentSection() { - return mSilentNotifSection; + public NotifSectioner getSilentSectioner() { + return mSilentNotifSectioner; } - private final NotifSection mAlertingNotifSection = new NotifSection("Alerting") { + private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting") { @Override public boolean isInSection(ListEntry entry) { return mHighPriorityProvider.isHighPriority(entry); } }; - private final NotifSection mSilentNotifSection = new NotifSection("Silent") { + private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent") { @Override public boolean isInSection(ListEntry entry) { return !mHighPriorityProvider.isHighPriority(entry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java new file mode 100644 index 000000000000..08030f8201a2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2020 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.notification.collection.coordinator; + +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; + +import android.annotation.NonNull; + +import androidx.annotation.VisibleForTesting; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationViewHierarchyManager; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; +import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; + +/** + * Ensures that notifications are visually stable if the user is looking at the notifications. + * Group and section changes are re-allowed when the notification entries are no longer being + * viewed. + * + * Previously this was implemented in the view-layer {@link NotificationViewHierarchyManager} by + * {@link com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager}. + * This is now integrated in the data-layer via + * {@link com.android.systemui.statusbar.notification.collection.ShadeListBuilder}. + */ +@SysUISingleton +public class VisualStabilityCoordinator implements Coordinator { + private final DelayableExecutor mDelayableExecutor; + private final WakefulnessLifecycle mWakefulnessLifecycle; + private final StatusBarStateController mStatusBarStateController; + private final HeadsUpManager mHeadsUpManager; + + private boolean mScreenOn; + private boolean mPanelExpanded; + private boolean mPulsing; + + private boolean mReorderingAllowed; + private boolean mIsSuppressingGroupChange = false; + private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>(); + + // key: notification key that can temporarily change its section + // value: runnable that when run removes its associated RemoveOverrideSuppressionRunnable + // from the DelayableExecutor's queue + private Map<String, Runnable> mEntriesThatCanChangeSection = new HashMap<>(); + + @VisibleForTesting + protected static final long ALLOW_SECTION_CHANGE_TIMEOUT = 500; + + @Inject + public VisualStabilityCoordinator( + HeadsUpManager headsUpManager, + WakefulnessLifecycle wakefulnessLifecycle, + StatusBarStateController statusBarStateController, + DelayableExecutor delayableExecutor + ) { + mHeadsUpManager = headsUpManager; + mWakefulnessLifecycle = wakefulnessLifecycle; + mStatusBarStateController = statusBarStateController; + mDelayableExecutor = delayableExecutor; + } + + @Override + public void attach(NotifPipeline pipeline) { + mWakefulnessLifecycle.addObserver(mWakefulnessObserver); + mScreenOn = mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE + || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING; + + mStatusBarStateController.addCallback(mStatusBarStateControllerListener); + mPulsing = mStatusBarStateController.isPulsing(); + + pipeline.setVisualStabilityManager(mNotifStabilityManager); + } + + private final NotifStabilityManager mNotifStabilityManager = + new NotifStabilityManager("VisualStabilityCoordinator") { + @Override + public void onBeginRun() { + mIsSuppressingGroupChange = false; + mEntriesWithSuppressedSectionChange.clear(); + } + + @Override + public boolean isGroupChangeAllowed(NotificationEntry entry) { + final boolean isGroupChangeAllowedForEntry = + mReorderingAllowed || mHeadsUpManager.isAlerting(entry.getKey()); + mIsSuppressingGroupChange |= isGroupChangeAllowedForEntry; + return isGroupChangeAllowedForEntry; + } + + @Override + public boolean isSectionChangeAllowed(NotificationEntry entry) { + final boolean isSectionChangeAllowedForEntry = + mReorderingAllowed + || mHeadsUpManager.isAlerting(entry.getKey()) + || mEntriesThatCanChangeSection.containsKey(entry.getKey()); + if (isSectionChangeAllowedForEntry) { + mEntriesWithSuppressedSectionChange.add(entry.getKey()); + } + return isSectionChangeAllowedForEntry; + } + }; + + private void updateAllowedStates() { + mReorderingAllowed = isReorderingAllowed(); + if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange())) { + mNotifStabilityManager.invalidateList(); + } + } + + private boolean isSuppressingSectionChange() { + return !mEntriesWithSuppressedSectionChange.isEmpty(); + } + + private boolean isReorderingAllowed() { + return (!mScreenOn || !mPanelExpanded) && !mPulsing; + } + + /** + * Allows this notification entry to be re-ordered in the notification list temporarily until + * the timeout has passed. + * + * Typically this is allowed because the user has directly changed something about the + * notification and we are reordering based on the user's change. + * + * @param entry notification entry that can change sections even if isReorderingAllowed is false + * @param now current time SystemClock.uptimeMillis + */ + public void temporarilyAllowSectionChanges(@NonNull NotificationEntry entry, long now) { + final String entryKey = entry.getKey(); + final boolean wasSectionChangeAllowed = + mNotifStabilityManager.isSectionChangeAllowed(entry); + + // If it exists, cancel previous timeout + if (mEntriesThatCanChangeSection.containsKey(entryKey)) { + mEntriesThatCanChangeSection.get(entryKey).run(); + } + + // Schedule & store new timeout cancellable + mEntriesThatCanChangeSection.put( + entryKey, + mDelayableExecutor.executeAtTime( + () -> mEntriesThatCanChangeSection.remove(entryKey), + now + ALLOW_SECTION_CHANGE_TIMEOUT)); + + if (!wasSectionChangeAllowed) { + mNotifStabilityManager.invalidateList(); + } + } + + final StatusBarStateController.StateListener mStatusBarStateControllerListener = + new StatusBarStateController.StateListener() { + @Override + public void onPulsingChanged(boolean pulsing) { + mPulsing = pulsing; + updateAllowedStates(); + } + + @Override + public void onExpandedChanged(boolean expanded) { + mPanelExpanded = expanded; + updateAllowedStates(); + } + }; + + final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { + @Override + public void onFinishedGoingToSleep() { + mScreenOn = false; + updateAllowedStates(); + } + + @Override + public void onStartedWakingUp() { + mScreenOn = true; + updateAllowedStates(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnDismissCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java index 36adf9b87674..19a356b89f18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnDismissCallbackImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.inflation; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; +import android.os.SystemClock; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; @@ -26,35 +27,43 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.HeadsUpManager; /** - * Callback used when a user: - * 1. Manually dismisses a notification {@see ExpandableNotificationRow}. - * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}. - * {@see StatusBarNotificationActivityStarter} + * Callback for when a user interacts with a {@see ExpandableNotificationRow}. Sends relevant + * information about the interaction to the notification pipeline. */ -public class OnDismissCallbackImpl implements OnDismissCallback { +public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback { private final NotifPipeline mNotifPipeline; private final NotifCollection mNotifCollection; private final HeadsUpManager mHeadsUpManager; private final StatusBarStateController mStatusBarStateController; + private final VisualStabilityCoordinator mVisualStabilityCoordinator; - public OnDismissCallbackImpl( + public OnUserInteractionCallbackImpl( NotifPipeline notifPipeline, NotifCollection notifCollection, HeadsUpManager headsUpManager, - StatusBarStateController statusBarStateController + StatusBarStateController statusBarStateController, + VisualStabilityCoordinator visualStabilityCoordinator ) { mNotifPipeline = notifPipeline; mNotifCollection = notifCollection; mHeadsUpManager = headsUpManager; mStatusBarStateController = statusBarStateController; + mVisualStabilityCoordinator = visualStabilityCoordinator; } + /** + * Callback triggered when a user: + * 1. Manually dismisses a notification {@see ExpandableNotificationRow}. + * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}. + * {@see StatusBarNotificationActivityStarter} + */ @Override public void onDismiss( NotificationEntry entry, @@ -80,4 +89,11 @@ public class OnDismissCallbackImpl implements OnDismissCallback { NotificationLogger.getNotificationLocation(entry))) ); } + + @Override + public void onImportanceChanged(NotificationEntry entry) { + mVisualStabilityCoordinator.temporarilyAllowSectionChanges( + entry, + SystemClock.uptimeMillis()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnDismissCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java index 94ffa8f8c5ed..cce8cdc64d30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnDismissCallbackImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java @@ -27,30 +27,36 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.HeadsUpManager; /** - * Callback used when a user: - * 1. Manually dismisses a notification {@see ExpandableNotificationRow}. - * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}. - * {@see StatusBarNotificationActivityStarter} + * Callback for when a user interacts with a {@see ExpandableNotificationRow}. */ -public class OnDismissCallbackImpl implements OnDismissCallback { +public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCallback { private final NotificationEntryManager mNotificationEntryManager; private final HeadsUpManager mHeadsUpManager; private final StatusBarStateController mStatusBarStateController; + private final VisualStabilityManager mVisualStabilityManager; - public OnDismissCallbackImpl( + public OnUserInteractionCallbackImplLegacy( NotificationEntryManager notificationEntryManager, HeadsUpManager headsUpManager, - StatusBarStateController statusBarStateController + StatusBarStateController statusBarStateController, + VisualStabilityManager visualStabilityManager ) { mNotificationEntryManager = notificationEntryManager; mHeadsUpManager = headsUpManager; mStatusBarStateController = statusBarStateController; + mVisualStabilityManager = visualStabilityManager; } + /** + * Callback triggered when a user: + * 1. Manually dismisses a notification {@see ExpandableNotificationRow}. + * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}. + * {@see StatusBarNotificationActivityStarter} + */ @Override public void onDismiss( NotificationEntry entry, @@ -77,5 +83,10 @@ public class OnDismissCallbackImpl implements OnDismissCallback { cancellationReason ); } + + @Override + public void onImportanceChanged(NotificationEntry entry) { + mVisualStabilityManager.temporarilyAllowReordering(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java index 8341c02b6b63..165df30b457d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.notification; +package com.android.systemui.statusbar.notification.collection.legacy; import android.os.Handler; import android.os.SystemClock; @@ -24,7 +24,11 @@ import androidx.collection.ArraySet; import com.android.systemui.Dumpable; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationEntryListener; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.VisibilityLocationProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -65,24 +69,44 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl */ public VisualStabilityManager( NotificationEntryManager notificationEntryManager, - @Main Handler handler) { + @Main Handler handler, + StatusBarStateController statusBarStateController, + WakefulnessLifecycle wakefulnessLifecycle) { mHandler = handler; - notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { - @Override - public void onPreEntryUpdated(NotificationEntry entry) { - final boolean ambientStateHasChanged = - entry.isAmbient() != entry.getRow().isLowPriority(); - if (ambientStateHasChanged) { - // note: entries are removed in onReorderingFinished - mLowPriorityReorderingViews.add(entry); + if (notificationEntryManager != null) { + notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { + @Override + public void onPreEntryUpdated(NotificationEntry entry) { + final boolean ambientStateHasChanged = + entry.isAmbient() != entry.getRow().isLowPriority(); + if (ambientStateHasChanged) { + // note: entries are removed in onReorderingFinished + mLowPriorityReorderingViews.add(entry); + } } - } - }); - } + }); + } + + if (statusBarStateController != null) { + setPulsing(statusBarStateController.isPulsing()); + statusBarStateController.addCallback(new StatusBarStateController.StateListener() { + @Override + public void onPulsingChanged(boolean pulsing) { + setPulsing(pulsing); + } + + @Override + public void onExpandedChanged(boolean expanded) { + setPanelExpanded(expanded); + } + }); + } - public void setUpWithPresenter(NotificationPresenter presenter) { + if (wakefulnessLifecycle != null) { + wakefulnessLifecycle.addObserver(mWakefulnessObserver); + } } /** @@ -120,25 +144,25 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl } /** - * Set the panel to be expanded. + * @param screenOn whether the screen is on */ - public void setPanelExpanded(boolean expanded) { - mPanelExpanded = expanded; + private void setScreenOn(boolean screenOn) { + mScreenOn = screenOn; updateAllowedStates(); } /** - * @param screenOn whether the screen is on + * Set the panel to be expanded. */ - public void setScreenOn(boolean screenOn) { - mScreenOn = screenOn; + private void setPanelExpanded(boolean expanded) { + mPanelExpanded = expanded; updateAllowedStates(); } /** * @param pulsing whether we are currently pulsing for ambient display. */ - public void setPulsing(boolean pulsing) { + private void setPulsing(boolean pulsing) { if (mPulsing == pulsing) { return; } @@ -215,6 +239,10 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl mVisibilityLocationProvider = visibilityLocationProvider; } + /** + * Notifications have been reordered, so reset all the allowed list of views that are allowed + * to reorder. + */ public void onReorderingFinished() { mAllowedReorderViews.clear(); mAddedChildren.clear(); @@ -271,11 +299,27 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl pw.println(); } + final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { + @Override + public void onFinishedGoingToSleep() { + setScreenOn(false); + } + + @Override + public void onStartedWakingUp() { + setScreenOn(true); + } + }; + + + /** + * See {@link Callback#onChangeAllowed()} + */ public interface Callback { + /** * Called when changing is allowed again. */ void onChangeAllowed(); } - } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt index 90187a298cf2..c09122ea3c26 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt @@ -14,31 +14,14 @@ * limitations under the License. */ -package com.android.systemui.onehanded; +package com.android.systemui.statusbar.notification.collection.listbuilder -/** - * The base class of OneHandedManager - */ -public interface OneHandedManager { - - /** - * Set one handed enabled or disabled - */ - default void setOneHandedEnabled(boolean enabled) {} - - /** - * Set task stack changed to exit - */ - default void setTaskChangeToExit(boolean enabled) {} - - /** - * Exit one handed mode - */ - default void stopOneHanded() {} - - /** - * Trigger one handed mode - */ - default void startOneHanded() {} +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner +data class NotifSection( + val sectioner: NotifSectioner, + val index: Int +) { + val label: String + get() = "Section($index, \"${sectioner.name}\")" } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java index f1f7d632b6f8..798bfe7f39d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java @@ -81,9 +81,10 @@ public class PipelineState { public static final int STATE_PRE_GROUP_FILTERING = 3; public static final int STATE_GROUPING = 4; public static final int STATE_TRANSFORMING = 5; - public static final int STATE_SORTING = 6; - public static final int STATE_FINALIZE_FILTERING = 7; - public static final int STATE_FINALIZING = 8; + public static final int STATE_GROUP_STABILIZING = 6; + public static final int STATE_SORTING = 7; + public static final int STATE_FINALIZE_FILTERING = 8; + public static final int STATE_FINALIZING = 9; @IntDef(prefix = { "STATE_" }, value = { STATE_IDLE, @@ -92,6 +93,7 @@ public class PipelineState { STATE_PRE_GROUP_FILTERING, STATE_GROUPING, STATE_TRANSFORMING, + STATE_GROUP_STABILIZING, STATE_SORTING, STATE_FINALIZE_FILTERING, STATE_FINALIZING, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt index 67f8bfeaf141..9ee7db738c20 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt @@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection import javax.inject.Inject class ShadeListBuilderLogger @Inject constructor( @@ -57,6 +56,15 @@ class ShadeListBuilderLogger @Inject constructor( }) } + fun logReorderingAllowedInvalidated(name: String, pipelineState: Int) { + buffer.log(TAG, DEBUG, { + str1 = name + int1 = pipelineState + }, { + """ReorderingNowAllowed "$str1" invalidated; pipeline state is $int1""" + }) + } + fun logPromoterInvalidated(name: String, pipelineState: Int) { buffer.log(TAG, DEBUG, { str1 = name @@ -156,6 +164,21 @@ class ShadeListBuilderLogger @Inject constructor( }) } + fun logParentChangeSuppressed( + buildId: Int, + suppressedParent: GroupEntry?, + keepingParent: GroupEntry? + ) { + buffer.log(TAG, INFO, { + int1 = buildId + str1 = suppressedParent?.key + str2 = keepingParent?.key + }, { + "(Build $long1) Change of parent to '$str1' suppressed; " + + "keeping parent '$str2'" + }) + } + fun logFilterChanged( buildId: Int, prevFilter: NotifFilter?, @@ -187,25 +210,35 @@ class ShadeListBuilderLogger @Inject constructor( fun logSectionChanged( buildId: Int, prevSection: NotifSection?, - prevIndex: Int, - newSection: NotifSection?, - newIndex: Int + newSection: NotifSection? ) { buffer.log(TAG, INFO, { long1 = buildId.toLong() - str1 = prevSection?.name - int1 = prevIndex - str2 = newSection?.name - int2 = newIndex + str1 = prevSection?.label + str2 = newSection?.label }, { if (str1 == null) { - "(Build $long1) Section assigned: '$str2' (#$int2)" + "(Build $long1) Section assigned: $str2" } else { - "(Build $long1) Section changed: '$str1' (#$int1) -> '$str2' (#$int2)" + "(Build $long1) Section changed: $str1 -> $str2" } }) } + fun logSectionChangeSuppressed( + buildId: Int, + suppressedSection: NotifSection?, + assignedSection: NotifSection? + ) { + buffer.log(TAG, INFO, { + long1 = buildId.toLong() + str1 = suppressedSection?.label + str2 = assignedSection?.label + }, { + "(Build $long1) Suppressing section change to $str1 (staying at $str2)" + }) + } + fun logFinalList(entries: List<ListEntry>) { if (entries.isEmpty()) { buffer.log(TAG, DEBUG, {}, { "(empty list)" }) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java index fe5ba3c8e6fc..b57f504189f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java @@ -22,8 +22,8 @@ import com.android.systemui.statusbar.notification.collection.ShadeListBuilder; /** * Pluggable for participating in notif sectioning. See {@link ShadeListBuilder#setSections}. */ -public abstract class NotifSection extends Pluggable<NotifSection> { - protected NotifSection(String name) { +public abstract class NotifSectioner extends Pluggable<NotifSectioner> { + protected NotifSectioner(String name) { super(name); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java new file mode 100644 index 000000000000..58d4b97f69b5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 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.notification.collection.listbuilder.pluggable; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +/** + * Pluggable for participating in notif stabilization. In particular, suppressing group and + * section changes. + * + * The stability manager should be invalidated when previously suppressed a group or + * section change is now allowed. + */ +public abstract class NotifStabilityManager extends Pluggable<NotifStabilityManager> { + + protected NotifStabilityManager(String name) { + super(name); + } + + /** + * Called at the beginning of every pipeline run to perform any necessary cleanup from the + * previous run. + */ + public abstract void onBeginRun(); + + /** + * Returns whether this notification can currently change groups/parents. + * Per iteration of the notification pipeline, locally stores this information until the next + * run of the pipeline. When this method returns true, it's expected that a group change for + * this entry is being suppressed. + */ + public abstract boolean isGroupChangeAllowed(NotificationEntry entry); + + /** + * Returns whether this notification entry can currently change sections. + * Per iteration of the notification pipeline, locally stores this information until the next + * run of the pipeline. When this method returns true, it's expected that a section change is + * being suppressed. + */ + public abstract boolean isSectionChangeAllowed(NotificationEntry entry); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt index e8124944bcb0..a1800ed12125 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt @@ -25,10 +25,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain * we should just modify NLC to implement the NodeController interface. */ class RootNodeController( - private val listContainer: NotificationListContainer + private val listContainer: NotificationListContainer, + override val view: View ) : NodeController { override val nodeLabel: String = "<root>" - override val view: View = listContainer as View override fun getChildAt(index: Int): View? { return listContainer.getContainerChildAt(index) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt index 118ff4a9fbb7..3c35b7bd8472 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.collection.render +import android.content.Context +import android.view.View import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -30,12 +32,15 @@ import javax.inject.Inject * currently populate the notification shade. */ class ShadeViewManager constructor( + context: Context, listContainer: NotificationListContainer, logger: ShadeViewDifferLogger, private val viewBarn: NotifViewBarn, private val notificationIconAreaController: NotificationIconAreaController ) { - private val rootController = RootNodeController(listContainer) + // We pass a shim view here because the listContainer may not actually have a view associated + // with it and the differ never actually cares about the root node's view. + private val rootController = RootNodeController(listContainer, View(context)) private val viewDiffer = ShadeViewDiffer(rootController, logger) fun attach(listBuilder: ShadeListBuilder) { @@ -82,11 +87,17 @@ class ShadeViewManager constructor( } class ShadeViewManagerFactory @Inject constructor( + private val context: Context, private val logger: ShadeViewDifferLogger, private val viewBarn: NotifViewBarn, private val notificationIconAreaController: NotificationIconAreaController ) { fun create(listContainer: NotificationListContainer): ShadeViewManager { - return ShadeViewManager(listContainer, logger, viewBarn, notificationIconAreaController) + return ShadeViewManager( + context, + listContainer, + logger, + viewBarn, + notificationIconAreaController) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 0573156b4804..6d01324f1b7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -32,6 +32,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.CurrentUserContextTracker; import com.android.systemui.statusbar.FeatureFlags; @@ -41,14 +42,16 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; +import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; -import com.android.systemui.statusbar.notification.collection.inflation.OnDismissCallbackImpl; +import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl; +import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.init.NotificationsController; @@ -62,7 +65,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationPanelLogg import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; @@ -115,7 +118,6 @@ public interface NotificationsModule { @Provides static NotificationGutsManager provideNotificationGutsManager( Context context, - VisualStabilityManager visualStabilityManager, Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler, @Background Handler bgHandler, @@ -129,10 +131,10 @@ public interface NotificationsModule { Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, BubbleController bubbleController, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + OnUserInteractionCallback onUserInteractionCallback) { return new NotificationGutsManager( context, - visualStabilityManager, statusBarLazy, mainHandler, bgHandler, @@ -146,15 +148,24 @@ public interface NotificationsModule { builderProvider, assistantFeedbackController, bubbleController, - uiEventLogger); + uiEventLogger, + onUserInteractionCallback); } /** Provides an instance of {@link VisualStabilityManager} */ @SysUISingleton @Provides static VisualStabilityManager provideVisualStabilityManager( - NotificationEntryManager notificationEntryManager, Handler handler) { - return new VisualStabilityManager(notificationEntryManager, handler); + FeatureFlags featureFlags, + NotificationEntryManager notificationEntryManager, + Handler handler, + StatusBarStateController statusBarStateController, + WakefulnessLifecycle wakefulnessLifecycle) { + return new VisualStabilityManager( + notificationEntryManager, + handler, + statusBarStateController, + wakefulnessLifecycle); } /** Provides an instance of {@link NotificationLogger} */ @@ -227,20 +238,27 @@ public interface NotificationsModule { */ @Provides @SysUISingleton - static OnDismissCallback provideOnDismissCallback( + static OnUserInteractionCallback provideOnUserInteractionCallback( FeatureFlags featureFlags, HeadsUpManager headsUpManager, StatusBarStateController statusBarStateController, Lazy<NotifPipeline> pipeline, Lazy<NotifCollection> notifCollection, - NotificationEntryManager entryManager) { + Lazy<VisualStabilityCoordinator> visualStabilityCoordinator, + NotificationEntryManager entryManager, + VisualStabilityManager visualStabilityManager) { return featureFlags.isNewNotifPipelineRenderingEnabled() - ? new OnDismissCallbackImpl( - pipeline.get(), notifCollection.get(), headsUpManager, - statusBarStateController) - : new com.android.systemui.statusbar.notification.collection - .legacy.OnDismissCallbackImpl( - entryManager, headsUpManager, statusBarStateController); + ? new OnUserInteractionCallbackImpl( + pipeline.get(), + notifCollection.get(), + headsUpManager, + statusBarStateController, + visualStabilityCoordinator.get()) + : new OnUserInteractionCallbackImplLegacy( + entryManager, + headsUpManager, + statusBarStateController, + visualStabilityManager); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java index dbdb42204867..4d56e6013d71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java @@ -29,8 +29,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; 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 d7fa54f0091d..9c09cba5e137 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 @@ -84,8 +84,8 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationUtils; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -323,7 +323,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private View mGroupParentWhenDismissed; private boolean mShelfIconVisible; private boolean mAboveShelf; - private OnDismissCallback mOnDismissCallback; + private OnUserInteractionCallback mOnUserInteractionCallback; private boolean mIsLowPriority; private boolean mIsColorized; private boolean mUseIncreasedCollapsedHeight; @@ -1445,8 +1445,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } dismiss(fromAccessibility); if (mEntry.isClearable()) { - if (mOnDismissCallback != null) { - mOnDismissCallback.onDismiss(mEntry, REASON_CANCEL); + if (mOnUserInteractionCallback != null) { + mOnUserInteractionCallback.onDismiss(mEntry, REASON_CANCEL); } } } @@ -1463,10 +1463,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mIsBlockingHelperShowing && mNotificationTranslationFinished; } - void setOnDismissCallback(OnDismissCallback onDismissCallback) { - mOnDismissCallback = onDismissCallback; - } - @Override public View getShelfTransformationTarget() { if (mIsSummaryWithChildren && !shouldShowPublic()) { @@ -1600,7 +1596,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView CoordinateOnClickListener onFeedbackClickListener, FalsingManager falsingManager, StatusBarStateController statusBarStateController, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + OnUserInteractionCallback onUserInteractionCallback) { mAppName = appName; if (mMenuRow == null) { mMenuRow = new NotificationMenuRow(mContext, peopleNotificationIdentifier); @@ -1624,6 +1621,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView for (NotificationContentView l : mLayouts) { l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier); } + mOnUserInteractionCallback = onUserInteractionCallback; } private void initDimens() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 690dce915cf3..f8bc2bebf93b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -71,7 +71,7 @@ public class ExpandableNotificationRowController implements NodeController { this::logNotificationExpansion; private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener; private final NotificationGutsManager mNotificationGutsManager; - private final OnDismissCallback mOnDismissCallback; + private final OnUserInteractionCallback mOnUserInteractionCallback; private final FalsingManager mFalsingManager; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; @@ -90,7 +90,7 @@ public class ExpandableNotificationRowController implements NodeController { StatusBarStateController statusBarStateController, NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, - OnDismissCallback onDismissCallback, FalsingManager falsingManager, + OnUserInteractionCallback onUserInteractionCallback, FalsingManager falsingManager, PeopleNotificationIdentifier peopleNotificationIdentifier) { mView = view; mListContainer = listContainer; @@ -108,7 +108,7 @@ public class ExpandableNotificationRowController implements NodeController { mOnExpandClickListener = onExpandClickListener; mStatusBarStateController = statusBarStateController; mNotificationGutsManager = notificationGutsManager; - mOnDismissCallback = onDismissCallback; + mOnUserInteractionCallback = onUserInteractionCallback; mOnFeedbackClickListener = mNotificationGutsManager::openGuts; mAllowLongPress = allowLongPress; mFalsingManager = falsingManager; @@ -133,9 +133,9 @@ public class ExpandableNotificationRowController implements NodeController { mOnFeedbackClickListener, mFalsingManager, mStatusBarStateController, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + mOnUserInteractionCallback ); - mView.setOnDismissCallback(mOnDismissCallback); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { mView.setLongPressListener((v, x, y, item) -> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index f543db74d91a..7c7bb5c83762 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -69,7 +69,6 @@ import com.android.systemui.bubbles.BubbleController; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.NotificationChannelHelper; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -89,7 +88,7 @@ public class NotificationConversationInfo extends LinearLayout implements private ShortcutManager mShortcutManager; private PackageManager mPm; private ConversationIconFactory mIconFactory; - private VisualStabilityManager mVisualStabilityManager; + private OnUserInteractionCallback mOnUserInteractionCallback; private Handler mMainHandler; private Handler mBgHandler; private BubbleController mBubbleController; @@ -207,7 +206,7 @@ public class NotificationConversationInfo extends LinearLayout implements ShortcutManager shortcutManager, PackageManager pm, INotificationManager iNotificationManager, - VisualStabilityManager visualStabilityManager, + OnUserInteractionCallback onUserInteractionCallback, String pkg, NotificationChannel notificationChannel, NotificationEntry entry, @@ -224,7 +223,7 @@ public class NotificationConversationInfo extends LinearLayout implements BubbleController bubbleController) { mSelectedAction = -1; mINotificationManager = iNotificationManager; - mVisualStabilityManager = visualStabilityManager; + mOnUserInteractionCallback = onUserInteractionCallback; mPackageName = pkg; mEntry = entry; mSbn = entry.getSbn(); @@ -513,7 +512,7 @@ public class NotificationConversationInfo extends LinearLayout implements mAppUid, mSelectedAction, mNotificationChannel)); mEntry.markForUserTriggeredMovement(true); mMainHandler.postDelayed( - mVisualStabilityManager::temporarilyAllowReordering, + () -> mOnUserInteractionCallback.onImportanceChanged(mEntry), StackStateAnimator.ANIMATION_DURATION_STANDARD); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 05d44f6eb406..60074f608969 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -60,7 +60,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; @@ -88,10 +87,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private final Context mContext; - private final VisualStabilityManager mVisualStabilityManager; private final AccessibilityManager mAccessibilityManager; private final HighPriorityProvider mHighPriorityProvider; private final ChannelEditorDialogController mChannelEditorDialogController; + private final OnUserInteractionCallback mOnUserInteractionCallback; // Dependencies: private final NotificationLockscreenUserManager mLockscreenUserManager = @@ -129,8 +128,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx /** * Injected constructor. See {@link NotificationsModule}. */ - public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager, - Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler, @Background Handler bgHandler, + public NotificationGutsManager(Context context, + Lazy<StatusBar> statusBarLazy, + @Main Handler mainHandler, + @Background Handler bgHandler, AccessibilityManager accessibilityManager, HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, @@ -141,9 +142,9 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, BubbleController bubbleController, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + OnUserInteractionCallback onUserInteractionCallback) { mContext = context; - mVisualStabilityManager = visualStabilityManager; mStatusBarLazy = statusBarLazy; mMainHandler = mainHandler; mBgHandler = bgHandler; @@ -158,6 +159,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mAssistantFeedbackController = assistantFeedbackController; mBubbleController = bubbleController; mUiEventLogger = uiEventLogger; + mOnUserInteractionCallback = onUserInteractionCallback; } public void setUpWithPresenter(NotificationPresenter presenter, @@ -363,7 +365,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx notificationInfoView.bindNotification( pmUser, mNotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, packageName, row.getEntry().getChannel(), @@ -474,7 +476,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mShortcutManager, pmUser, mNotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, packageName, entry.getChannel(), entry, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index a6ba85fc8bdf..7a976ac82223 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -60,7 +60,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.lang.annotation.Retention; @@ -93,9 +92,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private TextView mAutomaticDescriptionView; private INotificationManager mINotificationManager; + private OnUserInteractionCallback mOnUserInteractionCallback; private PackageManager mPm; private MetricsLogger mMetricsLogger; - private VisualStabilityManager mVisualStabilityManager; private ChannelEditorDialogController mChannelEditorDialogController; private String mPackageName; @@ -119,6 +118,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private boolean mIsAutomaticChosen; private boolean mIsSingleDefaultChannel; private boolean mIsNonblockable; + private NotificationEntry mEntry; private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; @@ -188,7 +188,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G public void bindNotification( PackageManager pm, INotificationManager iNotificationManager, - VisualStabilityManager visualStabilityManager, + OnUserInteractionCallback onUserInteractionCallback, ChannelEditorDialogController channelEditorDialogController, String pkg, NotificationChannel notificationChannel, @@ -204,11 +204,12 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G throws RemoteException { mINotificationManager = iNotificationManager; mMetricsLogger = Dependency.get(MetricsLogger.class); - mVisualStabilityManager = visualStabilityManager; + mOnUserInteractionCallback = onUserInteractionCallback; mChannelEditorDialogController = channelEditorDialogController; mPackageName = pkg; mUniqueChannelsInRow = uniqueChannelsInRow; mNumUniqueChannelsInRow = uniqueChannelsInRow.size(); + mEntry = entry; mSbn = entry.getSbn(); mPm = pm; mAppSettingsClickListener = onAppSettingsClick; @@ -438,7 +439,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null, mStartingChannelImportance, newImportance, mIsAutomaticChosen)); - mVisualStabilityManager.temporarilyAllowReordering(); + mOnUserInteractionCallback.onImportanceChanged(mEntry); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnDismissCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallback.java index f1aed899e060..0c3f553825d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnDismissCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallback.java @@ -21,15 +21,21 @@ import android.service.notification.NotificationListenerService; import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** - * Callback when a user clicks on an auto-cancelled notification or manually swipes to dismiss the - * notification. + * Callbacks for when a user interacts with an {@link ExpandableNotificationRow}. */ -public interface OnDismissCallback { +public interface OnUserInteractionCallback { /** - * Handle a user interaction that triggers a notification dismissal. + * Handle a user interaction that triggers a notification dismissal. Called when a user clicks + * on an auto-cancelled notification or manually swipes to dismiss the notification. */ void onDismiss( NotificationEntry entry, @NotificationListenerService.NotificationCancelReason int cancellationReason); + + /** + * Triggered after a user has changed the importance of the notification via its + * {@link NotificationGuts}. + */ + void onImportanceChanged(NotificationEntry entry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 7e4266c7cd28..a396305a49b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -22,7 +22,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.ColorDrawable; import android.service.notification.StatusBarNotification; -import android.util.ArraySet; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.NotificationHeaderView; @@ -36,7 +35,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.NotificationHeaderUtil; import com.android.systemui.statusbar.notification.NotificationUtils; -import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.HybridGroupManager; import com.android.systemui.statusbar.notification.row.HybridNotificationView; 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 2eb472b2f3a2..e061472b3939 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 @@ -95,21 +95,15 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SwipeHelper; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; @@ -125,10 +119,10 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.logging.NotificationLogger; @@ -145,7 +139,6 @@ import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; -import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -154,11 +147,8 @@ import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.ScrollAdapter; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.Assert; @@ -178,8 +168,7 @@ import javax.inject.Named; /** * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. */ -public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter, - ConfigurationListener, Dumpable, DynamicPrivacyController.Listener { +public class NotificationStackScrollLayout extends ViewGroup implements Dumpable { public static final float BACKGROUND_ALPHA_DIMMED = 0.7f; private static final String TAG = "StackScroller"; @@ -196,10 +185,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * gap is drawn between them). In this case we don't want to round their corners. */ private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1; - private final KeyguardBypassController mKeyguardBypassController; + private OnMenuEventListener mMenuEventListener; + private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; private final DynamicPrivacyController mDynamicPrivacyController; private final SysuiStatusBarStateController mStatusbarStateController; - private final KeyguardMediaController mKeyguardMediaController; private ExpandHelper mExpandHelper; private final NotificationSwipeHelper mSwipeHelper; @@ -250,6 +239,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mBottomMargin; private int mBottomInset = 0; private float mQsExpansionFraction; + private int mCurrentUserId; /** * The algorithm which calculates the properties for our children @@ -327,7 +317,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * motion. */ private int mMaxScrollAfterExpand; - private ExpandableNotificationRow.LongPressListener mLongPressListener; boolean mCheckForLeavebehind; /** @@ -349,12 +338,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return true; } }; - private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() { - @Override - public void onUserChanged(int userId) { - updateSensitiveness(false /* animated */); - } - }; + private StatusBar mStatusBar; private int[] mTempInt2 = new int[2]; private boolean mGenerateChildOrderChangedEvent; @@ -369,7 +353,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private boolean mForceNoOverlappingRendering; private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>(); private FalsingManager mFalsingManager; - private final ZenModeController mZenController; private boolean mAnimationRunning; private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater = new ViewTreeObserver.OnPreDrawListener() { @@ -499,7 +482,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); private int mHeadsUpInset; private HeadsUpAppearanceController mHeadsUpAppearanceController; - private final NotificationLockscreenUserManager mLockscreenUserManager; private final Rect mTmpRect = new Rect(); private final FeatureFlags mFeatureFlags; private final NotifPipeline mNotifPipeline; @@ -512,7 +494,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd protected final UiEventLogger mUiEventLogger; private final NotificationRemoteInputManager mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); - private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class); private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class); private final LockscreenGestureLogger mLockscreenGestureLogger = @@ -537,11 +518,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mWaterfallTopInset; private NotificationStackScrollLayoutController mController; - private SysuiColorExtractor.OnColorsChangedListener mOnColorsChangedListener = - (colorExtractor, which) -> { - final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); - updateDecorViews(useDarkText); - }; + private boolean mKeyguardMediaControllorVisible; private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener = new ExpandableView.OnHeightChangedListener() { @@ -556,20 +533,44 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } }; + private final ScrollAdapter mScrollAdapter = new ScrollAdapter() { + @Override + public boolean isScrolledToTop() { + if (ANCHOR_SCROLLING) { + updateScrollAnchor(); + // TODO: once we're recycling this will need to check the adapter position of the + // child + return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0; + } else { + return mOwnScrollY == 0; + } + } + + @Override + public boolean isScrolledToBottom() { + if (ANCHOR_SCROLLING) { + return getMaxPositiveScrollAmount() <= 0; + } else { + return mOwnScrollY >= getScrollRange(); + } + } + + @Override + public View getHostView() { + return NotificationStackScrollLayout.this; + } + }; + @Inject public NotificationStackScrollLayout( @Named(VIEW_CONTEXT) Context context, AttributeSet attrs, NotificationRoundnessManager notificationRoundnessManager, DynamicPrivacyController dynamicPrivacyController, - SysuiStatusBarStateController statusBarStateController, + SysuiStatusBarStateController statusbarStateController, HeadsUpManagerPhone headsUpManager, - KeyguardBypassController keyguardBypassController, - KeyguardMediaController keyguardMediaController, FalsingManager falsingManager, - NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGutsManager notificationGutsManager, - ZenModeController zenController, NotificationSectionsManager notificationSectionsManager, ForegroundServiceSectionController fgsSectionController, ForegroundServiceDismissalFeatureController fgsFeatureController, @@ -584,13 +585,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mRoundnessManager = notificationRoundnessManager; - mLockscreenUserManager = notificationLockscreenUserManager; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed); - mKeyguardBypassController = keyguardBypassController; mFalsingManager = falsingManager; - mZenController = zenController; mFgsSectionController = fgsSectionController; mSectionsManager = notificationSectionsManager; @@ -609,16 +607,33 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback, minHeight, maxHeight); mExpandHelper.setEventSource(this); - mExpandHelper.setScrollAdapter(this); + mExpandHelper.setScrollAdapter(mScrollAdapter); + + // TODO: move swipe helper into controller. + // The anonymous proxy through to mMenuEventListener is temporary until more can be moved + // into the controller. mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback, - getContext(), mMenuEventListener, mFalsingManager); + getContext(), new OnMenuEventListener() { + @Override + public void onMenuClicked(View row, int x, int y, MenuItem menu) { + mMenuEventListener.onMenuClicked(row, x, y, menu); + } + + @Override + public void onMenuReset(View row) { + mMenuEventListener.onMenuReset(row); + } + + @Override + public void onMenuShown(View row) { + mMenuEventListener.onMenuShown(row); + } + }, mFalsingManager); mStackScrollAlgorithm = createStackScrollAlgorithm(context); - initView(context); mShouldDrawNotificationBackground = res.getBoolean(R.bool.config_drawNotificationBackground); mFadeNotificationsOnDismiss = res.getBoolean(R.bool.config_fadeNotificationsOnDismiss); - mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); setOutlineProvider(mOutlineProvider); // Blocking helper manager wants to know the expanded state, update as well. @@ -667,22 +682,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd }); } - dynamicPrivacyController.addListener(this); mDynamicPrivacyController = dynamicPrivacyController; - mStatusbarStateController = statusBarStateController; + mStatusbarStateController = statusbarStateController; initializeForegroundServiceSection(fgsFeatureController); mUiEventLogger = uiEventLogger; - mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); - mKeyguardMediaController = keyguardMediaController; - keyguardMediaController.setVisibilityChangedListener((visible) -> { - if (visible) { - generateAddAnimation(keyguardMediaController.getView(), false /*fromMoreCard */); - } else { - generateRemoveAnimation(keyguardMediaController.getView()); - } - requestChildrenUpdate(); - return null; - }); } private void initializeForegroundServiceSection( @@ -721,7 +724,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd public float getWakeUpHeight() { ExpandableView firstChild = getFirstChildWithBackground(); if (firstChild != null) { - if (mKeyguardBypassController.getBypassEnabled()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled()) { return firstChild.getHeadsUpHeightWithoutHeader(); } else { return firstChild.getCollapsedHeight(); @@ -730,36 +733,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return 0f; } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onDensityOrFontScaleChanged() { - reinflateViews(); - } - - private void reinflateViews() { + void reinflateViews() { inflateFooterView(); inflateEmptyShadeView(); updateFooter(); mSectionsManager.reinflateViews(LayoutInflater.from(mContext)); } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onThemeChanged() { - updateFooter(); - } - - @Override - public void onOverlayChanged() { - int newRadius = mContext.getResources().getDimensionPixelSize( - Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius)); - if (mCornerRadius != newRadius) { - mCornerRadius = newRadius; - invalidate(); - } - reinflateViews(); - } - @VisibleForTesting @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void updateFooter() { @@ -819,31 +799,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd }; } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class)) - .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); - Dependency.get(ConfigurationController.class).addCallback(this); - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); - Dependency.get(ConfigurationController.class).removeCallback(this); - } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public NotificationSwipeActionHelper getSwipeActionHelper() { return mSwipeHelper; } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onUiModeChanged() { + void updateBgColor() { mBgColor = mContext.getColor(R.color.notification_shade_background_color); updateBackgroundDimming(); mShelf.onUiModeChanged(); @@ -927,7 +888,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } boolean shouldDrawBackground; - if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) { shouldDrawBackground = isPulseExpanding(); } else { shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild; @@ -1042,9 +1003,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } + private void reinitView() { + initView(getContext(), mKeyguardBypassEnabledProvider); + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void initView(Context context) { + void initView(Context context, + KeyguardBypassEnabledProvider keyguardBypassEnabledProvider) { mScroller = new OverScroller(getContext()); + mKeyguardBypassEnabledProvider = keyguardBypassEnabledProvider; + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setClipChildren(false); final ViewConfiguration configuration = ViewConfiguration.get(context); @@ -1076,6 +1044,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd R.dimen.heads_up_status_bar_padding); } + void updateCornerRadius() { + int newRadius = getResources().getDimensionPixelSize( + Utils.getThemeAttr(getContext(), android.R.attr.dialogCornerRadius)); + if (mCornerRadius != newRadius) { + mCornerRadius = newRadius; + invalidate(); + } + } + @ShadeViewRefactor(RefactorComponent.COORDINATOR) private void notifyHeightChangeListener(ExpandableView view) { notifyHeightChangeListener(view, false /* needsAnimation */); @@ -1247,7 +1224,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd float end = start + child.getActualHeight(); boolean clip = clipStart > start && clipStart < end || clipEnd >= start && clipEnd <= end; - clip &= !(first && isScrolledToTop()); + clip &= !(first && mScrollAdapter.isScrolledToTop()); child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) : ExpandableView.NO_ROUNDNESS); first = false; @@ -1307,7 +1284,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void requestChildrenUpdate() { + void requestChildrenUpdate() { if (!mChildrenUpdateRequested) { getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater); mChildrenUpdateRequested = true; @@ -1437,7 +1414,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private void notifyAppearChangedListeners() { float appear; float expandAmount; - if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) { appear = calculateAppearFractionBypass(); expandAmount = getPulseHeight(); } else { @@ -1839,7 +1816,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mSwipeHelper.setDensityScale(densityScale); float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop(); mSwipeHelper.setPagingTouchSlop(pagingTouchSlop); - initView(getContext()); + reinitView(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -2240,7 +2217,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd springBack(); } else { float overScrollTop = getCurrentOverScrollAmount(true /* top */); - if (isScrolledToTop() && mScrollAnchorViewY > 0) { + if (mScrollAdapter.isScrolledToTop() && mScrollAnchorViewY > 0) { notifyOverscrollTopListener(mScrollAnchorViewY, isRubberbanded(true /* onTop */)); } else { @@ -2288,7 +2265,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void springBack() { if (ANCHOR_SCROLLING) { - boolean overScrolledTop = isScrolledToTop() && mScrollAnchorViewY > 0; + boolean overScrolledTop = mScrollAdapter.isScrolledToTop() && mScrollAnchorViewY > 0; int maxPositiveScrollAmount = getMaxPositiveScrollAmount(); boolean overscrolledBottom = maxPositiveScrollAmount < 0; if (overScrolledTop || overscrolledBottom) { @@ -2565,8 +2542,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private void updateForwardAndBackwardScrollability() { - boolean forwardScrollable = mScrollable && !isScrolledToBottom(); - boolean backwardsScrollable = mScrollable && !isScrolledToTop(); + boolean forwardScrollable = mScrollable && !mScrollAdapter.isScrolledToBottom(); + boolean backwardsScrollable = mScrollable && !mScrollAdapter.isScrolledToTop(); boolean changed = forwardScrollable != mForwardScrollable || backwardsScrollable != mBackwardScrollable; mForwardScrollable = forwardScrollable; @@ -2687,7 +2664,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1 && (mAmbientState.isDozing() - || (mKeyguardBypassController.getBypassEnabled() && onKeyguard)); + || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard)); for (NotificationSection section : mSections) { int minBottomPosition = minTopPosition; if (section == lastSection) { @@ -2850,7 +2827,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mLastScrollerY = 0; // x velocity is set to 1 to avoid overscroller bug mScroller.fling(0, 0, 1, velocityY, 0, 0, minY, maxY, 0, - mExpandedInThisMotion && !isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2); + mExpandedInThisMotion + && !mScrollAdapter.isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2); } } @@ -2957,7 +2935,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } else { mTopPaddingOverflow = 0; } - setTopPadding(topPadding, animate && !mKeyguardBypassController.getBypassEnabled()); + setTopPadding(topPadding, animate && !mKeyguardBypassEnabledProvider.getBypassEnabled()); setExpandedHeight(mExpandedHeight); } @@ -3115,7 +3093,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * @return Whether an animation was generated. */ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private boolean generateRemoveAnimation(ExpandableView child) { + boolean generateRemoveAnimation(ExpandableView child) { if (removeRemovedChildFromHeadsUpChangeAnimations(child)) { mAddedHeadsUpChildren.remove(child); return false; @@ -3352,7 +3330,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - void onViewAddedInternal(ExpandableView child) { + private void onViewAddedInternal(ExpandableView child) { updateHideSensitiveForChild(child); child.setOnHeightChangedListener(mOnChildHeightChangedListener); generateAddAnimation(child, false /* fromMoreCard */); @@ -3363,7 +3341,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } if (ANCHOR_SCROLLING) { // TODO: once we're recycling this will need to check the adapter position of the child - if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) { + if (child == getFirstChildNotGone() + && (mScrollAdapter.isScrolledToTop() || !mIsExpanded)) { // New child was added at the top while we're scrolled to the top; // make it the new anchor view so that we stay at the top. mScrollAnchorView = child; @@ -3381,6 +3360,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd onViewRemovedInternal(row, childrenContainer); } + public void notifyGroupChildAdded(ExpandableView row) { + onViewAddedInternal(row); + } + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void setAnimationsEnabled(boolean animationsEnabled) { mAnimationsEnabled = animationsEnabled; @@ -3553,7 +3536,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd boolean performDisappearAnimation = !mIsExpanded // Only animate if we still have pinned heads up, otherwise we just have the // regular collapse animation of the lock screen - || (mKeyguardBypassController.getBypassEnabled() && onKeyguard() + || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard() && mHeadsUpManager.hasPinnedHeadsUp()); if (performDisappearAnimation && !isHeadsUp) { type = row.wasJustClicked() @@ -3789,11 +3772,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return y < getHeight() - getEmptyBottomMargin(); } - @ShadeViewRefactor(RefactorComponent.INPUT) - public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) { - mLongPressListener = listener; - } - private float getTouchSlop(MotionEvent event) { // Adjust the touch slop if another gesture may be being performed. return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE @@ -4247,7 +4225,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd case MotionEvent.ACTION_DOWN: { final int y = (int) ev.getY(); - mScrolledToTopOnFirstDown = isScrolledToTop(); + mScrolledToTopOnFirstDown = mScrollAdapter.isScrolledToTop(); final ExpandableView childAtTouchPos = getChildAtPosition( ev.getX(), y, false /* requireMinHeight */, false /* ignoreDecors */); if (childAtTouchPos == null) { @@ -4431,32 +4409,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @Override - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public boolean isScrolledToTop() { - if (ANCHOR_SCROLLING) { - updateScrollAnchor(); - // TODO: once we're recycling this will need to check the adapter position of the child - return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0; - } else { - return mOwnScrollY == 0; - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public boolean isScrolledToBottom() { - if (ANCHOR_SCROLLING) { - return getMaxPositiveScrollAmount() <= 0; - } else { - return mOwnScrollY >= getScrollRange(); - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public View getHostView() { - return this; + boolean isScrolledToBottom() { + return mScrollAdapter.isScrolledToBottom(); } @ShadeViewRefactor(RefactorComponent.COORDINATOR) @@ -4754,8 +4708,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void updateSensitiveness(boolean animate) { - boolean hideSensitive = mLockscreenUserManager.isAnyProfilePublicMode(); + void updateSensitiveness(boolean animate, boolean hideSensitive) { if (hideSensitive != mAmbientState.isHideSensitive()) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { @@ -4852,7 +4805,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * @param lightTheme True if light theme should be used. */ @ShadeViewRefactor(RefactorComponent.DECORATOR) - private void updateDecorViews(boolean lightTheme) { + void updateDecorViews(boolean lightTheme) { if (lightTheme == mUsingLightTheme) { return; } @@ -4947,7 +4900,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd // Since we are clipping to the outline we need to make sure that the shadows aren't // clipped when pulsing float ownTranslationZ = 0; - if (mKeyguardBypassController.getBypassEnabled() && mAmbientState.isHiddenAtAll()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && mAmbientState.isHiddenAtAll()) { ExpandableView firstChildNotGone = getFirstChildNotGone(); if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) { ownTranslationZ = firstChildNotGone.getTranslationZ(); @@ -5012,11 +4965,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void updateEmptyShadeView(boolean visible) { + void updateEmptyShadeView(boolean visible, boolean notifVisibleInShade) { mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled); int oldTextRes = mEmptyShadeView.getTextResource(); - int newTextRes = mZenController.areNotificationsHiddenInShade() + int newTextRes = notifVisibleInShade ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text; if (oldTextRes != newTextRes) { mEmptyShadeView.setText(newTextRes); @@ -5113,7 +5066,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void requestAnimateEverything() { + void requestAnimateEverything() { if (mIsExpanded && mAnimationsEnabled) { mEverythingNeedsAnimation = true; mNeedsAnimation = true; @@ -5451,17 +5404,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mAmbientState.setStatusBarState(statusBarState); } - private void onStatePostChange() { + void onStatePostChange(boolean fromShadeLocked) { boolean onKeyguard = onKeyguard(); + mAmbientState.setActivatedChild(null); + mAmbientState.setDimmed(onKeyguard); + if (mHeadsUpAppearanceController != null) { mHeadsUpAppearanceController.onStateChanged(); } - SysuiStatusBarStateController state = (SysuiStatusBarStateController) - Dependency.get(StatusBarStateController.class); - updateSensitiveness(state.goingToFullShade() /* animate */); - setDimmed(onKeyguard, state.fromShadeLocked() /* animate */); + setDimmed(onKeyguard, fromShadeLocked); setExpandingEnabled(!onKeyguard); ActivatableNotificationView activatedChild = getActivatedChild(); setActivatedChild(null); @@ -5791,7 +5744,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd */ public float setPulseHeight(float height) { mAmbientState.setPulseHeight(height); - if (mKeyguardBypassController.getBypassEnabled()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled()) { notifyAppearChangedListeners(); } requestChildrenUpdate(); @@ -5841,17 +5794,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mDimmedNeedsAnimation = true; } - @Override - public void onDynamicPrivacyChanged() { - if (mIsExpanded) { - // The bottom might change because we're using the final actual height of the view - mAnimateBottomOnLayout = true; - } - // Let's update the footer once the notifications have been updated (in the next frame) - post(() -> { - updateFooter(); - updateSectionBoundaries("dynamic privacy changed"); - }); + void setAnimateBottomOnLayout(boolean animateBottomOnLayout) { + mAnimateBottomOnLayout = animateBottomOnLayout; } public void setOnPulseHeightChangedListener(Runnable listener) { @@ -5878,6 +5822,31 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return mController; } + void setCurrentUserid(int userId) { + mCurrentUserId = userId; + } + + void onMenuShown(View row) { + mSwipeHelper.onMenuShown(row); + } + + void onMenuReset(View row) { + View translatingParentView = mSwipeHelper.getTranslatingParentView(); + if (translatingParentView != null && row == translatingParentView) { + mSwipeHelper.clearExposedMenuView(); + mSwipeHelper.clearTranslatingParentView(); + if (row instanceof ExpandableNotificationRow) { + mHeadsUpManager.setMenuShown( + ((ExpandableNotificationRow) row).getEntry(), false); + + } + } + } + + void setMenuEventListener(OnMenuEventListener menuEventListener) { + mMenuEventListener = menuEventListener; + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ @@ -6233,89 +6202,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private final StateListener mStateListener = new StateListener() { - @Override - public void onStatePreChange(int oldState, int newState) { - if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) { - requestAnimateEverything(); - } - } - - @Override - public void onStateChanged(int newState) { - setStatusBarState(newState); - } - - @Override - public void onStatePostChange() { - NotificationStackScrollLayout.this.onStatePostChange(); - } - }; - - @VisibleForTesting - @ShadeViewRefactor(RefactorComponent.INPUT) - protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { - @Override - public void onMenuClicked(View view, int x, int y, MenuItem item) { - if (mLongPressListener == null) { - return; - } - if (view instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) - .setType(MetricsEvent.TYPE_ACTION) - ); - } - mLongPressListener.onLongPress(view, x, y, item); - } - @Override - public void onMenuReset(View row) { - View translatingParentView = mSwipeHelper.getTranslatingParentView(); - if (translatingParentView != null && row == translatingParentView) { - mSwipeHelper.clearExposedMenuView(); - mSwipeHelper.clearTranslatingParentView(); - if (row instanceof ExpandableNotificationRow) { - mHeadsUpManager.setMenuShown( - ((ExpandableNotificationRow) row).getEntry(), false); - - } - } - } - - @Override - public void onMenuShown(View row) { - if (row instanceof ExpandableNotificationRow) { - ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row; - mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) - .setType(MetricsEvent.TYPE_ACTION)); - mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); - mSwipeHelper.onMenuShown(row); - mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, - false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - - // Check to see if we want to go directly to the notfication guts - NotificationMenuRowPlugin provider = notificationRow.getProvider(); - if (provider.shouldShowGutsOnSnapOpen()) { - MenuItem item = provider.menuItemToExposeOnSnap(); - if (item != null) { - Point origin = provider.getRevealAnimationOrigin(); - mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); - } else { - Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " - + "menu item in menuItemtoExposeOnSnap. Skipping."); - } - - // Close the menu row since we went directly to the guts - resetExposedMenuView(false, true); - } - } - } - }; @ShadeViewRefactor(RefactorComponent.INPUT) private final NotificationSwipeHelper.NotificationCallback mNotificationCallback = @@ -6546,7 +6433,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @SelectedRows int selectedRows) { if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { if (selectedRows == ROWS_ALL) { - mNotifCollection.dismissAllNotifications(mLockscreenUserManager.getCurrentUserId()); + mNotifCollection.dismissAllNotifications(mCurrentUserId); } else { final List<Pair<NotificationEntry, DismissedByUserStats>> entriesWithRowsDismissedFromShade = new ArrayList<>(); @@ -6575,7 +6462,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } if (selectedRows == ROWS_ALL) { try { - mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId()); + mBarService.onClearAllNotifications(mCurrentUserId); } catch (Exception ex) { } } @@ -6597,6 +6484,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd NotificationLogger.getNotificationLocation(entry))); } + public void setKeyguardMediaControllorVisible(boolean keyguardMediaControllorVisible) { + mKeyguardMediaControllorVisible = keyguardMediaControllorVisible; + } + // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ @ShadeViewRefactor(RefactorComponent.INPUT) @@ -6605,8 +6496,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd /* Only ever called as a consequence of a lockscreen expansion gesture. */ @Override public boolean onDraggedDown(View startingChild, int dragLengthY) { - boolean canDragDown = hasActiveNotifications() - || mKeyguardMediaController.getView().getVisibility() == VISIBLE; + boolean canDragDown = hasActiveNotifications() || mKeyguardMediaControllorVisible; if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) { mLockscreenGestureLogger.write( MetricsEvent.ACTION_LS_SHADE, @@ -6684,7 +6574,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override public boolean isDragDownAnywhereEnabled() { return mStatusbarStateController.getState() == StatusBarState.KEYGUARD - && !mKeyguardBypassController.getBypassEnabled(); + && !mKeyguardBypassEnabledProvider.getBypassEnabled(); } }; @@ -6867,4 +6757,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return INVALID; } } + + interface KeyguardBypassEnabledProvider { + boolean getBypassEnabled(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index d52b3038f93d..ca78b2a1fc68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -17,20 +17,34 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; -import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY; +import android.graphics.Point; import android.graphics.PointF; import android.provider.Settings; +import android.util.Log; import android.view.Display; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; import android.widget.FrameLayout; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.media.KeyguardMediaController; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; @@ -41,11 +55,15 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import java.util.function.BiConsumer; @@ -53,19 +71,172 @@ import java.util.function.BiConsumer; import javax.inject.Inject; import javax.inject.Named; +import kotlin.Unit; + /** * Controller for {@link NotificationStackScrollLayout}. */ @StatusBarComponent.StatusBarScope public class NotificationStackScrollLayoutController { + private static final String TAG = "StackScrollerController"; + private final boolean mAllowLongPress; private final NotificationGutsManager mNotificationGutsManager; private final HeadsUpManagerPhone mHeadsUpManager; private final NotificationRoundnessManager mNotificationRoundnessManager; private final TunerService mTunerService; + private final DynamicPrivacyController mDynamicPrivacyController; + private final ConfigurationController mConfigurationController; + private final ZenModeController mZenModeController; + private final MetricsLogger mMetricsLogger; + private final KeyguardMediaController mKeyguardMediaController; + private final SysuiStatusBarStateController mStatusBarStateController; + private final KeyguardBypassController mKeyguardBypassController; + private final SysuiColorExtractor mColorExtractor; + private final NotificationLockscreenUserManager mLockscreenUserManager; + + private NotificationStackScrollLayout mView; + private final NotificationListContainerImpl mNotificationListContainer = new NotificationListContainerImpl(); - private NotificationStackScrollLayout mView; + + @VisibleForTesting + final View.OnAttachStateChangeListener mOnAttachStateChangeListener = + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + mConfigurationController.addCallback(mConfigurationListener); + mStatusBarStateController.addCallback( + mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); + } + + @Override + public void onViewDetachedFromWindow(View v) { + mConfigurationController.removeCallback(mConfigurationListener); + mStatusBarStateController.removeCallback(mStateListener); + } + }; + + private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> { + if (mView.isExpanded()) { + // The bottom might change because we're using the final actual height of the view + mView.setAnimateBottomOnLayout(true); + } + // Let's update the footer once the notifications have been updated (in the next frame) + mView.post(() -> { + updateFooter(); + updateSectionBoundaries("dynamic privacy changed"); + }); + }; + + @VisibleForTesting + final ConfigurationListener mConfigurationListener = new ConfigurationListener() { + @Override + public void onDensityOrFontScaleChanged() { + mView.reinflateViews(); + } + + @Override + public void onOverlayChanged() { + mView.updateCornerRadius(); + mView.reinflateViews(); + } + + @Override + public void onUiModeChanged() { + mView.updateBgColor(); + } + + @Override + public void onThemeChanged() { + updateFooter(); + } + }; + + private final StatusBarStateController.StateListener mStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStatePreChange(int oldState, int newState) { + if (oldState == StatusBarState.SHADE_LOCKED + && newState == StatusBarState.KEYGUARD) { + mView.requestAnimateEverything(); + } + } + + @Override + public void onStateChanged(int newState) { + mView.setStatusBarState(newState); + } + + @Override + public void onStatePostChange() { + mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(), + mLockscreenUserManager.isAnyProfilePublicMode()); + mView.onStatePostChange(mStatusBarStateController.fromShadeLocked()); + } + }; + + private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() { + @Override + public void onUserChanged(int userId) { + mView.setCurrentUserid(userId); + mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode()); + } + }; + + private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { + @Override + public void onMenuClicked( + View view, int x, int y, NotificationMenuRowPlugin.MenuItem item) { + if (!mAllowLongPress) { + return; + } + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) + .setType(MetricsEvent.TYPE_ACTION) + ); + } + mNotificationGutsManager.openGuts(view, x, y, item); + } + + @Override + public void onMenuReset(View row) { + mView.onMenuReset(row); + } + + @Override + public void onMenuShown(View row) { + if (row instanceof ExpandableNotificationRow) { + ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row; + mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) + .setType(MetricsEvent.TYPE_ACTION)); + mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); + mView.onMenuShown(row); + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, + false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + + // Check to see if we want to go directly to the notification guts + NotificationMenuRowPlugin provider = notificationRow.getProvider(); + if (provider.shouldShowGutsOnSnapOpen()) { + NotificationMenuRowPlugin.MenuItem item = provider.menuItemToExposeOnSnap(); + if (item != null) { + Point origin = provider.getRevealAnimationOrigin(); + mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); + } else { + Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " + + "menu item in menuItemtoExposeOnSnap. Skipping."); + } + + // Close the menu row since we went directly to the guts + mView.resetExposedMenuView(false, true); + } + } + } + }; @Inject public NotificationStackScrollLayoutController( @@ -73,23 +244,44 @@ public class NotificationStackScrollLayoutController { NotificationGutsManager notificationGutsManager, HeadsUpManagerPhone headsUpManager, NotificationRoundnessManager notificationRoundnessManager, - TunerService tunerService) { + TunerService tunerService, + DynamicPrivacyController dynamicPrivacyController, + ConfigurationController configurationController, + SysuiStatusBarStateController statusBarStateController, + KeyguardMediaController keyguardMediaController, + KeyguardBypassController keyguardBypassController, + ZenModeController zenModeController, + SysuiColorExtractor colorExtractor, + NotificationLockscreenUserManager lockscreenUserManager, + MetricsLogger metricsLogger) { mAllowLongPress = allowLongPress; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; mNotificationRoundnessManager = notificationRoundnessManager; mTunerService = tunerService; + mDynamicPrivacyController = dynamicPrivacyController; + mConfigurationController = configurationController; + mStatusBarStateController = statusBarStateController; + mKeyguardMediaController = keyguardMediaController; + mKeyguardBypassController = keyguardBypassController; + mZenModeController = zenModeController; + mColorExtractor = colorExtractor; + mLockscreenUserManager = lockscreenUserManager; + mMetricsLogger = metricsLogger; } public void attach(NotificationStackScrollLayout view) { mView = view; mView.setController(this); - - if (mAllowLongPress) { - mView.setLongPressListener(mNotificationGutsManager::openGuts); - } + mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled); mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here? + mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); + + mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); + mView.setCurrentUserid(mLockscreenUserManager.getCurrentUserId()); + + mView.setMenuEventListener(mMenuEventListener); mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); @@ -104,6 +296,28 @@ public class NotificationStackScrollLayoutController { }, Settings.Secure.NOTIFICATION_DISMISS_RTL, Settings.Secure.NOTIFICATION_HISTORY_ENABLED); + + mColorExtractor.addOnColorsChangedListener((colorExtractor, which) -> { + final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); + mView.updateDecorViews(useDarkText); + }); + + mKeyguardMediaController.setVisibilityChangedListener(visible -> { + mView.setKeyguardMediaControllorVisible(visible); + if (visible) { + mView.generateAddAnimation( + mKeyguardMediaController.getView(), false /*fromMoreCard */); + } else { + mView.generateRemoveAnimation(mKeyguardMediaController.getView()); + } + mView.requestChildrenUpdate(); + return Unit.INSTANCE; + }); + + if (mView.isAttachedToWindow()) { + mOnAttachStateChangeListener.onViewAttachedToWindow(mView); + } + mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); } public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) { @@ -419,7 +633,7 @@ public class NotificationStackScrollLayoutController { } public void updateEmptyShadeView(boolean visible) { - mView.updateEmptyShadeView(visible); + mView.updateEmptyShadeView(visible, mZenModeController.areNotificationsHiddenInShade()); } public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { @@ -581,7 +795,7 @@ public class NotificationStackScrollLayoutController { @Override public void notifyGroupChildAdded(ExpandableView row) { - mView.onViewAddedInternal(row); + mView.notifyGroupChildAdded(row); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index 7e0e87277927..efb24693beff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -43,7 +43,6 @@ import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -83,7 +82,6 @@ public final class DozeServiceHost implements DozeHost { private final Lazy<AssistManager> mAssistManagerLazy; private final DozeScrimController mDozeScrimController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final VisualStabilityManager mVisualStabilityManager; private final PulseExpansionHandler mPulseExpansionHandler; private final NotificationShadeWindowController mNotificationShadeWindowController; private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; @@ -108,7 +106,6 @@ public final class DozeServiceHost implements DozeHost { KeyguardViewMediator keyguardViewMediator, Lazy<AssistManager> assistManagerLazy, DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor, - VisualStabilityManager visualStabilityManager, PulseExpansionHandler pulseExpansionHandler, NotificationShadeWindowController notificationShadeWindowController, NotificationWakeUpCoordinator notificationWakeUpCoordinator, @@ -129,7 +126,6 @@ public final class DozeServiceHost implements DozeHost { mAssistManagerLazy = assistManagerLazy; mDozeScrimController = dozeScrimController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mVisualStabilityManager = visualStabilityManager; mPulseExpansionHandler = pulseExpansionHandler; mNotificationShadeWindowController = notificationShadeWindowController; mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; @@ -261,11 +257,10 @@ public final class DozeServiceHost implements DozeHost { } private void setPulsing(boolean pulsing) { - mStatusBarStateController.setPulsing(pulsing); mStatusBarKeyguardViewManager.setPulsing(pulsing); mKeyguardViewMediator.setPulsing(pulsing); mNotificationPanel.setPulsing(pulsing); - mVisualStabilityManager.setPulsing(pulsing); + mStatusBarStateController.setPulsing(pulsing); mIgnoreTouchWhilePulsing = false; if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) { mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 1d82e0808332..8092cb910b07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -31,8 +31,8 @@ import com.android.systemui.R; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -58,6 +58,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, private final NotificationGroupManager mGroupManager; private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>(); private final int mAutoHeadsUpNotificationDecay; + // TODO (b/162832756): remove visual stability manager when migrating to new pipeline private VisualStabilityManager mVisualStabilityManager; private boolean mReleaseOnExpandFinish; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index a0914e2d06af..6e37f90f9d94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -203,8 +203,8 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; @@ -1849,7 +1849,7 @@ public class StatusBar extends SystemUI implements DemoMode, mPanelExpanded = isExpanded; updateHideIconsForBouncer(false /* animate */); mNotificationShadeWindowController.setPanelExpanded(isExpanded); - mVisualStabilityManager.setPanelExpanded(isExpanded); + mStatusBarStateController.setPanelExpanded(isExpanded); if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) { if (DEBUG) { Log.v(TAG, "clearing notification effects from setExpandedHeight"); @@ -2070,7 +2070,7 @@ public class StatusBar extends SystemUI implements DemoMode, mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); } mNotificationPanelViewController.expand(true /* animate */); - ((NotificationListContainer) mStackScroller).setWillExpand(true); + mStackScroller.setWillExpand(true); mHeadsUpManager.unpinAll(true /* userUnpinned */); mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1); } else if (!mNotificationPanelViewController.isInSettings() @@ -3787,7 +3787,6 @@ public class StatusBar extends SystemUI implements DemoMode, mDeviceInteractive = false; mWakeUpComingFromTouch = false; mWakeUpTouchLocation = null; - mVisualStabilityManager.setScreenOn(false); updateVisibleToUser(); updateNotificationPanelTouchState(); @@ -3824,7 +3823,6 @@ public class StatusBar extends SystemUI implements DemoMode, if (!mKeyguardBypassController.getBypassEnabled()) { mHeadsUpManager.releaseAllImmediately(); } - mVisualStabilityManager.setScreenOn(true); updateVisibleToUser(); updateIsKeyguard(); mDozeServiceHost.stopDozing(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index f1e2f774a954..de11c9023200 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -73,7 +73,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -124,7 +124,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotificationPresenter mPresenter; private final NotificationPanelViewController mNotificationPanel; private final ActivityLaunchAnimator mActivityLaunchAnimator; - private final OnDismissCallback mOnDismissCallback; + private final OnUserInteractionCallback mOnUserInteractionCallback; private boolean mIsCollapsingToShowActivityOverLockscreen; @@ -158,7 +158,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit FeatureFlags featureFlags, MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, - OnDismissCallback onDismissCallback, + OnUserInteractionCallback onUserInteractionCallback, StatusBar statusBar, NotificationPresenter presenter, @@ -193,7 +193,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mFeatureFlags = featureFlags; mMetricsLogger = metricsLogger; mLogger = logger; - mOnDismissCallback = onDismissCallback; + mOnUserInteractionCallback = onUserInteractionCallback; // TODO: use KeyguardStateController#isOccluded to remove this dependency mStatusBar = statusBar; @@ -575,10 +575,10 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit // To avoid lags we're only performing the remove // after the shade was collapsed mShadeController.addPostCollapseAction( - () -> mOnDismissCallback.onDismiss(entry, REASON_CLICK) + () -> mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK) ); } else { - mOnDismissCallback.onDismiss(entry, REASON_CLICK); + mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK); } }); } @@ -628,7 +628,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final FeatureFlags mFeatureFlags; private final MetricsLogger mMetricsLogger; private final StatusBarNotificationActivityStarterLogger mLogger; - private final OnDismissCallback mOnDismissCallback; + private final OnUserInteractionCallback mOnUserInteractionCallback; private StatusBar mStatusBar; private NotificationPresenter mNotificationPresenter; @@ -666,7 +666,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit FeatureFlags featureFlags, MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, - OnDismissCallback onDismissCallback) { + OnUserInteractionCallback onUserInteractionCallback) { mContext = context; mCommandQueue = commandQueue; @@ -697,7 +697,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mFeatureFlags = featureFlags; mMetricsLogger = metricsLogger; mLogger = logger; - mOnDismissCallback = onDismissCallback; + mOnUserInteractionCallback = onUserInteractionCallback; } /** Sets the status bar to use as {@link StatusBar}. */ @@ -754,7 +754,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mFeatureFlags, mMetricsLogger, mLogger, - mOnDismissCallback, + mOnUserInteractionCallback, mStatusBar, mNotificationPresenter, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 0ec22b4a415f..67adaaae402e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -61,9 +61,9 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -217,7 +217,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor); mLockscreenUserManager.setUpWithPresenter(this); mMediaManager.setUpWithPresenter(this); - mVisualStabilityManager.setUpWithPresenter(this); mGutsManager.setUpWithPresenter(this, stackScrollerController.getNotificationListContainer(), mCheckSaveListener, mOnSettingsClickListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 2355bc92f7b5..2768b826fcfe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -61,7 +61,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java index b7bc8c8fb7c4..302301d79c3a 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java @@ -24,7 +24,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SystemServicesModule; import com.android.systemui.dagger.SystemUIBinder; import com.android.systemui.dagger.SystemUIModule; -import com.android.systemui.onehanded.dagger.OneHandedModule; import dagger.Subcomponent; @@ -36,7 +35,6 @@ import dagger.Subcomponent; DefaultComponentBinder.class, DependencyProvider.class, DependencyBinder.class, - OneHandedModule.class, SystemServicesModule.class, SystemUIBinder.class, SystemUIModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index e10225cedc7e..ca9cb08c0a59 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -63,7 +63,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.wmshell.WindowManagerShellModule; +import com.android.systemui.wmshell.WMShellModule; import javax.inject.Named; @@ -78,7 +78,7 @@ import dagger.Provides; @Module(includes = { DividerModule.class, QSModule.class, - WindowManagerShellModule.class + WMShellModule.class }, subcomponents = { }) diff --git a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java index 3a2172ae0fae..66f8f74c7cab 100644 --- a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java +++ b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java @@ -25,13 +25,11 @@ import android.provider.Settings; import java.util.concurrent.Executor; -import javax.inject.Inject; - /** * Wrapper around DeviceConfig useful for testing. */ public class DeviceConfigProxy { - @Inject + public DeviceConfigProxy() { } diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt index f22f59bee42f..bcfb2afeeda1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt @@ -4,8 +4,7 @@ import android.graphics.Rect import android.util.Log import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.FloatingContentCoordinator.FloatingContent -import java.util.* -import javax.inject.Inject +import java.util.HashMap /** Tag for debug logging. */ private const val TAG = "FloatingCoordinator" @@ -20,9 +19,9 @@ private const val TAG = "FloatingCoordinator" * other content out of the way. [onContentRemoved] should be called when the content is removed or * no longer visible. */ -@SysUISingleton -class FloatingContentCoordinator @Inject constructor() { +@SysUISingleton +class FloatingContentCoordinator constructor() { /** * Represents a piece of floating content, such as PIP or the Bubbles stack. Provides methods * that allow the [FloatingContentCoordinator] to determine the current location of the content, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java new file mode 100644 index 000000000000..34398cc79bd2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2020 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.wmshell; + +import android.content.Context; +import android.os.Handler; +import android.view.IWindowManager; + +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.pip.PipUiEventLogger; +import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.FloatingContentCoordinator; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.TransactionPool; + +import dagger.Module; +import dagger.Provides; + +/** + * Provides basic dependencies from {@link com.android.wm.shell}, the dependencies declared here + * should be shared among different branches of SystemUI. + */ +// TODO(b/162923491): Move most of these dependencies into WMSingleton scope. +@Module +public class WMShellBaseModule { + @SysUISingleton + @Provides + static TransactionPool provideTransactionPool() { + return new TransactionPool(); + } + + @SysUISingleton + @Provides + static DisplayController provideDisplayController(Context context, @Main Handler handler, + IWindowManager wmService) { + return new DisplayController(context, handler, wmService); + } + + @SysUISingleton + @Provides + static DeviceConfigProxy provideDeviceConfigProxy() { + return new DeviceConfigProxy(); + } + @SysUISingleton + @Provides + static FloatingContentCoordinator provideFloatingContentCoordinator() { + return new FloatingContentCoordinator(); + } + + @SysUISingleton + @Provides + static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger) { + return new PipUiEventLogger(uiEventLogger); + } + + @SysUISingleton + @Provides + static SystemWindows provideSystemWindows(DisplayController displayController, + IWindowManager wmService) { + return new SystemWindows(displayController, wmService); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 821150ee7ea6..44bb6ac5a49c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -16,7 +16,6 @@ package com.android.systemui.wmshell; -import android.content.Context; import android.os.Handler; import android.view.IWindowManager; @@ -26,38 +25,18 @@ import com.android.systemui.pip.phone.PipMenuActivity; import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; import dagger.Module; import dagger.Provides; /** - * Provides dependencies from {@link com.android.wm.shell}. + * Provides dependencies from {@link com.android.wm.shell} which could be customized among different + * branches of SystemUI. */ -@Module -// TODO(b/161116823) Clean up dependencies after wm shell migration finished. -public class WindowManagerShellModule { - @SysUISingleton - @Provides - static TransactionPool provideTransactionPool() { - return new TransactionPool(); - } - - @SysUISingleton - @Provides - static DisplayController provideDisplayController(Context context, @Main Handler handler, - IWindowManager wmService) { - return new DisplayController(context, handler, wmService); - } - - @SysUISingleton - @Provides - static SystemWindows provideSystemWindows(DisplayController displayController, - IWindowManager wmService) { - return new SystemWindows(displayController, wmService); - } - +// TODO(b/162923491): Move most of these dependencies into WMSingleton scope. +@Module(includes = WMShellBaseModule.class) +public class WMShellModule { @SysUISingleton @Provides static DisplayImeController provideDisplayImeController(IWindowManager wmService, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index a0e5f73b6a4c..11920233e403 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -182,7 +182,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // IBiometricsFace@1.0 does not support detection, only authentication. when(mFaceSensorProperties.isEmpty()).thenReturn(false); when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorProperties(0 /* id */, - false /* supportsFaceDetection */)); + false /* supportsFaceDetection */, true /* supportsSelfIllumination */)); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java index d7dba5fdf8c8..02d587d90655 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java @@ -46,9 +46,9 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class OneHandedManagerImplTest extends OneHandedTestCase { +public class OneHandedControllerTest extends OneHandedTestCase { Display mDisplay; - OneHandedManagerImpl mOneHandedManagerImpl; + OneHandedController mOneHandedController; OneHandedTimeoutHandler mTimeoutHandler; @Mock @@ -70,7 +70,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mDisplay = mContext.getDisplay(); - mOneHandedManagerImpl = new OneHandedManagerImpl( + mOneHandedController = new OneHandedController( getContext(), mCommandQueue, mMockDisplayController, @@ -102,7 +102,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testStartOneHanded() { - mOneHandedManagerImpl.startOneHanded(); + mOneHandedController.startOneHanded(); verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt()); } @@ -110,7 +110,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testStopOneHanded() { when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); - mOneHandedManagerImpl.stopOneHanded(); + mOneHandedController.stopOneHanded(); verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); } @@ -122,7 +122,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testStopOneHanded_shouldRemoveTimer() { - mOneHandedManagerImpl.stopOneHanded(); + mOneHandedController.stopOneHanded(); verify(mTimeoutHandler).removeTimer(); } @@ -130,7 +130,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testUpdateIsEnabled() { final boolean enabled = true; - mOneHandedManagerImpl.setOneHandedEnabled(enabled); + mOneHandedController.setOneHandedEnabled(enabled); verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled); } @@ -138,7 +138,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testUpdateSwipeToNotificationIsEnabled() { final boolean enabled = true; - mOneHandedManagerImpl.setSwipeToNotificationEnabled(enabled); + mOneHandedController.setSwipeToNotificationEnabled(enabled); verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java index 95a230f6511c..756382a6c630 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java @@ -30,8 +30,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -48,7 +48,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; - OneHandedManagerImpl mOneHandedManagerImpl; + OneHandedController mOneHandedController; @Mock CommandQueue mCommandQueue; @Mock @@ -66,7 +66,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { mTutorialHandler = new OneHandedTutorialHandler(mContext); mGestureHandler = Mockito.spy(new OneHandedGestureHandler( mContext, mMockDisplayController, mMockNavigationModeController)); - mOneHandedManagerImpl = new OneHandedManagerImpl( + mOneHandedController = new OneHandedController( getContext(), mCommandQueue, mMockDisplayController, @@ -93,15 +93,15 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { public void testReceiveNewConfig_whenSetOneHandedEnabled() { // 1st called at init verify(mGestureHandler).onOneHandedEnabled(true); - mOneHandedManagerImpl.setOneHandedEnabled(true); + mOneHandedController.setOneHandedEnabled(true); // 2nd called by setOneHandedEnabled() verify(mGestureHandler, times(2)).onOneHandedEnabled(true); } @Test public void testOneHandedDisabled_shouldDisposeInputChannel() { - mOneHandedManagerImpl.setOneHandedEnabled(false); - mOneHandedManagerImpl.setSwipeToNotificationEnabled(false); + mOneHandedController.setOneHandedEnabled(false); + mOneHandedController.setSwipeToNotificationEnabled(false); assertThat(mGestureHandler.mInputMonitor).isNull(); assertThat(mGestureHandler.mInputEventReceiver).isNull(); @@ -111,7 +111,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { public void testChangeNavBarTo2Button_shouldDisposeInputChannel() { // 1st called at init verify(mGestureHandler).onOneHandedEnabled(true); - mOneHandedManagerImpl.setOneHandedEnabled(true); + mOneHandedController.setOneHandedEnabled(true); // 2nd called by setOneHandedEnabled() verify(mGestureHandler, times(2)).onOneHandedEnabled(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java index 8ae632dd5a47..3c3ace052e47 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java @@ -28,8 +28,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -46,7 +46,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; - OneHandedManagerImpl mOneHandedManagerImpl; + OneHandedController mOneHandedController; @Mock CommandQueue mCommandQueue; @Mock @@ -64,7 +64,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { mTouchHandler = Mockito.spy(new OneHandedTouchHandler()); mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, mMockNavigationModeController); - mOneHandedManagerImpl = new OneHandedManagerImpl( + mOneHandedController = new OneHandedController( getContext(), mCommandQueue, mMockDisplayController, @@ -88,14 +88,14 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { @Test public void testOneHandedDisabled_shouldDisposeInputChannel() { - mOneHandedManagerImpl.setOneHandedEnabled(false); + mOneHandedController.setOneHandedEnabled(false); assertThat(mTouchHandler.mInputMonitor).isNull(); assertThat(mTouchHandler.mInputEventReceiver).isNull(); } @Test public void testOneHandedEnabled_monitorInputChannel() { - mOneHandedManagerImpl.setOneHandedEnabled(true); + mOneHandedController.setOneHandedEnabled(true); assertThat(mTouchHandler.mInputMonitor).isNotNull(); assertThat(mTouchHandler.mInputEventReceiver).isNotNull(); } @@ -104,7 +104,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { public void testReceiveNewConfig_whenSetOneHandedEnabled() { // 1st called at init verify(mTouchHandler).onOneHandedEnabled(true); - mOneHandedManagerImpl.setOneHandedEnabled(true); + mOneHandedController.setOneHandedEnabled(true); // 2nd called by setOneHandedEnabled() verify(mTouchHandler, times(2)).onOneHandedEnabled(true); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java index c75a8d2f5454..1bffbf7eb8dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java @@ -24,8 +24,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -42,7 +42,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; - OneHandedManagerImpl mOneHandedManagerImpl; + OneHandedController mOneHandedController; @Mock CommandQueue mCommandQueue; @Mock @@ -61,7 +61,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { mTutorialHandler = Mockito.spy(new OneHandedTutorialHandler(mContext)); mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, mMockNavigationModeController); - mOneHandedManagerImpl = new OneHandedManagerImpl( + mOneHandedController = new OneHandedController( getContext(), mCommandQueue, mMockDisplayController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java index f0e713e42046..ae3df5db30bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java @@ -48,7 +48,7 @@ public class OneHandedUITest extends OneHandedTestCase { OneHandedUI mOneHandedUI; ScreenLifecycle mScreenLifecycle; @Mock - OneHandedManagerImpl mMockOneHandedManagerImpl; + OneHandedController mOneHandedController; @Mock OneHandedTimeoutHandler mMockTimeoutHandler; @@ -59,7 +59,7 @@ public class OneHandedUITest extends OneHandedTestCase { mScreenLifecycle = new ScreenLifecycle(); mOneHandedUI = new OneHandedUI(mContext, mCommandQueue, - mMockOneHandedManagerImpl, + mOneHandedController, mScreenLifecycle); mOneHandedUI.start(); mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class); @@ -74,14 +74,14 @@ public class OneHandedUITest extends OneHandedTestCase { public void testStartOneHanded() { mOneHandedUI.startOneHanded(); - verify(mMockOneHandedManagerImpl).startOneHanded(); + verify(mOneHandedController).startOneHanded(); } @Test public void testStopOneHanded() { mOneHandedUI.stopOneHanded(); - verify(mMockOneHandedManagerImpl).stopOneHanded(); + verify(mOneHandedController).stopOneHanded(); } @Test @@ -89,7 +89,7 @@ public class OneHandedUITest extends OneHandedTestCase { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.TAPS_APP_TO_EXIT, 1); - verify(mMockOneHandedManagerImpl).setTaskChangeToExit(true); + verify(mOneHandedController).setTaskChangeToExit(true); } @Test @@ -97,7 +97,7 @@ public class OneHandedUITest extends OneHandedTestCase { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.ONE_HANDED_MODE_ENABLED, 1); - verify(mMockOneHandedManagerImpl).setOneHandedEnabled(true); + verify(mOneHandedController).setOneHandedEnabled(true); } @Test @@ -115,7 +115,7 @@ public class OneHandedUITest extends OneHandedTestCase { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); - verify(mMockOneHandedManagerImpl).setSwipeToNotificationEnabled(true); + verify(mOneHandedController).setSwipeToNotificationEnabled(true); } @Ignore("Clarifying do not receive callback") @@ -123,14 +123,14 @@ public class OneHandedUITest extends OneHandedTestCase { public void testKeyguardBouncerShowing_shouldStopOneHanded() { mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true); - verify(mMockOneHandedManagerImpl).stopOneHanded(); + verify(mOneHandedController).stopOneHanded(); } @Test public void testScreenTurningOff_shouldStopOneHanded() { mScreenLifecycle.dispatchScreenTurningOff(); - verify(mMockOneHandedManagerImpl).stopOneHanded(); + verify(mOneHandedController).stopOneHanded(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java index e9d2b73182e0..cdb177096f11 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java @@ -19,7 +19,6 @@ package com.android.systemui.pip; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; import android.content.ComponentName; import android.graphics.Rect; @@ -33,7 +32,6 @@ import android.view.Gravity; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.wm.shell.common.DisplayController; import org.junit.Before; import org.junit.Test; @@ -65,8 +63,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase { @Before public void setUp() throws Exception { initializeMockResources(); - mPipBoundsHandler = new PipBoundsHandler(mContext, new PipSnapAlgorithm(mContext), - mock(DisplayController.class)); + mPipBoundsHandler = new PipBoundsHandler(mContext); mTestComponentName1 = new ComponentName(mContext, "component1"); mTestComponentName2 = new ComponentName(mContext, "component2"); @@ -113,7 +110,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase { res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, newDefaultAspectRatio); - mPipBoundsHandler.onConfigurationChanged(); + mPipBoundsHandler.onConfigurationChanged(mContext); assertEquals("Default aspect ratio should be reloaded", mPipBoundsHandler.getDefaultAspectRatio(), newDefaultAspectRatio, diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java index 9f67722041aa..c8d4aca90519 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java @@ -72,9 +72,6 @@ public class PipTouchHandlerTest extends SysuiTestCase { private InputConsumerController mInputConsumerController; @Mock - private PipBoundsHandler mPipBoundsHandler; - - @Mock private PipTaskOrganizer mPipTaskOrganizer; @Mock @@ -89,6 +86,7 @@ public class PipTouchHandlerTest extends SysuiTestCase { @Mock private PipUiEventLogger mPipUiEventLogger; + private PipBoundsHandler mPipBoundsHandler; private PipSnapAlgorithm mPipSnapAlgorithm; private PipMotionHelper mMotionHelper; private PipResizeGestureHandler mPipResizeGestureHandler; @@ -104,11 +102,13 @@ public class PipTouchHandlerTest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mPipBoundsHandler = new PipBoundsHandler(mContext); + mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm(); mPipSnapAlgorithm = new PipSnapAlgorithm(mContext); mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler, - mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, - mPipSnapAlgorithm, mSysUiState, mPipUiEventLogger); + mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, mSysUiState, + mPipUiEventLogger); mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper()); mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler()); mPipTouchHandler.setPipMotionHelper(mMotionHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index c35ada7c3501..1259d28c3400 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -19,12 +19,10 @@ package com.android.systemui.statusbar; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -46,9 +44,9 @@ import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java index ea1b498801ec..baeedcfc3fc5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java @@ -32,14 +32,17 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -54,109 +57,126 @@ public class VisualStabilityManagerTest extends SysuiTestCase { private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class); private NotificationEntry mEntry; + private StatusBarStateController.StateListener mStatusBarStateListener; + private WakefulnessLifecycle.Observer mWakefulnessObserver; + @Before public void setUp() { + StatusBarStateController statusBarStateController = mock(StatusBarStateController.class); + WakefulnessLifecycle wakefulnessLifecycle = mock(WakefulnessLifecycle.class); + mTestableLooper = TestableLooper.get(this); mVisualStabilityManager = new VisualStabilityManager( mock(NotificationEntryManager.class), - new Handler(mTestableLooper.getLooper())); + new Handler(mTestableLooper.getLooper()), + statusBarStateController, + wakefulnessLifecycle); - mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class)); mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider); mEntry = new NotificationEntryBuilder().build(); mEntry.setRow(mRow); when(mRow.getEntry()).thenReturn(mEntry); + + ArgumentCaptor<StatusBarStateController.StateListener> stateListenerCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + verify(statusBarStateController).addCallback(stateListenerCaptor.capture()); + mStatusBarStateListener = stateListenerCaptor.getValue(); + + ArgumentCaptor<WakefulnessLifecycle.Observer> wakefulnessObserverCaptor = + ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class); + verify(wakefulnessLifecycle).addObserver(wakefulnessObserverCaptor.capture()); + mWakefulnessObserver = wakefulnessObserverCaptor.getValue(); } @Test public void testPanelExpansion() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); - mVisualStabilityManager.setPanelExpanded(false); + setPanelExpanded(false); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testScreenOn() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingAllowedChangesScreenOn() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); assertFalse(mVisualStabilityManager.isReorderingAllowed()); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @Test public void testReorderingAllowedChangesPanel() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); assertFalse(mVisualStabilityManager.isReorderingAllowed()); - mVisualStabilityManager.setPanelExpanded(false); + setPanelExpanded(false); assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @Test public void testCallBackCalledScreenOn() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); verify(mCallback).onChangeAllowed(); } @Test public void testCallBackCalledPanelExpanded() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); - mVisualStabilityManager.setPanelExpanded(false); + setPanelExpanded(false); verify(mCallback).onChangeAllowed(); } @Test public void testCallBackExactlyOnce() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); - mVisualStabilityManager.setScreenOn(false); - mVisualStabilityManager.setScreenOn(true); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); + setScreenOn(true); + setScreenOn(false); verify(mCallback).onChangeAllowed(); } @Test public void testCallBackCalledContinuouslyWhenRequested() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, true /* persistent */); - mVisualStabilityManager.setScreenOn(false); - mVisualStabilityManager.setScreenOn(true); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); + setScreenOn(true); + setScreenOn(false); verify(mCallback, times(2)).onChangeAllowed(); } @Test public void testAddedCanReorder() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.notifyViewAddition(mRow); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingVisibleHeadsUpNotAllowed() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(true); mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true); assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); @@ -164,8 +184,8 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testReorderingVisibleHeadsUpAllowed() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false); mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); @@ -173,8 +193,8 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testReorderingVisibleHeadsUpAllowedOnce() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false); mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true); mVisualStabilityManager.onReorderingFinished(); @@ -183,33 +203,33 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testPulsing() { - mVisualStabilityManager.setPulsing(true); + setPulsing(true); assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); - mVisualStabilityManager.setPulsing(false); + setPulsing(false); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingAllowedChanges_Pulsing() { - mVisualStabilityManager.setPulsing(true); + setPulsing(true); assertFalse(mVisualStabilityManager.isReorderingAllowed()); - mVisualStabilityManager.setPulsing(false); + setPulsing(false); assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @Test public void testCallBackCalled_Pulsing() { - mVisualStabilityManager.setPulsing(true); + setPulsing(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); - mVisualStabilityManager.setPulsing(false); + setPulsing(false); verify(mCallback).onChangeAllowed(); } @Test public void testTemporarilyAllowReorderingNotifiesCallbacks() { // GIVEN having the panel open (which would block reordering) - mVisualStabilityManager.setScreenOn(true); - mVisualStabilityManager.setPanelExpanded(true); + setScreenOn(true); + setPanelExpanded(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); // WHEN we temprarily allow reordering @@ -223,7 +243,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testTemporarilyAllowReorderingDoesntOverridePulsing() { // GIVEN we are in a pulsing state - mVisualStabilityManager.setPulsing(true); + setPulsing(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); // WHEN we temprarily allow reordering @@ -237,8 +257,8 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testTemporarilyAllowReorderingExpires() { // GIVEN having the panel open (which would block reordering) - mVisualStabilityManager.setScreenOn(true); - mVisualStabilityManager.setPanelExpanded(true); + setScreenOn(true); + setPanelExpanded(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); // WHEN we temprarily allow reordering and then wait until the window expires @@ -249,4 +269,20 @@ public class VisualStabilityManagerTest extends SysuiTestCase { // THEN reordering is no longer allowed assertFalse(mVisualStabilityManager.isReorderingAllowed()); } + + private void setPanelExpanded(boolean expanded) { + mStatusBarStateListener.onExpandedChanged(expanded); + } + + private void setPulsing(boolean pulsing) { + mStatusBarStateListener.onPulsingChanged(pulsing); + } + + private void setScreenOn(boolean screenOn) { + if (screenOn) { + mWakefulnessObserver.onStartedWakingUp(); + } else { + mWakefulnessObserver.onFinishedGoingToSleep(); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java index 1523653dec3c..3dc29a1ae4d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java @@ -53,7 +53,6 @@ public class NotificationEntryBuilder { /* ListEntry properties */ private GroupEntry mParent; - private int mSection = -1; /* If set, use this creation time instead of mClock.uptimeMillis */ private long mCreationTime = -1; @@ -68,7 +67,6 @@ public class NotificationEntryBuilder { mRankingBuilder = new RankingBuilder(source.getRanking()); mParent = source.getParent(); - mSection = source.getSection(); mCreationTime = source.getCreationTime(); } @@ -104,7 +102,6 @@ public class NotificationEntryBuilder { /* ListEntry properties */ entry.setParent(mParent); - entry.getAttachState().setSectionIndex(mSection); return entry; } @@ -117,14 +114,6 @@ public class NotificationEntryBuilder { } /** - * Sets the section. - */ - public NotificationEntryBuilder setSection(int section) { - mSection = section; - return this; - } - - /** * Sets the SBN directly. If set, causes all calls to delegated SbnBuilder methods to be * ignored. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java index 6fa5055c875d..8acb705c744d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java @@ -35,6 +35,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static java.util.Collections.singletonList; + import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -46,6 +48,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationInteractionTracker; import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.OnRenderListListener; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; @@ -54,7 +57,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeL import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; import com.android.systemui.util.time.FakeSystemClock; @@ -71,7 +74,6 @@ import org.mockito.Spy; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -496,7 +498,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { assertTrue(entry.hasFinishedInitialization()); // WHEN the pipeline is kicked off - mReadyForBuildListener.onBuildList(Arrays.asList(entry)); + mReadyForBuildListener.onBuildList(singletonList(entry)); // THEN the entry's initialization time is reset assertFalse(entry.hasFinishedInitialization()); @@ -609,13 +611,18 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a filter that removes all PACKAGE_4 notifs and sections that divide // notifs based on package name mListBuilder.addPreGroupFilter(new PackageFilter(PACKAGE_4)); - final NotifSection pkg1Section = spy(new PackageSection(PACKAGE_1)); - final NotifSection pkg2Section = spy(new PackageSection(PACKAGE_2)); + final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1)); + final NotifSectioner pkg2Sectioner = spy(new PackageSectioner(PACKAGE_2)); // NOTE: no package 3 section explicitly added, so notifs with package 3 will get set by // ShadeListBuilder's sDefaultSection which will demote it to the last section - final NotifSection pkg4Section = spy(new PackageSection(PACKAGE_4)); - final NotifSection pkg5Section = spy(new PackageSection(PACKAGE_5)); - mListBuilder.setSections(Arrays.asList(pkg1Section, pkg2Section, pkg4Section, pkg5Section)); + final NotifSectioner pkg4Sectioner = spy(new PackageSectioner(PACKAGE_4)); + final NotifSectioner pkg5Sectioner = spy(new PackageSectioner(PACKAGE_5)); + mListBuilder.setSectioners( + Arrays.asList(pkg1Sectioner, pkg2Sectioner, pkg4Sectioner, pkg5Sectioner)); + + final NotifSection pkg1Section = new NotifSection(pkg1Sectioner, 0); + final NotifSection pkg2Section = new NotifSection(pkg2Sectioner, 1); + final NotifSection pkg5Section = new NotifSection(pkg5Sectioner, 3); // WHEN we build a list with different packages addNotif(0, PACKAGE_4); @@ -648,72 +655,61 @@ public class ShadeListBuilderTest extends SysuiTestCase { // THEN the first section (pkg1Section) is called on all top level elements (but // no children and no entries that were filtered out) - verify(pkg1Section).isInSection(mEntrySet.get(1)); - verify(pkg1Section).isInSection(mEntrySet.get(2)); - verify(pkg1Section).isInSection(mEntrySet.get(3)); - verify(pkg1Section).isInSection(mEntrySet.get(7)); - verify(pkg1Section).isInSection(mEntrySet.get(8)); - verify(pkg1Section).isInSection(mEntrySet.get(9)); - verify(pkg1Section).isInSection(mBuiltList.get(3)); - - verify(pkg1Section, never()).isInSection(mEntrySet.get(0)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(4)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(5)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(6)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(10)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(1)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(2)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(3)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(7)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(8)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(9)); + verify(pkg1Sectioner).isInSection(mBuiltList.get(3)); + + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(0)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(4)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(5)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(6)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(10)); // THEN the last section (pkg5Section) is not called on any of the entries that were // filtered or already in a section - verify(pkg5Section, never()).isInSection(mEntrySet.get(0)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(1)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(2)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(4)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(5)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(6)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(7)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(8)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(10)); - - verify(pkg5Section).isInSection(mEntrySet.get(3)); - verify(pkg5Section).isInSection(mEntrySet.get(9)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(0)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(1)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(2)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(4)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(5)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(6)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(7)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(8)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(10)); + + verify(pkg5Sectioner).isInSection(mEntrySet.get(3)); + verify(pkg5Sectioner).isInSection(mEntrySet.get(9)); // THEN the correct section is assigned for entries in pkg1Section - assertEquals(pkg1Section, mEntrySet.get(2).getNotifSection()); - assertEquals(0, mEntrySet.get(2).getSection()); - assertEquals(pkg1Section, mEntrySet.get(7).getNotifSection()); - assertEquals(0, mEntrySet.get(7).getSection()); + assertEquals(pkg1Section, mEntrySet.get(2).getSection()); + assertEquals(pkg1Section, mEntrySet.get(7).getSection()); // THEN the correct section is assigned for entries in pkg2Section - assertEquals(pkg2Section, mEntrySet.get(1).getNotifSection()); - assertEquals(1, mEntrySet.get(1).getSection()); - assertEquals(pkg2Section, mEntrySet.get(8).getNotifSection()); - assertEquals(1, mEntrySet.get(8).getSection()); - assertEquals(pkg2Section, mBuiltList.get(3).getNotifSection()); - assertEquals(1, mBuiltList.get(3).getSection()); + assertEquals(pkg2Section, mEntrySet.get(1).getSection()); + assertEquals(pkg2Section, mEntrySet.get(8).getSection()); + assertEquals(pkg2Section, mBuiltList.get(3).getSection()); // THEN no section was assigned to entries in pkg4Section (since they were filtered) - assertEquals(null, mEntrySet.get(0).getNotifSection()); - assertEquals(-1, mEntrySet.get(0).getSection()); - assertEquals(null, mEntrySet.get(10).getNotifSection()); - assertEquals(-1, mEntrySet.get(10).getSection()); - + assertNull(mEntrySet.get(0).getSection()); + assertNull(mEntrySet.get(10).getSection()); // THEN the correct section is assigned for entries in pkg5Section - assertEquals(pkg5Section, mEntrySet.get(9).getNotifSection()); - assertEquals(3, mEntrySet.get(9).getSection()); + assertEquals(pkg5Section, mEntrySet.get(9).getSection()); // THEN the children entries are assigned the same section as its parent - assertEquals(mBuiltList.get(3).getNotifSection(), child(5).entry.getNotifSection()); assertEquals(mBuiltList.get(3).getSection(), child(5).entry.getSection()); - assertEquals(mBuiltList.get(3).getNotifSection(), child(6).entry.getNotifSection()); assertEquals(mBuiltList.get(3).getSection(), child(6).entry.getSection()); } @Test public void testNotifUsesDefaultSection() { // GIVEN a Section for Package2 - final NotifSection pkg2Section = spy(new PackageSection(PACKAGE_2)); - mListBuilder.setSections(Arrays.asList(pkg2Section)); + final NotifSectioner pkg2Section = spy(new PackageSectioner(PACKAGE_2)); + mListBuilder.setSectioners(singletonList(pkg2Section)); // WHEN we build a list with pkg1 and pkg2 packages addNotif(0, PACKAGE_1); @@ -727,8 +723,8 @@ public class ShadeListBuilderTest extends SysuiTestCase { ); // THEN the entry that didn't have an explicit section gets assigned the DefaultSection - assertEquals(1, notif(0).entry.getSection()); - assertNotNull(notif(0).entry.getNotifSection()); + assertNotNull(notif(0).entry.getSection()); + assertEquals(1, notif(0).entry.getSectionIndex()); } @Test @@ -763,15 +759,15 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a bunch of registered listeners and pluggables NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_1)); NotifPromoter promoter = spy(new IdPromoter(3)); - NotifSection section = spy(new PackageSection(PACKAGE_1)); + NotifSectioner section = spy(new PackageSectioner(PACKAGE_1)); NotifComparator comparator = spy(new HypeComparator(PACKAGE_4)); NotifFilter preRenderFilter = spy(new PackageFilter(PACKAGE_5)); mListBuilder.addPreGroupFilter(preGroupFilter); mListBuilder.addOnBeforeTransformGroupsListener(mOnBeforeTransformGroupsListener); mListBuilder.addPromoter(promoter); mListBuilder.addOnBeforeSortListener(mOnBeforeSortListener); - mListBuilder.setComparators(Collections.singletonList(comparator)); - mListBuilder.setSections(Arrays.asList(section)); + mListBuilder.setComparators(singletonList(comparator)); + mListBuilder.setSectioners(singletonList(section)); mListBuilder.addOnBeforeFinalizeFilterListener(mOnBeforeFinalizeFilterListener); mListBuilder.addFinalizeFilter(preRenderFilter); mListBuilder.addOnBeforeRenderListListener(mOnBeforeRenderListListener); @@ -821,13 +817,13 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a variety of pluggables NotifFilter packageFilter = new PackageFilter(PACKAGE_1); NotifPromoter idPromoter = new IdPromoter(4); - NotifSection section = new PackageSection(PACKAGE_1); + NotifSectioner section = new PackageSectioner(PACKAGE_1); NotifComparator hypeComparator = new HypeComparator(PACKAGE_2); mListBuilder.addPreGroupFilter(packageFilter); mListBuilder.addPromoter(idPromoter); - mListBuilder.setSections(Arrays.asList(section)); - mListBuilder.setComparators(Collections.singletonList(hypeComparator)); + mListBuilder.setSectioners(singletonList(section)); + mListBuilder.setComparators(singletonList(hypeComparator)); // GIVEN a set of random notifs addNotif(0, PACKAGE_1); @@ -973,7 +969,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { RecordingOnBeforeSortListener listener = new RecordingOnBeforeSortListener(); mListBuilder.addOnBeforeSortListener(listener); - mListBuilder.setComparators(Arrays.asList(new HypeComparator(PACKAGE_3))); + mListBuilder.setComparators(singletonList(new HypeComparator(PACKAGE_3))); // GIVEN some new notifs out of order addNotif(0, PACKAGE_1); @@ -1093,7 +1089,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { NotifComparator comparator = new HypeComparator(PACKAGE_5); OnBeforeRenderListListener listener = (list) -> comparator.invalidateList(); - mListBuilder.setComparators(Collections.singletonList(comparator)); + mListBuilder.setComparators(singletonList(comparator)); mListBuilder.addOnBeforeRenderListListener(listener); // WHEN we try to run the pipeline and the comparator is invalidated @@ -1420,10 +1416,10 @@ public class ShadeListBuilderTest extends SysuiTestCase { } /** Represents a section for the passed pkg */ - private static class PackageSection extends NotifSection { + private static class PackageSectioner extends NotifSectioner { private final String mPackage; - PackageSection(String pkg) { + PackageSectioner(String pkg) { super("PackageSection_" + pkg); mPackage = pkg; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java index ce8ce2e39bcc..639e791cbf23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java @@ -21,11 +21,9 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_MIN; -import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,7 +35,6 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.ArraySet; import androidx.test.filters.SmallTest; @@ -48,8 +45,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -61,8 +57,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -78,7 +72,7 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { private AppOpsCoordinator mAppOpsCoordinator; private NotifFilter mForegroundFilter; private NotifLifetimeExtender mForegroundNotifLifetimeExtender; - private NotifSection mFgsSection; + private NotifSectioner mFgsSection; private FakeSystemClock mClock = new FakeSystemClock(); private FakeExecutor mExecutor = new FakeExecutor(mClock); @@ -111,7 +105,7 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { lifetimeExtenderCaptor.capture()); mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue(); - mFgsSection = mAppOpsCoordinator.getSection(); + mFgsSection = mAppOpsCoordinator.getSectioner(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt index be5c8a846afb..c49393d2ed34 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt @@ -25,7 +25,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON import org.junit.Assert.assertFalse @@ -45,7 +45,7 @@ import org.mockito.Mockito.`when` as whenever class ConversationCoordinatorTest : SysuiTestCase() { // captured listeners and pluggables: private lateinit var promoter: NotifPromoter - private lateinit var peopleSection: NotifSection + private lateinit var peopleSectioner: NotifSectioner @Mock private lateinit var pipeline: NotifPipeline @@ -70,7 +70,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { verify(pipeline).addPromoter(notifPromoterCaptor.capture()) promoter = notifPromoterCaptor.value - peopleSection = coordinator.getSection() + peopleSectioner = coordinator.sectioner entry = NotificationEntryBuilder().setChannel(channel).build() } @@ -88,7 +88,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { entry.sbn, entry.ranking)).thenReturn(TYPE_PERSON) // only put people notifications in this section - assertTrue(peopleSection.isInSection(entry)) - assertFalse(peopleSection.isInSection(NotificationEntryBuilder().build())) + assertTrue(peopleSectioner.isInSection(entry)) + assertFalse(peopleSectioner.isInSection(NotificationEntryBuilder().build())) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java index 730481afe638..fa992a5d5dbb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder; @@ -64,7 +64,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { private NotifPromoter mNotifPromoter; private NotifLifetimeExtender mNotifLifetimeExtender; private OnHeadsUpChangedListener mOnHeadsUpChangedListener; - private NotifSection mNotifSection; + private NotifSectioner mNotifSectioner; @Mock private NotifPipeline mNotifPipeline; @Mock private HeadsUpManager mHeadsUpManager; @@ -111,7 +111,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue(); mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue(); - mNotifSection = mCoordinator.getSection(); + mNotifSectioner = mCoordinator.getSectioner(); mNotifLifetimeExtender.setCallback(mEndLifetimeExtension); mEntry = new NotificationEntryBuilder().build(); } @@ -132,8 +132,8 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { setCurrentHUN(mEntry); // THEN only section the current HUN, mEntry - assertTrue(mNotifSection.isInSection(mEntry)); - assertFalse(mNotifSection.isInSection(new NotificationEntryBuilder().build())); + assertTrue(mNotifSectioner.isInSection(mEntry)); + assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder().build())); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java index 5f10f38b2ee8..3a7d28ab56ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import org.junit.Before; @@ -61,8 +61,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { private NotifFilter mCapturedSuspendedFilter; private NotifFilter mCapturedDozingFilter; - private NotifSection mAlertingSection; - private NotifSection mSilentSection; + private NotifSectioner mAlertingSectioner; + private NotifSectioner mSilentSectioner; @Before public void setup() { @@ -76,8 +76,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0); mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1); - mAlertingSection = rankingCoordinator.getAlertingSection(); - mSilentSection = rankingCoordinator.getSilentSection(); + mAlertingSectioner = rankingCoordinator.getAlertingSectioner(); + mSilentSectioner = rankingCoordinator.getSilentSectioner(); } @Test @@ -146,8 +146,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true); // THEN entry is in the alerting section - assertTrue(mAlertingSection.isInSection(mEntry)); - assertFalse(mSilentSection.isInSection(mEntry)); + assertTrue(mAlertingSectioner.isInSection(mEntry)); + assertFalse(mSilentSectioner.isInSection(mEntry)); } @Test @@ -156,8 +156,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); // THEN entry is in the silent section - assertFalse(mAlertingSection.isInSection(mEntry)); - assertTrue(mSilentSection.isInSection(mEntry)); + assertFalse(mAlertingSectioner.isInSection(mEntry)); + assertTrue(mSilentSectioner.isInSection(mEntry)); } private RankingBuilder getRankingForUnfilteredNotif() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java new file mode 100644 index 000000000000..605b4d18d2f1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2020 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.notification.collection.coordinator; + +import static junit.framework.Assert.assertFalse; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class VisualStabilityCoordinatorTest extends SysuiTestCase { + + private VisualStabilityCoordinator mCoordinator; + + // captured listeners and pluggables: + private NotifCollectionListener mCollectionListener; + + @Mock private NotifPipeline mNotifPipeline; + @Mock private WakefulnessLifecycle mWakefulnessLifecycle; + @Mock private StatusBarStateController mStatusBarStateController; + @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener; + @Mock private HeadsUpManager mHeadsUpManager; + + @Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor; + @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor; + @Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor; + @Captor private ArgumentCaptor<NotifCollectionListener> mNotifCollectionListenerCaptor; + + private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); + private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock); + + private WakefulnessLifecycle.Observer mWakefulnessObserver; + private StatusBarStateController.StateListener mStatusBarStateListener; + private NotifStabilityManager mNotifStabilityManager; + private NotificationEntry mEntry; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mCoordinator = new VisualStabilityCoordinator( + mHeadsUpManager, + mWakefulnessLifecycle, + mStatusBarStateController, + mFakeExecutor); + + mCoordinator.attach(mNotifPipeline); + + // capture arguments: + verify(mWakefulnessLifecycle).addObserver(mWakefulnessObserverCaptor.capture()); + mWakefulnessObserver = mWakefulnessObserverCaptor.getValue(); + + verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture()); + mStatusBarStateListener = mSBStateListenerCaptor.getValue(); + + verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture()); + mNotifStabilityManager = mNotifStabilityManagerCaptor.getValue(); + mNotifStabilityManager.setInvalidationListener(mInvalidateListener); + + mEntry = new NotificationEntryBuilder() + .setPkg("testPkg1") + .build(); + + when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(false); + } + + @Test + public void testScreenOff_groupAndSectionChangesAllowed() { + // GIVEN screen is off, panel isn't expanded and device isn't pulsing + setScreenOn(false); + setPanelExpanded(false); + setPulsing(false); + + // THEN group changes are allowed + assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are allowed + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testPanelNotExpanded_groupAndSectionChangesAllowed() { + // GIVEN screen is on but the panel isn't expanded and device isn't pulsing + setScreenOn(true); + setPanelExpanded(false); + setPulsing(false); + + // THEN group changes are allowed + assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are allowed + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testPanelExpanded_groupAndSectionChangesNotAllowed() { + // GIVEN the panel true expanded and device isn't pulsing + setScreenOn(true); + setPanelExpanded(true); + setPulsing(false); + + // THEN group changes are NOT allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are NOT allowed + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testPulsing_screenOff_groupAndSectionChangesNotAllowed() { + // GIVEN the device is pulsing and screen is off + setScreenOn(false); + setPulsing(true); + + // THEN group changes are NOT allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are NOT allowed + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testPulsing_panelNotExpanded_groupAndSectionChangesNotAllowed() { + // GIVEN the device is pulsing and screen is off with the panel not expanded + setScreenOn(false); + setPanelExpanded(false); + setPulsing(true); + + // THEN group changes are NOT allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are NOT allowed + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testOverrideReorderingSuppression_onlySectionChangesAllowed() { + // GIVEN section changes typically wouldn't be allowed because the panel is expanded and + // we're not pulsing + setScreenOn(true); + setPanelExpanded(true); + setPulsing(true); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); + + // THEN group changes aren't allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are allowed for this notification but not other notifications + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + assertFalse(mNotifStabilityManager.isSectionChangeAllowed( + new NotificationEntryBuilder() + .setPkg("testPkg2") + .build())); + } + + @Test + public void testTemporarilyAllowSectionChanges_callsInvalidate() { + // GIVEN section changes typically wouldn't be allowed because the panel is expanded + setScreenOn(true); + setPanelExpanded(true); + setPulsing(false); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.uptimeMillis()); + + // THEN the notification list is invalidated + verifyInvalidateCalled(true); + } + + @Test + public void testTemporarilyAllowSectionChanges_noInvalidationCalled() { + // GIVEN section changes typically WOULD be allowed + setScreenOn(false); + setPanelExpanded(false); + setPulsing(false); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); + + // THEN invalidate is not called because this entry was never suppressed from reordering + verifyInvalidateCalled(false); + } + + @Test + public void testTemporarilyAllowSectionChangesTimeout() { + // GIVEN section changes typically WOULD be allowed + setScreenOn(false); + setPanelExpanded(false); + setPulsing(false); + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); + + // THEN invalidate is not called because this entry was never suppressed from reordering; + // THEN section changes are allowed for this notification + verifyInvalidateCalled(false); + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + // WHEN we're pulsing (now disallowing reordering) + setPulsing(true); + + // THEN we're still allowed to reorder this section because it's still in the list of + // notifications to allow section changes + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + // WHEN the timeout for the temporarily allow section reordering runnable is finsihed + mFakeExecutor.advanceClockToNext(); + mFakeExecutor.runNextReady(); + + // THEN section changes aren't allowed anymore + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testTemporarilyAllowSectionChanges_isPulsingChangeBeforeTimeout() { + // GIVEN section changes typically wouldn't be allowed because the device is pulsing + setScreenOn(false); + setPanelExpanded(false); + setPulsing(true); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); + verifyInvalidateCalled(true); // can now reorder, so invalidates + + // WHEN reordering is now allowed because device isn't pulsing anymore + setPulsing(false); + + // THEN invalidate isn't called since reordering was already allowed + verifyInvalidateCalled(false); + } + + @Test + public void testNeverSuppressedChanges_noInvalidationCalled() { + // GIVEN no notifications are currently being suppressed from grouping nor being sorted + + // WHEN device isn't pulsing anymore + setPulsing(false); + + // WHEN screen isn't on + setScreenOn(false); + + // WHEN panel isn't expanded + setPanelExpanded(false); + + // THEN we never see any calls to invalidate since there weren't any notifications that + // were being suppressed from grouping or section changes + verifyInvalidateCalled(false); + } + + @Test + public void testHeadsUp_allowedToChangeGroupAndSection() { + // GIVEN group + section changes disallowed + setScreenOn(true); + setPanelExpanded(true); + setPulsing(true); + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + // GIVEN mEntry is a HUN + when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(true); + + // THEN group + section changes are allowed + assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + } + + private void setPulsing(boolean pulsing) { + mStatusBarStateListener.onPulsingChanged(pulsing); + } + + private void setScreenOn(boolean screenOn) { + if (screenOn) { + mWakefulnessObserver.onStartedWakingUp(); + } else { + mWakefulnessObserver.onFinishedGoingToSleep(); + } + } + + private void setPanelExpanded(boolean expanded) { + mStatusBarStateListener.onExpandedChanged(expanded); + } + + private void verifyInvalidateCalled(boolean invalidateCalled) { + if (invalidateCalled) { + verify(mInvalidateListener).onPluggableInvalidated(mNotifStabilityManager); + } else { + verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager); + } + + reset(mInvalidateListener); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 83fc82634b60..3c5aa1ae9519 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -79,7 +79,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.BubblesTestActivity; import com.android.systemui.statusbar.SbnBuilder; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.phone.ShadeController; @@ -135,7 +134,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Mock private PackageManager mMockPackageManager; @Mock - private VisualStabilityManager mVisualStabilityManager; + private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private BubbleController mBubbleController; @Mock @@ -244,7 +243,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -268,7 +267,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -293,7 +292,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mLauncherApps, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -319,7 +318,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -344,7 +343,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -368,7 +367,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -403,7 +402,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, entry, @@ -428,7 +427,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -457,7 +456,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -481,7 +480,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -509,7 +508,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -537,7 +536,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -568,7 +567,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -598,7 +597,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -642,7 +641,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -685,7 +684,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -729,7 +728,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -766,7 +765,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -802,7 +801,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -840,7 +839,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -876,7 +875,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -912,7 +911,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -947,7 +946,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -981,7 +980,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -1006,7 +1005,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -1041,7 +1040,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -1081,7 +1080,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 54ccc4d8320d..c2c40cac3d0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -74,7 +74,6 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; @@ -114,7 +113,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); @Mock private MetricsLogger mMetricsLogger; - @Mock private VisualStabilityManager mVisualStabilityManager; + @Mock private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private NotificationPresenter mPresenter; @Mock private NotificationActivityStarter mNotificationActivityStarter; @Mock private NotificationListContainer mNotificationListContainer; @@ -143,19 +142,21 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mDependency.injectTestDependency(DeviceProvisionedController.class, mDeviceProvisionedController); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); - mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); + mDependency.injectTestDependency( + OnUserInteractionCallback.class, + mOnUserInteractionCallback); mDependency.injectTestDependency(BubbleController.class, mBubbleController); mDependency.injectMockDependency(NotificationLockscreenUserManager.class); mHandler = Handler.createAsync(mTestableLooper.getLooper()); mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)); when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); - mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager, + mGutsManager = new NotificationGutsManager(mContext, () -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider, mINotificationManager, mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker, mProvider, mAssistantFeedbackController, mBubbleController, - new UiEventLoggerFake()); + new UiEventLoggerFake(), mOnUserInteractionCallback); mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer, mCheckSaveListener, mOnSettingsClickListener); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); @@ -360,7 +361,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), - eq(mVisualStabilityManager), + eq(mOnUserInteractionCallback), eq(mChannelEditorDialogController), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), @@ -394,7 +395,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), - eq(mVisualStabilityManager), + eq(mOnUserInteractionCallback), eq(mChannelEditorDialogController), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), @@ -426,7 +427,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), - eq(mVisualStabilityManager), + eq(mOnUserInteractionCallback), eq(mChannelEditorDialogController), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index fd8b72bb15db..02a3e11f312b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -66,7 +66,6 @@ import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -114,7 +113,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Mock private PackageManager mMockPackageManager; @Mock - private VisualStabilityManager mVisualStabilityManager; + private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private ChannelEditorDialogController mChannelEditorDialogController; @@ -181,7 +180,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -207,7 +206,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -229,7 +228,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -260,7 +259,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -283,7 +282,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -311,7 +310,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -334,7 +333,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -356,7 +355,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mDefaultNotificationChannel, @@ -382,7 +381,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mDefaultNotificationChannel, @@ -404,7 +403,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -427,7 +426,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -455,7 +454,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -478,7 +477,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -502,7 +501,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -518,7 +517,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -541,7 +540,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), @@ -569,7 +568,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -593,7 +592,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -617,7 +616,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -643,7 +642,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -665,7 +664,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -688,7 +687,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -709,7 +708,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -730,7 +729,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -751,7 +750,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -774,7 +773,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -798,7 +797,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -825,7 +824,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -852,7 +851,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -879,7 +878,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -914,7 +913,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -942,7 +941,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -982,7 +981,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1017,7 +1016,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1047,7 +1046,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1082,7 +1081,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1120,7 +1119,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1157,7 +1156,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1175,7 +1174,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.findViewById(R.id.done).performClick(); mNotificationInfo.handleCloseControls(true, false); - verify(mVisualStabilityManager).temporarilyAllowReordering(); + verify(mOnUserInteractionCallback).onImportanceChanged(mEntry); } @Test @@ -1185,7 +1184,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1216,7 +1215,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1250,7 +1249,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1283,7 +1282,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1316,7 +1315,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1342,7 +1341,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index fa2fa04abaa3..9a8678f01870 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -424,7 +424,8 @@ public class NotificationTestHelper { mock(ExpandableNotificationRow.CoordinateOnClickListener.class), mock(FalsingManager.class), mStatusBarStateController, - mPeopleNotificationIdentifier); + mPeopleNotificationIdentifier, + mock(OnUserInteractionCallback.class)); row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); inflateAndWait(entry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 03263dba4cc3..bca7b312ff15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -24,9 +24,7 @@ import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doNothing; @@ -47,19 +45,15 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; -import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -74,27 +68,26 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; -import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.leak.LeakDetector; import org.junit.After; @@ -103,7 +96,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -135,14 +127,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private MetricsLogger mMetricsLogger; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; - @Mock private KeyguardBypassController mKeyguardBypassController; - @Mock private KeyguardMediaController mKeyguardMediaController; - @Mock private ZenModeController mZenModeController; + @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; @Mock private NotificationSectionsManager mNotificationSectionsManager; @Mock private NotificationSection mNotificationSection; - @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private FeatureFlags mFeatureFlags; - private UserChangedListener mUserChangedListener; + @Mock private SysuiStatusBarStateController mStatusBarStateController; private NotificationEntryManager mEntryManager; private int mOriginalInterruptionModelSetting; private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake(); @@ -172,8 +161,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mDependency.injectMockDependency(ShadeController.class); when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); - ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor - .forClass(UserChangedListener.class); mEntryManager = new NotificationEntryManager( mock(NotificationEntryManagerLogger.class), mock(NotificationGroupManager.class), @@ -211,17 +198,15 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // which refer to members of NotificationStackScrollLayout. The spy // holds a copy of the CUT's instances of these KeyguardBypassController, so they still // refer to the CUT's member variables, not the spy's member variables. - mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null, + mStackScrollerInternal = new NotificationStackScrollLayout( + getContext(), + null, mNotificationRoundnessManager, mock(DynamicPrivacyController.class), - mock(SysuiStatusBarStateController.class), + mStatusBarStateController, mHeadsUpManager, - mKeyguardBypassController, - mKeyguardMediaController, new FalsingManagerFake(), - mLockscreenUserManager, mock(NotificationGutsManager.class), - mZenModeController, mNotificationSectionsManager, mock(ForegroundServiceSectionController.class), mock(ForegroundServiceDismissalFeatureController.class), @@ -231,8 +216,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mock(NotifCollection.class), mUiEventLoggerFake ); - verify(mLockscreenUserManager).addUserChangedListener(userChangedCaptor.capture()); - mUserChangedListener = userChangedCaptor.getValue(); + mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider); mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelfController(notificationShelfController); mStackScroller.setStatusBar(mBar); @@ -269,9 +253,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void updateEmptyView_dndSuppressing() { when(mEmptyShadeView.willBeGone()).thenReturn(true); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, true); verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text); } @@ -280,9 +263,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { public void updateEmptyView_dndNotSuppressing() { mStackScroller.setEmptyShadeView(mEmptyShadeView); when(mEmptyShadeView.willBeGone()).thenReturn(true); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, false); verify(mEmptyShadeView).setText(R.string.empty_shade_text); } @@ -291,12 +273,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { public void updateEmptyView_noNotificationsToDndSuppressing() { mStackScroller.setEmptyShadeView(mEmptyShadeView); when(mEmptyShadeView.willBeGone()).thenReturn(true); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, false); verify(mEmptyShadeView).setText(R.string.empty_shade_text); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, true); verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text); } @@ -312,12 +292,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - public void testOnStatePostChange_verifyIfProfileIsPublic() { - mUserChangedListener.onUserChanged(0); - verify(mLockscreenUserManager).isAnyProfilePublicMode(); - } - - @Test public void manageNotifications_visible() { FooterView view = mock(FooterView.class); mStackScroller.setFooterView(view); @@ -436,9 +410,9 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() { + public void testReInflatesFooterViews() { clearInvocations(mStackScroller); - mStackScroller.onDensityOrFontScaleChanged(); + mStackScroller.reinflateViews(); verify(mStackScroller).setFooterView(any()); verify(mStackScroller).setEmptyShadeView(any()); } @@ -473,61 +447,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { assertNull(swipeActionHelper.getExposedMenuView()); } - class LogMatcher implements ArgumentMatcher<LogMaker> { - private int mCategory, mType; - - LogMatcher(int category, int type) { - mCategory = category; - mType = type; - } - public boolean matches(LogMaker l) { - return (l.getCategory() == mCategory) - && (l.getType() == mType); - } - - public String toString() { - return String.format("LogMaker(%d, %d)", mCategory, mType); - } - } - - private LogMaker logMatcher(int category, int type) { - return argThat(new LogMatcher(category, type)); - } - - @Test - @UiThreadTest - public void testOnMenuClickedLogging() { - // Set up the object under test to have a valid mLongPressListener. We're testing an - // anonymous-class member, mMenuEventListener, so we need to modify the state of the - // class itself, not the Mockito spy copied from it. See notes in setup. - mStackScrollerInternal.setLongPressListener( - mock(ExpandableNotificationRow.LongPressListener.class)); - - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); - when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( - MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - - mStackScroller.mMenuEventListener.onMenuClicked(row, 0, 0, mock( - NotificationMenuRowPlugin.MenuItem.class)); - verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data - verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR, - MetricsProto.MetricsEvent.TYPE_ACTION)); - } - - @Test - @UiThreadTest - public void testOnMenuShownLogging() { ; - - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); - when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( - MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - - mStackScroller.mMenuEventListener.onMenuShown(row); - verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data - verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR, - MetricsProto.MetricsEvent.TYPE_ACTION)); - } - @Test public void testClearNotifications_All() { mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_ALL, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java new file mode 100644 index 000000000000..83d6ac904bfe --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2020 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.notification.stack; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.metrics.LogMaker; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.media.KeyguardMediaController; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.tuner.TunerService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link NotificationStackScrollLayoutController}. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class NotificationStackScrollerControllerTest extends SysuiTestCase { + + @Mock + private NotificationGutsManager mNotificationGutsManager; + @Mock + private HeadsUpManagerPhone mHeadsUpManager; + @Mock + private NotificationRoundnessManager mNotificationRoundnessManager; + @Mock + private TunerService mTunerService; + @Mock + private DynamicPrivacyController mDynamicPrivacyController; + @Mock + private ConfigurationController mConfigurationController; + @Mock + private NotificationStackScrollLayout mNotificationStackScrollLayout; + @Mock + private ZenModeController mZenModeController; + @Mock + private KeyguardMediaController mKeyguardMediaController; + @Mock + private SysuiStatusBarStateController mSysuiStatusBarStateController; + @Mock + private KeyguardBypassController mKeyguardBypassController; + @Mock + private SysuiColorExtractor mColorExtractor; + @Mock + private NotificationLockscreenUserManager mNotificationLockscreenUserManager; + @Mock + private MetricsLogger mMetricsLogger; + + private NotificationStackScrollLayoutController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mController = new NotificationStackScrollLayoutController( + true, + mNotificationGutsManager, + mHeadsUpManager, + mNotificationRoundnessManager, + mTunerService, + mDynamicPrivacyController, + mConfigurationController, + mSysuiStatusBarStateController, + mKeyguardMediaController, + mKeyguardBypassController, + mZenModeController, + mColorExtractor, + mNotificationLockscreenUserManager, + mMetricsLogger + ); + + when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); + } + + + @Test + public void testAttach_viewAlreadyAttached() { + mController.attach(mNotificationStackScrollLayout); + + verify(mConfigurationController).addCallback( + any(ConfigurationController.ConfigurationListener.class)); + } + @Test + public void testAttach_viewAttachedAfterInit() { + when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(false); + + mController.attach(mNotificationStackScrollLayout); + + verify(mConfigurationController, never()).addCallback( + any(ConfigurationController.ConfigurationListener.class)); + + mController.mOnAttachStateChangeListener.onViewAttachedToWindow( + mNotificationStackScrollLayout); + + verify(mConfigurationController).addCallback( + any(ConfigurationController.ConfigurationListener.class)); + } + + @Test + public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() { + mController.attach(mNotificationStackScrollLayout); + mController.mConfigurationListener.onDensityOrFontScaleChanged(); + verify(mNotificationStackScrollLayout).reinflateViews(); + } + + @Test + public void testUpdateEmptyShadeView_notificationsVisible() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); + mController.attach(mNotificationStackScrollLayout); + + mController.updateEmptyShadeView(true /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + true /* visible */, + true /* notifVisibleInShade */); + reset(mNotificationStackScrollLayout); + mController.updateEmptyShadeView(false /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + false /* visible */, + true /* notifVisibleInShade */); + } + + @Test + public void testUpdateEmptyShadeView_notificationsHidden() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); + mController.attach(mNotificationStackScrollLayout); + + mController.updateEmptyShadeView(true /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + true /* visible */, + false /* notifVisibleInShade */); + reset(mNotificationStackScrollLayout); + mController.updateEmptyShadeView(false /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + false /* visible */, + false /* notifVisibleInShade */); + } + + @Test + public void testOnUserChange_verifySensitiveProfile() { + when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true); + + ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor + .forClass(UserChangedListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationLockscreenUserManager) + .addUserChangedListener(userChangedCaptor.capture()); + reset(mNotificationStackScrollLayout); + + UserChangedListener changedListener = userChangedCaptor.getValue(); + changedListener.onUserChanged(0); + verify(mNotificationStackScrollLayout).setCurrentUserid(0); + verify(mNotificationStackScrollLayout).updateSensitiveness(false, true); + } + + @Test + public void testOnStatePostChange_verifyIfProfileIsPublic() { + when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true); + + ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mSysuiStatusBarStateController).addCallback( + stateListenerArgumentCaptor.capture(), anyInt()); + + StatusBarStateController.StateListener stateListener = + stateListenerArgumentCaptor.getValue(); + + stateListener.onStatePostChange(); + verify(mNotificationStackScrollLayout).updateSensitiveness(false, true); + } + + + @Test + public void testOnMenuShownLogging() { ; + + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + + ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor = + ArgumentCaptor.forClass(OnMenuEventListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout).setMenuEventListener( + onMenuEventListenerArgumentCaptor.capture()); + + OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue(); + + onMenuEventListener.onMenuShown(row); + verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + + @Test + public void testOnMenuClickedLogging() { + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + + ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor = + ArgumentCaptor.forClass(OnMenuEventListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout).setMenuEventListener( + onMenuEventListenerArgumentCaptor.capture()); + + OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue(); + + onMenuEventListener.onMenuClicked(row, 0, 0, mock( + NotificationMenuRowPlugin.MenuItem.class)); + verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + + private LogMaker logMatcher(int category, int type) { + return argThat(new LogMatcher(category, type)); + } + + static class LogMatcher implements ArgumentMatcher<LogMaker> { + private int mCategory, mType; + + LogMatcher(int category, int type) { + mCategory = category; + mType = type; + } + public boolean matches(LogMaker l) { + return (l.getCategory() == mCategory) + && (l.getType() == mType); + } + + public String toString() { + return String.format("LogMaker(%d, %d)", mCategory, mType); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index 2bcdda4029ff..37ccac0b23b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -46,7 +46,6 @@ import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -70,7 +69,6 @@ public class DozeServiceHostTest extends SysuiTestCase { @Mock private HeadsUpManagerPhone mHeadsUpManager; @Mock private ScrimController mScrimController; @Mock private DozeScrimController mDozeScrimController; - @Mock private VisualStabilityManager mVisualStabilityManager; @Mock private KeyguardViewMediator mKeyguardViewMediator; @Mock private StatusBarStateControllerImpl mStatusBarStateController; @Mock private BatteryController mBatteryController; @@ -100,7 +98,7 @@ public class DozeServiceHostTest extends SysuiTestCase { mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager, mBatteryController, mScrimController, () -> mBiometricUnlockController, mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController, - mKeyguardUpdateMonitor, mVisualStabilityManager, mPulseExpansionHandler, + mKeyguardUpdateMonitor, mPulseExpansionHandler, mNotificationShadeWindowController, mNotificationWakeUpCoordinator, mLockscreenLockIconController, mAuthController, mNotificationIconAreaController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index 48bf2db0be6a..2239b1b96ac8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -34,9 +34,9 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AlertingNotificationManagerTest; import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index a71b10cf9264..3f631b1f6282 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -73,7 +73,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -132,7 +132,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private Intent mContentIntentInner; @Mock - private OnDismissCallback mOnDismissCallback; + private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private NotificationActivityStarter mNotificationActivityStarter; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -210,7 +210,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mFeatureFlags, mock(MetricsLogger.class), mock(StatusBarNotificationActivityStarterLogger.class), - mOnDismissCallback) + mOnUserInteractionCallback) .setStatusBar(mStatusBar) .setNotificationPresenter(mock(NotificationPresenter.class)) .setNotificationPanelViewController(mock(NotificationPanelViewController.class)) @@ -267,7 +267,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { eq(sbn.getKey()), any(NotificationVisibility.class)); // Notification calls dismiss callback to remove notification due to FLAG_AUTO_CANCEL - verify(mOnDismissCallback).onDismiss(mNotificationRow.getEntry(), REASON_CLICK); + verify(mOnUserInteractionCallback).onDismiss(mNotificationRow.getEntry(), REASON_CLICK); } @Test @@ -296,7 +296,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { verifyZeroInteractions(mContentIntent); // Notification should not be cancelled. - verify(mOnDismissCallback, never()).onDismiss(eq(mNotificationRow.getEntry()), anyInt()); + verify(mOnUserInteractionCallback, never()).onDismiss(eq(mNotificationRow.getEntry()), + anyInt()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index e46aa04c4504..c0ebfadf6b57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -50,9 +50,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index cb9f50d8ec96..8462386aaf2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -117,9 +117,9 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java index 260ff2dafeed..33ece0084906 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java @@ -70,9 +70,7 @@ public class DeviceConfigProxyFake extends DeviceConfigProxy { for (Pair<Executor, OnPropertiesChangedListener> listener : mListeners) { Properties.Builder propBuilder = new Properties.Builder(namespace); - for (String key : mProperties.get(namespace).keySet()) { - propBuilder.setString(key, mProperties.get(namespace).get(key)); - } + propBuilder.setString(name, value); listener.first.execute(() -> listener.second.onPropertiesChanged(propBuilder.build())); } return true; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java new file mode 100644 index 000000000000..64e89579393e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java @@ -0,0 +1,100 @@ +/* + * 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. + */ + +package com.android.systemui.util; + +import static android.provider.DeviceConfig.Properties; + +import static com.google.common.truth.Truth.assertThat; + +import android.provider.DeviceConfig.OnPropertiesChangedListener; + +import androidx.annotation.NonNull; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DeviceConfigProxyFakeTest extends SysuiTestCase { + private static final String NAMESPACE = "foobar"; + + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); + + private DeviceConfigProxyFake mDeviceConfigProxyFake; + + @Before + public void setup() { + mDeviceConfigProxyFake = new DeviceConfigProxyFake(); + } + + @Test + public void testOnPropertiesChanged() { + TestableListener onPropertiesChangedListener = new TestableListener(); + String key = "foo"; + String value = "bar"; + + mDeviceConfigProxyFake.addOnPropertiesChangedListener( + NAMESPACE, mFakeExecutor, onPropertiesChangedListener); + + mDeviceConfigProxyFake.setProperty(NAMESPACE, key, value, false); + mFakeExecutor.runAllReady(); + assertThat(onPropertiesChangedListener.mProperties).isNotNull(); + assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1); + assertThat(onPropertiesChangedListener.mProperties.getString(key, "")).isEqualTo(value); + } + + @Test + public void testOnMultiplePropertiesChanged() { + TestableListener onPropertiesChangedListener = new TestableListener(); + String keyA = "foo"; + String valueA = "bar"; + String keyB = "bada"; + String valueB = "boom"; + + mDeviceConfigProxyFake.addOnPropertiesChangedListener( + NAMESPACE, mFakeExecutor, onPropertiesChangedListener); + mDeviceConfigProxyFake.setProperty(NAMESPACE, keyA, valueA, false); + mFakeExecutor.runAllReady(); + assertThat(onPropertiesChangedListener.mProperties).isNotNull(); + assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1); + assertThat(onPropertiesChangedListener.mProperties.getString(keyA, "")).isEqualTo(valueA); + + mDeviceConfigProxyFake.setProperty(NAMESPACE, keyB, valueB, false); + mFakeExecutor.runAllReady(); + assertThat(onPropertiesChangedListener.mProperties).isNotNull(); + assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1); + assertThat(onPropertiesChangedListener.mProperties.getString(keyB, "")).isEqualTo(valueB); + } + + private static class TestableListener implements OnPropertiesChangedListener { + Properties mProperties; + + TestableListener() { + } + @Override + public void onPropertiesChanged(@NonNull Properties properties) { + mProperties = properties; + } + } +} diff --git a/packages/Tethering/AndroidManifestBase.xml b/packages/Tethering/AndroidManifestBase.xml index fa85f66489d7..97c3988829fe 100644 --- a/packages/Tethering/AndroidManifestBase.xml +++ b/packages/Tethering/AndroidManifestBase.xml @@ -23,6 +23,7 @@ <application android:label="Tethering" android:defaultToDeviceProtectedStorage="true" - android:directBootAware="true"> + android:directBootAware="true" + android:usesCleartextTraffic="true"> </application> </manifest> diff --git a/services/Android.bp b/services/Android.bp index b348b91a1bd7..ef52c2aff002 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -156,10 +156,14 @@ droidstubs { java_library { name: "android_system_server_stubs_current", + defaults: ["android_stubs_dists_default"], srcs: [":services-stubs.sources"], installable: false, static_libs: ["android_module_lib_stubs_current"], sdk_version: "none", system_modules: "none", java_version: "1.8", + dist: { + dir: "apistubs/android/system-server", + }, } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 669bb24e0e77..a75b64ce08f5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -284,11 +284,13 @@ public class AccessibilityWindowManager { * Computes partial interactive region of given windowId. * * @param windowId The windowId + * @param forceComputeRegion set outRegion when the windowId matches one on the screen even + * though the region is not covered by other windows above it. * @param outRegion The output to which to write the bounds. - * @return true if outRegion is not empty. + * @return {@code true} if outRegion is not empty. */ boolean computePartialInteractiveRegionForWindowLocked(int windowId, - @NonNull Region outRegion) { + boolean forceComputeRegion, @NonNull Region outRegion) { if (mWindows == null) { return false; } @@ -309,6 +311,9 @@ public class AccessibilityWindowManager { currentWindow.getRegionInScreen(currentWindowRegions); outRegion.set(currentWindowRegions); windowInteractiveRegion = outRegion; + if (forceComputeRegion) { + windowInteractiveRegionChanged = true; + } continue; } } else if (currentWindow.getType() @@ -1240,10 +1245,13 @@ public class AccessibilityWindowManager { */ public boolean computePartialInteractiveRegionForWindowLocked(int windowId, @NonNull Region outRegion) { - windowId = resolveParentWindowIdLocked(windowId); - final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId); + final int parentWindowId = resolveParentWindowIdLocked(windowId); + final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked( + parentWindowId); + if (observer != null) { - return observer.computePartialInteractiveRegionForWindowLocked(windowId, outRegion); + return observer.computePartialInteractiveRegionForWindowLocked(parentWindowId, + parentWindowId != windowId, outRegion); } return false; diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index a2d58c8019fc..b587dd33c4e5 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -561,24 +561,6 @@ public class TouchExplorer extends BaseEventStreamTransformation // stream consistent. sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); } - if (mGestureDetector.isMultiFingerGesturesEnabled() - && mGestureDetector.isTwoFingerPassthroughEnabled()) { - if (event.getPointerCount() == 3) { - boolean isOnBottomEdge = false; - // If three fingers go down on the bottom edge of the screen, delegate immediately. - final long screenHeight = mContext.getResources().getDisplayMetrics().heightPixels; - for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) { - if (mReceivedPointerTracker.getReceivedPointerDownY(i) - > (screenHeight - mEdgeSwipeHeightPixels)) { - isOnBottomEdge = true; - } - } - if (isOnBottomEdge) { - mState.startDelegating(); - mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); - } - } - } } /** @@ -644,12 +626,34 @@ public class TouchExplorer extends BaseEventStreamTransformation break; default: if (mGestureDetector.isMultiFingerGesturesEnabled()) { - return; + if (mGestureDetector.isTwoFingerPassthroughEnabled()) { + if (event.getPointerCount() == 3) { + boolean isOnBottomEdge = true; + // If three fingers went down on the bottom edge of the screen, delegate + // immediately. + final long screenHeight = + mContext.getResources().getDisplayMetrics().heightPixels; + for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) { + if (mReceivedPointerTracker.getReceivedPointerDownY(i) + < (screenHeight - mEdgeSwipeHeightPixels)) { + isOnBottomEdge = false; + } + } + if (isOnBottomEdge) { + if (DEBUG) { + Slog.d(LOG_TAG, "Three-finger edge swipe detected."); + } + mState.startDelegating(); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); + } + } + } + } else { + // More than two pointers are delegated to the view hierarchy. + mState.startDelegating(); + event = MotionEvent.obtainNoHistory(event); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); } - // More than two pointers are delegated to the view hierarchy. - mState.startDelegating(); - event = MotionEvent.obtainNoHistory(event); - mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); break; } } diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index df966c1ea6ac..34e63705d781 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -47,8 +47,8 @@ import java.util.ArrayList; * * {@hide} */ -public class SystemServiceManager { - private static final String TAG = "SystemServiceManager"; +public final class SystemServiceManager { + private static final String TAG = SystemServiceManager.class.getSimpleName(); private static final boolean DEBUG = false; private static final int SERVICE_CALL_WARN_TIME_MS = 50; @@ -250,76 +250,76 @@ public class SystemServiceManager { mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } - private @NonNull TargetUser getTargetUser(@UserIdInt int userHandle) { + private @NonNull TargetUser getTargetUser(@UserIdInt int userId) { final TargetUser targetUser; synchronized (mTargetUsers) { - targetUser = mTargetUsers.get(userHandle); + targetUser = mTargetUsers.get(userId); } - Preconditions.checkState(targetUser != null, "No TargetUser for " + userHandle); + Preconditions.checkState(targetUser != null, "No TargetUser for " + userId); return targetUser; } /** * Starts the given user. */ - public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) { + public void startUser(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) { // Create cached TargetUser - final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle); - Preconditions.checkState(userInfo != null, "No UserInfo for " + userHandle); + final UserInfo userInfo = mUserManagerInternal.getUserInfo(userId); + Preconditions.checkState(userInfo != null, "No UserInfo for " + userId); synchronized (mTargetUsers) { - mTargetUsers.put(userHandle, new TargetUser(userInfo)); + mTargetUsers.put(userId, new TargetUser(userInfo)); } - onUser(t, START, userHandle); + onUser(t, START, userId); } /** * Unlocks the given user. */ - public void unlockUser(final @UserIdInt int userHandle) { - onUser(UNLOCKING, userHandle); + public void unlockUser(@UserIdInt int userId) { + onUser(UNLOCKING, userId); } /** * Called after the user was unlocked. */ - public void onUserUnlocked(final @UserIdInt int userHandle) { - onUser(UNLOCKED, userHandle); + public void onUserUnlocked(@UserIdInt int userId) { + onUser(UNLOCKED, userId); } /** * Switches to the given user. */ - public void switchUser(final @UserIdInt int from, final @UserIdInt int to) { + public void switchUser(@UserIdInt int from, @UserIdInt int to) { onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, to, from); } /** * Stops the given user. */ - public void stopUser(final @UserIdInt int userHandle) { - onUser(STOP, userHandle); + public void stopUser(@UserIdInt int userId) { + onUser(STOP, userId); } /** * Cleans up the given user. */ - public void cleanupUser(final @UserIdInt int userHandle) { - onUser(CLEANUP, userHandle); + public void cleanupUser(@UserIdInt int userId) { + onUser(CLEANUP, userId); // Remove cached TargetUser synchronized (mTargetUsers) { - mTargetUsers.remove(userHandle); + mTargetUsers.remove(userId); } } - private void onUser(@NonNull String onWhat, @UserIdInt int userHandle) { - onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle); + private void onUser(@NonNull String onWhat, @UserIdInt int userId) { + onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userId); } private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat, - @UserIdInt int userHandle) { - onUser(t, onWhat, userHandle, UserHandle.USER_NULL); + @UserIdInt int userId) { + onUser(t, onWhat, userId, UserHandle.USER_NULL); } private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat, diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 6d71c8e68f77..19871f993d42 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -315,11 +315,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>> mPreciseDataConnectionStates; - static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = - PhoneStateListener.LISTEN_REGISTRATION_FAILURE - | PhoneStateListener.LISTEN_BARRING_INFO; - - static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK = + // Starting in Q, almost all cellular location requires FINE location enforcement. + // Prior to Q, cellular was available with COARSE location enforcement. Bits in this + // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later. + static final int ENFORCE_LOCATION_PERMISSION_MASK = PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_CELL_INFO | PhoneStateListener.LISTEN_REGISTRATION_FAILURE @@ -376,7 +375,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + " newDefaultPhoneId=" + newDefaultPhoneId); } - //Due to possible risk condition,(notify call back using the new + //Due to possible race condition,(notify call back using the new //defaultSubId comes before new defaultSubId update) we need to recall all //possible missed notify callback synchronized (mRecords) { @@ -909,7 +908,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { try { if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { // null will be translated to empty CellLocation object in client. r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } @@ -964,7 +964,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + mCellInfo.get(phoneId)); - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -1518,7 +1519,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && idMatch(r.subId, subId, phoneId) && - checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { log("notifyCellInfoForSubscriber: mCellInfo=" + cellInfo @@ -1797,7 +1799,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && idMatch(r.subId, subId, phoneId) && - checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { log("notifyCellLocation: cellLocation=" + cellLocation @@ -2544,16 +2547,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { boolean shouldCheckLocationPermissions = false; - if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) { + if ((events & ENFORCE_LOCATION_PERMISSION_MASK) != 0) { // Everything that requires fine location started in Q. So far... locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q); - // If we're enforcing fine starting in Q, we also want to enforce coarse starting in Q. - locationQueryBuilder.setMinSdkVersionForCoarse(Build.VERSION_CODES.Q); - locationQueryBuilder.setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q); - shouldCheckLocationPermissions = true; - } - - if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) { + // If we're enforcing fine starting in Q, we also want to enforce coarse even for + // older SDK versions. locationQueryBuilder.setMinSdkVersionForCoarse(0); locationQueryBuilder.setMinSdkVersionForEnforcement(0); shouldCheckLocationPermissions = true; @@ -2750,8 +2748,16 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" + mServiceState[phoneId]); - r.callback.onServiceStateChanged( - new ServiceState(mServiceState[phoneId])); + ServiceState ss = new ServiceState(mServiceState[phoneId]); + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(ss); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged( + ss.createLocationInfoSanitizedCopy(false)); + } else { + r.callback.onServiceStateChanged( + ss.createLocationInfoSanitizedCopy(true)); + } } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -2796,7 +2802,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " + mCellInfo.get(phoneId)); } - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -2862,7 +2869,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = " + mCellIdentity[phoneId]); } - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { // null will be translated to empty CellLocation object in client. r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 1815dac4c3a8..990a547144a0 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -99,6 +99,7 @@ public class Watchdog { "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.audio@6.0::IDevicesFactory", + "android.hardware.audio@7.0::IDevicesFactory", "android.hardware.biometrics.face@1.0::IBiometricsFace", "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint", "android.hardware.bluetooth@1.0::IBluetoothHci", diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index bf7c68a845c5..0a179e89f757 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1283,6 +1283,7 @@ public class AudioService extends IAudioService.Stub } if (isPlatformTelevision()) { + checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, caller); synchronized (mHdmiClientLock) { if (mHdmiManager != null && mHdmiPlaybackClient != null) { updateHdmiCecSinkLocked(mHdmiCecSink | false); @@ -1302,54 +1303,22 @@ public class AudioService extends IAudioService.Stub } } - /** - * Update volume states for the given device. - * - * This will initialize the volume index if no volume index is available. - * If the device is the currently routed device, fixed/full volume policies will be applied. - * - * @param device a single audio device, ensure that this is not a devices bitmask - * @param caller caller of this method - */ - private void updateVolumeStatesForAudioDevice(int device, String caller) { + private void checkAddAllFixedVolumeDevices(int device, String caller) { final int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int streamType = 0; streamType < numStreamTypes; streamType++) { - updateVolumeStates(device, streamType, caller); - } - } - - /** - * Update volume states for the given device and given stream. - * - * This will initialize the volume index if no volume index is available. - * If the device is the currently routed device, fixed/full volume policies will be applied. - * - * @param device a single audio device, ensure that this is not a devices bitmask - * @param streamType streamType to be updated - * @param caller caller of this method - */ - private void updateVolumeStates(int device, int streamType, String caller) { - if (!mStreamStates[streamType].hasIndexForDevice(device)) { - // set the default value, if device is affected by a full/fix/abs volume rule, it - // will taken into account in checkFixedVolumeDevices() - mStreamStates[streamType].setIndex( - mStreamStates[mStreamVolumeAlias[streamType]] - .getIndex(AudioSystem.DEVICE_OUT_DEFAULT), - device, caller, true /*hasModifyAudioSettings*/); - } - - // Check if device to be updated is routed for the given audio stream - List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributesInt( - new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build()); - for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) { - if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType( - device)) { - mStreamStates[streamType].checkFixedVolumeDevices(); + if (!mStreamStates[streamType].hasIndexForDevice(device)) { + // set the default value, if device is affected by a full/fix/abs volume rule, it + // will taken into account in checkFixedVolumeDevices() + mStreamStates[streamType].setIndex( + mStreamStates[mStreamVolumeAlias[streamType]] + .getIndex(AudioSystem.DEVICE_OUT_DEFAULT), + device, caller, true /*hasModifyAudioSettings*/); + } + mStreamStates[streamType].checkFixedVolumeDevices(); - // Unmute streams if required if device is full volume - if (isStreamMute(streamType) && mFullVolumeDevices.contains(device)) { - mStreamStates[streamType].mute(false); - } + // Unmute streams if device is full volume + if (mFullVolumeDevices.contains(device)) { + mStreamStates[streamType].mute(false); } } } @@ -1899,13 +1868,8 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */ public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes( @NonNull AudioAttributes attributes) { - enforceModifyAudioRoutingPermission(); - return getDevicesForAttributesInt(attributes); - } - - protected @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesInt( - @NonNull AudioAttributes attributes) { Objects.requireNonNull(attributes); + enforceModifyAudioRoutingPermission(); return AudioSystem.getDevicesForAttributes(attributes); } @@ -4952,15 +4916,7 @@ public class AudioService extends IAudioService.Stub synchronized (VolumeStreamState.class) { for (int stream = 0; stream < mStreamStates.length; stream++) { if (stream != skipStream) { - int devices = mStreamStates[stream].observeDevicesForStream_syncVSS( - false /*checkOthers*/); - - Set<Integer> devicesSet = AudioSystem.generateAudioDeviceTypesSet(devices); - for (Integer device : devicesSet) { - // Update volume states for devices routed for the stream - updateVolumeStates(device, stream, - "AudioService#observeDevicesForStreams"); - } + mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/); } } } @@ -5029,7 +4985,7 @@ public class AudioService extends IAudioService.Stub + Integer.toHexString(audioSystemDeviceOut) + " from:" + caller)); // make sure we have a volume entry for this device, and that volume is updated according // to volume behavior - updateVolumeStatesForAudioDevice(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller); + checkAddAllFixedVolumeDevices(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller); } /** @@ -7251,9 +7207,10 @@ public class AudioService extends IAudioService.Stub // HDMI output removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI); } - updateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI, - "HdmiPlaybackClient.DisplayStatusCallback"); } + + checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, + "HdmiPlaybackClient.DisplayStatusCallback"); } private class MyHdmiControlStatusChangeListenerCallback diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java index 7dd6217016eb..32bb2db77ddc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java @@ -45,6 +45,7 @@ import android.os.UserManager; import android.provider.Settings; import android.util.Slog; +import com.android.internal.R; import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AcquisitionClient; @@ -275,7 +276,10 @@ class Face10 implements IHwBinder.DeathRecipient { Face10(@NonNull Context context, int sensorId, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { - mFaceSensorProperties = new FaceSensorProperties(sensorId, false /* supportsFaceDetect */); + final boolean supportsSelfIllumination = context.getResources() + .getBoolean(R.bool.config_faceAuthSupportsSelfIllumination); + mFaceSensorProperties = new FaceSensorProperties(sensorId, false /* supportsFaceDetect */, + supportsSelfIllumination); mContext = context; mSensorId = sensorId; mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 041bedc3c575..ec12a971e445 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -210,6 +210,7 @@ public class SyncManager { private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock"; + private static final boolean USE_WTF_FOR_ACCOUNT_ERROR = false; private static final int SYNC_OP_STATE_VALID = 0; // "1" used to include errors 3, 4 and 5 but now it's split up. @@ -3446,7 +3447,7 @@ public class SyncManager { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: account doesn't exist."); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: account doesn't exist."); + logAccountError("SYNC_OP_STATE_INVALID: account doesn't exist."); return SYNC_OP_STATE_INVALID_NO_ACCOUNT; } // Drop this sync request if it isn't syncable. @@ -3456,14 +3457,14 @@ public class SyncManager { Slog.v(TAG, " Dropping sync operation: " + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS"); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS"); + logAccountError("SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS"); return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS; } if (state == AuthorityInfo.NOT_SYNCABLE) { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: isSyncable == NOT_SYNCABLE"); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: NOT_SYNCABLE"); + logAccountError("SYNC_OP_STATE_INVALID: NOT_SYNCABLE"); return SYNC_OP_STATE_INVALID_NOT_SYNCABLE; } @@ -3482,12 +3483,20 @@ public class SyncManager { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: disallowed by settings/network."); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: disallowed by settings/network"); + logAccountError("SYNC_OP_STATE_INVALID: disallowed by settings/network"); return SYNC_OP_STATE_INVALID_SYNC_DISABLED; } return SYNC_OP_STATE_VALID; } + private void logAccountError(String message) { + if (USE_WTF_FOR_ACCOUNT_ERROR) { + Slog.wtf(TAG, message); + } else { + Slog.e(TAG, message); + } + } + private boolean dispatchSyncOperation(SyncOperation op) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index dab8c7fc9d48..2c632d96e738 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2174,10 +2174,14 @@ public final class DisplayManagerService extends SystemService { } } - if (callingUid == Process.SYSTEM_UID - || checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { - flags |= VIRTUAL_DISPLAY_FLAG_TRUSTED; - } else { + if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) { + if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { + throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to " + + "create a trusted virtual display."); + } + } + + if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) { flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 8777ceacf884..1e02f49c43e4 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -36,6 +36,7 @@ import android.media.session.MediaController; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; import android.media.session.MediaSession.QueueItem; +import android.media.session.ParcelableListBinder; import android.media.session.PlaybackState; import android.net.Uri; import android.os.Binder; @@ -905,14 +906,24 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } @Override - public void setQueue(ParceledListSlice queue) throws RemoteException { + public void resetQueue() throws RemoteException { synchronized (mLock) { - mQueue = queue == null ? null : (List<QueueItem>) queue.getList(); + mQueue = null; } mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); } @Override + public IBinder getBinderForSetQueue() throws RemoteException { + return new ParcelableListBinder<QueueItem>((list) -> { + synchronized (mLock) { + mQueue = list; + } + mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); + }); + } + + @Override public void setQueueTitle(CharSequence title) throws RemoteException { mQueueTitle = title; mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index a8a0e2e937d0..9521611c241d 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -1918,7 +1918,7 @@ public class MediaSessionService extends SystemService implements Monitor { final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); final long token = Binder.clearCallingIdentity(); try { - // Don't perform sanity check between controllerPackageName and controllerUid. + // Don't perform check between controllerPackageName and controllerUid. // When an (activity|service) runs on the another apps process by specifying // android:process in the AndroidManifest.xml, then PID and UID would have the // running process' information instead of the (activity|service) that has created diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 8af74631fb2e..4b246c3b330c 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -1284,14 +1284,21 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements pw.increaseIndent(); List<PackageInstallerSession> finalizedSessions = new ArrayList<>(); + List<PackageInstallerSession> orphanedChildSessions = new ArrayList<>(); int N = mSessions.size(); for (int i = 0; i < N; i++) { final PackageInstallerSession session = mSessions.valueAt(i); - // Do not print finalized staged session as active install sessions final PackageInstallerSession rootSession = session.hasParentSessionId() ? getSession(session.getParentSessionId()) : session; + // Do not print orphaned child sessions as active install sessions + if (rootSession == null) { + orphanedChildSessions.add(session); + continue; + } + + // Do not print finalized staged session as active install sessions if (rootSession.isStagedAndInTerminalState()) { finalizedSessions.add(session); continue; @@ -1303,6 +1310,21 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements pw.println(); pw.decreaseIndent(); + if (!orphanedChildSessions.isEmpty()) { + // Presence of orphaned sessions indicate leak in cleanup for multi-package and + // should be cleaned up. + pw.println("Orphaned install sessions:"); + pw.increaseIndent(); + N = orphanedChildSessions.size(); + for (int i = 0; i < N; i++) { + final PackageInstallerSession session = orphanedChildSessions.get(i); + session.dump(pw); + pw.println(); + } + pw.println(); + pw.decreaseIndent(); + } + pw.println("Finalized install sessions:"); pw.increaseIndent(); N = finalizedSessions.size(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9c8b972985eb..5fba8b93ff4c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -367,7 +367,6 @@ import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; -import com.android.server.pm.permission.PermissionsState; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.security.VerityUtils; @@ -1818,7 +1817,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { removeMessages(WRITE_SETTINGS); removeMessages(WRITE_PACKAGE_RESTRICTIONS); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); mDirtyUsers.clear(); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -1838,6 +1837,7 @@ public class PackageManagerService extends IPackageManager.Stub Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mLock) { removeMessages(WRITE_PACKAGE_LIST); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.writePackageListLPr(msg.arg1); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -2518,7 +2518,7 @@ public class PackageManagerService extends IPackageManager.Stub } mSettings.onVolumeForgotten(fsUuid); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } }; @@ -3442,6 +3442,7 @@ public class PackageManagerService extends IPackageManager.Stub + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); + mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some @@ -3561,7 +3562,7 @@ public class PackageManagerService extends IPackageManager.Stub // can downgrade to reader t.traceBegin("write settings"); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); t.traceEnd(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); @@ -3765,7 +3766,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); } mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } catch (PackageManagerException e) { // Whoops! Something went very wrong; roll back to the stub and disable the package @@ -3776,9 +3777,8 @@ public class PackageManagerService extends IPackageManager.Stub // If we don't, installing the system package fails during scan enableSystemPackageLPw(stubPkg); } - installPackageFromSystemLIF(stubPkg.getCodePath(), - null /*allUserHandles*/, null /*origUserHandles*/, - null /*origPermissionsState*/, true /*writeSettings*/); + installPackageFromSystemLIF(stubPkg.getCodePath(), null /*allUserHandles*/, + null /*origUserHandles*/, true /*writeSettings*/); } catch (PackageManagerException pme) { // Serious WTF; we have to be able to install the stub Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(), @@ -3792,7 +3792,7 @@ public class PackageManagerService extends IPackageManager.Stub stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); } - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } return false; @@ -13092,7 +13092,7 @@ public class PackageManagerService extends IPackageManager.Stub return true; } if (sendRemoved) { - killApplication(packageName, UserHandle.getUid(userId, pkgSetting.appId), + killApplication(packageName, pkgSetting.appId, userId, "hiding pkg"); sendApplicationHiddenForUser(packageName, pkgSetting, userId); return true; @@ -16280,7 +16280,7 @@ public class PackageManagerService extends IPackageManager.Stub res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); //to update install status Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings"); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -18877,6 +18877,10 @@ public class PackageManagerService extends IPackageManager.Stub if (outInfo != null) { outInfo.removedAppId = removedAppId; } + if ((deletedPs.sharedUser == null || deletedPs.sharedUser.packages.size() == 0) + && !isUpdatedSystemApp(deletedPs)) { + mPermissionManager.removePermissionsStateTEMP(removedAppId); + } mPermissionManager.updatePermissions(deletedPs.name, null); if (deletedPs.sharedUser != null) { // Remove permissions associated with package. Since runtime @@ -18886,10 +18890,10 @@ public class PackageManagerService extends IPackageManager.Stub // package is successful and this causes a change in gids. boolean shouldKill = false; for (int userId : UserManagerService.getInstance().getUserIds()) { - final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs, - userId); - shouldKill |= userIdToKill == UserHandle.USER_ALL - || userIdToKill >= UserHandle.USER_SYSTEM; + final int userIdToKill = mPermissionManager + .revokeSharedUserPermissionsForDeletedPackageTEMP(deletedPs, + userId); + shouldKill |= userIdToKill != UserHandle.USER_NULL; } // If gids changed, kill all affected packages. if (shouldKill) { @@ -18933,7 +18937,7 @@ public class PackageManagerService extends IPackageManager.Stub // can downgrade to reader if (writeSettings) { // Save settings now - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } if (installedStateChanged) { mSettings.writeKernelMappingLPr(deletedPs); @@ -19020,8 +19024,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); try { installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles, - outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(), - writeSettings); + outInfo == null ? null : outInfo.origUsers, writeSettings); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": " + e.getMessage()); @@ -19052,9 +19055,8 @@ public class PackageManagerService extends IPackageManager.Stub * Installs a package that's already on the system partition. */ private AndroidPackage installPackageFromSystemLIF(@NonNull String codePathString, - @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, - @Nullable PermissionsState origPermissionState, boolean writeSettings) - throws PackageManagerException { + @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) + throws PackageManagerException { final File codePath = new File(codePathString); @ParseFlags int parseFlags = mDefParseFlags @@ -19091,12 +19093,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); - // Propagate the permissions state as we do not want to drop on the floor - // runtime permissions. The update permissions method below will take - // care of removing obsolete permissions and grant install permissions. - if (origPermissionState != null) { - ps.getPermissionsState().copyFrom(origPermissionState); - } + // The update permissions method below will take care of removing obsolete permissions + // and granting install permissions. mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); final boolean applyUserRestrictions @@ -19130,7 +19128,7 @@ public class PackageManagerService extends IPackageManager.Stub } // can downgrade to reader here if (writeSettings) { - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } return pkg; @@ -19204,7 +19202,7 @@ public class PackageManagerService extends IPackageManager.Stub } else { ps.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER; } - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } return true; } @@ -20396,7 +20394,7 @@ public class PackageManagerService extends IPackageManager.Stub (parser1, userId1) -> { synchronized (mLock) { mSettings.readAllDomainVerificationsLPr(parser1, userId1); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } }); } catch (Exception e) { @@ -21747,6 +21745,8 @@ public class PackageManagerService extends IPackageManager.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + DumpState dumpState = new DumpState(); boolean fullPreferred = false; boolean checkin = false; @@ -21942,7 +21942,7 @@ public class PackageManagerService extends IPackageManager.Stub dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS); } else if ("write".equals(cmd)) { synchronized (mLock) { - mSettings.writeLPr(); + writeSettingsLPrTEMP(); pw.println("Settings written."); return; } @@ -22660,7 +22660,7 @@ public class PackageManagerService extends IPackageManager.Stub // Yay, everything is now upgraded ver.forceCurrent(); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } for (PackageFreezer freezer : freezers) { @@ -22710,7 +22710,7 @@ public class PackageManagerService extends IPackageManager.Stub AttributeCache.instance().removePackage(ps.name); } - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } @@ -23623,6 +23623,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { mDirtyUsers.remove(userId); mUserNeedsBadging.delete(userId); + mPermissionManager.onUserRemoved(userId); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.removeUserLPw(userId); mPendingBroadcasts.remove(userId); mInstantAppRegistry.onUserRemovedLPw(userId); @@ -23723,7 +23725,9 @@ public class PackageManagerService extends IPackageManager.Stub boolean readPermissionStateForUser(@UserIdInt int userId) { synchronized (mPackages) { + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.readPermissionStateForUserSyncLPr(userId); + mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); return mPmInternal.isPermissionUpgradeNeeded(userId); } } @@ -25179,7 +25183,7 @@ public class PackageManagerService extends IPackageManager.Stub if (async) { scheduleWriteSettingsLocked(); } else { - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } } @@ -25226,7 +25230,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } mSettings.mReadExternalStorageEnforced = enforced ? Boolean.TRUE : Boolean.FALSE; - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } @@ -25740,6 +25744,17 @@ public class PackageManagerService extends IPackageManager.Stub public List<String> getMimeGroup(String packageName, String mimeGroup) { return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup); } + + /** + * Temporary method that wraps mSettings.writeLPr() and calls + * mPermissionManager.writePermissionsStateToPackageSettingsTEMP() beforehand. + * + * TODO(zhanghai): This should be removed once we finish migration of permission storage. + */ + private void writeSettingsLPrTEMP() { + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + mSettings.writeLPr(); + } } interface PackageSender { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index de0e4b53adab..491b4fc515ce 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -34,7 +34,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.ResolveInfo; @@ -68,7 +67,6 @@ import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.permission.PermissionsState; import dalvik.system.VMRuntime; @@ -968,20 +966,6 @@ public class PackageManagerServiceUtils { } /** - * Returns the {@link PermissionsState} for the given package. If the {@link PermissionsState} - * could not be found, {@code null} will be returned. - */ - public static PermissionsState getPermissionsState( - PackageManagerInternal packageManagerInternal, AndroidPackage pkg) { - final PackageSetting packageSetting = packageManagerInternal.getPackageSetting( - pkg.getPackageName()); - if (packageSetting == null) { - return null; - } - return packageSetting.getPermissionsState(); - } - - /** * Recursively create target directory */ public static void makeDirRecursive(File targetDir, int mode) throws ErrnoException { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index acb149b9ec3d..d545bd4c1531 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -955,93 +955,6 @@ public final class Settings { } } - /* - * Update the shared user setting when a package with a shared user id is removed. The gids - * associated with each permission of the deleted package are removed from the shared user' - * gid list only if its not in use by other permissions of packages in the shared user setting. - * - * @return the affected user id - */ - @UserIdInt - int updateSharedUserPermsLPw(PackageSetting deletedPs, int userId) { - if ((deletedPs == null) || (deletedPs.pkg == null)) { - Slog.i(PackageManagerService.TAG, - "Trying to update info for null package. Just ignoring"); - return UserHandle.USER_NULL; - } - - // No sharedUserId - if (deletedPs.sharedUser == null) { - return UserHandle.USER_NULL; - } - - SharedUserSetting sus = deletedPs.sharedUser; - - int affectedUserId = UserHandle.USER_NULL; - // Update permissions - for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) { - BasePermission bp = mPermissions.getPermission(eachPerm); - if (bp == null) { - continue; - } - - // Check if another package in the shared user needs the permission. - boolean used = false; - for (PackageSetting pkg : sus.packages) { - if (pkg.pkg != null - && !pkg.pkg.getPackageName().equals(deletedPs.pkg.getPackageName()) - && pkg.pkg.getRequestedPermissions().contains(eachPerm)) { - used = true; - break; - } - } - if (used) { - continue; - } - - PermissionsState permissionsState = sus.getPermissionsState(); - PackageSetting disabledPs = getDisabledSystemPkgLPr(deletedPs.pkg.getPackageName()); - - // If the package is shadowing is a disabled system package, - // do not drop permissions that the shadowed package requests. - if (disabledPs != null) { - boolean reqByDisabledSysPkg = false; - for (String permission : disabledPs.pkg.getRequestedPermissions()) { - if (permission.equals(eachPerm)) { - reqByDisabledSysPkg = true; - break; - } - } - if (reqByDisabledSysPkg) { - continue; - } - } - - // Try to revoke as an install permission which is for all users. - // The package is gone - no need to keep flags for applying policy. - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); - - if (permissionsState.revokeInstallPermission(bp) == - PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - affectedUserId = UserHandle.USER_ALL; - } - - // Try to revoke as an install permission which is per user. - if (permissionsState.revokeRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - if (affectedUserId == UserHandle.USER_NULL) { - affectedUserId = userId; - } else if (affectedUserId != userId) { - // Multiple users affected. - affectedUserId = UserHandle.USER_ALL; - } - } - } - - return affectedUserId; - } - int removePackageLPw(String name) { final PackageSetting p = mPackages.get(name); if (p != null) { @@ -5533,32 +5446,11 @@ public final class Settings { // Make sure we do not mHandler.removeMessages(userId); - for (SettingBase sb : mPackages.values()) { - revokeRuntimePermissionsAndClearFlags(sb, userId); - } - - for (SettingBase sb : mSharedUsers.values()) { - revokeRuntimePermissionsAndClearFlags(sb, userId); - } - mPermissionUpgradeNeeded.delete(userId); mVersions.delete(userId); mFingerprints.remove(userId); } - private void revokeRuntimePermissionsAndClearFlags(SettingBase sb, int userId) { - PermissionsState permissionsState = sb.getPermissionsState(); - for (PermissionState permissionState - : permissionsState.getRuntimePermissionStates(userId)) { - BasePermission bp = mPermissions.getPermission(permissionState.getName()); - if (bp != null) { - permissionsState.revokeRuntimePermission(bp, userId); - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); - } - } - } - public void deleteUserRuntimePermissionsFile(int userId) { mPersistence.deleteForUser(UserHandle.of(userId)); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d137fd05f793..c1aebd33889c 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -84,6 +84,7 @@ import android.os.storage.StorageManager; import android.security.GateKeeper; import android.service.gatekeeper.IGateKeeperService; import android.stats.devicepolicy.DevicePolicyEnums; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -141,7 +142,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicInteger; /** * Service for {@link UserManager}. @@ -159,6 +159,10 @@ public class UserManagerService extends IUserManager.Stub { private static final String LOG_TAG = "UserManagerService"; static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE + + // TODO(b/164159026): remove once owner_name issue on automotive is fixed + // Can be used to track getUsers() / userWithNameLU() behavior + public static final boolean DBG_CACHED_USERINFOS = false; // DO NOT SUBMIT WITH TRUE // Can be used for manual testing of id recycling private static final boolean RELEASE_DELETED_USER_ID = false; // DO NOT SUBMIT WITH TRUE @@ -272,6 +276,25 @@ public class UserManagerService extends IUserManager.Stub { private DevicePolicyManagerInternal mDevicePolicyManagerInternal; /** + * Reference to the {@link UserHandle#SYSTEM} user's UserInfo; it's {@code name} was either + * manually set, or it's {@code null}. + * + * <p>The reference is set just once, but it's {@code name} is updated when it's manually set. + */ + @GuardedBy("mUsersLock") + private UserInfo mSystemUserInfo; + + /** + * Reference to the {@link UserHandle#SYSTEM} user's UserInfo, with its {@code name} set to + * the localized value of {@code owner_name}. + * + * <p>The reference is set just once, but it's {@code name} is updated everytime the reference + * is used and the locale changed. + */ + @GuardedBy("mUsersLock") + private UserInfo mSystemUserInfoWithName; + + /** * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps. */ @VisibleForTesting @@ -444,11 +467,6 @@ public class UserManagerService extends IUserManager.Stub { } }; - // TODO(b/161915546): remove once userWithName() is fixed / removed - // Use to debug / dump when user 0 is allocated at userWithName() - public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE - public final AtomicInteger mUser0Allocations; - /** * Start an {@link IntentSender} when user is unlocked after disabling quiet mode. * @@ -638,7 +656,6 @@ public class UserManagerService extends IUserManager.Stub { LocalServices.addService(UserManagerInternal.class, mLocalService); mLockPatternUtils = new LockPatternUtils(mContext); mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING); - mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null; } void systemReady() { @@ -784,7 +801,7 @@ public class UserManagerService extends IUserManager.Stub { || (excludePreCreated && ui.preCreated)) { continue; } - users.add(userWithName(ui)); + users.add(userWithNameLU(ui)); } return users; } @@ -853,7 +870,7 @@ public class UserManagerService extends IUserManager.Stub { userInfo.name = null; userInfo.iconPath = null; } else { - userInfo = userWithName(userInfo); + userInfo = userWithNameLU(userInfo); } users.add(userInfo); } @@ -1310,26 +1327,57 @@ public class UserManagerService extends IUserManager.Stub { public UserInfo getUserInfo(@UserIdInt int userId) { checkManageOrCreateUsersPermission("query user"); synchronized (mUsersLock) { - return userWithName(getUserInfoLU(userId)); + return userWithNameLU(getUserInfoLU(userId)); } } /** * Returns a UserInfo object with the name filled in, for Owner, or the original * if the name is already set. + * + * <p>Note:</p> the Owner name is localized, so the current value must be checked every time + * this method is called. */ - private UserInfo userWithName(UserInfo orig) { - if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) { - if (DBG_ALLOCATION) { - final int number = mUser0Allocations.incrementAndGet(); - Slog.w(LOG_TAG, "System user instantiated at least " + number + " times"); - } - UserInfo withName = new UserInfo(orig); - withName.name = getOwnerName(); - return withName; - } else { - return orig; + private UserInfo userWithNameLU(UserInfo orig) { + // Only the system user uses the owner_name string. + if (orig == null || orig.id != UserHandle.USER_SYSTEM) return orig; + + if (mSystemUserInfo == null) { + mSystemUserInfo = orig; + if (DBG_CACHED_USERINFOS) { + Slog.d(LOG_TAG, "Set mSystemUserInfo:" + mSystemUserInfo.toFullString()); + } } + + if (mSystemUserInfo.name != null) { + if (DBG_CACHED_USERINFOS) { + Slog.v(LOG_TAG, "Returning mSystemUserInfo: " + mSystemUserInfo.toFullString()); + } + return mSystemUserInfo; + } + + final String ownerName = getOwnerName(); + + if (mSystemUserInfoWithName == null) { + mSystemUserInfoWithName = new UserInfo(orig); + mSystemUserInfoWithName.name = ownerName; + if (DBG_CACHED_USERINFOS) { + Slog.d(LOG_TAG, "Set mSystemUserInfoWithName: " + + mSystemUserInfoWithName.toFullString()); + } + } else if (!TextUtils.equals(ownerName, mSystemUserInfoWithName.name)) { + if (DBG_CACHED_USERINFOS) { + Slog.d(LOG_TAG, "Updating mSystemUserInfoWithName.name from " + + mSystemUserInfoWithName.name + " to " + ownerName); + } + mSystemUserInfoWithName.name = ownerName; + } + + if (DBG_CACHED_USERINFOS) { + Slog.v(LOG_TAG, "Returning mSystemUserInfoWithName:" + + mSystemUserInfoWithName.toFullString()); + } + return mSystemUserInfoWithName; } /** Returns whether the given user type is one of the FULL user types. */ @@ -1482,7 +1530,7 @@ public class UserManagerService extends IUserManager.Stub { } final int userId = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mUsersLock) { - UserInfo userInfo = userWithName(getUserInfoLU(userId)); + UserInfo userInfo = userWithNameLU(getUserInfoLU(userId)); return userInfo == null ? "" : userInfo.name; } } @@ -1597,6 +1645,13 @@ public class UserManagerService extends IUserManager.Stub { Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId); return null; } + + if (DBG_CACHED_USERINFOS && userId == UserHandle.USER_SYSTEM && userData != null + && userData.info != mSystemUserInfo) { + Slog.wtf(LOG_TAG, "getUserInfoLU(): system user on userData (" + userData.info + + ") is not the same as mSystemUserInfo (" + mSystemUserInfo + ")"); + } + return userData != null ? userData.info : null; } @@ -4855,8 +4910,15 @@ public class UserManagerService extends IUserManager.Stub { pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode()); pw.println(" User version: " + mUserVersion); pw.println(" Owner name: " + getOwnerName()); - if (DBG_ALLOCATION) { - pw.println(" System user allocations: " + mUser0Allocations.get()); + if (mSystemUserInfo == null) { + pw.println(" (mSystemUserInfo not set)"); + } else { + pw.println(" System user: " + mSystemUserInfo.toFullString()); + } + if (mSystemUserInfoWithName == null) { + pw.println(" (mSystemUserInfoWithName not set)"); + } else { + pw.println(" System user (with name): " + mSystemUserInfoWithName.toFullString()); } // Dump UserTypes diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index cfa0449aaf33..962638b4f63c 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -38,7 +38,6 @@ import android.util.Slog; import com.android.server.pm.DumpState; import com.android.server.pm.PackageManagerService; -import com.android.server.pm.PackageSetting; import com.android.server.pm.PackageSettingBase; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -420,8 +419,7 @@ public final class BasePermission { } public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg, - PackageSetting pkgSetting) { - final PermissionsState permsState = pkgSetting.getPermissionsState(); + PermissionsState permsState) { int index = pkg.getRequestedPermissions().indexOf(name); if (!permsState.hasRequestedPermission(name) && index == -1) { throw new SecurityException("Package " + pkg.getPackageName() diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 1be74154b53a..f5dd918a18f3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -162,6 +162,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -227,6 +228,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** Internal connection to the user manager */ private final UserManagerInternal mUserManagerInt; + /** Maps from App ID to PermissionsState */ + private final SparseArray<PermissionsState> mAppIdStates = new SparseArray<>(); + /** Permission controller: User space permission management */ private PermissionControllerManager mPermissionControllerManager; @@ -671,11 +675,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (pkg == null) { return 0; } - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { - return 0; - } synchronized (mLock) { if (mSettings.getPermissionLocked(permName) == null) { return 0; @@ -684,7 +683,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { return 0; } - PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return 0; + } return permissionsState.getPermissionFlags(permName, userId); } @@ -771,9 +774,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - packageName); - if (pkg == null || ps == null) { + if (pkg == null) { Log.e(TAG, "Unknown package: " + packageName); return; } @@ -789,7 +790,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - final PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return; + } + final boolean hadState = permissionsState.getRuntimePermissionState(permName, userId) != null; if (!hadState) { @@ -864,12 +870,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean[] changed = new boolean[1]; mPackageManagerInt.forEachPackage(pkg -> { - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } - final PermissionsState permissionsState = ps.getPermissionsState(); changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions( userId, effectiveFlagMask, effectiveFlagValues); mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid()); @@ -923,12 +928,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final int uid = UserHandle.getUid(userId, pkg.getUid()); - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return PackageManager.PERMISSION_DENIED; } - final PermissionsState permissionsState = ps.getPermissionsState(); if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) { return PackageManager.PERMISSION_GRANTED; @@ -1139,9 +1143,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long identity = Binder.clearCallingIdentity(); try { - final PermissionsState permissionsState = - PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg); + final PermissionsState permissionsState = getPermissionsState(pkg); if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); return null; } @@ -1451,7 +1455,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown package: " + packageName); } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + return; + } + + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1464,8 +1474,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid())); - final PermissionsState permissionsState = ps.getPermissionsState(); - final int flags = permissionsState.getPermissionFlags(permName, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { Log.e(TAG, "Cannot grant system fixed permission " @@ -1599,9 +1607,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { "revokeRuntimePermission"); final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - packageName); - if (pkg == null || ps == null) { + if (pkg == null) { Log.e(TAG, "Unknown package: " + packageName); return; } @@ -1613,7 +1619,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + return; + } + + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1624,8 +1636,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } - final PermissionsState permissionsState = ps.getPermissionsState(); - final int flags = permissionsState.getPermissionFlags(permName, userId); // Only the system may revoke SYSTEM_FIXED permissions. if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 @@ -2456,14 +2466,36 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + private void onUserRemoved(@UserIdInt int userId) { + synchronized (mLock) { + final int appIdStatesSize = mAppIdStates.size(); + for (int i = 0; i < appIdStatesSize; i++) { + PermissionsState permissionsState = mAppIdStates.valueAt(i); + for (PermissionState permissionState + : permissionsState.getRuntimePermissionStates(userId)) { + BasePermission bp = mSettings.getPermission(permissionState.getName()); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); + } + } + } + } + } + @NonNull private Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) { final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName); if (ps == null) { - return null; + return Collections.emptySet(); + } + final PermissionsState permissionsState = getPermissionsState(ps); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return Collections.emptySet(); } - final PermissionsState permissionsState = ps.getPermissionsState(); if (!ps.getInstantApp(userId)) { return permissionsState.getPermissions(userId); } else { @@ -2503,7 +2535,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return null; } - final PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getPermissionsState(ps); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return null; + } return permissionsState.computeGids(userId); } @@ -2541,8 +2577,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return; } - - final PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getOrCreatePermissionsState(ps); final int[] userIds = getAllUserIds(); @@ -2614,8 +2649,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { // changed runtime permissions here are promotion of an install to // runtime and revocation of a runtime from a shared user. synchronized (mLock) { - updatedUserIds = revokeUnusedSharedUserPermissionsLocked(ps.getSharedUser(), - userIds); + updatedUserIds = revokeUnusedSharedUserPermissionsLocked( + ps.getSharedUser().getPackages(), permissionsState, userIds); if (!ArrayUtils.isEmpty(updatedUserIds)) { runtimePermissionsRevoked = true; } @@ -3091,6 +3126,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { updatedUserIds); } + // TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important + // for shared users. // Persist the runtime permissions state for users with changes. If permissions // were revoked because no app in the shared user declares them we have to // write synchronously to avoid losing runtime permissions state. @@ -3554,37 +3591,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PackageSetting disabledPs = mPackageManagerInt .getDisabledSystemPackage(pkg.getPackageName()); final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg; - if (disabledPs != null - && disabledPs.getPermissionsState().hasInstallPermission(perm)) { - // If the original was granted this permission, we take - // that grant decision as read and propagate it to the - // update. - if ((privilegedPermission && disabledPs.isPrivileged()) - || (oemPermission && disabledPs.isOem() - && canGrantOemPermission(disabledPs, perm))) { - allowed = true; - } - } else { - // The system apk may have been updated with an older - // version of the one on the data partition, but which - // granted a new system permission that it didn't have - // before. In this case we do want to allow the app to - // now get the new permission if the ancestral apk is - // privileged to get it. - if (disabledPs != null && disabledPkg != null - && isPackageRequestingPermission(disabledPkg, perm) - && ((privilegedPermission && disabledPs.isPrivileged()) - || (oemPermission && disabledPs.isOem() - && canGrantOemPermission(disabledPs, perm)))) { - allowed = true; - } + if (disabledPkg != null && isPackageRequestingPermission(disabledPkg, perm) + && ((privilegedPermission && disabledPkg.isPrivileged()) + || (oemPermission && canGrantOemPermission(disabledPkg, + perm)))) { + allowed = true; } } else { - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); allowed = (privilegedPermission && pkg.isPrivileged()) - || (oemPermission && pkg.isOem() - && canGrantOemPermission(ps, perm)); + || (oemPermission && canGrantOemPermission(pkg, perm)); } // In any case, don't grant a privileged permission to privileged vendor apps, if // the permission's protectionLevel does not have the extra 'vendorPrivileged' @@ -3735,16 +3750,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { return false; } - private static boolean canGrantOemPermission(PackageSetting ps, String permission) { - if (!ps.isOem()) { + private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) { + if (!pkg.isOem()) { return false; } // all oem permissions must explicitly be granted or denied final Boolean granted = - SystemConfig.getInstance().getOemPermissions(ps.name).get(permission); + SystemConfig.getInstance().getOemPermissions(pkg.getPackageName()).get(permission); if (granted == null) { throw new IllegalStateException("OEM permission" + permission + " requested by package " - + ps.name + " must be explicitly declared granted or not"); + + pkg.getPackageName() + " must be explicitly declared granted or not"); } return Boolean.TRUE == granted; } @@ -3757,12 +3772,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } // Legacy apps have the permission and get user consent on launch. - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return false; } - final PermissionsState permissionsState = ps.getPermissionsState(); return permissionsState.isPermissionReviewRequired(userId); } @@ -3787,14 +3801,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId, String[] grantedPermissions, int callingUid, PermissionCallback callback) { - PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } - PermissionsState permissionsState = ps.getPermissionsState(); - final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -3838,9 +3850,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg, @UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid, @PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) { - final PermissionsState permissionsState = - PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg); + final PermissionsState permissionsState = getPermissionsState(pkg); if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } @@ -3958,9 +3970,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int j = 0; j < oldGrantedCount; j++) { final String permission = oldPermsForUser.valueAt(j); // Sometimes we create a new permission state instance during update. - final PermissionsState newPermissionsState = - PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, - pkg); + final PermissionsState newPermissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + continue; + } if (!newPermissionsState.hasPermission(permission, userId)) { callback.onPermissionRevoked(pkg.getUid(), userId, null); break; @@ -3970,12 +3984,100 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + @UserIdInt + private int revokeSharedUserPermissionsForDeletedPackage(@NonNull PackageSetting deletedPs, + @UserIdInt int userId) { + if ((deletedPs == null) || (deletedPs.pkg == null)) { + Slog.i(TAG, "Trying to update info for null package. Just ignoring"); + return UserHandle.USER_NULL; + } + + SharedUserSetting sus = deletedPs.getSharedUser(); + + // No sharedUserId + if (sus == null) { + return UserHandle.USER_NULL; + } + + int affectedUserId = UserHandle.USER_NULL; + // Update permissions + for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) { + BasePermission bp = mSettings.getPermission(eachPerm); + if (bp == null) { + continue; + } + + // Check if another package in the shared user needs the permission. + boolean used = false; + final List<AndroidPackage> pkgs = sus.getPackages(); + if (pkgs != null) { + for (AndroidPackage pkg : pkgs) { + if (pkg != null + && !pkg.getPackageName().equals(deletedPs.pkg.getPackageName()) + && pkg.getRequestedPermissions().contains(eachPerm)) { + used = true; + break; + } + } + } + if (used) { + continue; + } + + PermissionsState permissionsState = getPermissionsState(deletedPs.pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()); + continue; + } + + PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage( + deletedPs.pkg.getPackageName()); + + // If the package is shadowing is a disabled system package, + // do not drop permissions that the shadowed package requests. + if (disabledPs != null) { + boolean reqByDisabledSysPkg = false; + for (String permission : disabledPs.pkg.getRequestedPermissions()) { + if (permission.equals(eachPerm)) { + reqByDisabledSysPkg = true; + break; + } + } + if (reqByDisabledSysPkg) { + continue; + } + } + + // Try to revoke as an install permission which is for all users. + // The package is gone - no need to keep flags for applying policy. + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); + + if (permissionsState.revokeInstallPermission(bp) + == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + affectedUserId = UserHandle.USER_ALL; + } + + // Try to revoke as a runtime permission which is per user. + if (permissionsState.revokeRuntimePermission(bp, userId) + == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + if (affectedUserId == UserHandle.USER_NULL) { + affectedUserId = userId; + } else if (affectedUserId != userId) { + // Multiple users affected. + affectedUserId = UserHandle.USER_ALL; + } + } + } + + return affectedUserId; + } + @GuardedBy("mLock") private int[] revokeUnusedSharedUserPermissionsLocked( - SharedUserSetting suSetting, int[] allUserIds) { + List<AndroidPackage> pkgList, PermissionsState permissionsState, int[] allUserIds) { // Collect all used permissions in the UID final ArraySet<String> usedPermissions = new ArraySet<>(); - final List<AndroidPackage> pkgList = suSetting.getPackages(); if (pkgList == null || pkgList.size() == 0) { return EmptyArray.INT; } @@ -3993,7 +4095,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - PermissionsState permissionsState = suSetting.getPermissionsState(); // Prune install permissions List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates(); final int installPermCount = installPermStates.size(); @@ -4279,12 +4380,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } else { mPackageManagerInt.forEachPackage(p -> { - PackageSetting ps = mPackageManagerInt.getPackageSetting( - p.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(p); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + p.getPackageName()); return; } - PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.getInstallPermissionState(bp.getName()) != null) { permissionsState.revokeInstallPermission(bp); permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, @@ -4695,6 +4795,67 @@ public class PermissionManagerService extends IPermissionManager.Stub { return mBackgroundPermissions; } + @Nullable + private PermissionsState getPermissionsState(@NonNull PackageSetting ps) { + return getPermissionsState(ps.getAppId()); + } + + @Nullable + private PermissionsState getPermissionsState(@NonNull AndroidPackage pkg) { + return getPermissionsState(pkg.getUid()); + } + + @Nullable + private PermissionsState getPermissionsState(int appId) { + synchronized (mLock) { + return mAppIdStates.get(appId); + } + } + + @Nullable + private PermissionsState getOrCreatePermissionsState(@NonNull PackageSetting ps) { + return getOrCreatePermissionsState(ps.getAppId()); + } + + @Nullable + private PermissionsState getOrCreatePermissionsState(int appId) { + synchronized (mLock) { + PermissionsState state = mAppIdStates.get(appId); + if (state == null) { + state = new PermissionsState(); + mAppIdStates.put(appId, state); + } + return state; + } + } + + private void removePermissionsState(int appId) { + synchronized (mLock) { + mAppIdStates.remove(appId); + } + } + + private void readPermissionsStateFromPackageSettings() { + mPackageManagerInt.forEachPackageSetting(ps -> { + synchronized (mLock) { + mAppIdStates.put(ps.getAppId(), new PermissionsState(ps.getPermissionsState())); + } + }); + } + + private void writePermissionsStateToPackageSettings() { + mPackageManagerInt.forEachPackageSetting(ps -> { + synchronized (mLock) { + final PermissionsState permissionsState = mAppIdStates.get(ps.getAppId()); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + ps.name); + return; + } + ps.getPermissionsState().copyFrom(permissionsState); + } + }); + } + private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal { @Override public void systemReady() { @@ -4726,6 +4887,29 @@ public class PermissionManagerService extends IPermissionManager.Stub { public void removeAllPermissions(AndroidPackage pkg, boolean chatty) { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } + @Override + public void readPermissionsStateFromPackageSettingsTEMP() { + PermissionManagerService.this.readPermissionsStateFromPackageSettings(); + } + @Override + public void writePermissionsStateToPackageSettingsTEMP() { + PermissionManagerService.this.writePermissionsStateToPackageSettings(); + } + @Override + public void onUserRemoved(@UserIdInt int userId) { + PermissionManagerService.this.onUserRemoved(userId); + } + @Override + public void removePermissionsStateTEMP(int appId) { + PermissionManagerService.this.removePermissionsState(appId); + } + @Override + @UserIdInt + public int revokeSharedUserPermissionsForDeletedPackageTEMP( + @NonNull PackageSetting deletedPs, @UserIdInt int userId) { + return PermissionManagerService.this.revokeSharedUserPermissionsForDeletedPackage( + deletedPs, userId); + } @NonNull @Override public Set<String> getGrantedPermissions(@NonNull String packageName, diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index cfa371ddbad3..f319bf495e8b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.permission.PermissionManagerInternal; +import com.android.server.pm.PackageSetting; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.ArrayList; @@ -265,6 +266,52 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); /** + * Read {@code PermissionsState} from package settings. + * + * TODO(zhanghai): This is a temporary method because we should not expose + * {@code PackageSetting} which is a implementation detail that permission should not know. + * Instead, it should retrieve the legacy state via a defined API. + */ + public abstract void readPermissionsStateFromPackageSettingsTEMP(); + + /** + * Write {@code PermissionsState} from to settings. + * + * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence + * for permission. + */ + public abstract void writePermissionsStateToPackageSettingsTEMP(); + + /** + * Notify that a user has been removed and its permission state should be removed as well. + */ + public abstract void onUserRemoved(@UserIdInt int userId); + + /** + * Remove the {@code PermissionsState} associated with an app ID, called the same time as the + * removal of a {@code PackageSetitng}. + * + * TODO(zhanghai): This is a temporary method before we figure out a way to get notified of app + * ID removal via API. + */ + public abstract void removePermissionsStateTEMP(int appId); + + /** + * Update the shared user setting when a package with a shared user id is removed. The gids + * associated with each permission of the deleted package are removed from the shared user' + * gid list only if its not in use by other permissions of packages in the shared user setting. + * + * TODO(zhanghai): We should not need this when permission no longer sees an incomplete package + * state where the updated system package is uninstalled but the disabled system package is yet + * to be installed. Then we should handle this in restorePermissionState(). + * + * @return the affected user id, may be a real user ID, USER_ALL, or USER_NULL when none. + */ + @UserIdInt + public abstract int revokeSharedUserPermissionsForDeletedPackageTEMP( + @NonNull PackageSetting deletedPs, @UserIdInt int userId); + + /** * Get all the permissions granted to a package. */ @NonNull diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4e05a138c543..9316c4657826 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1212,14 +1212,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return task; } - /** - * Sets the Task on this activity for the purposes of re-use during launch where we will - * re-use another activity instead of this one for the launch. - */ - void setTaskForReuse(Task task) { - this.task = task; - } - Task getStack() { return task != null ? task.getRootTask() : null; } @@ -4672,7 +4664,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // case where this is the top activity in a pinned stack. final boolean isTop = this == stack.getTopNonFinishingActivity(); final boolean isTopNotPinnedStack = stack.isAttached() - && stack.getDisplayArea().isTopNotPinnedStack(stack); + && stack.getDisplayArea().isTopNotFinishNotPinnedStack(stack); final boolean visibleIgnoringDisplayStatus = stack.checkKeyguardVisibility(this, visibleIgnoringKeyguard, isTop && isTopNotPinnedStack); @@ -4814,6 +4806,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Slog.v(TAG_VISIBILITY, "Start visible activity, " + this); } setState(STARTED, "makeActiveIfNeeded"); + + // Update process info while making an activity from invisible to visible, to make + // sure the process state is updated to foreground. + if (app != null) { + app.updateProcessInfo(false /* updateServiceConnectionActivities */, + true /* activityChange */, true /* updateOomAdj */, + true /* addPendingTopUid */); + } + try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, StartActivityItem.obtain()); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 15e88fc44746..f6158383d28a 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1766,8 +1766,8 @@ class ActivityStarter { } else if (mInTask != null) { return mInTask; } else { - final Task stack = getLaunchStack(mStartActivity, mLaunchFlags, - null /* task */, mOptions); + final Task stack = getLaunchStack(mStartActivity, mLaunchFlags, null /* task */, + mOptions); final ActivityRecord top = stack.getTopNonFinishingActivity(); if (top != null) { return top.getTask(); @@ -1870,13 +1870,7 @@ class ActivityStarter { return START_SUCCESS; } - boolean clearTaskForReuse = false; if (reusedTask != null) { - if (mStartActivity.getTask() == null) { - mStartActivity.setTaskForReuse(reusedTask); - clearTaskForReuse = true; - } - if (targetTask.intent == null) { // This task was started because of movement of the activity based on // affinity... @@ -1923,13 +1917,6 @@ class ActivityStarter { complyActivityFlags(targetTask, reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants); - if (clearTaskForReuse) { - // Clear task for re-use so later code to methods - // {@link #setTaskFromReuseOrCreateNewTask}, {@link #setTaskFromSourceRecord}, or - // {@link #setTaskFromInTask} can parent it to the task. - mStartActivity.setTaskForReuse(null); - } - if (mAddingToTask) { return START_SUCCESS; } @@ -2515,8 +2502,8 @@ class ActivityStarter { intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask()); } - final Task launchStack = - getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions); + final Task launchStack = getLaunchStack(mStartActivity, mLaunchFlags, intentTask, + mOptions); if (launchStack == null || launchStack == mTargetStack) { // Do not set mMovedToFront to true below for split-screen-top stack, or // START_TASK_TO_FRONT will be returned and trigger unexpected animations when a diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2e879810c085..0215ead7e5de 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3982,9 +3982,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation)); - // TODO: Not sure if we really need to set the rotation here since we are updating from - // the display info above... - mDisplayFrames.mRotation = getRotation(); mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode); int seq = mLayoutSeq + 1; diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 2e03cb80b189..40fc25b41d9f 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -246,6 +246,9 @@ public class DisplayPolicy { | View.STATUS_BAR_TRANSPARENT | View.NAVIGATION_BAR_TRANSPARENT; + private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR}; + private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR}; + private final WindowManagerService mService; private final Context mContext; private final Context mUiContext; @@ -3353,8 +3356,15 @@ public class DisplayPolicy { return; } + final InsetsState requestedState = controlTarget.getRequestedInsetsState(); + final @InsetsType int restorePositionTypes = + (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) + ? Type.navigationBars() : 0) + | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) + ? Type.statusBars() : 0); + if (swipeTarget == mNavigationBar - && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) { + && (restorePositionTypes & Type.navigationBars()) != 0) { // Don't show status bar when swiping on already visible navigation bar. // But restore the position of navigation bar if it has been moved by the control // target. @@ -3362,14 +3372,13 @@ public class DisplayPolicy { return; } - int insetsTypesToShow = Type.systemBars(); - if (controlTarget.canShowTransient()) { - insetsTypesToShow &= ~mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap( - new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); - } - if (insetsTypesToShow != 0) { - controlTarget.showInsets(insetsTypesToShow, false); + // Show transient bars if they are hidden; restore position if they are visible. + mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE); + controlTarget.showInsets(restorePositionTypes, false); + } else { + // Restore visibilities and positions of system bars. + controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false); } } else { boolean sb = mStatusBarController.checkShowTransientBarLw(); @@ -3770,8 +3779,7 @@ public class DisplayPolicy { // we're no longer on the Keyguard and the screen is ready. We can now request the bars. mPendingPanicGestureUptime = 0; if (!isNavBarEmpty(vis)) { - mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap( - new int[] {ITYPE_NAVIGATION_BAR})); + mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC); } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 0fe9735c9e46..cec797c43bcd 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -227,6 +227,11 @@ final class InputMonitor { WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory) { + if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) { + throw new IllegalArgumentException("Illegal input consumer : " + name + + ", display: " + mDisplayId); + } + if (mInputConsumers.containsKey(name)) { throw new IllegalStateException("Existing input consumer found with name: " + name + ", display: " + mDisplayId); @@ -256,6 +261,11 @@ final class InputMonitor { // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL; break; + case INPUT_CONSUMER_RECENTS_ANIMATION: + break; + default: + throw new IllegalArgumentException("Illegal input consumer : " + name + + ", display: " + mDisplayId); } addInputConsumer(name, consumer); } @@ -463,9 +473,6 @@ final class InputMonitor { mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */); - if (mAddWallpaperInputConsumerHandle) { - mWallpaperInputConsumer.show(mInputTransaction, 0); - } if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); mDisplayContent.scheduleAnimation(); diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index 3ffc26a7a8ad..5e7ed3f80e43 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -17,6 +17,7 @@ package com.android.server.wm; import android.inputmethodservice.InputMethodService; +import android.view.InsetsState; import android.view.WindowInsets.Type.InsetsType; /** @@ -38,6 +39,13 @@ interface InsetsControlTarget { } /** + * @return The requested {@link InsetsState} of this target. + */ + default InsetsState getRequestedInsetsState() { + return InsetsState.EMPTY; + } + + /** * Instructs the control target to show inset sources. * * @param types to specify which types of insets source window should be shown. diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index b7287e718bd6..18a25033b1e6 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -42,7 +42,6 @@ import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; import android.view.ViewRootImpl; -import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimationControlListener; @@ -153,15 +152,13 @@ class InsetsPolicy { return provider != null && provider.hasWindow() && !provider.getSource().isVisible(); } - @InsetsType int showTransient(IntArray types) { - @InsetsType int showingTransientTypes = 0; + void showTransient(@InternalInsetsType int[] types) { boolean changed = false; - for (int i = types.size() - 1; i >= 0; i--) { - final int type = types.get(i); + for (int i = types.length - 1; i >= 0; i--) { + final @InternalInsetsType int type = types[i]; if (!isHidden(type)) { continue; } - showingTransientTypes |= InsetsState.toPublicType(type); if (mShowingTransientTypes.indexOf(type) != -1) { continue; } @@ -189,7 +186,6 @@ class InsetsPolicy { } }); } - return showingTransientTypes; } void hideTransient() { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index fe2d08f6a4c2..aeaffd98f820 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2816,7 +2816,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @param launchParams The resolved launch params to use. * @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid} * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid} - * * @return The stack to use for the launch or INVALID_STACK_ID. */ Task getLaunchStack(@Nullable ActivityRecord r, @@ -2965,10 +2964,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // If {@code r} is already in target display area and its task is the same as the candidate // task, the intention should be getting a launch stack for the reusable activity, so we can // use the existing stack. - if (candidateTask != null && (r.getTask() == null || r.getTask() == candidateTask)) { - // TODO(b/153920825): Fix incorrect evaluation of attached state - final TaskDisplayArea attachedTaskDisplayArea = r.getTask() != null - ? r.getTask().getDisplayArea() : r.getDisplayArea(); + if (candidateTask != null) { + final TaskDisplayArea attachedTaskDisplayArea = candidateTask.getDisplayArea(); if (attachedTaskDisplayArea == null || attachedTaskDisplayArea == taskDisplayArea) { return candidateTask.getRootTask(); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 0529abf89f6e..50c269e23c53 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3820,7 +3820,10 @@ class Task extends WindowContainer<WindowContainer> { @Override boolean fillsParent() { - return matchParentBounds(); + // 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 diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 32511108836e..6550167683a0 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1489,9 +1489,13 @@ final class TaskDisplayArea extends DisplayArea<Task> { return stack == getTopStack(); } - boolean isTopNotPinnedStack(Task stack) { + boolean isTopNotFinishNotPinnedStack(Task stack) { for (int i = getStackCount() - 1; i >= 0; --i) { final Task current = getStackAt(i); + final ActivityRecord topAct = current.getTopNonFinishingActivity(); + if (topAct == null) { + continue; + } if (!current.inPinnedWindowingMode()) { return current == stack; } diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java index 61e9e5082d17..5e81e4008680 100644 --- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java +++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java @@ -24,8 +24,6 @@ import android.annotation.NonNull; import android.util.ArrayMap; import android.util.Slog; -import com.android.server.wm.WindowManagerService.H; - import java.io.PrintWriter; /** @@ -102,7 +100,13 @@ class UnknownAppVisibilityController { if (DEBUG_UNKNOWN_APP_VISIBILITY) { Slog.d(TAG, "App launched activity=" + activity); } - mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RESUME); + // If the activity was started with launchTaskBehind, the lifecycle will goes to paused + // directly, and the process will pass onResume, so we don't need to waiting resume for it. + if (!activity.mLaunchTaskBehind) { + mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RESUME); + } else { + mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RELAYOUT); + } } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index cd222a97f4d9..bf900f7a91c8 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.Manifest.permission.INPUT_CONSUMER; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.MANAGE_APP_TOKENS; @@ -929,7 +930,7 @@ public class WindowManagerService extends IWindowManager.Stub private void setShadowRenderer() { mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(), - DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0; + DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0; } PowerManager mPowerManager; @@ -5861,6 +5862,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void createInputConsumer(IBinder token, String name, int displayId, InputChannel inputChannel) { + if (!mAtmInternal.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("createInputConsumer requires INPUT_CONSUMER permission"); + } + synchronized (mGlobalLock) { DisplayContent display = mRoot.getDisplayContent(displayId); if (display != null) { @@ -5872,6 +5878,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean destroyInputConsumer(String name, int displayId) { + if (!mAtmInternal.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("destroyInputConsumer requires INPUT_CONSUMER permission"); + } + synchronized (mGlobalLock) { DisplayContent display = mRoot.getDisplayContent(displayId); if (display != null) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 49e623d8dd11..0e455d2a5aa6 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -728,7 +728,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * @return The insets state as requested by the client, i.e. the dispatched insets state * for which the visibilities are overridden with what the client requested. */ - InsetsState getRequestedInsetsState() { + @Override + public InsetsState getRequestedInsetsState() { return mRequestedInsetsState; } diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index bc75dcd91813..12c69eaa0f8d 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -150,7 +150,8 @@ public final class ProfcollectForwardingService extends SystemService { } // Sample for a fraction of app launches. - int traceFrequency = SystemProperties.getInt("profcollectd.applaunch_trace_freq", 2); + int traceFrequency = + SystemProperties.getInt("persist.profcollectd.applaunch_trace_freq", 2); int randomNum = ThreadLocalRandom.current().nextInt(100); if (randomNum < traceFrequency) { try { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java index c29c510b35b5..42ba842f8434 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java @@ -17,23 +17,33 @@ package com.android.server.accessibility.magnification; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.view.Display; import android.view.accessibility.IWindowMagnificationConnection; import android.view.accessibility.IWindowMagnificationConnectionCallback; +/** + * Mocks the basic logic of window magnification in System UI. We assume the screen size is + * unlimited, so source bounds is always on the center of the mirror window bounds. + */ class MockWindowMagnificationConnection { + public static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; private final IWindowMagnificationConnection mConnection; private final Binder mBinder; private IBinder.DeathRecipient mDeathRecipient; private IWindowMagnificationConnectionCallback mIMirrorWindowCallback; + private Rect mMirrorWindowFrame = new Rect(0, 0, 500, 500); MockWindowMagnificationConnection() throws RemoteException { mConnection = mock(IWindowMagnificationConnection.class); @@ -50,6 +60,30 @@ class MockWindowMagnificationConnection { return null; }).when(mBinder).linkToDeath( any(IBinder.DeathRecipient.class), eq(0)); + stubConnection(); + } + + private void stubConnection() throws RemoteException { + doAnswer((invocation) -> { + final int displayId = invocation.getArgument(0); + if (displayId != TEST_DISPLAY) { + throw new IllegalArgumentException("only support default display :" + displayId); + } + computeMirrorWindowFrame(invocation.getArgument(1), invocation.getArgument(2)); + + mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY, + mMirrorWindowFrame); + return null; + }).when(mConnection).enableWindowMagnification(anyInt(), + anyFloat(), anyFloat(), anyFloat()); + } + + private void computeMirrorWindowFrame(float centerX, float centerY) { + final float offsetX = Float.isNaN(centerX) ? 0 + : centerX - mMirrorWindowFrame.exactCenterX(); + final float offsetY = Float.isNaN(centerY) ? 0 + : centerY - mMirrorWindowFrame.exactCenterY(); + mMirrorWindowFrame.offset((int) offsetX, (int) offsetY); } IWindowMagnificationConnection getConnection() { @@ -60,12 +94,16 @@ class MockWindowMagnificationConnection { return mBinder; } - public IBinder.DeathRecipient getDeathRecipient() { + IBinder.DeathRecipient getDeathRecipient() { return mDeathRecipient; } - public IWindowMagnificationConnectionCallback getConnectionCallback() { + IWindowMagnificationConnectionCallback getConnectionCallback() { return mIMirrorWindowCallback; } + + public Rect getMirrorWindowFrame() { + return new Rect(mMirrorWindowFrame); + } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java index 2b1bdc59d9c8..ed8dc4e470de 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java @@ -87,15 +87,15 @@ public class TwoFingersDownTest { secondPointerCoords.x = DEFAULT_X + 10; secondPointerCoords.y = DEFAULT_Y + 10; - final MotionEvent pointerDownEvent = TouchEventGenerator.pointerDownEvent( + final MotionEvent twoPointersDownEvent = TouchEventGenerator.twoPointersDownEvent( Display.DEFAULT_DISPLAY, defPointerCoords, secondPointerCoords); mGesturesObserver.onMotionEvent(downEvent, downEvent, 0); - mGesturesObserver.onMotionEvent(pointerDownEvent, pointerDownEvent, 0); + mGesturesObserver.onMotionEvent(twoPointersDownEvent, twoPointersDownEvent, 0); verify(mListener, timeout(sTimeoutMillis)).onGestureCompleted( - MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, pointerDownEvent, - pointerDownEvent, 0); + MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, twoPointersDownEvent, + twoPointersDownEvent, 0); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java index e580340a29f7..bec9f26672f4 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java @@ -16,14 +16,9 @@ package com.android.server.accessibility.magnification; -import static android.view.MotionEvent.ACTION_POINTER_DOWN; - import static com.android.server.testutils.TestUtils.strictMock; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import android.content.Context; @@ -63,11 +58,9 @@ public class WindowMagnificationGestureHandlerTest { public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP; // Co-prime x and y, to potentially catch x-y-swapped errors - public static final float DEFAULT_X = 301; - public static final float DEFAULT_Y = 299; - //Assume first pointer position (DEFAULT_X,DEFAULT_Y) is in the window. - public static Rect DEFAULT_WINDOW_FRAME = new Rect(0, 0, 500, 500); - private static final int DISPLAY_0 = 0; + public static final float DEFAULT_TAP_X = 301; + public static final float DEFAULT_TAP_Y = 299; + private static final int DISPLAY_0 = MockWindowMagnificationConnection.TEST_DISPLAY; private Context mContext; private WindowMagnificationManager mWindowMagnificationManager; @@ -83,14 +76,6 @@ public class WindowMagnificationGestureHandlerTest { mContext, mWindowMagnificationManager, mock(ScaleChangedListener.class), /** detectTripleTap= */true, /** detectShortcutTrigger= */true, DISPLAY_0); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(DISPLAY_0, - DEFAULT_WINDOW_FRAME); - doAnswer((invocation) -> { - mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(DISPLAY_0, - DEFAULT_WINDOW_FRAME); - return null; - }).when(mMockConnection.getConnection()).enableWindowMagnification(eq(DISPLAY_0), - anyFloat(), anyFloat(), anyFloat()); mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class)); } @@ -208,10 +193,11 @@ public class WindowMagnificationGestureHandlerTest { break; case STATE_TWO_FINGERS_DOWN: { goFromStateIdleTo(STATE_SHOW_MAGNIFIER); - send(downEvent()); + final Rect frame = mMockConnection.getMirrorWindowFrame(); + send(downEvent(frame.centerX(), frame.centerY())); //Second finger is outside the window. - send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_WINDOW_FRAME.right + 10, - DEFAULT_WINDOW_FRAME.bottom + 10)); + send(twoPointerDownEvent(new float[]{frame.centerX(), frame.centerX() + 10}, + new float[]{frame.centerY(), frame.centerY() + 10})); } break; case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: { @@ -243,7 +229,8 @@ public class WindowMagnificationGestureHandlerTest { } break; case STATE_TWO_FINGERS_DOWN: { - send(upEvent()); + final Rect frame = mMockConnection.getMirrorWindowFrame(); + send(upEvent(frame.centerX(), frame.centerY())); returnToNormalFrom(STATE_SHOW_MAGNIFIER); } break; @@ -286,12 +273,8 @@ public class WindowMagnificationGestureHandlerTest { } } - private MotionEvent downEvent() { - return TouchEventGenerator.downEvent(DISPLAY_0, DEFAULT_X, DEFAULT_Y); - } - - private MotionEvent upEvent() { - return upEvent(DEFAULT_X, DEFAULT_Y); + private MotionEvent downEvent(float x, float y) { + return TouchEventGenerator.downEvent(DISPLAY_0, x, y); } private MotionEvent upEvent(float x, float y) { @@ -299,18 +282,18 @@ public class WindowMagnificationGestureHandlerTest { } private void tap() { - send(downEvent()); - send(upEvent()); + send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y)); + send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y)); } - private MotionEvent pointerEvent(int action, float x, float y) { + private MotionEvent twoPointerDownEvent(float[] x, float[] y) { final MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords(); - defPointerCoords.x = DEFAULT_X; - defPointerCoords.y = DEFAULT_Y; + defPointerCoords.x = x[0]; + defPointerCoords.y = y[0]; final MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords(); - pointerCoords.x = x; - pointerCoords.y = y; - return TouchEventGenerator.pointerDownEvent(DISPLAY_0, defPointerCoords, pointerCoords); + pointerCoords.x = x[1]; + pointerCoords.y = y[1]; + return TouchEventGenerator.twoPointersDownEvent(DISPLAY_0, defPointerCoords, pointerCoords); } private String stateDump() { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java index 7cbf3ee46594..a05881f78892 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java @@ -43,9 +43,9 @@ public class TouchEventGenerator { return generateSingleTouchEvent(displayId, ACTION_UP, x, y); } - public static MotionEvent pointerDownEvent(int displayId, PointerCoords defPointerCoords, + public static MotionEvent twoPointersDownEvent(int displayId, PointerCoords defPointerCoords, PointerCoords pointerCoords) { - return generatePointerEvent(displayId, ACTION_POINTER_DOWN, defPointerCoords, + return generateTwoPointersEvent(displayId, ACTION_POINTER_DOWN, defPointerCoords, pointerCoords); } @@ -59,7 +59,7 @@ public class TouchEventGenerator { return ev; } - private static MotionEvent generatePointerEvent(int displayId, int action, + private static MotionEvent generateTwoPointersEvent(int displayId, int action, PointerCoords defPointerCoords, PointerCoords pointerCoords) { final long downTime = SystemClock.uptimeMillis(); MotionEvent.PointerProperties defPointerProperties = new MotionEvent.PointerProperties(); diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt index 946f27e09fdb..d36dcce800eb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt @@ -20,7 +20,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageParser -import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.Postsubmit import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.appInfo import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.pkgInfo import com.android.server.pm.parsing.pkg.AndroidPackage @@ -38,7 +38,7 @@ import org.junit.runners.Parameterized * This test has to be updated manually whenever the info generation behavior changes, since * there's no single place where flag -> field is defined besides this test. */ -@Presubmit +@Postsubmit @RunWith(Parameterized::class) class AndroidPackageInfoFlagBehaviorTest : AndroidPackageParsingTestBase() { diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt index f96ebda67602..574921cdbd05 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt @@ -17,26 +17,20 @@ package com.android.server.pm.parsing import android.content.pm.PackageManager -import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.Postsubmit import androidx.test.filters.LargeTest import com.google.common.truth.Expect - import org.junit.Rule import org.junit.Test -import org.junit.rules.Timeout -import java.util.concurrent.TimeUnit /** * Collects APKs from the device and verifies that the new parsing behavior outputs * the same exposed Info object as the old parsing logic. */ -@Presubmit +@Postsubmit class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { @get:Rule - val timeout = Timeout(4, TimeUnit.MINUTES) - - @get:Rule val expect = Expect.create() @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index 555906d4c910..608305c33168 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -43,7 +43,6 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; import android.platform.test.annotations.Presubmit; -import android.util.IntArray; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.test.InsetsModeSession; @@ -242,8 +241,7 @@ public class InsetsPolicyTest extends WindowTestsBase { }).when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(mAppWindow); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -271,8 +269,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(mAppWindow); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -301,8 +298,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(mAppWindow); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -340,8 +336,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(app); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(app); policy.updateBarControlTarget(app2); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 085230d35c6a..59f8cc8c3412 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -346,8 +346,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertTrue(rotatedState.getSource(ITYPE_STATUS_BAR).isVisible()); provider.getSource().setVisible(false); - mDisplayContent.getInsetsPolicy().showTransient( - IntArray.wrap(new int[] { ITYPE_STATUS_BAR })); + mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR }); assertTrue(mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR)); assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 1ec9bd24ad59..b89d16807a6e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -890,8 +890,8 @@ public class RootActivityContainerTests extends WindowTestsBase { // Make sure the root task is valid and can be reused on default display. final Task stack = mRootWindowContainer.getValidLaunchStackInTaskDisplayArea( - mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, null, - null); + mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, + null /* options */, null /* launchParams */); assertEquals(task, stack); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index fc54e1de888f..0fe6510b0fce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -50,6 +50,7 @@ import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -245,17 +246,17 @@ public class TaskRecordTests extends WindowTestsBase { final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); final DisplayContent display = new TestDisplayContent.Builder(mAtm, fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); - assertTrue(mRootWindowContainer.getDisplayContent(display.mDisplayId) != null); + assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); // Fix the display orientation to landscape which is the natural rotation (0) for the test // display. final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); - Task stack = new StackBuilder(mRootWindowContainer) + final Task stack = new StackBuilder(mRootWindowContainer) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); - Task task = stack.getBottomMostTask(); - ActivityRecord root = task.getTopNonFinishingActivity(); + final Task task = stack.getBottomMostTask(); + final ActivityRecord root = task.getTopNonFinishingActivity(); assertEquals(fullScreenBounds, task.getBounds()); @@ -267,7 +268,7 @@ public class TaskRecordTests extends WindowTestsBase { assertEquals(fullScreenBounds.height(), task.getBounds().height()); // Top activity gets used - ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setStack(stack).build(); + final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setStack(stack).build(); assertEquals(top, task.getTopNonFinishingActivity()); top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height()); @@ -304,6 +305,33 @@ public class TaskRecordTests extends WindowTestsBase { } @Test + public void testReportsOrientationRequestInLetterboxForOrientation() { + final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); + final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); + final DisplayContent display = new TestDisplayContent.Builder(mAtm, + fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); + assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); + // Fix the display orientation to landscape which is the natural rotation (0) for the test + // display. + final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); + dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); + dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); + + final Task stack = new StackBuilder(mRootWindowContainer) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); + final Task task = stack.getBottomMostTask(); + ActivityRecord root = task.getTopNonFinishingActivity(); + + assertEquals(fullScreenBounds, task.getBounds()); + + // Setting app to fixed portrait fits within parent + root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + assertThat(task.getBounds().width()).isLessThan(task.getBounds().height()); + + assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation()); + } + + @Test public void testIgnoresForcedOrientationWhenParentHandles() { final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); DisplayContent display = new TestDisplayContent.Builder( diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index 6ed762283524..78dfd407ff4e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -55,6 +55,18 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { } @Test + public void testSkipResume() { + final ActivityRecord activity = createTestActivityRecord(mDisplayContent); + activity.mLaunchTaskBehind = true; + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity); + mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(activity); + + // Make sure our handler processed the message. + waitHandlerIdle(mWm.mH); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); + } + + @Test public void testMultiple() { final ActivityRecord activity1 = createTestActivityRecord(mDisplayContent); final ActivityRecord activity2 = createTestActivityRecord(mDisplayContent); diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt index 09c16595e2c7..52e0953813a0 100644 --- a/telephony/api/system-current.txt +++ b/telephony/api/system-current.txt @@ -1625,6 +1625,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 1a606b7ae6a7..2a073a1f1d81 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -851,6 +851,19 @@ public class ProvisioningManager { public static final int KEY_RTT_ENABLED = 66; /** + * An obfuscated string defined by the carrier to indicate VoWiFi entitlement status. + * + * <p>Implementation note: how to generate the value and how it affects VoWiFi service + * should follow carrier requirements. For example, set an empty string could result in + * VoWiFi being disabled by IMS service, and set to a specific string could enable. + * + * <p>Value is in String format. + * @see #setProvisioningStringValue(int, String) + * @see #getProvisioningStringValue(int) + */ + public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; + + /** * Callback for IMS provisioning changes. */ public static class Callback { diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java index d0cec52dfc86..487786045b8e 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -729,7 +729,8 @@ public class ImsConfig { // Expand the operator config items as needed here, need to change // PROVISIONED_CONFIG_END after that. - public static final int PROVISIONED_CONFIG_END = RTT_SETTING_ENABLED; + public static final int PROVISIONED_CONFIG_END = + ProvisioningManager.KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID; // Expand the operator config items as needed here. } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index 9a8e37b19e56..57d6127b1cd1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -88,7 +88,7 @@ class OpenAppWarmTest( noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128) navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation) statusBarLayerRotatesScales(Surface.ROTATION_0, rotation) - navBarLayerIsAlwaysVisible() + navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible(enabled = false) wallpaperLayerBecomesInvisible() } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt index 91ec211805f7..279092d716e2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt @@ -87,9 +87,9 @@ class OpenAppToSplitScreenTest( } layersTrace { - navBarLayerIsAlwaysVisible() + navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible() - noUncoveredRegions(rotation) + noUncoveredRegions(rotation, enabled = false) navBarLayerRotatesAndScales(rotation, bugId = 140855415) statusBarLayerRotatesScales(rotation) |