Merge "Fix new Expandable animation lag" into main
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
index 9e45c4a..bcc0a3b 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -112,36 +112,20 @@
for (EndpointFactory endpointFactory : EndpointFactory.values()) {
for (ChannelType channelType : ChannelType.values()) {
for (PerfTestProtocol protocol : PerfTestProtocol.values()) {
- params.add(
- new Object[] {
- new Config(
- endpointFactory,
- endpointFactory,
- 64,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- channelType,
- protocol)
- });
- params.add(
- new Object[] {
- new Config(
- endpointFactory,
- endpointFactory,
- 512,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- channelType,
- protocol)
- });
- params.add(
- new Object[] {
- new Config(
- endpointFactory,
- endpointFactory,
- 4096,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- channelType,
- protocol)
- });
+ for (int messageSize : ConscryptParams.messageSizes) {
+ for (String cipher : ConscryptParams.ciphers) {
+ params.add(
+ new Object[] {
+ new Config(
+ endpointFactory,
+ endpointFactory,
+ messageSize,
+ cipher,
+ channelType,
+ protocol)
+ });
+ }
+ }
}
}
}
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ConscryptParams.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ConscryptParams.java
new file mode 100644
index 0000000..e5131b8
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ConscryptParams.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2025 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.conscrypt;
+
+import java.util.List;
+
+public class ConscryptParams {
+ public static final List<String> ciphers = List.of(
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
+ );
+
+ public static final List<Integer> messageSizes = List.of(
+ 64,
+ 512,
+ 4096
+ );
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java
index cd0ac96..341d8e6 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java
@@ -87,11 +87,13 @@
}
}
+
public Collection getParams() {
final List<Object[]> params = new ArrayList<>();
for (BufferType bufferType : BufferType.values()) {
- params.add(new Object[] {new Config(bufferType,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 100)});
+ for (String cipher : ConscryptParams.ciphers) {
+ params.add(new Object[] {new Config(bufferType, cipher, 100)});
+ }
}
return params;
}
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java
index 1fee218..23b642e 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java
@@ -37,10 +37,10 @@
import static org.junit.Assert.assertEquals;
import java.nio.ByteBuffer;
-import java.util.Locale;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Locale;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
@@ -94,12 +94,11 @@
public Collection getParams() {
final List<Object[]> params = new ArrayList<>();
for (BufferType bufferType : BufferType.values()) {
- params.add(new Object[] {new Config(bufferType, 64,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")});
- params.add(new Object[] {new Config(bufferType, 512,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")});
- params.add(new Object[] {new Config(bufferType, 4096,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")});
+ for (int messageSize : ConscryptParams.messageSizes) {
+ for (String cipher : ConscryptParams.ciphers) {
+ params.add(new Object[] {new Config(bufferType, messageSize, cipher)});
+ }
+ }
}
return params;
}
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
index 90a87ce..343bb12 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -102,15 +102,12 @@
final List<Object[]> params = new ArrayList<>();
for (EndpointFactory endpointFactory : EndpointFactory.values()) {
for (ChannelType channelType : ChannelType.values()) {
- params.add(new Object[] {new Config(endpointFactory,
- endpointFactory, 64,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", channelType)});
- params.add(new Object[] {new Config(endpointFactory,
- endpointFactory, 512,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", channelType)});
- params.add(new Object[] {new Config(endpointFactory,
- endpointFactory, 4096,
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", channelType)});
+ for (int messageSize : ConscryptParams.messageSizes) {
+ for (String cipher : ConscryptParams.ciphers) {
+ params.add(new Object[] {new Config(endpointFactory,
+ endpointFactory, messageSize, cipher, channelType)});
+ }
+ }
}
}
return params;
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 3659e78..0d82acd 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1578,11 +1578,7 @@
Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.pendingAnim", 0);
}
- if (Flags.refactorInsetsController()) {
- onAnimationStateChanged(typesReady, true /* running */);
- } else {
- onAnimationStateChanged(types, true /* running */);
- }
+ onAnimationStateChanged(types, true /* running */);
if (fromIme) {
switch (animationType) {
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 32175f1..63c55ad 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -1152,7 +1152,6 @@
mEnterResId = in.readInt();
mChangeResId = in.readInt();
mExitResId = in.readInt();
- mBackgroundColor = in.readInt();
mOverrideTaskTransition = in.readBoolean();
mPackageName = in.readString();
mTransitionBounds.readFromParcel(in);
@@ -1203,23 +1202,6 @@
}
/**
- * Make options for a custom animation based on anim resources.
- *
- * @param packageName the package name to find the animation resources
- * @param enterResId the open animation resources ID
- * @param exitResId the close animation resources ID
- * @param backgroundColor the background color
- * @param overrideTaskTransition whether to override the task transition
- */
- @NonNull
- public static AnimationOptions makeCustomAnimOptions(@NonNull String packageName,
- @AnimRes int enterResId, @AnimRes int exitResId, @ColorInt int backgroundColor,
- boolean overrideTaskTransition) {
- return makeCustomAnimOptions(packageName, enterResId, DEFAULT_ANIMATION_RESOURCES_ID,
- exitResId, backgroundColor, overrideTaskTransition);
- }
-
- /**
* Creates a {@link android.app.ActivityOptions#ANIM_CUSTOM} {@link AnimationOptions}.
*
* @param packageName the package name that includes the animation resources.
@@ -1231,13 +1213,12 @@
@NonNull
public static AnimationOptions makeCustomAnimOptions(@NonNull String packageName,
@AnimRes int enterResId, @AnimRes int changeResId, @AnimRes int exitResId,
- @ColorInt int backgroundColor, boolean overrideTaskTransition) {
+ boolean overrideTaskTransition) {
AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
options.mPackageName = packageName;
options.mEnterResId = enterResId;
options.mChangeResId = changeResId;
options.mExitResId = exitResId;
- options.mBackgroundColor = backgroundColor;
options.mOverrideTaskTransition = overrideTaskTransition;
return options;
}
@@ -1313,10 +1294,6 @@
return mExitResId;
}
- public @ColorInt int getBackgroundColor() {
- return mBackgroundColor;
- }
-
public boolean getOverrideTaskTransition() {
return mOverrideTaskTransition;
}
@@ -1352,7 +1329,6 @@
dest.writeInt(mEnterResId);
dest.writeInt(mChangeResId);
dest.writeInt(mExitResId);
- dest.writeInt(mBackgroundColor);
dest.writeBoolean(mOverrideTaskTransition);
dest.writeString(mPackageName);
mTransitionBounds.writeToParcel(dest, flags);
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index de49eae..9f768f0 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -7,13 +7,6 @@
flag {
namespace: "windowing_sdk"
- name: "activity_embedding_overlay_presentation_flag"
- description: "Whether the overlay presentation feature is enabled"
- bug: "293370683"
-}
-
-flag {
- namespace: "windowing_sdk"
name: "task_fragment_system_organizer_flag"
description: "Whether the TaskFragment system organizer feature is enabled"
bug: "284050041"
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index b0fadb0..e141f70 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -107,7 +107,6 @@
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
import java.util.ArrayList;
import java.util.Collections;
@@ -421,9 +420,6 @@
public void setActivityStackAttributesCalculator(
@NonNull Function<ActivityStackAttributesCalculatorParams, ActivityStackAttributes>
calculator) {
- if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
- return;
- }
synchronized (mLock) {
mActivityStackAttributesCalculator = calculator;
}
@@ -431,9 +427,6 @@
@Override
public void clearActivityStackAttributesCalculator() {
- if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
- return;
- }
synchronized (mLock) {
mActivityStackAttributesCalculator = null;
}
@@ -623,9 +616,6 @@
@Override
public void updateActivityStackAttributes(@NonNull ActivityStack.Token activityStackToken,
@NonNull ActivityStackAttributes attributes) {
- if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
- return;
- }
Objects.requireNonNull(activityStackToken);
Objects.requireNonNull(attributes);
@@ -652,9 +642,6 @@
@Nullable
public ParentContainerInfo getParentContainerInfo(
@NonNull ActivityStack.Token activityStackToken) {
- if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
- return null;
- }
Objects.requireNonNull(activityStackToken);
synchronized (mLock) {
final TaskFragmentContainer container = getContainer(activityStackToken.getRawToken());
@@ -670,9 +657,6 @@
@Override
@Nullable
public ActivityStack.Token getActivityStackToken(@NonNull String tag) {
- if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
- return null;
- }
Objects.requireNonNull(tag);
synchronized (mLock) {
final TaskFragmentContainer taskFragmentContainer =
@@ -3152,8 +3136,7 @@
final TaskFragmentContainer launchedInTaskFragment;
if (launchingActivity != null) {
final String overlayTag = options.getString(KEY_OVERLAY_TAG);
- if (Flags.activityEmbeddingOverlayPresentationFlag()
- && overlayTag != null) {
+ if (overlayTag != null) {
launchedInTaskFragment = createOrUpdateOverlayTaskFragmentIfNeeded(wct,
options, intent, launchingActivity);
} else {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 7ab9e2e..f2c1115 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -62,7 +62,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
import java.util.ArrayList;
import java.util.List;
@@ -465,9 +464,6 @@
void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container,
boolean isolatedNavigationEnabled) {
- if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) {
- return;
- }
if (container.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
return;
}
@@ -488,9 +484,6 @@
void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container,
boolean pinned) {
- if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) {
- return;
- }
if (container.isPinned() == pinned) {
return;
}
@@ -692,7 +685,7 @@
final TaskContainer taskContainer = container.getTaskContainer();
final int windowingMode = taskContainer.getWindowingModeForTaskFragment(relativeBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
- if (container.isOverlay() && isOverlayTransitionSupported()) {
+ if (container.isOverlay()) {
// Use the overlay transition for the overlay container if it's supported.
final TaskFragmentAnimationParams params = createOverlayAnimationParams(relativeBounds,
taskContainer.getBounds(), container);
@@ -704,10 +697,6 @@
setTaskFragmentDimOnTask(wct, fragmentToken, dimOnTask);
}
- private static boolean isOverlayTransitionSupported() {
- return Flags.activityEmbeddingOverlayPresentationFlag();
- }
-
@NonNull
private static TaskFragmentAnimationParams createOverlayAnimationParams(
@NonNull Rect relativeBounds, @NonNull Rect parentContainerBounds,
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 5b97e7e..4334a9c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -58,7 +58,6 @@
import static org.mockito.Mockito.never;
import android.app.Activity;
-import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -70,7 +69,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Size;
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
@@ -85,8 +83,6 @@
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutInfo;
-import com.android.window.flags.Flags;
-
import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
@@ -121,9 +117,6 @@
private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
new ComponentName("test", "placeholder"));
- @Rule
- public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
-
private SplitController.ActivityStartMonitor mMonitor;
private Intent mIntent;
@@ -168,8 +161,6 @@
doReturn(activityConfig).when(mActivityResources).getConfiguration();
doReturn(mHandler).when(mSplitController).getHandler();
mActivity = createMockActivity();
-
- mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
}
/** Creates a mock activity in the organizer process. */
@@ -187,44 +178,6 @@
}
@Test
- public void testStartActivity_overlayFeatureDisabled_notInvokeCreateOverlayContainer() {
- mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
-
- final Bundle optionsBundle = ActivityOptions.makeBasic().toBundle();
- optionsBundle.putString(KEY_OVERLAY_TAG, "test");
- mMonitor.onStartActivity(mActivity, mIntent, optionsBundle);
-
- verify(mSplitController, never()).createOrUpdateOverlayTaskFragmentIfNeeded(any(), any(),
- any(), any());
- }
-
- @Test
- public void testSetIsolatedNavigation_overlayFeatureDisabled_earlyReturn() {
- mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
-
- final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
-
- mSplitPresenter.setTaskFragmentIsolatedNavigation(mTransaction, container,
- !container.isIsolatedNavigationEnabled());
-
- verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
- any(IBinder.class), anyBoolean());
- }
-
- @Test
- public void testSetPinned_overlayFeatureDisabled_earlyReturn() {
- mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
-
- final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
-
- mSplitPresenter.setTaskFragmentPinned(mTransaction, container,
- !container.isPinned());
-
- verify(mSplitPresenter, never()).setTaskFragmentPinned(any(), any(IBinder.class),
- anyBoolean());
- }
-
- @Test
public void testGetAllNonFinishingOverlayContainers() {
assertThat(mSplitController.getAllNonFinishingOverlayContainers()).isEmpty();
diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decor.xml b/libs/WindowManager/Shell/res/layout/caption_window_decor.xml
index f3d2198..819d4ab 100644
--- a/libs/WindowManager/Shell/res/layout/caption_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/caption_window_decor.xml
@@ -37,20 +37,17 @@
style="@style/CaptionButtonStyle"
android:id="@+id/minimize_window"
android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/minimize_button_text"
android:background="@drawable/decor_minimize_button_dark"
android:duplicateParentState="true"/>
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/maximize_window"
android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/maximize_button_text"
android:background="@drawable/decor_maximize_button_dark"
android:duplicateParentState="true"/>
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/close_window"
- android:contentDescription="@string/close_button_text"
android:background="@drawable/decor_close_button_dark"
android:duplicateParentState="true"/>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index f576549..2179128 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -277,11 +277,13 @@
<!-- Freeform window caption strings -->
<!-- Accessibility text for the maximize window button [CHAR LIMIT=NONE] -->
- <string name="maximize_button_text">Maximize</string>
+ <string name="maximize_button_text">Maximize <xliff:g id="app_name" example="Chrome">%1$s</xliff:g></string>
+ <!-- Accessibility text for the restore window button [CHAR LIMIT=NONE] -->
+ <string name="restore_button_text">Restore <xliff:g id="app_name" example="Chrome">%1$s</xliff:g></string>
<!-- Accessibility text for the minimize window button [CHAR LIMIT=NONE] -->
- <string name="minimize_button_text">Minimize</string>
+ <string name="minimize_button_text">Minimize <xliff:g id="app_name" example="Chrome">%1$s</xliff:g></string>
<!-- Accessibility text for the close window button [CHAR LIMIT=NONE] -->
- <string name="close_button_text">Close</string>
+ <string name="close_button_text">Close <xliff:g id="app_name" example="Chrome">%1$s</xliff:g></string>
<!-- Accessibility text for the caption back button [CHAR LIMIT=NONE] -->
<string name="back_button_text">Back</string>
<!-- Accessibility text for the caption handle [CHAR LIMIT=NONE] -->
@@ -353,10 +355,14 @@
<string name="maximize_menu_talkback_action_snap_right_text">Resize window to right</string>
<!-- Accessibility action replacement for maximize menu enter maximize/restore button [CHAR LIMIT=NONE] -->
<string name="maximize_menu_talkback_action_maximize_restore_text">Maximize or restore window size</string>
- <!-- Accessibility action replacement for app header maximize/restore button [CHAR LIMIT=NONE] -->
- <string name="maximize_button_talkback_action_maximize_restore_text">Maximize or restore window size</string>
+ <!-- Accessibility action replacement for app header maximize button [CHAR LIMIT=NONE] -->
+ <string name="app_header_talkback_action_maximize_button_text">Maximize app window size</string>
+ <!-- Accessibility action replacement for app header restore button [CHAR LIMIT=NONE] -->
+ <string name="app_header_talkback_action_restore_button_text">Restore window size</string>
<!-- Accessibility action replacement for app header minimize button [CHAR LIMIT=NONE] -->
- <string name="minimize_button_talkback_action_maximize_restore_text">Minimize app window</string>
+ <string name="app_header_talkback_action_minimize_button_text">Minimize app window</string>
+ <!-- Accessibility action replacement for app header close button [CHAR LIMIT=NONE] -->
+ <string name="app_header_talkback_action_close_button_text">Close app window</string>
<!-- Accessibility text for open by default settings button [CHAR LIMIT=NONE] -->
<string name="open_by_default_settings_text">Open by default settings</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 78f5154..c3e783d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -46,7 +46,6 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
import com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationAdapter.SnapshotAdapter;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.shared.TransitionUtil;
@@ -443,7 +442,7 @@
}
}
- calculateParentBounds(change, boundsAnimationChange, parentBounds);
+ calculateParentBounds(change, parentBounds);
// There are two animations in the array. The first one is for the start leash
// (snapshot), and the second one is for the end leash (TaskFragment).
final Animation[] animations =
@@ -529,32 +528,19 @@
*/
@VisibleForTesting
static void calculateParentBounds(@NonNull TransitionInfo.Change change,
- @NonNull TransitionInfo.Change boundsAnimationChange, @NonNull Rect outParentBounds) {
- if (Flags.activityEmbeddingOverlayPresentationFlag()) {
- final Point endParentSize = change.getEndParentSize();
- if (endParentSize.equals(0, 0)) {
- return;
- }
- final Point endRelPosition = change.getEndRelOffset();
- final Point endAbsPosition = new Point(change.getEndAbsBounds().left,
- change.getEndAbsBounds().top);
- final Point parentEndAbsPosition = new Point(endAbsPosition.x - endRelPosition.x,
- endAbsPosition.y - endRelPosition.y);
- outParentBounds.set(parentEndAbsPosition.x, parentEndAbsPosition.y,
- parentEndAbsPosition.x + endParentSize.x,
- parentEndAbsPosition.y + endParentSize.y);
- } else {
- // The TaskFragment may be enter/exit split, so we take the union of both as
- // the parent size.
- outParentBounds.union(boundsAnimationChange.getStartAbsBounds());
- outParentBounds.union(boundsAnimationChange.getEndAbsBounds());
- if (boundsAnimationChange != change) {
- // Union the change starting bounds in case the activity is resized and
- // reparented to a TaskFragment. In that case, the TaskFragment may not cover
- // the activity's starting bounds.
- outParentBounds.union(change.getStartAbsBounds());
- }
+ @NonNull Rect outParentBounds) {
+ final Point endParentSize = change.getEndParentSize();
+ if (endParentSize.equals(0, 0)) {
+ return;
}
+ final Point endRelPosition = change.getEndRelOffset();
+ final Point endAbsPosition = new Point(change.getEndAbsBounds().left,
+ change.getEndAbsBounds().top);
+ final Point parentEndAbsPosition = new Point(endAbsPosition.x - endRelPosition.x,
+ endAbsPosition.y - endRelPosition.y);
+ outParentBounds.set(parentEndAbsPosition.x, parentEndAbsPosition.y,
+ parentEndAbsPosition.x + endParentSize.x,
+ parentEndAbsPosition.y + endParentSize.y);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 99f05283..56de48d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -49,6 +49,7 @@
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.windowdecor.tiling.SnapEventHandler;
/**
* Animated visual indicator for Desktop Mode windowing transitions.
@@ -98,7 +99,9 @@
return FROM_SPLIT;
} else if (taskInfo.isFreeform()) {
return FROM_FREEFORM;
- } else return null;
+ } else {
+ return null;
+ }
}
}
@@ -110,6 +113,7 @@
private IndicatorType mCurrentType;
private final DragStartState mDragStartState;
+ private final SnapEventHandler mSnapEventHandler;
public DesktopModeVisualIndicator(@ShellDesktopThread ShellExecutor desktopExecutor,
@ShellMainThread ShellExecutor mainExecutor,
@@ -118,18 +122,20 @@
Context context, SurfaceControl taskSurface,
RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer,
DragStartState dragStartState,
- @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider) {
+ @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider,
+ SnapEventHandler snapEventHandler) {
SurfaceControl.Builder builder = new SurfaceControl.Builder();
taskDisplayAreaOrganizer.attachToDisplayArea(taskInfo.displayId, builder);
mVisualIndicatorViewContainer = new VisualIndicatorViewContainer(
DesktopModeFlags.ENABLE_DESKTOP_INDICATOR_IN_SEPARATE_THREAD_BUGFIX.isTrue()
? desktopExecutor : mainExecutor,
- mainExecutor, builder, syncQueue, bubbleBoundsProvider);
+ mainExecutor, builder, syncQueue, bubbleBoundsProvider, snapEventHandler);
mTaskInfo = taskInfo;
mDisplayController = displayController;
mContext = context;
mCurrentType = NO_INDICATOR;
mDragStartState = dragStartState;
+ mSnapEventHandler = snapEventHandler;
mVisualIndicatorViewContainer.createView(
mContext,
mDisplayController.getDisplay(mTaskInfo.displayId),
@@ -143,7 +149,8 @@
public void fadeOutIndicator(
@NonNull Runnable callback) {
mVisualIndicatorViewContainer.fadeOutIndicator(
- mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType, callback
+ mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType, callback,
+ mTaskInfo.displayId, mSnapEventHandler
);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 1c88056..2d9aea0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -3051,6 +3051,7 @@
rootTaskDisplayAreaOrganizer,
dragStartState,
bubbleController.getOrNull()?.bubbleDropTargetBoundsProvider,
+ snapEventHandler,
)
if (visualIndicator == null) visualIndicator = indicator
return indicator.updateIndicatorType(PointF(inputX, taskTop))
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index cb23180..d396d8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -1138,8 +1138,11 @@
.spring(FloatProperties.RECT_HEIGHT, endBounds.height().toFloat(), sizeSpringConfig)
.addUpdateListener { animBounds, _ ->
val animFraction =
- (animBounds.width() - startBounds.width()).toFloat() /
- (endBounds.width() - startBounds.width())
+ getAnimationFraction(
+ startBounds = startBounds,
+ endBounds = endBounds,
+ animBounds = animBounds,
+ )
val animScale = startScale + animFraction * (1 - startScale)
// Freeform animation starts with freeform animation offset relative to the commit
// animation and plays until the commit animation ends. For instance:
@@ -1191,16 +1194,38 @@
.start()
}
- private fun logV(msg: String, vararg arguments: Any?) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
- }
-
- private fun logE(msg: String, vararg arguments: Any?) {
- ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
- }
-
companion object {
private const val TAG = "SpringDragToDesktopTransitionHandler"
+
+ @VisibleForTesting
+ fun getAnimationFraction(startBounds: Rect, endBounds: Rect, animBounds: Rect): Float {
+ if (startBounds.width() != endBounds.width()) {
+ return (animBounds.width() - startBounds.width()).toFloat() /
+ (endBounds.width() - startBounds.width())
+ }
+ if (startBounds.height() != endBounds.height()) {
+ return (animBounds.height() - startBounds.height()).toFloat() /
+ (endBounds.height() - startBounds.height())
+ }
+ logW(
+ "same start and end sizes, returning 0: " +
+ "startBounds=$startBounds, endBounds=$endBounds, animBounds=$animBounds"
+ )
+ return 0f
+ }
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private fun logE(msg: String, vararg arguments: Any?) {
+ ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
/** The freeform tasks initial scale when committing the drag-to-desktop gesture. */
private val FREEFORM_TASKS_INITIAL_SCALE =
propertyValue("freeform_tasks_initial_scale", scale = 100f, default = 0.9f)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
index 2317274..919e816 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
@@ -44,6 +44,7 @@
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider
import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
+import com.android.wm.shell.windowdecor.tiling.SnapEventHandler
/**
* Container for the view / viewhost of the indicator, ensuring it is created / animated off the
@@ -60,6 +61,7 @@
private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory =
object : SurfaceControlViewHostFactory {},
private val bubbleBoundsProvider: BubbleDropTargetBoundsProvider?,
+ private val snapEventHandler: SnapEventHandler,
) {
@VisibleForTesting var indicatorView: View? = null
private var indicatorViewHost: SurfaceControlViewHost? = null
@@ -164,9 +166,15 @@
displayController.getDisplayLayout(taskInfo.displayId)
?: error("Expected to find DisplayLayout for taskId${taskInfo.taskId}.")
if (currentType == IndicatorType.NO_INDICATOR) {
- fadeInIndicator(layout, newType)
+ fadeInIndicator(layout, newType, taskInfo.displayId, snapEventHandler)
} else if (newType == IndicatorType.NO_INDICATOR) {
- fadeOutIndicator(layout, currentType, /* finishCallback= */ null)
+ fadeOutIndicator(
+ layout,
+ currentType,
+ /* finishCallback= */ null,
+ taskInfo.displayId,
+ snapEventHandler,
+ )
} else {
val animStartType = IndicatorType.valueOf(currentType.name)
val animator =
@@ -177,6 +185,8 @@
animStartType,
newType,
bubbleBoundsProvider,
+ taskInfo.displayId,
+ snapEventHandler,
)
} ?: return@execute
animator.start()
@@ -188,12 +198,24 @@
* Fade indicator in as provided type. Animator fades it in while expanding the bounds outwards.
*/
@VisibleForTesting
- fun fadeInIndicator(layout: DisplayLayout, type: IndicatorType) {
+ fun fadeInIndicator(
+ layout: DisplayLayout,
+ type: IndicatorType,
+ displayId: Int,
+ snapEventHandler: SnapEventHandler,
+ ) {
desktopExecutor.assertCurrentThread()
indicatorView?.let {
it.setBackgroundResource(R.drawable.desktop_windowing_transition_background)
val animator =
- VisualIndicatorAnimator.fadeBoundsIn(it, type, layout, bubbleBoundsProvider)
+ VisualIndicatorAnimator.fadeBoundsIn(
+ it,
+ type,
+ layout,
+ bubbleBoundsProvider,
+ displayId,
+ snapEventHandler,
+ )
animator.start()
}
}
@@ -207,6 +229,8 @@
layout: DisplayLayout,
currentType: IndicatorType,
finishCallback: Runnable?,
+ displayId: Int,
+ snapEventHandler: SnapEventHandler,
) {
if (currentType == IndicatorType.NO_INDICATOR) {
// In rare cases, fade out can be requested before the indicator has determined its
@@ -223,6 +247,8 @@
animStartType,
layout,
bubbleBoundsProvider,
+ displayId,
+ snapEventHandler,
)
animator.addListener(
object : AnimatorListenerAdapter() {
@@ -328,8 +354,17 @@
type: IndicatorType,
displayLayout: DisplayLayout,
bubbleBoundsProvider: BubbleDropTargetBoundsProvider?,
+ displayId: Int,
+ snapEventHandler: SnapEventHandler,
): VisualIndicatorAnimator {
- val endBounds = getIndicatorBounds(displayLayout, type, bubbleBoundsProvider)
+ val endBounds =
+ getIndicatorBounds(
+ displayLayout,
+ type,
+ bubbleBoundsProvider,
+ displayId,
+ snapEventHandler,
+ )
val startBounds = getMinBounds(endBounds)
view.background.bounds = startBounds
@@ -345,11 +380,19 @@
type: IndicatorType,
displayLayout: DisplayLayout,
bubbleBoundsProvider: BubbleDropTargetBoundsProvider?,
+ displayId: Int,
+ snapEventHandler: SnapEventHandler,
): VisualIndicatorAnimator {
- val startBounds = getIndicatorBounds(displayLayout, type, bubbleBoundsProvider)
+ val startBounds =
+ getIndicatorBounds(
+ displayLayout,
+ type,
+ bubbleBoundsProvider,
+ displayId,
+ snapEventHandler,
+ )
val endBounds = getMinBounds(startBounds)
view.background.bounds = startBounds
-
val animator = VisualIndicatorAnimator(view, startBounds, endBounds)
animator.interpolator = DecelerateInterpolator()
setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_FADE_OUT_ANIM)
@@ -375,9 +418,25 @@
origType: IndicatorType,
newType: IndicatorType,
bubbleBoundsProvider: BubbleDropTargetBoundsProvider?,
+ displayId: Int,
+ snapEventHandler: SnapEventHandler,
): VisualIndicatorAnimator {
- val startBounds = getIndicatorBounds(displayLayout, origType, bubbleBoundsProvider)
- val endBounds = getIndicatorBounds(displayLayout, newType, bubbleBoundsProvider)
+ val startBounds =
+ getIndicatorBounds(
+ displayLayout,
+ origType,
+ bubbleBoundsProvider,
+ displayId,
+ snapEventHandler,
+ )
+ val endBounds =
+ getIndicatorBounds(
+ displayLayout,
+ newType,
+ bubbleBoundsProvider,
+ displayId,
+ snapEventHandler,
+ )
val animator = VisualIndicatorAnimator(view, startBounds, endBounds)
animator.interpolator = DecelerateInterpolator()
setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_NO_CHANGE_ANIM)
@@ -389,6 +448,8 @@
layout: DisplayLayout,
type: IndicatorType,
bubbleBoundsProvider: BubbleDropTargetBoundsProvider?,
+ displayId: Int,
+ snapEventHandler: SnapEventHandler,
): Rect {
val desktopStableBounds = Rect()
layout.getStableBounds(desktopStableBounds)
@@ -417,21 +478,25 @@
)
}
- IndicatorType.TO_SPLIT_LEFT_INDICATOR ->
+ IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
+ val currentLeftBounds = snapEventHandler.getLeftSnapBoundsIfTiled(displayId)
return Rect(
padding,
padding,
- desktopStableBounds.width() / 2 - padding,
+ currentLeftBounds.right - padding,
desktopStableBounds.height(),
)
-
- IndicatorType.TO_SPLIT_RIGHT_INDICATOR ->
+ }
+ IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
+ val currentRightBounds =
+ snapEventHandler.getRightSnapBoundsIfTiled(displayId)
return Rect(
- desktopStableBounds.width() / 2 + padding,
+ currentRightBounds.left + padding,
padding,
desktopStableBounds.width() - padding,
desktopStableBounds.height(),
)
+ }
IndicatorType.TO_BUBBLE_LEFT_INDICATOR ->
return bubbleBoundsProvider?.getBubbleBarExpandedViewDropTargetBounds(
/* onLeft= */ true
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index d9afd15..0d773ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -159,6 +159,8 @@
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
+import org.jetbrains.annotations.NotNull;
+
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
import kotlinx.coroutines.MainCoroutineDispatcher;
@@ -935,6 +937,18 @@
return mDesktopTilingDecorViewModel.moveTaskToFrontIfTiled(taskInfo);
}
+ @Override
+ @NotNull
+ public Rect getLeftSnapBoundsIfTiled(int displayId) {
+ return mDesktopTilingDecorViewModel.getLeftSnapBoundsIfTiled(displayId);
+ }
+
+ @Override
+ @NotNull
+ public Rect getRightSnapBoundsIfTiled(int displayId) {
+ return mDesktopTilingDecorViewModel.getRightSnapBoundsIfTiled(displayId);
+ }
+
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
View.OnGenericMotionListener, DragDetector.MotionEventHandler {
@@ -974,7 +988,7 @@
final int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
final long appHandleHoldToDragDuration =
DesktopModeFlags.ENABLE_HOLD_TO_DRAG_APP_HANDLE.isTrue()
- ? APP_HANDLE_HOLD_TO_DRAG_DURATION_MS : 0;
+ ? APP_HANDLE_HOLD_TO_DRAG_DURATION_MS : 0;
mHandleDragDetector = new DragDetector(this, appHandleHoldToDragDuration,
touchSlop);
mHeaderDragDetector = new DragDetector(this, APP_HEADER_HOLD_TO_DRAG_DURATION_MS,
@@ -1027,7 +1041,7 @@
decoration.mTaskInfo.userId);
if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()
&& desktopRepository.isTaskInFullImmersiveState(
- decoration.mTaskInfo.taskId)) {
+ decoration.mTaskInfo.taskId)) {
// Task is in immersive and should exit.
onEnterOrExitImmersive(decoration.mTaskInfo);
} else {
@@ -1321,6 +1335,7 @@
/**
* Perform a task size toggle on release of the double-tap, assuming no drag event
* was handled during the double-tap.
+ *
* @param e The motion event that occurred during the double-tap gesture.
* @return true if the event should be consumed, false if not
*/
@@ -1346,6 +1361,7 @@
class EventReceiver extends InputEventReceiver {
private InputMonitor mInputMonitor;
private int mTasksOnDisplay;
+
EventReceiver(InputMonitor inputMonitor, InputChannel channel, Looper looper) {
super(channel, looper);
mInputMonitor = inputMonitor;
@@ -1397,6 +1413,7 @@
/**
* Check if an EventReceiver exists on a particular display.
* If it does, increment its task count. Otherwise, create one for that display.
+ *
* @param displayId the display to check against
*/
private void incrementEventReceiverTasks(int displayId) {
@@ -1902,7 +1919,7 @@
@Override
public void onAnimationStart(int taskId, Transaction t, Rect bounds) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
- if (decoration == null) {
+ if (decoration == null) {
t.apply();
return;
}
@@ -1986,15 +2003,15 @@
return activityTaskManager.getRecentTasks(Integer.MAX_VALUE,
ActivityManager.RECENT_WITH_EXCLUDED,
info.userId).getList().stream().filter(
- recentTaskInfo -> {
- if (recentTaskInfo.taskId == info.taskId) {
- return false;
- }
- final String recentTaskPackageName =
- ComponentUtils.getPackageName(recentTaskInfo);
- return packageName != null
- && packageName.equals(recentTaskPackageName);
- }
+ recentTaskInfo -> {
+ if (recentTaskInfo.taskId == info.taskId) {
+ return false;
+ }
+ final String recentTaskPackageName =
+ ComponentUtils.getPackageName(recentTaskInfo);
+ return packageName != null
+ && packageName.equals(recentTaskPackageName);
+ }
).toList().size();
} catch (RemoteException e) {
throw new RuntimeException(e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
index 581d186..bdde096d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -39,10 +39,7 @@
private const val OPEN_MAXIMIZE_MENU_DELAY_ON_HOVER_MS = 350
private const val MAX_DRAWABLE_ALPHA = 255
-class MaximizeButtonView(
- context: Context,
- attrs: AttributeSet
-) : FrameLayout(context, attrs) {
+class MaximizeButtonView(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {
lateinit var onHoverAnimationFinishedListener: () -> Unit
private val hoverProgressAnimatorSet = AnimatorSet()
var hoverDisabled = false
@@ -53,10 +50,6 @@
(stubProgressBarContainer.inflate() as FrameLayout)
.requireViewById(R.id.progress_bar)
}
- private val maximizeButtonText =
- context.resources.getString(R.string.desktop_mode_maximize_menu_maximize_button_text)
- private val restoreButtonText =
- context.resources.getString(R.string.desktop_mode_maximize_menu_restore_button_text)
init {
LayoutInflater.from(context).inflate(R.layout.maximize_menu_button, this, true)
@@ -158,12 +151,6 @@
/** Set the drawable resource to use for the maximize button. */
fun setIcon(@DrawableRes icon: Int) {
maximizeWindow.setImageResource(icon)
- when (icon) {
- R.drawable.decor_desktop_mode_immersive_or_maximize_exit_button_dark ->
- maximizeWindow.contentDescription = restoreButtonText
- R.drawable.decor_desktop_mode_maximize_button_dark ->
- maximizeWindow.contentDescription = maximizeButtonText
- }
}
companion object {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
index 8747f63..ee5d0e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
@@ -25,6 +25,7 @@
import android.window.WindowContainerTransaction
import androidx.core.util.valueIterator
import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayChangeController
@@ -148,4 +149,45 @@
if ((fromRotation % 2 == toRotation % 2)) return
tilingTransitionHandlerByDisplayId.get(displayId)?.resetTilingSession()
}
+
+ fun getRightSnapBoundsIfTiled(displayId: Int): Rect {
+ val tilingBounds =
+ tilingTransitionHandlerByDisplayId.get(displayId)?.getRightSnapBoundsIfTiled()
+ if (tilingBounds != null) {
+ return tilingBounds
+ }
+ val displayLayout = displayController.getDisplayLayout(displayId)
+ val stableBounds = Rect()
+ displayLayout?.getStableBounds(stableBounds)
+ val snapBounds =
+ Rect(
+ stableBounds.left +
+ stableBounds.width() / 2 +
+ context.resources.getDimensionPixelSize(R.dimen.split_divider_bar_width) / 2,
+ stableBounds.top,
+ stableBounds.right,
+ stableBounds.bottom,
+ )
+ return snapBounds
+ }
+
+ fun getLeftSnapBoundsIfTiled(displayId: Int): Rect {
+ val tilingBounds =
+ tilingTransitionHandlerByDisplayId.get(displayId)?.getLeftSnapBoundsIfTiled()
+ if (tilingBounds != null) {
+ return tilingBounds
+ }
+ val displayLayout = displayController.getDisplayLayout(displayId)
+ val stableBounds = Rect()
+ displayLayout?.getStableBounds(stableBounds)
+ val snapBounds =
+ Rect(
+ stableBounds.left,
+ stableBounds.top,
+ stableBounds.left + stableBounds.width() / 2 -
+ context.resources.getDimensionPixelSize(R.dimen.split_divider_bar_width) / 2,
+ stableBounds.bottom,
+ )
+ return snapBounds
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 666d4bd..9833325 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -36,7 +36,6 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.launcher3.icons.BaseIconFactory
import com.android.window.flags.Flags
-import com.android.wm.shell.shared.FocusTransitionListener
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
@@ -50,6 +49,7 @@
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.shared.FocusTransitionListener
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.FocusTransitionObserver
@@ -112,7 +112,7 @@
position: SnapPosition,
currentBounds: Rect,
): Boolean {
- val destinationBounds = getSnapBounds(taskInfo, position)
+ val destinationBounds = getSnapBounds(position)
val resizeMetadata =
AppResizingHelper(
taskInfo,
@@ -502,9 +502,11 @@
}
// Overriding FocusTransitionListener
- override fun onFocusedTaskChanged(taskId: Int,
- isFocusedOnDisplay: Boolean,
- isFocusedGlobally: Boolean) {
+ override fun onFocusedTaskChanged(
+ taskId: Int,
+ isFocusedOnDisplay: Boolean,
+ isFocusedGlobally: Boolean,
+ ) {
if (!Flags.enableDisplayFocusInShellTransitions()) return
moveTiledPairToFront(taskId, isFocusedOnDisplay)
}
@@ -512,7 +514,7 @@
// Only called if [taskInfo] relates to a focused task
private fun isTilingRefocused(taskId: Int): Boolean {
return taskId == leftTaskResizingHelper?.taskInfo?.taskId ||
- taskId == rightTaskResizingHelper?.taskInfo?.taskId
+ taskId == rightTaskResizingHelper?.taskInfo?.taskId
}
private fun buildTiledTasksMoveToFront(leftOnTop: Boolean): WindowContainerTransaction {
@@ -623,26 +625,24 @@
val t = transactionSupplier.get()
if (!Flags.enableDisplayFocusInShellTransitions()) isTilingFocused = true
if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
- desktopTilingDividerWindowManager?.onRelativeLeashChanged(
- leftTiledTask.getLeash(),
- t,
- )
+ desktopTilingDividerWindowManager?.onRelativeLeashChanged(leftTiledTask.getLeash(), t)
}
if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
- desktopTilingDividerWindowManager?.onRelativeLeashChanged(
- rightTiledTask.getLeash(),
- t,
- )
+ desktopTilingDividerWindowManager?.onRelativeLeashChanged(rightTiledTask.getLeash(), t)
}
- transitions.startTransition(
- TRANSIT_TO_FRONT,
- buildTiledTasksMoveToFront(isLeftOnTop),
- null,
- )
+ transitions.startTransition(TRANSIT_TO_FRONT, buildTiledTasksMoveToFront(isLeftOnTop), null)
t.apply()
return true
}
+ fun getRightSnapBoundsIfTiled(): Rect {
+ return getSnapBounds(SnapPosition.RIGHT)
+ }
+
+ fun getLeftSnapBoundsIfTiled(): Rect {
+ return getSnapBounds(SnapPosition.LEFT)
+ }
+
private fun allTiledTasksVisible(): Boolean {
val leftTiledTask = leftTaskResizingHelper ?: return false
val rightTiledTask = rightTaskResizingHelper ?: return false
@@ -674,8 +674,8 @@
)
}
- private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect {
- val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect()
+ private fun getSnapBounds(position: SnapPosition): Rect {
+ val displayLayout = displayController.getDisplayLayout(displayId) ?: return Rect()
val stableBounds = Rect()
displayLayout.getStableBounds(stableBounds)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/SnapEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/SnapEventHandler.kt
index 52e24d6..b9d6741 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/SnapEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/SnapEventHandler.kt
@@ -40,4 +40,16 @@
/** If a task is tiled, delegate moving to front to tiling infrastructure. */
fun moveTaskToFrontIfTiled(taskInfo: RunningTaskInfo): Boolean
+
+ /**
+ * Returns the bounds of a task tiled on the left on the specified display, defaults to default
+ * snapping bounds if no task is tiled.
+ */
+ fun getLeftSnapBoundsIfTiled(displayId: Int): Rect
+
+ /**
+ * Returns the bounds of a task tiled on the right on the specified display, defaults to default
+ * snapping bounds if no task is tiled.
+ */
+ fun getRightSnapBoundsIfTiled(displayId: Int): Rect
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index 90c865e..870c894 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -37,10 +37,13 @@
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
+import android.window.DesktopModeFlags
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.ui.graphics.toArgb
import androidx.core.content.withStyledAttributes
+import androidx.core.view.ViewCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.android.internal.R.color.materialColorOnSecondaryContainer
@@ -50,9 +53,6 @@
import com.android.internal.R.color.materialColorSurfaceContainerLow
import com.android.internal.R.color.materialColorSurfaceDim
import com.android.wm.shell.R
-import android.window.DesktopModeFlags
-import androidx.core.view.ViewCompat
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import com.android.wm.shell.windowdecor.MaximizeButtonView
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.common.OPACITY_100
@@ -145,6 +145,15 @@
val appNameTextWidth: Int
get() = appNameTextView.width
+ private val a11yAnnounceTextMaximize: String =
+ context.getString(R.string.app_header_talkback_action_maximize_button_text)
+ private val a11yAnnounceTextRestore: String =
+ context.getString(R.string.app_header_talkback_action_restore_button_text)
+
+ private lateinit var sizeToggleDirection: SizeToggleDirection
+ private lateinit var a11yTextMaximize: String
+ private lateinit var a11yTextRestore: String
+
init {
captionView.setOnTouchListener(onCaptionTouchListener)
captionHandle.setOnTouchListener(onCaptionTouchListener)
@@ -163,15 +172,15 @@
val a11yActionSnapLeft = AccessibilityAction(
R.id.action_snap_left,
- context.resources.getString(R.string.desktop_mode_a11y_action_snap_left)
+ context.getString(R.string.desktop_mode_a11y_action_snap_left)
)
val a11yActionSnapRight = AccessibilityAction(
R.id.action_snap_right,
- context.resources.getString(R.string.desktop_mode_a11y_action_snap_right)
+ context.getString(R.string.desktop_mode_a11y_action_snap_right)
)
val a11yActionMaximizeRestore = AccessibilityAction(
R.id.action_maximize_restore,
- context.resources.getString(R.string.desktop_mode_a11y_action_maximize_restore)
+ context.getString(R.string.desktop_mode_a11y_action_maximize_restore)
)
captionHandle.accessibilityDelegate = object : View.AccessibilityDelegate() {
@@ -236,19 +245,19 @@
null
)
- // Update a11y announcement to say "double tap to maximize or restore window size"
- ViewCompat.replaceAccessibilityAction(
- maximizeWindowButton,
- AccessibilityActionCompat.ACTION_CLICK,
- context.getString(R.string.maximize_button_talkback_action_maximize_restore_text),
- null
- )
-
- // Update a11y announcement out to say "double tap to minimize app window"
+ // Update a11y announcement to say "double tap to minimize app window"
ViewCompat.replaceAccessibilityAction(
minimizeWindowButton,
AccessibilityActionCompat.ACTION_CLICK,
- context.getString(R.string.minimize_button_talkback_action_maximize_restore_text),
+ context.getString(R.string.app_header_talkback_action_minimize_button_text),
+ null
+ )
+
+ // Update a11y announcement to say "double tap to close app window"
+ ViewCompat.replaceAccessibilityAction(
+ closeWindowButton,
+ AccessibilityActionCompat.ACTION_CLICK,
+ context.getString(R.string.app_header_talkback_action_close_button_text),
null
)
}
@@ -268,6 +277,26 @@
appNameTextView.text = name
openMenuButton.contentDescription =
context.getString(R.string.desktop_mode_app_header_chip_text, name)
+
+ closeWindowButton.contentDescription = context.getString(R.string.close_button_text, name)
+ minimizeWindowButton.contentDescription =
+ context.getString(R.string.minimize_button_text, name)
+
+ a11yTextMaximize = context.getString(R.string.maximize_button_text, name)
+ a11yTextRestore = context.getString(R.string.restore_button_text, name)
+
+ updateMaximizeButtonContentDescription()
+ }
+
+ private fun updateMaximizeButtonContentDescription() {
+ if (this::a11yTextRestore.isInitialized &&
+ this::a11yTextMaximize.isInitialized &&
+ this::sizeToggleDirection.isInitialized) {
+ maximizeWindowButton.contentDescription = when (sizeToggleDirection) {
+ SizeToggleDirection.MAXIMIZE -> a11yTextMaximize
+ SizeToggleDirection.RESTORE -> a11yTextRestore
+ }
+ }
}
/** Sets the app's icon in the header. */
@@ -388,7 +417,34 @@
drawableInsets = maximizeDrawableInsets
)
)
- setIcon(getMaximizeButtonIcon(isTaskMaximized, inFullImmersiveState))
+ val icon = getMaximizeButtonIcon(isTaskMaximized, inFullImmersiveState)
+ setIcon(icon)
+
+ when (icon) {
+ R.drawable.decor_desktop_mode_immersive_or_maximize_exit_button_dark -> {
+ sizeToggleDirection = SizeToggleDirection.RESTORE
+
+ // Update a11y announcement to say "double tap to maximize app window size"
+ ViewCompat.replaceAccessibilityAction(
+ maximizeWindowButton,
+ AccessibilityActionCompat.ACTION_CLICK,
+ a11yAnnounceTextRestore,
+ null
+ )
+ }
+ R.drawable.decor_desktop_mode_maximize_button_dark -> {
+ sizeToggleDirection = SizeToggleDirection.MAXIMIZE
+
+ // Update a11y announcement to say "double tap to restore app window size"
+ ViewCompat.replaceAccessibilityAction(
+ maximizeWindowButton,
+ AccessibilityActionCompat.ACTION_CLICK,
+ a11yAnnounceTextMaximize,
+ null
+ )
+ }
+ }
+ updateMaximizeButtonContentDescription()
}
// Close button.
closeWindowButton.apply {
@@ -625,6 +681,10 @@
)
}
+ private enum class SizeToggleDirection {
+ MAXIMIZE, RESTORE
+ }
+
private data class DrawableInsets(val l: Int, val t: Int, val r: Int, val b: Int) {
constructor(vertical: Int = 0, horizontal: Int = 0) :
this(horizontal, vertical, horizontal, vertical)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index d4d8d93..bc965b9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -39,14 +39,12 @@
import android.annotation.NonNull;
import android.graphics.Point;
import android.graphics.Rect;
-import android.platform.test.annotations.EnableFlags;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.window.TransitionInfo;
import androidx.test.filters.SmallTest;
-import com.android.window.flags.Flags;
import com.android.wm.shell.transition.TransitionInfoBuilder;
import com.google.testing.junit.testparameterinjector.TestParameter;
@@ -135,8 +133,8 @@
.addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, TRANSIT_OPEN))
.build();
info.getChanges().getFirst().setAnimationOptions(TransitionInfo.AnimationOptions
- .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
- 0 /* backgroundColor */, false /* overrideTaskTransition */));
+ .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* changeResId */,
+ 0 /* exitResId */, false /* overrideTaskTransition */));
final Animator animator = mAnimRunner.createAnimator(
info, mStartTransaction, mFinishTransaction,
() -> mFinishCallback.onTransitionFinished(null /* wct */),
@@ -146,12 +144,9 @@
assertEquals(0, animator.getDuration());
}
- // TODO(b/243518738): Rewrite with TestParameter
- @EnableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
@Test
public void testCalculateParentBounds_flagEnabled_emptyParentSize() {
TransitionInfo.Change change;
- final TransitionInfo.Change stubChange = createChange(0 /* flags */);
final Rect actualParentBounds = new Rect();
change = prepareChangeForParentBoundsCalculationTest(
new Point(0, 0) /* endRelOffset */,
@@ -159,17 +154,15 @@
new Point() /* endParentSize */
);
- calculateParentBounds(change, stubChange, actualParentBounds);
+ calculateParentBounds(change, actualParentBounds);
assertTrue("Parent bounds must be empty because end parent size is not set.",
actualParentBounds.isEmpty());
}
- @EnableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
@Test
public void testCalculateParentBounds_flagEnabled(
@TestParameter ParentBoundsTestParameters params) {
- final TransitionInfo.Change stubChange = createChange(0 /*flags*/);
final Rect parentBounds = params.getParentBounds();
final Rect endAbsBounds = params.getEndAbsBounds();
final TransitionInfo.Change change = prepareChangeForParentBoundsCalculationTest(
@@ -178,7 +171,7 @@
endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
final Rect actualParentBounds = new Rect();
- calculateParentBounds(change, stubChange, actualParentBounds);
+ calculateParentBounds(change, actualParentBounds);
assertEquals(parentBounds, actualParentBounds);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index 53a13d0..da4543b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -195,8 +195,8 @@
final TransitionInfo.Change change = info.getChanges().getFirst();
change.setAnimationOptions(TransitionInfo.AnimationOptions
- .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
- 0 /* backgroundColor */, false /* overrideTaskTransition */));
+ .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* changeResId */,
+ 0 /* exitResId */, false /* overrideTaskTransition */));
assertTrue(mController.shouldAnimate(info));
change.setAnimationOptions(TransitionInfo.AnimationOptions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index 20d50aa..dcc9e24 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -36,6 +36,7 @@
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider
+import com.android.wm.shell.windowdecor.tiling.SnapEventHandler
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -67,6 +68,7 @@
@Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@Mock private lateinit var displayLayout: DisplayLayout
@Mock private lateinit var bubbleBoundsProvider: BubbleDropTargetBoundsProvider
+ @Mock private lateinit var snapEventHandler: SnapEventHandler
private lateinit var visualIndicator: DesktopModeVisualIndicator
private val desktopExecutor = TestShellExecutor()
@@ -356,6 +358,7 @@
taskDisplayAreaOrganizer,
dragStartState,
bubbleBoundsProvider,
+ snapEventHandler,
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 85f6cd3..de55db8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -8,6 +8,7 @@
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WindowingMode
import android.graphics.PointF
+import android.graphics.Rect
import android.os.IBinder
import android.os.SystemProperties
import android.testing.AndroidTestingRunner
@@ -36,6 +37,7 @@
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
+import com.google.common.truth.Truth.assertThat
import java.util.Optional
import java.util.function.Supplier
import junit.framework.Assert.assertEquals
@@ -694,6 +696,50 @@
.cancel(eq(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD))
}
+ @Test
+ fun getAnimationFraction_returnsFraction() {
+ val fraction =
+ SpringDragToDesktopTransitionHandler.getAnimationFraction(
+ startBounds = Rect(0, 0, 0, 0),
+ endBounds = Rect(0, 0, 10, 10),
+ animBounds = Rect(0, 0, 5, 5),
+ )
+ assertThat(fraction).isWithin(TOLERANCE).of(0.5f)
+ }
+
+ @Test
+ fun getAnimationFraction_animBoundsSameAsEnd_returnsOne() {
+ val fraction =
+ SpringDragToDesktopTransitionHandler.getAnimationFraction(
+ startBounds = Rect(0, 0, 0, 0),
+ endBounds = Rect(0, 0, 10, 10),
+ animBounds = Rect(0, 0, 10, 10),
+ )
+ assertThat(fraction).isWithin(TOLERANCE).of(1f)
+ }
+
+ @Test
+ fun getAnimationFraction_startAndEndBoundsSameWidth_usesHeight() {
+ val fraction =
+ SpringDragToDesktopTransitionHandler.getAnimationFraction(
+ startBounds = Rect(0, 0, 10, 10),
+ endBounds = Rect(0, 0, 10, 30),
+ animBounds = Rect(0, 0, 10, 25),
+ )
+ assertThat(fraction).isWithin(TOLERANCE).of(0.75f)
+ }
+
+ @Test
+ fun getAnimationFraction_startAndEndBoundsSame_returnsZero() {
+ val fraction =
+ SpringDragToDesktopTransitionHandler.getAnimationFraction(
+ startBounds = Rect(0, 0, 10, 10),
+ endBounds = Rect(0, 0, 10, 10),
+ animBounds = Rect(0, 0, 10, 25),
+ )
+ assertThat(fraction).isWithin(TOLERANCE).of(0f)
+ }
+
private fun startDrag(
handler: DragToDesktopTransitionHandler,
task: RunningTaskInfo = createTask(),
@@ -826,4 +872,8 @@
private fun systemPropertiesKey(name: String) =
"${SpringDragToDesktopTransitionHandler.SYSTEM_PROPERTIES_GROUP}.$name"
+
+ private companion object {
+ private const val TOLERANCE = 1e-5f
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
index 79b0f1c..4c8cb38 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
@@ -38,6 +38,7 @@
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider
import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
+import com.android.wm.shell.windowdecor.tiling.SnapEventHandler
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.Before
@@ -71,6 +72,7 @@
@Mock private lateinit var mockSurfaceControlViewHostFactory: SurfaceControlViewHostFactory
@Mock private lateinit var mockBackground: LayerDrawable
@Mock private lateinit var bubbleDropTargetBoundsProvider: BubbleDropTargetBoundsProvider
+ @Mock private lateinit var snapEventHandler: SnapEventHandler
private val taskInfo: RunningTaskInfo = createTaskInfo()
private val mainExecutor = TestShellExecutor()
private val desktopExecutor = TestShellExecutor()
@@ -81,6 +83,8 @@
whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
(i.arguments.first() as Rect).set(DISPLAY_BOUNDS)
}
+ whenever(snapEventHandler.getRightSnapBoundsIfTiled(any())).thenReturn(Rect(1, 2, 3, 4))
+ whenever(snapEventHandler.getLeftSnapBoundsIfTiled(any())).thenReturn(Rect(5, 6, 7, 8))
whenever(mockSurfaceControlViewHostFactory.create(any(), any(), any()))
.thenReturn(mock(SurfaceControlViewHost::class.java))
}
@@ -117,7 +121,7 @@
DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
)
desktopExecutor.flushAll()
- verify(spyViewContainer).fadeInIndicator(any(), any())
+ verify(spyViewContainer).fadeInIndicator(any(), any(), any(), any())
}
@Test
@@ -135,6 +139,8 @@
any(),
eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
anyOrNull(),
+ eq(taskInfo.displayId),
+ eq(snapEventHandler),
)
}
@@ -167,6 +173,8 @@
DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
displayLayout,
bubbleDropTargetBoundsProvider,
+ taskInfo.displayId,
+ snapEventHandler,
)
}
assertThat(animator?.indicatorStartBounds).isEqualTo(Rect(15, 15, 985, 985))
@@ -174,6 +182,46 @@
}
@Test
+ fun testFadeInBoundsCalculationForLeftSnap() {
+ val spyIndicator = setupSpyViewContainer()
+ val animator =
+ spyIndicator.indicatorView?.let {
+ VisualIndicatorViewContainer.VisualIndicatorAnimator.fadeBoundsIn(
+ it,
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR,
+ displayLayout,
+ bubbleDropTargetBoundsProvider,
+ taskInfo.displayId,
+ snapEventHandler,
+ )
+ }
+
+ // Right bound is the same as whatever right bound snapEventHandler returned minus padding,
+ // in this case, the right bound for the left app is 7.
+ assertThat(animator?.indicatorEndBounds).isEqualTo(Rect(0, 0, 7, 1000))
+ }
+
+ @Test
+ fun testFadeInBoundsCalculationForRightSnap() {
+ val spyIndicator = setupSpyViewContainer()
+ val animator =
+ spyIndicator.indicatorView?.let {
+ VisualIndicatorViewContainer.VisualIndicatorAnimator.fadeBoundsIn(
+ it,
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
+ displayLayout,
+ bubbleDropTargetBoundsProvider,
+ taskInfo.displayId,
+ snapEventHandler,
+ )
+ }
+
+ // Left bound is the same as whatever left bound snapEventHandler returned plus padding
+ // in this case, the left bound of the right app is 1.
+ assertThat(animator?.indicatorEndBounds).isEqualTo(Rect(1, 0, 1000, 1000))
+ }
+
+ @Test
fun testFadeOutBoundsCalculation() {
val spyIndicator = setupSpyViewContainer()
val animator =
@@ -183,6 +231,8 @@
DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
displayLayout,
bubbleDropTargetBoundsProvider,
+ taskInfo.displayId,
+ snapEventHandler,
)
}
assertThat(animator?.indicatorStartBounds).isEqualTo(Rect(0, 0, 1000, 1000))
@@ -199,6 +249,8 @@
DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR,
bubbleDropTargetBoundsProvider,
+ taskInfo.displayId,
+ snapEventHandler,
)
// Test desktop to split-right bounds.
animator =
@@ -208,6 +260,8 @@
DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
bubbleDropTargetBoundsProvider,
+ taskInfo.displayId,
+ snapEventHandler,
)
}
@@ -220,6 +274,7 @@
syncQueue,
mockSurfaceControlViewHostFactory,
bubbleDropTargetBoundsProvider,
+ snapEventHandler,
)
viewContainer.createView(
context,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
index 2cabb9a..646ec21 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor.tiling
import android.content.Context
+import android.content.res.Resources
import android.graphics.Rect
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -23,12 +24,13 @@
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
-import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
import com.android.wm.shell.transition.FocusTransitionObserver
@@ -52,6 +54,7 @@
@RunWith(AndroidTestingRunner::class)
class DesktopTilingDecorViewModelTest : ShellTestCase() {
private val contextMock: Context = mock()
+ private val resourcesMock: Resources = mock()
private val mainDispatcher: MainCoroutineDispatcher = mock()
private val bgScope: CoroutineScope = mock()
private val displayControllerMock: DisplayController = mock()
@@ -70,6 +73,7 @@
private val desktopTilingDecoration: DesktopTilingWindowDecoration = mock()
private val taskResourceLoader: WindowDecorTaskResourceLoader = mock()
private val focusTransitionObserver: FocusTransitionObserver = mock()
+ private val displayLayout: DisplayLayout = mock()
private val mainExecutor: ShellExecutor = mock()
private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
@@ -91,9 +95,16 @@
desktopModeEventLogger,
taskResourceLoader,
focusTransitionObserver,
- mainExecutor
+ mainExecutor,
)
whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
+ whenever(displayControllerMock.getDisplayLayout(any())).thenReturn(displayLayout)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
+ whenever(contextMock.createContextAsUser(any(), any())).thenReturn(context)
+ whenever(contextMock.resources).thenReturn(resourcesMock)
+ whenever(resourcesMock.getDimensionPixelSize(any())).thenReturn(10)
}
@Test
@@ -202,7 +213,21 @@
verify(desktopTilingDecoration, times(1)).resetTilingSession()
}
+ @Test
+ fun getTiledAppBounds_NoTilingTransitionHandlerObject() {
+ // Right bound of the left app here represents default 8 / 2 - 2 ( {Right bound} / 2 -
+ // {divider pixel size})
+ assertThat(desktopTilingDecorViewModel.getLeftSnapBoundsIfTiled(1))
+ .isEqualTo(Rect(6, 7, 2, 9))
+
+ // Left bound of the right app here represents default 8 / 2 + 6 + 2 ( {Left bound} +
+ // {width}/ 2 + {divider pixel size})
+ assertThat(desktopTilingDecorViewModel.getRightSnapBoundsIfTiled(1))
+ .isEqualTo(Rect(12, 7, 8, 9))
+ }
+
companion object {
private val BOUNDS = Rect(1, 2, 3, 4)
+ private val STABLE_BOUNDS = Rect(6, 7, 8, 9)
}
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 61c287b..ae54c18 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2990,11 +2990,16 @@
}
*memory = context->toHidlMemory();
}
- if (context->mBlock == nullptr || context->mReadWriteMapping == nullptr) {
- ALOGW("extractMemoryFromContext: Cannot extract memory as C2Block is not created/mapped");
+ if (context->mBlock == nullptr) {
+ // this should be ok as we may only have IMemory/hidlMemory
+ // e.g. video codecs may only have IMemory and no mBlock
return;
}
- if (context->mReadWriteMapping->error() != C2_OK) {
+
+ // if we have mBlock and memory, then we will copy data from mBlock to hidlMemory
+ // e.g. audio codecs may only have mBlock and wanted to decrypt using hidlMemory
+ // and also wanted to re-use mBlock
+ if (context->mReadWriteMapping == nullptr || context->mReadWriteMapping->error() != C2_OK) {
ALOGW("extractMemoryFromContext: failed to map C2Block (%d)",
context->mReadWriteMapping->error());
return;
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
index 1256641..ed144bd 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
@@ -41,6 +41,7 @@
*/
@Immutable
class AndroidColorScheme(
+ // fixed tokens
val primaryFixed: Color,
val primaryFixedDim: Color,
val onPrimaryFixed: Color,
@@ -53,6 +54,30 @@
val tertiaryFixedDim: Color,
val onTertiaryFixed: Color,
val onTertiaryFixedVariant: Color,
+
+ // custom tokens
+ val brandA: Color,
+ val brandB: Color,
+ val brandC: Color,
+ val brandD: Color,
+ val clockHour: Color,
+ val clockMinute: Color,
+ val clockSecond: Color,
+ val onShadeActive: Color,
+ val onShadeActiveVariant: Color,
+ val onShadeInactive: Color,
+ val onShadeInactiveVariant: Color,
+ val onThemeApp: Color,
+ val overviewBackground: Color,
+ val shadeActive: Color,
+ val shadeDisabled: Color,
+ val shadeInactive: Color,
+ val themeApp: Color,
+ val themeAppRing: Color,
+ val themeNotif: Color,
+ val underSurface: Color,
+ val weatherTemp: Color,
+ val widgetBackground: Color,
) {
companion object {
internal fun color(context: Context, @ColorRes id: Int): Color {
@@ -61,6 +86,7 @@
operator fun invoke(context: Context): AndroidColorScheme {
return AndroidColorScheme(
+ // Fixed tokens.
primaryFixed = color(context, R.color.system_primary_fixed),
primaryFixedDim = color(context, R.color.system_primary_fixed_dim),
onPrimaryFixed = color(context, R.color.system_on_primary_fixed),
@@ -73,6 +99,30 @@
tertiaryFixedDim = color(context, R.color.system_tertiary_fixed_dim),
onTertiaryFixed = color(context, R.color.system_on_tertiary_fixed),
onTertiaryFixedVariant = color(context, R.color.system_on_tertiary_fixed_variant),
+
+ // Custom tokens.
+ brandA = color(context, R.color.customColorBrandA),
+ brandB = color(context, R.color.customColorBrandB),
+ brandC = color(context, R.color.customColorBrandC),
+ brandD = color(context, R.color.customColorBrandD),
+ clockHour = color(context, R.color.customColorClockHour),
+ clockMinute = color(context, R.color.customColorClockMinute),
+ clockSecond = color(context, R.color.customColorClockSecond),
+ onShadeActive = color(context, R.color.customColorOnShadeActive),
+ onShadeActiveVariant = color(context, R.color.customColorOnShadeActiveVariant),
+ onShadeInactive = color(context, R.color.customColorOnShadeInactive),
+ onShadeInactiveVariant = color(context, R.color.customColorOnShadeInactiveVariant),
+ onThemeApp = color(context, R.color.customColorOnThemeApp),
+ overviewBackground = color(context, R.color.customColorOverviewBackground),
+ shadeActive = color(context, R.color.customColorShadeActive),
+ shadeDisabled = color(context, R.color.customColorShadeDisabled),
+ shadeInactive = color(context, R.color.customColorShadeInactive),
+ themeApp = color(context, R.color.customColorThemeApp),
+ themeAppRing = color(context, R.color.customColorThemeAppRing),
+ themeNotif = color(context, R.color.customColorThemeNotif),
+ underSurface = color(context, R.color.customColorUnderSurface),
+ weatherTemp = color(context, R.color.customColorWeatherTemp),
+ widgetBackground = color(context, R.color.customColorWidgetBackground),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
index 83e26c4..5d8b68e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
@@ -65,8 +65,8 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
- fun getGroupRoot_adapter() {
- assertThat(underTest.entryAdapter.groupRoot).isEqualTo(underTest.entryAdapter)
+ fun isGroupRoot_adapter() {
+ assertThat(underTest.entryAdapter.isGroupRoot).isTrue()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 1f5c672..34dff24 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -542,7 +542,7 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getGroupRoot_adapter_groupSummary() {
+ public void isGroupRoot_adapter_groupSummary() {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
Notification notification = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
@@ -562,12 +562,12 @@
.build();
entry.setRow(row);
- assertThat(entry.getEntryAdapter().getGroupRoot()).isNull();
+ assertThat(entry.getEntryAdapter().isGroupRoot()).isFalse();
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getGroupRoot_adapter_groupChild() {
+ public void isGroupRoot_adapter_groupChild() {
Notification notification = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
.setGroupSummary(true)
@@ -591,7 +591,7 @@
.setParent(groupEntry.build())
.build();
- assertThat(entry.getEntryAdapter().getGroupRoot()).isEqualTo(parent.getEntryAdapter());
+ assertThat(entry.getEntryAdapter().isGroupRoot()).isFalse();
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 2aa1efa..2aa405a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -22,9 +22,10 @@
import static junit.framework.Assert.assertFalse;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-import static org.junit.Assume.assumeFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -36,8 +37,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
@@ -74,13 +73,16 @@
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.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.test.TestScope;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -90,15 +92,12 @@
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
-import java.util.List;
-import java.util.Set;
-
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.test.TestScope;
-
import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;
+import java.util.List;
+import java.util.Set;
+
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
@@ -118,7 +117,7 @@
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
@Mock private SeenNotificationsInteractor mSeenNotificationsInteractor;
- @Mock private HeadsUpManager mHeadsUpManager;
+ @Mock private HeadsUpRepository mHeadsUpRepository;
@Mock private VisibilityLocationProvider mVisibilityLocationProvider;
@Mock private VisualStabilityProvider mVisualStabilityProvider;
@Mock private VisualStabilityCoordinatorLogger mLogger;
@@ -159,7 +158,7 @@
mFakeBackgroundExecutor,
mFakeMainExecutor,
mDumpManager,
- mHeadsUpManager,
+ mHeadsUpRepository,
mShadeAnimationInteractor,
mJavaAdapter,
mSeenNotificationsInteractor,
@@ -172,6 +171,8 @@
mKosmos.getKeyguardTransitionInteractor(),
mKeyguardStateController,
mLogger);
+
+ when(mHeadsUpRepository.isTrackingHeadsUp()).thenReturn(MutableStateFlow(false));
mCoordinator.attach(mNotifPipeline);
mTestScope.getTestScheduler().runCurrent();
@@ -194,7 +195,7 @@
.setSummary(mEntry)
.build();
- when(mHeadsUpManager.isHeadsUpEntry(mEntry.getKey())).thenReturn(false);
+ when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(false);
// Whenever we invalidate, the pipeline runs again, so we invalidate the state
doAnswer(i -> {
@@ -461,7 +462,7 @@
setSleepy(false);
// WHEN a notification is alerting and visible
- when(mHeadsUpManager.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
+ when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
when(mVisibilityLocationProvider.isInVisibleLocation(any(NotificationEntry.class)))
.thenReturn(true);
@@ -477,7 +478,7 @@
setSleepy(false);
// WHEN a notification is alerting but not visible
- when(mHeadsUpManager.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
+ when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
when(mVisibilityLocationProvider.isInVisibleLocation(any(NotificationEntry.class)))
.thenReturn(false);
@@ -701,7 +702,7 @@
assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
// GIVEN mEntry is a HUN
- when(mHeadsUpManager.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
+ when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
// THEN group + section changes are allowed
assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
@@ -768,7 +769,7 @@
// GIVEN - there is a group heads-up.
String headsUpGroupKey = "heads_up_group_key";
mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
+ when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
// GIVEN - HUN Group Summary
final NotificationEntry nonHeadsUpGroupSummary = mock(NotificationEntry.class);
@@ -793,7 +794,7 @@
// GIVEN - there is a group heads-up.
final String headsUpGroupKey = "heads_up_group_key";
mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
+ when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
// GIVEN - HUN Group
final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class);
@@ -825,7 +826,7 @@
// GIVEN - there is a group heads-up.
final String headsUpGroupKey = "heads_up_group_key";
mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
+ when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
// GIVEN - HUN Group
final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class);
@@ -858,7 +859,7 @@
// GIVEN - there is a group heads-up.
String headsUpGroupKey = "heads_up_group_key";
mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
+ when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
// GIVEN - non HUN parent Group Summary
final NotificationEntry groupSummary = mock(NotificationEntry.class);
@@ -891,7 +892,7 @@
// GIVEN - there is a group heads-up.
final String headsUpGroupKey = "heads_up_group_key";
mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
+ when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
// GIVEN - HUN Group Summary
final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
index 3dd0982..8e6aedca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.render
import android.os.Build
+import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
@@ -29,9 +30,12 @@
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
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.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -55,56 +59,58 @@
private lateinit var underTest: GroupExpansionManagerImpl
+ private lateinit var testHelper: NotificationTestHelper
private val dumpManager: DumpManager = mock()
private val groupMembershipManager: GroupMembershipManager = mock()
private val pipeline: NotifPipeline = mock()
private lateinit var beforeRenderListListener: OnBeforeRenderListListener
- private val summary1 = notificationSummaryEntry("foo", 1)
- private val summary2 = notificationSummaryEntry("bar", 1)
- private val entries =
- listOf<ListEntry>(
- GroupEntryBuilder()
- .setSummary(summary1)
- .setChildren(
- listOf(
- notificationEntry("foo", 2),
- notificationEntry("foo", 3),
- notificationEntry("foo", 4)
- )
- )
- .build(),
- GroupEntryBuilder()
- .setSummary(summary2)
- .setChildren(
- listOf(
- notificationEntry("bar", 2),
- notificationEntry("bar", 3),
- notificationEntry("bar", 4)
- )
- )
- .build(),
- notificationEntry("baz", 1)
- )
+ private lateinit var summary1: NotificationEntry
+ private lateinit var summary2: NotificationEntry
+ private lateinit var entries: List<ListEntry>
- private fun notificationEntry(pkg: String, id: Int) =
- NotificationEntryBuilder().setPkg(pkg).setId(id).build().apply { row = mock() }
-
- private fun notificationSummaryEntry(pkg: String, id: Int) =
- NotificationEntryBuilder().setPkg(pkg).setId(id).setParent(GroupEntry.ROOT_ENTRY).build()
- .apply { row = mock() }
+ private fun notificationEntry(pkg: String, id: Int, parent: ExpandableNotificationRow?) =
+ NotificationEntryBuilder().setPkg(pkg).setId(id).build().apply {
+ row = testHelper.createRow().apply {
+ setIsChildInGroup(true, parent)
+ }
+ }
@Before
fun setUp() {
+ testHelper = NotificationTestHelper(mContext, mDependency)
+
+ summary1 = testHelper.createRow().entry
+ summary2 = testHelper.createRow().entry
+ entries =
+ listOf<ListEntry>(
+ GroupEntryBuilder()
+ .setSummary(summary1)
+ .setChildren(
+ listOf(
+ notificationEntry("foo", 2, summary1.row),
+ notificationEntry("foo", 3, summary1.row),
+ notificationEntry("foo", 4, summary1.row)
+ )
+ )
+ .build(),
+ GroupEntryBuilder()
+ .setSummary(summary2)
+ .setChildren(
+ listOf(
+ notificationEntry("bar", 2, summary2.row),
+ notificationEntry("bar", 3, summary2.row),
+ notificationEntry("bar", 4, summary2.row)
+ )
+ )
+ .build(),
+ notificationEntry("baz", 1, null)
+ )
+
whenever(groupMembershipManager.getGroupSummary(summary1)).thenReturn(summary1)
whenever(groupMembershipManager.getGroupSummary(summary2)).thenReturn(summary2)
- whenever(groupMembershipManager.getGroupRoot(summary1.entryAdapter))
- .thenReturn(summary1.entryAdapter)
- whenever(groupMembershipManager.getGroupRoot(summary2.entryAdapter))
- .thenReturn(summary2.entryAdapter)
-
underTest = GroupExpansionManagerImpl(dumpManager, groupMembershipManager)
}
@@ -221,4 +227,15 @@
verify(listener).onGroupExpansionChange(summary1.row, false)
verifyNoMoreInteractions(listener)
}
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isGroupExpanded() {
+ underTest.setGroupExpanded(summary1.entryAdapter, true)
+
+ assertThat(underTest.isGroupExpanded(summary1.entryAdapter)).isTrue();
+ assertThat(underTest.isGroupExpanded(
+ (entries[0] as? GroupEntry)?.getChildren()?.get(0)?.entryAdapter))
+ .isTrue();
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
index dcbf44e..2bbf094 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
@@ -170,6 +170,21 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isChildEntryAdapterInGroup_child() {
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
+
+ assertThat(underTest.isChildInGroup(entry.entryAdapter)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isGroupRoot_topLevelEntry() {
val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
assertThat(underTest.isGroupRoot(entry.entryAdapter)).isFalse()
@@ -203,40 +218,4 @@
assertThat(underTest.isGroupRoot(entry.entryAdapter)).isFalse()
}
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- fun getGroupRoot_topLevelEntry() {
- val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
- assertThat(underTest.getGroupRoot(entry.entryAdapter)).isNull()
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- fun getGroupRoot_summary() {
- val groupKey = "group"
- val summary =
- NotificationEntryBuilder()
- .setGroup(mContext, groupKey)
- .setGroupSummary(mContext, true)
- .build()
- GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
-
- assertThat(underTest.getGroupRoot(summary.entryAdapter)).isEqualTo(summary.entryAdapter)
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- fun getGroupRoot_child() {
- val groupKey = "group"
- val summary =
- NotificationEntryBuilder()
- .setGroup(mContext, groupKey)
- .setGroupSummary(mContext, true)
- .build()
- val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
- GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
-
- assertThat(underTest.getGroupRoot(entry.entryAdapter)).isEqualTo(summary.entryAdapter)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index f2131da..92ceb60 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -101,6 +101,7 @@
import kotlin.coroutines.CoroutineContext;
+import kotlinx.coroutines.flow.StateFlowKt;
import kotlinx.coroutines.test.TestScope;
import org.mockito.ArgumentCaptor;
@@ -166,11 +167,21 @@
this(context, dependency, testLooper, new FakeFeatureFlagsClassic());
}
+
public NotificationTestHelper(
Context context,
TestableDependency dependency,
@Nullable TestableLooper testLooper,
@NonNull FakeFeatureFlagsClassic featureFlags) {
+ this(context, dependency, testLooper, featureFlags, mockHeadsUpManager());
+ }
+
+ public NotificationTestHelper(
+ Context context,
+ TestableDependency dependency,
+ @Nullable TestableLooper testLooper,
+ @NonNull FakeFeatureFlagsClassic featureFlags,
+ @NonNull HeadsUpManager headsUpManager) {
mContext = context;
mFeatureFlags = Objects.requireNonNull(featureFlags);
dependency.injectTestDependency(FeatureFlagsClassic.class, mFeatureFlags);
@@ -182,7 +193,7 @@
mKeyguardBypassController = mock(KeyguardBypassController.class);
mGroupMembershipManager = mock(GroupMembershipManager.class);
mGroupExpansionManager = mock(GroupExpansionManager.class);
- mHeadsUpManager = mock(HeadsUpManager.class);
+ mHeadsUpManager = headsUpManager;
mIconManager = new IconManager(
mock(CommonNotifCollection.class),
mock(LauncherApps.class),
@@ -689,6 +700,12 @@
.build();
}
+ private static HeadsUpManager mockHeadsUpManager() {
+ HeadsUpManager mock = mock(HeadsUpManager.class);
+ when(mock.isTrackingHeadsUp()).thenReturn(StateFlowKt.MutableStateFlow(false));
+ return mock;
+ }
+
private static class MockSmartReplyInflater implements SmartReplyStateInflater {
@Override
public InflatedSmartReplyState inflateSmartReplyState(NotificationEntry entry) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
index 885e71e..ccc8be7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
@@ -80,7 +80,7 @@
// THEN the magnetic and roundable targets are defined and the state is TARGETS_SET
assertThat(underTest.currentState).isEqualTo(State.TARGETS_SET)
assertThat(underTest.currentMagneticListeners.isNotEmpty()).isTrue()
- assertThat(underTest.currentRoundableTargets).isNotNull()
+ assertThat(underTest.isSwipedViewRoundableSet).isTrue()
}
@Test
@@ -281,6 +281,19 @@
assertThat(magneticAnimationsCancelled[neighborIndex]).isTrue()
}
+ @Test
+ fun onResetRoundness_swipedRoundableGetsCleared() =
+ kosmos.testScope.runTest {
+ // GIVEN targets are set
+ setTargets()
+
+ // WHEN we reset the roundness
+ underTest.resetRoundness()
+
+ // THEN the swiped roundable gets cleared
+ assertThat(underTest.isSwipedViewRoundableSet).isFalse()
+ }
+
@After
fun tearDown() {
// We reset the manager so that all MagneticRowListener can cancel all animations
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
index baea1a1..47967b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
@@ -54,6 +54,7 @@
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.visualInterruptionDecisionProvider
import com.android.systemui.statusbar.notificationLockscreenUserManager
@@ -293,6 +294,7 @@
@Test
@EnableSceneContainer
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
fun testExpandSensitiveNotification_onLockScreen_opensShade() =
kosmos.runTest {
// Given we are on the keyguard
@@ -303,11 +305,10 @@
)
// When the user expands a sensitive Notification
- val row = createRow()
- val entry =
- row.entry.apply { setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true) }
+ val row = createRow(createNotificationEntry())
+ row.entry.apply { setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true) }
- underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
+ underTest.onExpandClicked(row.entry, mock(), /* nowExpanded= */ true)
// Then we open the locked shade
verify(kosmos.lockscreenShadeTransitionController)
@@ -317,6 +318,32 @@
@Test
@EnableSceneContainer
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun testExpandSensitiveNotification_onLockScreen_opensShade_entryAdapter() =
+ kosmos.runTest {
+ // Given we are on the keyguard
+ kosmos.sysuiStatusBarStateController.state = StatusBarState.KEYGUARD
+ // And the device is locked
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+
+ // When the user expands a sensitive Notification
+ val entry = createNotificationEntry()
+ val row = createRow(entry)
+ entry.setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
+
+ underTest.onExpandClicked(row, row.entryAdapter, /* nowExpanded= */ true)
+
+ // Then we open the locked shade
+ verify(kosmos.lockscreenShadeTransitionController)
+ // Explicit parameters to avoid issues with Kotlin default arguments in Mockito
+ .goToLockedShade(row, true)
+ }
+
+ @Test
+ @EnableSceneContainer
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
fun testExpandSensitiveNotification_onLockedShade_showsBouncer() =
kosmos.runTest {
// Given we are on the locked shade
@@ -328,7 +355,7 @@
// When the user expands a sensitive Notification
val entry =
- createRow().entry.apply {
+ createRow(createNotificationEntry()).entry.apply {
setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
}
underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
@@ -337,6 +364,29 @@
verify(kosmos.activityStarter).dismissKeyguardThenExecute(any(), eq(null), eq(false))
}
+ @Test
+ @EnableSceneContainer
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun testExpandSensitiveNotification_onLockedShade_showsBouncer_entryAdapter() =
+ kosmos.runTest {
+ // Given we are on the locked shade
+ kosmos.sysuiStatusBarStateController.state = StatusBarState.SHADE_LOCKED
+ // And the device is locked
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+
+ // When the user expands a sensitive Notification
+ val entry = createNotificationEntry()
+ val row = createRow(entry)
+ entry.setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
+
+ underTest.onExpandClicked(row, row.entryAdapter, /* nowExpanded= */ true)
+
+ // Then we show the bouncer
+ verify(kosmos.activityStarter).dismissKeyguardThenExecute(any(), eq(null), eq(false))
+ }
+
private fun createPresenter(): StatusBarNotificationPresenter {
val initController: InitController = InitController()
return StatusBarNotificationPresenter(
@@ -398,10 +448,13 @@
interruptSuppressor = suppressorCaptor.lastValue
}
- private fun createRow(): ExpandableNotificationRow {
+ private fun createRow(entry: NotificationEntry): ExpandableNotificationRow {
val row: ExpandableNotificationRow = mock()
- val entry: NotificationEntry = createNotificationEntry()
- whenever(row.entry).thenReturn(entry)
+ if (NotificationBundleUi.isEnabled) {
+ whenever(row.entryAdapter).thenReturn(entry.entryAdapter)
+ } else {
+ whenever(row.entry).thenReturn(entry)
+ }
entry.row = row
return row
}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
deleted file mode 100644
index 1a5e605..0000000
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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.model;
-
-import android.annotation.NonNull;
-import android.util.Log;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
-
-import dalvik.annotation.optimization.NeverCompile;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Contains sysUi state flags and notifies registered
- * listeners whenever changes happen.
- */
-@SysUISingleton
-public class SysUiState implements Dumpable {
-
- private static final String TAG = SysUiState.class.getSimpleName();
- public static final boolean DEBUG = false;
-
- private final DisplayTracker mDisplayTracker;
- private final SceneContainerPlugin mSceneContainerPlugin;
- private @SystemUiStateFlags long mFlags;
- private final List<SysUiStateCallback> mCallbacks = new ArrayList<>();
- private long mFlagsToSet = 0;
- private long mFlagsToClear = 0;
-
- public SysUiState(DisplayTracker displayTracker, SceneContainerPlugin sceneContainerPlugin) {
- mDisplayTracker = displayTracker;
- mSceneContainerPlugin = sceneContainerPlugin;
- }
-
- /**
- * Add listener to be notified of changes made to SysUI state.
- * The callback will also be called as part of this function.
- */
- public void addCallback(@NonNull SysUiStateCallback callback) {
- mCallbacks.add(callback);
- callback.onSystemUiStateChanged(mFlags);
- }
-
- /** Callback will no longer receive events on state change */
- public void removeCallback(@NonNull SysUiStateCallback callback) {
- mCallbacks.remove(callback);
- }
-
- /** Returns the current sysui state flags. */
- @SystemUiStateFlags
- public long getFlags() {
- return mFlags;
- }
-
- public boolean isFlagEnabled(@SystemUiStateFlags long flag) {
- return (mFlags & flag) != 0;
- }
-
- /** Methods to this call can be chained together before calling {@link #commitUpdate(int)}. */
- public SysUiState setFlag(@SystemUiStateFlags long flag, boolean enabled) {
- final Boolean overrideOrNull = mSceneContainerPlugin != null
- ? mSceneContainerPlugin.flagValueOverride(flag) : null;
- if (overrideOrNull != null && enabled != overrideOrNull) {
- if (DEBUG) {
- Log.d(TAG, "setFlag for flag " + flag + " and value " + enabled + " overridden to "
- + overrideOrNull + " by scene container plugin");
- }
-
- enabled = overrideOrNull;
- }
-
- if (enabled) {
- mFlagsToSet |= flag;
- } else {
- mFlagsToClear |= flag;
- }
- return this;
- }
-
- /** Call to save all the flags updated from {@link #setFlag(long, boolean)}. */
- public void commitUpdate(int displayId) {
- updateFlags(displayId);
- mFlagsToSet = 0;
- mFlagsToClear = 0;
- }
-
- private void updateFlags(int displayId) {
- if (displayId != mDisplayTracker.getDefaultDisplayId()) {
- // Ignore non-default displays for now
- Log.w(TAG, "Ignoring flag update for display: " + displayId, new Throwable());
- return;
- }
-
- long newState = mFlags;
- newState |= mFlagsToSet;
- newState &= ~mFlagsToClear;
- notifyAndSetSystemUiStateChanged(newState, mFlags);
- }
-
- /** Notify all those who are registered that the state has changed. */
- private void notifyAndSetSystemUiStateChanged(long newFlags, long oldFlags) {
- if (DEBUG) {
- Log.d(TAG, "SysUiState changed: old=" + oldFlags + " new=" + newFlags);
- }
- if (newFlags != oldFlags) {
- mCallbacks.forEach(callback -> callback.onSystemUiStateChanged(newFlags));
- mFlags = newFlags;
- }
- }
-
- @NeverCompile
- @Override
- public void dump(PrintWriter pw, String[] args) {
- pw.println("SysUiState state:");
- pw.print(" mSysUiStateFlags="); pw.println(mFlags);
- pw.println(" " + QuickStepContract.getSystemUiStateString(mFlags));
- pw.print(" backGestureDisabled=");
- pw.println(QuickStepContract.isBackGestureDisabled(mFlags, false /* forTrackpad */));
- pw.print(" assistantGestureDisabled=");
- pw.println(QuickStepContract.isAssistantGestureDisabled(mFlags));
- }
-
- /** Callback to be notified whenever system UI state flags are changed. */
- public interface SysUiStateCallback{
- /** To be called when any SysUiStateFlag gets updated */
- void onSystemUiStateChanged(@SystemUiStateFlags long sysUiFlags);
- }
-}
-
-
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
new file mode 100644
index 0000000..ed190a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 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.model
+
+import android.util.Log
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags
+import dalvik.annotation.optimization.NeverCompile
+import java.io.PrintWriter
+
+/** Contains sysUi state flags and notifies registered listeners whenever changes happen. */
+@SysUISingleton
+class SysUiState(
+ private val displayTracker: DisplayTracker,
+ private val sceneContainerPlugin: SceneContainerPlugin?,
+) : Dumpable {
+ /** Returns the current sysui state flags. */
+ @get:SystemUiStateFlags
+ @SystemUiStateFlags
+ var flags: Long = 0
+ private set
+
+ private val callbacks: MutableList<SysUiStateCallback> = ArrayList()
+ private var flagsToSet: Long = 0
+ private var flagsToClear: Long = 0
+
+ /**
+ * Add listener to be notified of changes made to SysUI state. The callback will also be called
+ * as part of this function.
+ */
+ fun addCallback(callback: SysUiStateCallback) {
+ callbacks.add(callback)
+ callback.onSystemUiStateChanged(flags)
+ }
+
+ /** Callback will no longer receive events on state change */
+ fun removeCallback(callback: SysUiStateCallback) {
+ callbacks.remove(callback)
+ }
+
+ fun isFlagEnabled(@SystemUiStateFlags flag: Long): Boolean {
+ return (flags and flag) != 0L
+ }
+
+ /** Methods to this call can be chained together before calling [.commitUpdate]. */
+ fun setFlag(@SystemUiStateFlags flag: Long, enabled: Boolean): SysUiState {
+ var enabled = enabled
+ val overrideOrNull = sceneContainerPlugin?.flagValueOverride(flag)
+ if (overrideOrNull != null && enabled != overrideOrNull) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "setFlag for flag $flag and value $enabled overridden to $overrideOrNull by scene container plugin",
+ )
+ }
+
+ enabled = overrideOrNull
+ }
+
+ if (enabled) {
+ flagsToSet = flagsToSet or flag
+ } else {
+ flagsToClear = flagsToClear or flag
+ }
+ return this
+ }
+
+ /** Call to save all the flags updated from [.setFlag]. */
+ fun commitUpdate(displayId: Int) {
+ updateFlags(displayId)
+ flagsToSet = 0
+ flagsToClear = 0
+ }
+
+ private fun updateFlags(displayId: Int) {
+ if (displayId != displayTracker.defaultDisplayId) {
+ // Ignore non-default displays for now
+ Log.w(TAG, "Ignoring flag update for display: $displayId", Throwable())
+ return
+ }
+
+ var newState = flags
+ newState = newState or flagsToSet
+ newState = newState and flagsToClear.inv()
+ notifyAndSetSystemUiStateChanged(newState, flags)
+ }
+
+ /** Notify all those who are registered that the state has changed. */
+ private fun notifyAndSetSystemUiStateChanged(newFlags: Long, oldFlags: Long) {
+ if (DEBUG) {
+ Log.d(TAG, "SysUiState changed: old=$oldFlags new=$newFlags")
+ }
+ if (newFlags != oldFlags) {
+ callbacks.forEach { callback: SysUiStateCallback ->
+ callback.onSystemUiStateChanged(newFlags)
+ }
+
+ flags = newFlags
+ }
+ }
+
+ @NeverCompile
+ override fun dump(pw: PrintWriter, args: Array<String>) {
+ pw.println("SysUiState state:")
+ pw.print(" mSysUiStateFlags=")
+ pw.println(flags)
+ pw.println(" " + QuickStepContract.getSystemUiStateString(flags))
+ pw.print(" backGestureDisabled=")
+ pw.println(QuickStepContract.isBackGestureDisabled(flags, false /* forTrackpad */))
+ pw.print(" assistantGestureDisabled=")
+ pw.println(QuickStepContract.isAssistantGestureDisabled(flags))
+ }
+
+ /** Callback to be notified whenever system UI state flags are changed. */
+ interface SysUiStateCallback {
+ /** To be called when any SysUiStateFlag gets updated */
+ fun onSystemUiStateChanged(@SystemUiStateFlags sysUiFlags: Long)
+ }
+
+ companion object {
+ private val TAG: String = SysUiState::class.java.simpleName
+ const val DEBUG: Boolean = false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 28d619a..09a04a7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1788,7 +1788,8 @@
// height - which means user is swiping down. Otherwise shade QS will either not show at all
// with HUN movement or it will blink when touching HUN initially
boolean qsShouldExpandWithHeadsUp = !mSplitShadeEnabled
- || (!mHeadsUpManager.isTrackingHeadsUp() || expandedHeight > mHeadsUpStartHeight);
+ || (!mHeadsUpManager.isTrackingHeadsUp().getValue()
+ || expandedHeight > mHeadsUpStartHeight);
if (goingBetweenClosedShadeAndExpandedQs && qsShouldExpandWithHeadsUp) {
float qsExpansionFraction;
if (mSplitShadeEnabled) {
@@ -2048,7 +2049,7 @@
// motion has the expected speed. We also only want this on non-lockscreen for now.
if (mSplitShadeEnabled && mBarState == SHADE) {
boolean transitionFromHeadsUp = (mHeadsUpManager != null
- && mHeadsUpManager.isTrackingHeadsUp()) || mExpandingFromHeadsUp;
+ && mHeadsUpManager.isTrackingHeadsUp().getValue()) || mExpandingFromHeadsUp;
// heads-up starting height is too close to mSplitShadeFullTransitionDistance and
// when dragging HUN transition is already 90% complete. It makes shade become
// immediately visible when starting to drag. We want to set distance so that
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index d058372..42c63f9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -2158,6 +2158,8 @@
/** */
public final class QsFragmentListener implements FragmentHostManager.FragmentListener {
+ private boolean mPreviouslyVisibleMedia = false;
+
/** */
@Override
public void onFragmentViewCreated(String tag, Fragment fragment) {
@@ -2183,7 +2185,12 @@
setAnimateNextNotificationBounds(
StackStateAnimator.ANIMATION_DURATION_STANDARD, 0);
mNotificationStackScrollLayoutController.animateNextTopPaddingChange();
+ if (QSComposeFragment.isEnabled() && mPreviouslyVisibleMedia && !visible) {
+ updateHeightsOnShadeLayoutChange();
+ mPanelViewControllerLazy.get().positionClockAndNotifications();
+ }
}
+ mPreviouslyVisibleMedia = visible;
});
mLockscreenShadeTransitionController.setQS(mQs);
if (QSComposeFragment.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 6ebe024..4bb12e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -57,12 +57,6 @@
private static final VisibilityApplicator VISIBILITY_APPLICATOR = new VisibilityApplicator();
private static final VisibilityApplicator APP_NAME_APPLICATOR = new AppNameApplicator();
private static final ResultApplicator LEFT_ICON_APPLICATOR = new LeftIconApplicator();
- private static final DataExtractor ICON_EXTRACTOR = new DataExtractor() {
- @Override
- public Object extractData(ExpandableNotificationRow row) {
- return row.getEntry().getSbn().getNotification();
- }
- };
private final ExpandableNotificationRow mRow;
private final ArrayList<Processor> mProcessors = new ArrayList<>();
@@ -109,31 +103,26 @@
// To hide the icons if they are the same and the color is the same
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.icon,
- ICON_EXTRACTOR,
iconVisibilityComparator,
VISIBILITY_APPLICATOR));
// To grey out the icons when they are not the same, or they have the same color
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.status_bar_latest_event_content,
- ICON_EXTRACTOR,
greyComparator,
greyApplicator));
// To show the large icon on the left side instead if all the small icons are the same
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.status_bar_latest_event_content,
- ICON_EXTRACTOR,
iconVisibilityComparator,
LEFT_ICON_APPLICATOR));
// To only show the work profile icon in the group header
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.profile_badge,
- null /* Extractor */,
BADGE_COMPARATOR,
VISIBILITY_APPLICATOR));
// To hide the app name in group children
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.app_name_text,
- null,
APP_NAME_COMPARATOR,
APP_NAME_APPLICATOR));
// To hide the header text if it's the same
@@ -253,23 +242,20 @@
private static class Processor {
private final int mId;
- private final DataExtractor mExtractor;
private final ViewComparator mComparator;
private final ResultApplicator mApplicator;
private final ExpandableNotificationRow mParentRow;
private boolean mApply;
private View mParentView;
- private Object mParentData;
public static Processor forTextView(ExpandableNotificationRow row, int id) {
- return new Processor(row, id, null, TEXT_VIEW_COMPARATOR, VISIBILITY_APPLICATOR);
+ return new Processor(row, id, TEXT_VIEW_COMPARATOR, VISIBILITY_APPLICATOR);
}
- Processor(ExpandableNotificationRow row, int id, DataExtractor extractor,
+ Processor(ExpandableNotificationRow row, int id,
ViewComparator comparator,
ResultApplicator applicator) {
mId = id;
- mExtractor = extractor;
mApplicator = applicator;
mComparator = comparator;
mParentRow = row;
@@ -279,7 +265,6 @@
NotificationViewWrapper wrapper = mParentRow.getNotificationViewWrapper();
View header = wrapper == null ? null : wrapper.getNotificationHeader();
mParentView = header == null ? null : header.findViewById(mId);
- mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow);
mApply = !mComparator.isEmpty(mParentView);
}
@@ -297,9 +282,7 @@
// when for example showing an undo notification
return;
}
- Object childData = mExtractor == null ? null : mExtractor.extractData(row);
- mApply = mComparator.compare(mParentView, ownView,
- mParentData, childData);
+ mApply = mComparator.compare(mParentView, ownView);
}
public void apply(ExpandableNotificationRow row) {
@@ -331,11 +314,9 @@
/**
* @param parent the view with the given id in the group header
* @param child the view with the given id in the child notification
- * @param parentData optional data for the parent
- * @param childData optional data for the child
* @return whether to views are the same
*/
- boolean compare(View parent, View child, Object parentData, Object childData);
+ boolean compare(View parent, View child);
boolean isEmpty(View view);
}
@@ -346,7 +327,7 @@
private static class BadgeComparator implements ViewComparator {
@Override
- public boolean compare(View parent, View child, Object parentData, Object childData) {
+ public boolean compare(View parent, View child) {
return parent.getVisibility() != View.GONE;
}
@@ -364,7 +345,7 @@
private static class TextViewComparator implements ViewComparator {
@Override
- public boolean compare(View parent, View child, Object parentData, Object childData) {
+ public boolean compare(View parent, View child) {
TextView parentView = (TextView) parent;
CharSequence parentText = parentView == null ? "" : parentView.getText();
TextView childView = (TextView) child;
@@ -380,7 +361,7 @@
private abstract static class IconComparator implements ViewComparator {
@Override
- public boolean compare(View parent, View child, Object parentData, Object childData) {
+ public boolean compare(View parent, View child) {
return false;
}
@@ -440,14 +421,14 @@
private static class AppNameComparator extends TextViewComparator {
@Override
- public boolean compare(View parent, View child, Object parentData, Object childData) {
+ public boolean compare(View parent, View child) {
if (isEmpty(child)) {
// In headerless notifications the AppName view exists but is usually GONE (and not
// populated). We need to treat this case as equal to the header in order to
// deduplicate the view.
return true;
}
- return super.compare(parent, child, parentData, childData);
+ return super.compare(parent, child);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 31fdec6..f88c618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -50,6 +50,7 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationMinimalism;
import com.android.systemui.statusbar.notification.shelf.NotificationShelfBackgroundView;
import com.android.systemui.statusbar.notification.shelf.NotificationShelfIconContainer;
@@ -672,7 +673,9 @@
// if the shelf is clipped, lets make sure we also clip the icon
maxTop = Math.max(maxTop, getTranslationY() + getClipTopAmount());
}
- StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon();
+ StatusBarIconView icon = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getIcons().getShelfIcon()
+ : row.getEntry().getIcons().getShelfIcon();
float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
if (shelfIconPosition < maxTop && !mAmbientState.isFullyHidden()) {
int top = (int) (maxTop - shelfIconPosition);
@@ -684,7 +687,9 @@
}
private void updateContinuousClipping(final ExpandableNotificationRow row) {
- StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon();
+ StatusBarIconView icon = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getIcons().getShelfIcon()
+ : row.getEntry().getIcons().getShelfIcon();
boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing();
boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
if (needsContinuousClipping && !isContinuousClipping) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index 35a2828..c79cae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.os.Build;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -34,6 +35,10 @@
import java.util.List;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlow;
+import kotlinx.coroutines.flow.StateFlowKt;
+
/**
* Class to represent notifications bundled by classification.
*/
@@ -41,6 +46,9 @@
private final BundleEntryAdapter mEntryAdapter;
+ // TODO(b/394483200): move NotificationEntry's implementation to PipelineEntry?
+ private final MutableStateFlow<Boolean> mSensitive = StateFlowKt.MutableStateFlow(false);
+
// TODO (b/389839319): implement the row
private ExpandableNotificationRow mRow;
@@ -97,20 +105,26 @@
return true;
}
+ @NonNull
@Override
public String getKey() {
return mKey;
}
@Override
+ @Nullable
public ExpandableNotificationRow getRow() {
return mRow;
}
- @Nullable
@Override
- public EntryAdapter getGroupRoot() {
- return this;
+ public boolean isGroupRoot() {
+ return true;
+ }
+
+ @Override
+ public StateFlow<Boolean> isSensitive() {
+ return BundleEntry.this.mSensitive;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 6431cac..109ebe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -24,6 +24,8 @@
import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import kotlinx.coroutines.flow.StateFlow;
+
/**
* Adapter interface for UI to get relevant info.
*/
@@ -51,15 +53,10 @@
ExpandableNotificationRow getRow();
/**
- * Gets the EntryAdapter that is the nearest root of the collection of rows the given entry
- * belongs to. If the given entry is a BundleEntry or an isolated child of a BundleEntry, the
- * BundleEntry will be returned. If the given notification is a group summary NotificationEntry,
- * or a child of a group summary, the summary NotificationEntry will be returned, even if that
- * summary belongs to a BundleEntry. If the entry is a notification that does not belong to any
- * group or bundle grouping, null will be returned.
+ * Whether this entry is the root of its collapsable 'group' - either a BundleEntry or a
+ * notification group summary
*/
- @Nullable
- EntryAdapter getGroupRoot();
+ boolean isGroupRoot();
/**
* @return whether the row can be removed with the 'Clear All' action
@@ -107,4 +104,9 @@
* Retrieves the pack of icons associated with this entry
*/
IconPack getIcons();
+
+ /**
+ * Returns whether the content of this entry is sensitive
+ */
+ StateFlow<Boolean> isSensitive();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index e5b72d4..fb2a66c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -297,20 +297,20 @@
return NotificationEntry.this.getRow();
}
- @Nullable
@Override
- public EntryAdapter getGroupRoot() {
- // TODO (b/395857098): for backwards compatibility this will return null if called
- // on a group summary that's not in a bundles, but it should return itself.
+ public boolean isGroupRoot() {
if (isTopLevelEntry() || getParent() == null) {
- return null;
+ return false;
}
if (NotificationEntry.this.getParent() instanceof GroupEntry parentGroupEntry) {
- if (parentGroupEntry.getSummary() != null) {
- return parentGroupEntry.getSummary().mEntryAdapter;
- }
+ return parentGroupEntry.getSummary() == NotificationEntry.this;
}
- return null;
+ return false;
+ }
+
+ @Override
+ public StateFlow<Boolean> isSensitive() {
+ return NotificationEntry.this.isSensitive();
}
@Override
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
index 3e5655a..bdbdc53 100644
--- 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
@@ -45,8 +45,8 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.shared.NotificationMinimalism;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -72,7 +72,7 @@
public class VisualStabilityCoordinator implements Coordinator, Dumpable {
private final DelayableExecutor mDelayableExecutor;
private final DelayableExecutor mMainExecutor;
- private final HeadsUpManager mHeadsUpManager;
+ private final HeadsUpRepository mHeadsUpRepository;
private final SeenNotificationsInteractor mSeenNotificationsInteractor;
private final ShadeAnimationInteractor mShadeAnimationInteractor;
private final StatusBarStateController mStatusBarStateController;
@@ -94,6 +94,7 @@
private boolean mNotifPanelLaunchingActivity;
private boolean mCommunalShowing = false;
private boolean mLockscreenShowing = false;
+ private boolean mTrackingHeadsUp = false;
private boolean mLockscreenInGoneTransition = false;
private Set<String> mHeadsUpGroupKeys = new HashSet<>();
@@ -117,7 +118,7 @@
@Background DelayableExecutor delayableExecutor,
@Main DelayableExecutor mainExecutor,
DumpManager dumpManager,
- HeadsUpManager headsUpManager,
+ HeadsUpRepository headsUpRepository,
ShadeAnimationInteractor shadeAnimationInteractor,
JavaAdapter javaAdapter,
SeenNotificationsInteractor seenNotificationsInteractor,
@@ -130,7 +131,7 @@
KeyguardTransitionInteractor keyguardTransitionInteractor,
KeyguardStateController keyguardStateController,
VisualStabilityCoordinatorLogger logger) {
- mHeadsUpManager = headsUpManager;
+ mHeadsUpRepository = headsUpRepository;
mShadeAnimationInteractor = shadeAnimationInteractor;
mJavaAdapter = javaAdapter;
mSeenNotificationsInteractor = seenNotificationsInteractor;
@@ -177,6 +178,8 @@
mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.transitionValue(
KeyguardState.LOCKSCREEN),
this::onLockscreenKeyguardStateTransitionValueChanged);
+ mJavaAdapter.alwaysCollectFlow(mHeadsUpRepository.isTrackingHeadsUp(),
+ this::onTrackingHeadsUpModeChanged);
}
if (Flags.checkLockscreenGoneTransition()) {
@@ -239,7 +242,7 @@
boolean isTopUnseen = NotificationMinimalism.isEnabled()
&& (mSeenNotificationsInteractor.isTopUnseenNotification(entry)
|| mSeenNotificationsInteractor.isTopOngoingNotification(entry));
- if (isTopUnseen || mHeadsUpManager.isHeadsUpEntry(entry.getKey())) {
+ if (isTopUnseen || mHeadsUpRepository.isHeadsUpEntry(entry.getKey())) {
return !mVisibilityLocationProvider.isInVisibleLocation(entry);
}
return false;
@@ -275,7 +278,7 @@
return false;
}
- return mHeadsUpManager.isHeadsUpEntry(summary.getKey());
+ return mHeadsUpRepository.isHeadsUpEntry(summary.getKey());
}
/**
* When reordering is enabled, non-heads-up groups can be pruned.
@@ -415,7 +418,7 @@
if (summary == null) continue;
final String summaryKey = summary.getKey();
- if (mHeadsUpManager.isHeadsUpEntry(summaryKey)) {
+ if (mHeadsUpRepository.isHeadsUpEntry(summaryKey)) {
currentHeadsUpKeys.add(summaryKey);
}
}
@@ -475,11 +478,19 @@
private boolean isReorderingAllowed() {
final boolean sleepyAndDozed = mFullyDozed && mSleepy;
- final boolean stackShowing = mPanelExpanded
- || (SceneContainerFlag.isEnabled() && mLockscreenShowing);
+ final boolean stackShowing = isStackShowing();
return (sleepyAndDozed || !stackShowing || mCommunalShowing) && !mPulsing;
}
+ /** @return true when the notification stack is visible to the user */
+ private boolean isStackShowing() {
+ if (SceneContainerFlag.isEnabled()) {
+ return mPanelExpanded || mLockscreenShowing || mTrackingHeadsUp;
+ } else {
+ return mPanelExpanded;
+ }
+ }
+
/**
* Allows this notification entry to be re-ordered in the notification list temporarily until
* the timeout has passed.
@@ -610,6 +621,11 @@
updateAllowedStates("lockscreenShowing", isShowing);
}
+ private void onTrackingHeadsUpModeChanged(boolean isTrackingHeadsUp) {
+ mTrackingHeadsUp = isTrackingHeadsUp;
+ updateAllowedStates("trackingHeadsUp", isTrackingHeadsUp);
+ }
+
private void onLockscreenInGoneTransitionChanged(boolean inGoneTransition) {
if (!Flags.checkLockscreenGoneTransition()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index b179a69..c5ae875 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import java.io.PrintWriter;
@@ -162,41 +163,38 @@
@Override
public boolean isGroupExpanded(EntryAdapter entry) {
NotificationBundleUi.assertInNewMode();
- return mExpandedCollections.contains(mGroupMembershipManager.getGroupRoot(entry));
+ ExpandableNotificationRow parent = entry.getRow().getNotificationParent();
+ return mExpandedCollections.contains(entry)
+ || (parent != null && mExpandedCollections.contains(parent.getEntryAdapter()));
}
@Override
- public void setGroupExpanded(EntryAdapter entry, boolean expanded) {
+ public void setGroupExpanded(EntryAdapter groupRoot, boolean expanded) {
NotificationBundleUi.assertInNewMode();
- EntryAdapter groupParent = mGroupMembershipManager.getGroupRoot(entry);
- if (!entry.isAttached()) {
+ if (!groupRoot.isAttached()) {
if (expanded) {
Log.wtf(TAG, "Cannot expand group that is not attached");
- } else {
- // The entry is no longer attached, but we still want to make sure we don't have
- // a stale expansion state.
- groupParent = entry;
}
}
boolean changed;
if (expanded) {
- changed = mExpandedCollections.add(groupParent);
+ changed = mExpandedCollections.add(groupRoot);
} else {
- changed = mExpandedCollections.remove(groupParent);
+ changed = mExpandedCollections.remove(groupRoot);
}
// Only notify listeners if something changed.
if (changed) {
- sendOnGroupExpandedChange(entry, expanded);
+ sendOnGroupExpandedChange(groupRoot, expanded);
}
}
@Override
- public boolean toggleGroupExpansion(EntryAdapter entry) {
+ public boolean toggleGroupExpansion(EntryAdapter groupRoot) {
NotificationBundleUi.assertInNewMode();
- setGroupExpanded(entry, !isGroupExpanded(entry));
- return isGroupExpanded(entry);
+ setGroupExpanded(groupRoot, !isGroupExpanded(groupRoot));
+ return isGroupExpanded(groupRoot);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
index 3edbfaf..86aa4a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
@@ -51,17 +51,6 @@
NotificationEntry getGroupSummary(@NonNull NotificationEntry entry);
/**
- * Gets the EntryAdapter that is the nearest root of the collection of rows the given entry
- * belongs to. If the given entry is a BundleEntry or an isolated child of a BundleEntry, the
- * BundleEntry will be returned. If the given notification is a group summary NotificationEntry,
- * or a child of a group summary, the summary NotificationEntry will be returned, even if that
- * summary belongs to a BundleEntry. If the entry is a notification that does not belong to any
- * group or bundle grouping, null will be returned.
- */
- @Nullable
- EntryAdapter getGroupRoot(@NonNull EntryAdapter entry);
-
- /**
* @return whether a given notification is a child in a group
*/
boolean isChildInGroup(@NonNull NotificationEntry entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
index a1a23e3..aec0d70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
@@ -60,7 +60,7 @@
@Override
public boolean isGroupRoot(@NonNull EntryAdapter entry) {
NotificationBundleUi.assertInNewMode();
- return entry == entry.getGroupRoot();
+ return entry.isGroupRoot();
}
@Nullable
@@ -76,13 +76,6 @@
return null;
}
- @Nullable
- @Override
- public EntryAdapter getGroupRoot(@NonNull EntryAdapter entry) {
- NotificationBundleUi.assertInNewMode();
- return entry.getGroupRoot();
- }
-
@Override
public boolean isChildInGroup(@NonNull NotificationEntry entry) {
NotificationBundleUi.assertInLegacyMode();
@@ -94,7 +87,7 @@
public boolean isChildInGroup(@NonNull EntryAdapter entry) {
NotificationBundleUi.assertInNewMode();
// An entry is a child if it's not a group root or top level entry, but it is attached.
- return entry.isAttached() && entry != getGroupRoot(entry) && !entry.isTopLevelEntry();
+ return entry.isAttached() && !entry.isGroupRoot() && !entry.isTopLevelEntry();
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt
index 28e3995..222f94b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt
@@ -40,6 +40,16 @@
/** Set of currently active top-level heads up rows to be displayed. */
val activeHeadsUpRows: Flow<Set<HeadsUpRowRepository>>
+ /** Whether the user is swiping on a heads up row */
+ val isTrackingHeadsUp: Flow<Boolean>
+
+ /** @return true if the actively managed heads up notifications contain an entry for this key */
+ fun isHeadsUpEntry(key: String): Boolean
+
+ /**
+ * set whether a HUN is currently animation out, to keep its view container visible during the
+ * animation
+ */
fun setHeadsUpAnimatingAway(animatingAway: Boolean)
/** Snooze the currently pinned HUN. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt
index 95234da..9728fcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt
@@ -19,12 +19,16 @@
import android.graphics.Region
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import dagger.Binds
import dagger.Module
import java.io.PrintWriter
import java.util.stream.Stream
import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
/**
* A manager which handles heads up notifications which is a special mode where they simply peek
@@ -110,7 +114,7 @@
* Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
* well.
*/
- fun isTrackingHeadsUp(): Boolean
+ fun isTrackingHeadsUp(): StateFlow<Boolean>
fun onExpandingFinished()
@@ -151,6 +155,12 @@
fun setAnimationStateHandler(handler: AnimationStateHandler)
/**
+ * Set an entry to be expanded and therefore stick in the heads up area if it's pinned until
+ * it's collapsed again.
+ */
+ fun setExpanded(key: String, row: ExpandableNotificationRow, expanded: Boolean)
+
+ /**
* Set an entry to be expanded and therefore stick in the heads up area if it's pinned until
* it's collapsed again.
*/
@@ -284,12 +294,12 @@
override fun isHeadsUpAnimatingAwayValue() = false
+ override fun isTrackingHeadsUp(): StateFlow<Boolean> = MutableStateFlow(false)
+
override fun isSnoozed(packageName: String) = false
override fun isSticky(key: String?) = false
- override fun isTrackingHeadsUp() = false
-
override fun onExpandingFinished() {}
override fun releaseAllImmediately() {}
@@ -308,6 +318,8 @@
override fun setAnimationStateHandler(handler: AnimationStateHandler) {}
+ override fun setExpanded(key: String, row: ExpandableNotificationRow, expanded: Boolean) {}
+
override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {}
override fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
index 010f080..7c5f3b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
@@ -135,6 +135,8 @@
StateFlowKt.MutableStateFlow(new HashSet<>());
private final MutableStateFlow<Boolean> mHeadsUpAnimatingAway =
StateFlowKt.MutableStateFlow(false);
+ private final MutableStateFlow<Boolean> mTrackingHeadsUp =
+ StateFlowKt.MutableStateFlow(false);
private final HashSet<String> mSwipedOutKeys = new HashSet<>();
private final HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
@VisibleForTesting
@@ -142,7 +144,6 @@
= new ArraySet<>();
private boolean mReleaseOnExpandFinish;
- private boolean mTrackingHeadsUp;
private boolean mIsShadeOrQsExpanded;
private boolean mIsQsExpanded;
private int mStatusBarState;
@@ -417,8 +418,8 @@
}
@Override
- public void setTrackingHeadsUp(boolean trackingHeadsUp) {
- mTrackingHeadsUp = trackingHeadsUp;
+ public void setTrackingHeadsUp(boolean isTrackingHeadsUp) {
+ mTrackingHeadsUp.setValue(isTrackingHeadsUp);
}
@Override
@@ -510,9 +511,7 @@
|| !mAvalancheController.getWaitingEntryList().isEmpty();
}
- /**
- * @return true if the notification is managed by this manager
- */
+ @Override
public boolean isHeadsUpEntry(@NonNull String key) {
return mHeadsUpEntryMap.containsKey(key) || mAvalancheController.isWaiting(key);
}
@@ -879,10 +878,8 @@
ExpandableNotificationRow topRow = topEntry.getRow();
if (topEntry.rowIsChildInGroup()) {
if (NotificationBundleUi.isEnabled()) {
- final EntryAdapter adapter = mGroupMembershipManager.getGroupRoot(
- topRow.getEntryAdapter());
- if (adapter != null) {
- topRow = adapter.getRow();
+ if (topRow.getNotificationParent() != null) {
+ topRow = topRow.getNotificationParent();
}
} else {
final NotificationEntry groupSummary =
@@ -1066,8 +1063,9 @@
}
}
+ @NonNull
@Override
- public boolean isTrackingHeadsUp() {
+ public StateFlow<Boolean> isTrackingHeadsUp() {
return mTrackingHeadsUp;
}
@@ -1093,7 +1091,23 @@
* Set an entry to be expanded and therefore stick in the heads up area if it's pinned
* until it's collapsed again.
*/
+ @Override
+ public void setExpanded(@NonNull String entryKey, @NonNull ExpandableNotificationRow row,
+ boolean expanded) {
+ NotificationBundleUi.assertInNewMode();
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(entryKey);
+ if (headsUpEntry != null && row.getPinnedStatus().isPinned()) {
+ headsUpEntry.setExpanded(expanded);
+ }
+ }
+
+ /**
+ * Set an entry to be expanded and therefore stick in the heads up area if it's pinned
+ * until it's collapsed again.
+ */
+ @Override
public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) {
+ NotificationBundleUi.assertInLegacyMode();
HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
if (headsUpEntry != null && entry.isRowPinned()) {
headsUpEntry.setExpanded(expanded);
@@ -1660,7 +1674,7 @@
mEntriesToRemoveWhenReorderingAllowed.add(entry);
mVisualStabilityProvider.addTemporaryReorderingAllowedListener(
mOnReorderingAllowedListener);
- } else if (mTrackingHeadsUp) {
+ } else if (mTrackingHeadsUp.getValue()) {
mEntriesToRemoveAfterExpand.add(entry);
mLogger.logRemoveEntryAfterExpand(entry);
} else if (mVisualStabilityProvider.isReorderingAllowed()
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 4c74408..5e34b3b 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
@@ -404,17 +404,25 @@
: mGroupMembershipManager.isGroupSummary(mEntry);
if (!shouldShowPublic() && (!mIsMinimized || isExpanded()) && isGroupRoot) {
mGroupExpansionChanging = true;
- final boolean wasExpanded = NotificationBundleUi.isEnabled()
- ? mGroupExpansionManager.isGroupExpanded(mEntryAdapter)
- : mGroupExpansionManager.isGroupExpanded(mEntry);
- boolean nowExpanded = NotificationBundleUi.isEnabled()
- ? mGroupExpansionManager.toggleGroupExpansion(mEntryAdapter)
- : mGroupExpansionManager.toggleGroupExpansion(mEntry);
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
- if (shouldLogExpandClickMetric) {
- mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
+ if (NotificationBundleUi.isEnabled()) {
+ final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntryAdapter);
+ boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntryAdapter);
+ mOnExpandClickListener.onExpandClicked(this, mEntryAdapter, nowExpanded);
+ if (shouldLogExpandClickMetric) {
+ mMetricsLogger.action(
+ MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
+ }
+ onExpansionChanged(true /* userAction */, wasExpanded);
+ } else {
+ final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
+ boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
+ mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ if (shouldLogExpandClickMetric) {
+ mMetricsLogger.action(
+ MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
+ }
+ onExpansionChanged(true /* userAction */, wasExpanded);
}
- onExpansionChanged(true /* userAction */, wasExpanded);
} else if (mEnableNonGroupedNotificationExpand) {
if (v.isAccessibilityFocused()) {
mPrivateLayout.setFocusOnVisibilityChange();
@@ -435,7 +443,11 @@
}
notifyHeightChanged(/* needsAnimation= */ true);
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ if (NotificationBundleUi.isEnabled()) {
+ mOnExpandClickListener.onExpandClicked(this, mEntryAdapter, nowExpanded);
+ } else {
+ mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ }
if (shouldLogExpandClickMetric) {
mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded);
}
@@ -2946,7 +2958,9 @@
&& !mChildrenContainer.showingAsLowPriority()) {
final boolean wasExpanded = isGroupExpanded();
if (NotificationBundleUi.isEnabled()) {
- mGroupExpansionManager.setGroupExpanded(mEntryAdapter, userExpanded);
+ if (mEntryAdapter.isGroupRoot()) {
+ mGroupExpansionManager.setGroupExpanded(mEntryAdapter, userExpanded);
+ }
} else {
mGroupExpansionManager.setGroupExpanded(mEntry, userExpanded);
}
@@ -3440,9 +3454,9 @@
public void makeActionsVisibile() {
setUserExpanded(true, true);
if (isChildInGroup()) {
- if (NotificationBundleUi.isEnabled()) {
- mGroupExpansionManager.setGroupExpanded(mEntryAdapter, true);
- } else {
+ if (!NotificationBundleUi.isEnabled()) {
+ // this is only called if row.getParent() instanceof NotificationStackScrollLayout,
+ // so there is never a group to expand
mGroupExpansionManager.setGroupExpanded(mEntry, true);
}
}
@@ -3728,7 +3742,7 @@
if (!ignoreTemporaryStates && mGuts != null && mGuts.isExposed()) {
return mGuts.getIntrinsicHeight();
} else if (!ignoreTemporaryStates && canShowHeadsUp() && mIsHeadsUp
- && mHeadsUpManager.isTrackingHeadsUp()) {
+ && mHeadsUpManager.isTrackingHeadsUp().getValue()) {
return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
} else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) {
return mChildrenContainer.getMinHeight();
@@ -4023,6 +4037,9 @@
public interface OnExpandClickListener {
void onExpandClicked(NotificationEntry clickedEntry, View clickedView, boolean nowExpanded);
+
+ void onExpandClicked(ExpandableNotificationRow row, EntryAdapter clickedEntry,
+ boolean nowExpanded);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 752a8ab..3987ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
/**
* Wraps the actual notification content view; used to implement behaviors which are different for
@@ -135,7 +136,10 @@
}
// Apps targeting Q should fix their dark mode bugs.
- if (mRow.getEntry().targetSdk >= Build.VERSION_CODES.Q) {
+ int targetSdk = NotificationBundleUi.isEnabled()
+ ? mRow.getEntryAdapter().getTargetSdk()
+ : mRow.getEntry().targetSdk;
+ if (targetSdk >= Build.VERSION_CODES.Q) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
index 02336e4..aa69517 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
@@ -87,6 +87,9 @@
*/
fun onMagneticInteractionEnd(row: ExpandableNotificationRow, velocity: Float? = null)
+ /* Reset any roundness that magnetic targets may have */
+ fun resetRoundness()
+
/**
* Reset any magnetic and roundable targets set, as well as any internal state.
*
@@ -124,6 +127,8 @@
velocity: Float?,
) {}
+ override fun resetRoundness() {}
+
override fun reset() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
index de4af37..da98858 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
@@ -27,6 +27,7 @@
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.pow
+import org.jetbrains.annotations.TestOnly
@SysUISingleton
class MagneticNotificationRowManagerImpl
@@ -41,15 +42,16 @@
var currentState = State.IDLE
private set
- // Magnetic and roundable targets
+ // Magnetic targets
var currentMagneticListeners = listOf<MagneticRowListener?>()
private set
- var currentRoundableTargets: RoundableTargets? = null
- private set
-
private var magneticDetachThreshold = Float.POSITIVE_INFINITY
+ // Has the roundable target been set for the magnetic view that is being swiped.
+ val isSwipedViewRoundableSet: Boolean
+ @TestOnly get() = notificationRoundnessManager.isSwipedViewSet
+
// Animation spring forces
private val detachForce =
SpringForce().setStiffness(DETACH_STIFFNESS).setDampingRatio(DETACH_DAMPING_RATIO)
@@ -83,12 +85,14 @@
sectionsManager: NotificationSectionsManager,
) {
// Update roundable targets
- currentRoundableTargets =
+ notificationRoundnessManager.clear()
+ val currentRoundableTargets =
notificationTargetsHelper.findRoundableTargets(
expandableNotificationRow,
stackScrollLayout,
sectionsManager,
)
+ notificationRoundnessManager.setRoundableTargets(currentRoundableTargets)
// Update magnetic targets
val newListeners =
@@ -127,6 +131,7 @@
currentState = State.PULLING
}
State.PULLING -> {
+ updateRoundness(translation)
if (canTargetBeDismissed) {
pullDismissibleRow(translation)
} else {
@@ -141,6 +146,14 @@
return true
}
+ private fun updateRoundness(translation: Float) {
+ val normalizedTranslation = abs(swipedRowMultiplier * translation) / magneticDetachThreshold
+ notificationRoundnessManager.setRoundnessForAffectedViews(
+ /* roundness */ normalizedTranslation.coerceIn(0f, MAX_PRE_DETACH_ROUNDNESS),
+ /* animate */ false,
+ )
+ }
+
private fun pullDismissibleRow(translation: Float) {
val targetTranslation = swipedRowMultiplier * translation
val crossedThreshold = abs(targetTranslation) >= magneticDetachThreshold
@@ -203,9 +216,10 @@
private fun detach(listener: MagneticRowListener, toPosition: Float) {
listener.cancelMagneticAnimations()
listener.triggerMagneticForce(toPosition, detachForce)
- currentRoundableTargets?.let {
- notificationRoundnessManager.setViewsAffectedBySwipe(it.before, it.swiped, it.after)
- }
+ notificationRoundnessManager.setRoundnessForAffectedViews(
+ /* roundness */ 1f,
+ /* animate */ true,
+ )
msdlPlayer.playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR)
}
@@ -240,6 +254,8 @@
}
}
+ override fun resetRoundness() = notificationRoundnessManager.clear()
+
override fun reset() {
currentMagneticListeners.forEach {
it?.cancelMagneticAnimations()
@@ -247,7 +263,7 @@
}
currentState = State.IDLE
currentMagneticListeners = listOf()
- currentRoundableTargets = null
+ notificationRoundnessManager.clear()
}
private fun List<MagneticRowListener?>.swipedListener(): MagneticRowListener? =
@@ -256,6 +272,11 @@
private fun ExpandableNotificationRow.isSwipedTarget(): Boolean =
magneticRowListener == currentMagneticListeners.swipedListener()
+ private fun NotificationRoundnessManager.clear() = setViewsAffectedBySwipe(null, null, null)
+
+ private fun NotificationRoundnessManager.setRoundableTargets(targets: RoundableTargets) =
+ setViewsAffectedBySwipe(targets.before, targets.swiped, targets.after)
+
enum class State {
IDLE,
TARGETS_SET,
@@ -280,6 +301,9 @@
private const val SNAP_BACK_STIFFNESS = 550f
private const val SNAP_BACK_DAMPING_RATIO = 0.52f
+ // Maximum value of corner roundness that gets applied during the pre-detach dragging
+ private const val MAX_PRE_DETACH_ROUNDNESS = 0.8f
+
private val VIBRATION_ATTRIBUTES_PIPELINING =
VibrationAttributes.Builder()
.setUsage(VibrationAttributes.USAGE_TOUCH)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index fa1843e..a53e837 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -71,10 +71,8 @@
Roundable viewBefore,
ExpandableView viewSwiped,
Roundable viewAfter) {
- // This method requires you to change the roundness of the current View targets and reset
- // the roundness of the old View targets (if any) to 0f.
- // To avoid conflicts, it generates a set of old Views and removes the current Views
- // from this set.
+ // This method caches a new set of current View targets and reset the roundness of the old
+ // View targets (if any) to 0f.
HashSet<Roundable> oldViews = new HashSet<>();
if (mViewBeforeSwipedView != null) oldViews.add(mViewBeforeSwipedView);
if (mSwipedView != null) oldViews.add(mSwipedView);
@@ -83,19 +81,16 @@
mViewBeforeSwipedView = viewBefore;
if (viewBefore != null) {
oldViews.remove(viewBefore);
- viewBefore.requestRoundness(/* top = */ 0f, /* bottom = */ 1f, DISMISS_ANIMATION);
}
mSwipedView = viewSwiped;
if (viewSwiped != null) {
oldViews.remove(viewSwiped);
- viewSwiped.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, DISMISS_ANIMATION);
}
mViewAfterSwipedView = viewAfter;
if (viewAfter != null) {
oldViews.remove(viewAfter);
- viewAfter.requestRoundness(/* top = */ 1f, /* bottom = */ 0f, DISMISS_ANIMATION);
}
// After setting the current Views, reset the views that are still present in the set.
@@ -104,6 +99,34 @@
}
}
+ void setRoundnessForAffectedViews(float roundness) {
+ if (mViewBeforeSwipedView != null) {
+ mViewBeforeSwipedView.requestBottomRoundness(roundness, DISMISS_ANIMATION);
+ }
+
+ if (mSwipedView != null) {
+ mSwipedView.requestRoundness(roundness, roundness, DISMISS_ANIMATION);
+ }
+
+ if (mViewAfterSwipedView != null) {
+ mViewAfterSwipedView.requestTopRoundness(roundness, DISMISS_ANIMATION);
+ }
+ }
+
+ void setRoundnessForAffectedViews(float roundness, boolean animate) {
+ if (mViewBeforeSwipedView != null) {
+ mViewBeforeSwipedView.requestBottomRoundness(roundness, DISMISS_ANIMATION, animate);
+ }
+
+ if (mSwipedView != null) {
+ mSwipedView.requestRoundness(roundness, roundness, DISMISS_ANIMATION, animate);
+ }
+
+ if (mViewAfterSwipedView != null) {
+ mViewAfterSwipedView.requestTopRoundness(roundness, DISMISS_ANIMATION, animate);
+ }
+ }
+
void setClearAllInProgress(boolean isClearingAll) {
mIsClearAllInProgress = isClearingAll;
}
@@ -138,4 +161,8 @@
public void setShouldRoundPulsingViews(boolean shouldRoundPulsingViews) {
mRoundForPulsingViews = shouldRoundPulsingViews;
}
+
+ public boolean isSwipedViewSet() {
+ return mSwipedView != null;
+ }
}
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 3ff18ef..89ede09 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
@@ -1810,16 +1810,22 @@
private ExpandableNotificationRow getTopHeadsUpRow() {
ExpandableNotificationRow row = mTopHeadsUpRow;
- if (row.isChildInGroup()) {
- final NotificationEntry groupSummary =
- mGroupMembershipManager.getGroupSummary(row.getEntry());
- if (groupSummary != null) {
- row = groupSummary.getRow();
+ if (NotificationBundleUi.isEnabled()) {
+ if (mGroupMembershipManager.isChildInGroup(row.getEntryAdapter())
+ && row.isChildInGroup()) {
+ row = row.getNotificationParent();
+ }
+ } else {
+ if (row.isChildInGroup()) {
+ final NotificationEntry groupSummary =
+ mGroupMembershipManager.getGroupSummary(row.getEntry());
+ if (groupSummary != null) {
+ row = groupSummary.getRow();
+ }
}
}
return row;
}
-
/**
* @return the position from where the appear transition ends when expanding.
* Measured in absolute height.
@@ -1966,10 +1972,19 @@
&& touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
if (slidingChild instanceof ExpandableNotificationRow row) {
NotificationEntry entry = row.getEntry();
+ boolean isEntrySummaryForTopHun;
+ if (NotificationBundleUi.isEnabled()) {
+ isEntrySummaryForTopHun = Objects.equals(
+ ((ExpandableNotificationRow) slidingChild).getNotificationParent(),
+ mTopHeadsUpRow);
+ } else {
+ isEntrySummaryForTopHun =
+ mGroupMembershipManager.getGroupSummary(mTopHeadsUpRow.getEntry())
+ == entry;
+ }
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
&& mTopHeadsUpRow != row
- && mGroupMembershipManager.getGroupSummary(mTopHeadsUpRow.getEntry())
- != entry) {
+ && !isEntrySummaryForTopHun) {
continue;
}
return row.getViewAtPosition(touchY - childTop);
@@ -5825,7 +5840,8 @@
targets.getBefore(),
targets.getSwiped(),
targets.getAfter());
-
+ mController.getNotificationRoundnessManager()
+ .setRoundnessForAffectedViews(/* roundness */ 1f);
}
updateFirstAndLastBackgroundViews();
@@ -5836,8 +5852,10 @@
void onSwipeEnd() {
updateFirstAndLastBackgroundViews();
- mController.getNotificationRoundnessManager()
- .setViewsAffectedBySwipe(null, null, null);
+ if (!magneticNotificationSwipes()) {
+ mController.getNotificationRoundnessManager()
+ .setViewsAffectedBySwipe(null, null, null);
+ }
// Round bottom corners for notification right before shelf.
mShelf.updateAppearance();
}
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 888c8cc..01ef90a 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
@@ -545,6 +545,7 @@
public void handleChildViewDismissed(View view) {
// The View needs to clean up the Swipe states, e.g. roundness.
+ mMagneticNotificationRowManager.resetRoundness();
mView.onSwipeEnd();
if (mView.getClearAllInProgress()) {
return;
@@ -629,7 +630,7 @@
@Override
public void onChildSnapBackOvershoots() {
if (Flags.magneticNotificationSwipes()) {
- mNotificationRoundnessManager.setViewsAffectedBySwipe(null, null, null);
+ mMagneticNotificationRowManager.resetRoundness();
}
}
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 4d1d64e..74b1c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
@@ -262,6 +263,23 @@
}
}
+ @Override
+ public void onExpandClicked(ExpandableNotificationRow row, EntryAdapter clickedEntry,
+ boolean nowExpanded) {
+ mHeadsUpManager.setExpanded(clickedEntry.getKey(), row, nowExpanded);
+ mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE);
+ if (nowExpanded) {
+ if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+ mShadeTransitionController.goToLockedShade(row, /* needsQSAnimation = */ true);
+ } else if (clickedEntry.isSensitive().getValue() && isInLockedDownShade()) {
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ // launch the bouncer if the device is locked
+ mActivityStarter.dismissKeyguardThenExecute(() -> false /* dismissAction */
+ , null /* cancelRunnable */, false /* afterKeyguardGone */);
+ }
+ }
+ }
+
/** @return true if the Shade is shown over the Lockscreen, and the device is locked */
private boolean isInLockedDownShade() {
if (SceneContainerFlag.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 0c10aaa..feb4769 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -148,7 +148,7 @@
junkListener?.let(animation::removeUpdateListener)
junkListener =
jankListenerFactory.show(view).also(animation::addUpdateListener)
- animation.animateToFinalPosition(FRACTION_SHOW)
+ animation.suspendAnimate(FRACTION_SHOW)
}
is VolumeDialogVisibilityModel.Dismissed -> {
tracer.traceVisibilityEnd(it)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
index 516541d..242865b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
@@ -28,6 +28,7 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.ExtendedMockito.times
import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.dx.mockito.inline.extended.MockedVoidMethod
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
@@ -245,7 +246,7 @@
@Test
fun onInputDeviceAdded_btStylus_firstUsed_setsFlag() {
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
- verify({ InputSettings.setStylusEverUsed(mContext, true) }, times(1))
+ verify(MockedVoidMethod { InputSettings.setStylusEverUsed(mContext, true) }, times(1))
}
@Test
@@ -511,7 +512,7 @@
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
- verify({ InputSettings.setStylusEverUsed(mContext, true) }, times(1))
+ verify(MockedVoidMethod { InputSettings.setStylusEverUsed(mContext, true) }, times(1))
}
@Test
@@ -612,7 +613,7 @@
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
- verify({ InputSettings.setStylusEverUsed(mContext, true) }, never())
+ verify(MockedVoidMethod { InputSettings.setStylusEverUsed(mContext, true) }, never())
}
@Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 09c632c..771e1a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -91,16 +91,17 @@
import com.android.systemui.util.Assert.runWithCurrentThreadAsMainThread
import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import org.junit.Assert.assertTrue
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mockito
+import org.mockito.kotlin.whenever
class ExpandableNotificationRowBuilder(
private val context: Context,
@@ -149,7 +150,10 @@
mGroupExpansionManager = GroupExpansionManagerImpl(mDumpManager, mGroupMembershipManager)
mUserManager = Mockito.mock(UserManager::class.java, STUB_ONLY)
- mHeadsUpManager = Mockito.mock(HeadsUpManager::class.java, STUB_ONLY)
+ mHeadsUpManager =
+ Mockito.mock(HeadsUpManager::class.java, STUB_ONLY).apply {
+ whenever(isTrackingHeadsUp()).thenReturn(MutableStateFlow(false))
+ }
mIconManager =
IconManager(
Mockito.mock(CommonNotifCollection::class.java, STUB_ONLY),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
index 1fa6236..3406d81 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
@@ -35,6 +35,10 @@
orderedHeadsUpRows.map { it.firstOrNull() }.distinctUntilChanged()
override val activeHeadsUpRows: Flow<Set<HeadsUpRowRepository>> =
orderedHeadsUpRows.map { it.toSet() }.distinctUntilChanged()
+ override val isTrackingHeadsUp: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ override fun isHeadsUpEntry(key: String): Boolean =
+ orderedHeadsUpRows.value.any { it.key == key }
override fun setHeadsUpAnimatingAway(animatingAway: Boolean) {
isHeadsUpAnimatingAway.value = animatingAway
diff --git a/services/core/java/com/android/server/integrity/OWNERS b/services/core/java/com/android/server/integrity/OWNERS
index 33561fd..352724a 100644
--- a/services/core/java/com/android/server/integrity/OWNERS
+++ b/services/core/java/com/android/server/integrity/OWNERS
@@ -1,5 +1,4 @@
omernebil@google.com
khelmy@google.com
mdchurchill@google.com
-sturla@google.com
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 05794cd..7ce52b1 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1515,8 +1515,10 @@
r.mOverrideTaskTransition);
r.mTransitionController.setOverrideAnimation(
TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
- enterAnim, exitAnim, backgroundColor, r.mOverrideTaskTransition), r,
- null /* startCallback */, null /* finishCallback */);
+ enterAnim, 0 /* changeResId */, exitAnim,
+ r.mOverrideTaskTransition),
+ r, null /* startCallback */, null /* finishCallback */);
+ r.mTransitionController.setOverrideBackgroundColor(backgroundColor);
}
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8f816d2..dcc3cf7 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5075,8 +5075,8 @@
switch (animationType) {
case ANIM_CUSTOM:
options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
- pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
- pendingOptions.getCustomBackgroundColor(),
+ pendingOptions.getCustomEnterResId(), 0 /* changeResId */,
+ pendingOptions.getCustomExitResId(),
pendingOptions.getOverrideTaskTransition());
startCallback = pendingOptions.getAnimationStartedListener();
finishCallback = pendingOptions.getAnimationFinishedListener();
@@ -5137,6 +5137,10 @@
mTransitionController.setOverrideAnimation(options, this, startCallback,
finishCallback);
}
+ final int backgroundColor = pendingOptions.getCustomBackgroundColor();
+ if (backgroundColor != 0) {
+ mTransitionController.setOverrideBackgroundColor(backgroundColor);
+ }
}
void clearAllDrawn() {
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
index 1d447dd..5041ded 100644
--- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -208,6 +208,8 @@
setCurrentAlphaBlur(dim, finishTransaction);
if (targetAlpha == 0f && !dim.isDimming()) {
dim.remove(finishTransaction);
+ // Ensure the finishTransaction is applied if pending
+ dim.mHostContainer.scheduleAnimation();
}
mLocalAnimationAdapter = null;
mAlphaAnimationSpec = null;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index da28378..a874ef6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -46,6 +46,7 @@
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.STATE_UNKNOWN;
+import static android.view.Display.TYPE_EXTERNAL;
import static android.view.Display.isSuspendedState;
import static android.view.InsetsSource.ID_IME;
import static android.view.Surface.ROTATION_0;
@@ -155,6 +156,7 @@
import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
+import static com.android.window.flags.Flags.enablePersistingDensityScaleForConnectedDisplays;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -426,6 +428,12 @@
* @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int)
*/
int mBaseDisplayDensity = 0;
+
+ /**
+ * Ratio between overridden display density for current user and the initial display density,
+ * used only for external displays.
+ */
+ float mExternalDisplayForcedDensityRatio = 0.0f;
boolean mIsDensityForced = false;
/**
@@ -3065,6 +3073,17 @@
mDisplayPolicy.physicalDisplayChanged();
}
+ // Real display metrics changed, so we should also update initial values.
+ mInitialDisplayWidth = newWidth;
+ mInitialDisplayHeight = newHeight;
+ mInitialDisplayDensity = newDensity;
+ mInitialPhysicalXDpi = newXDpi;
+ mInitialPhysicalYDpi = newYDpi;
+ mInitialDisplayCutout = newCutout;
+ mInitialRoundedCorners = newRoundedCorners;
+ mInitialDisplayShape = newDisplayShape;
+ mCurrentUniqueDisplayId = newUniqueId;
+
// If there is an override set for base values - use it, otherwise use new values.
updateBaseDisplayMetrics(mIsSizeForced ? mBaseDisplayWidth : newWidth,
mIsSizeForced ? mBaseDisplayHeight : newHeight,
@@ -3081,16 +3100,6 @@
mWmService.mDisplayWindowSettings.applyRotationSettingsToDisplayLocked(this);
}
- // Real display metrics changed, so we should also update initial values.
- mInitialDisplayWidth = newWidth;
- mInitialDisplayHeight = newHeight;
- mInitialDisplayDensity = newDensity;
- mInitialPhysicalXDpi = newXDpi;
- mInitialPhysicalYDpi = newYDpi;
- mInitialDisplayCutout = newCutout;
- mInitialRoundedCorners = newRoundedCorners;
- mInitialDisplayShape = newDisplayShape;
- mCurrentUniqueDisplayId = newUniqueId;
reconfigureDisplayLocked();
if (physicalDisplayChanged) {
@@ -3143,6 +3152,12 @@
+ mBaseDisplayHeight + " on display:" + getDisplayId());
}
}
+ // Update the base density if there is a forced density ratio.
+ if (enablePersistingDensityScaleForConnectedDisplays()
+ && mIsDensityForced && mExternalDisplayForcedDensityRatio != 0.0f) {
+ mBaseDisplayDensity = (int)
+ (mInitialDisplayDensity * mExternalDisplayForcedDensityRatio + 0.5);
+ }
if (mDisplayReady && !mDisplayPolicy.shouldKeepCurrentDecorInsets()) {
mDisplayPolicy.mDecorInsets.invalidate();
}
@@ -3172,6 +3187,14 @@
if (density == getInitialDisplayDensity()) {
density = 0;
}
+ // Save the new density ratio to settings for external displays.
+ if (enablePersistingDensityScaleForConnectedDisplays()
+ && mDisplayInfo.type == TYPE_EXTERNAL) {
+ mExternalDisplayForcedDensityRatio = (float)
+ mBaseDisplayDensity / getInitialDisplayDensity();
+ mWmService.mDisplayWindowSettings.setForcedDensityRatio(getDisplayInfo(),
+ mExternalDisplayForcedDensityRatio);
+ }
mWmService.mDisplayWindowSettings.setForcedDensity(getDisplayInfo(), density, userId);
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a03ecf5..4908df0 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1875,18 +1875,40 @@
}
void notifyDisplayAddSystemDecorations() {
- mHandler.post(() -> {
+ if (enableDisplayContentModeManagement()) {
final int displayId = getDisplayId();
- StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
- if (statusBar != null) {
- statusBar.onDisplayAddSystemDecorations(displayId);
- }
- final WallpaperManagerInternal wpMgr = LocalServices
- .getService(WallpaperManagerInternal.class);
- if (wpMgr != null) {
- wpMgr.onDisplayAddSystemDecorations(displayId);
- }
- });
+ final boolean isSystemDecorationsSupported =
+ mDisplayContent.isSystemDecorationsSupported();
+ final boolean isHomeSupported = mDisplayContent.isHomeSupported();
+ mHandler.post(() -> {
+ if (isSystemDecorationsSupported) {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.onDisplayAddSystemDecorations(displayId);
+ }
+ }
+ if (isHomeSupported) {
+ final WallpaperManagerInternal wpMgr =
+ LocalServices.getService(WallpaperManagerInternal.class);
+ if (wpMgr != null) {
+ wpMgr.onDisplayAddSystemDecorations(displayId);
+ }
+ }
+ });
+ } else {
+ mHandler.post(() -> {
+ final int displayId = getDisplayId();
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.onDisplayAddSystemDecorations(displayId);
+ }
+ final WallpaperManagerInternal wpMgr = LocalServices
+ .getService(WallpaperManagerInternal.class);
+ if (wpMgr != null) {
+ wpMgr.onDisplayAddSystemDecorations(displayId);
+ }
+ });
+ }
}
void notifyDisplayRemoveSystemDecorations() {
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 1173875..c6892e9 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -23,11 +23,10 @@
import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
+import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement;
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
-import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
@@ -100,6 +99,13 @@
mSettingsProvider.updateOverrideSettings(info, overrideSettings);
}
+ void setForcedDensityRatio(@NonNull DisplayInfo info, float ratio) {
+ final SettingsProvider.SettingsEntry overrideSettings =
+ mSettingsProvider.getOverrideSettings(info);
+ overrideSettings.mForcedDensityRatio = ratio;
+ mSettingsProvider.updateOverrideSettings(info, overrideSettings);
+ }
+
void setForcedScalingMode(@NonNull DisplayContent displayContent, @ForceScalingMode int mode) {
if (displayContent.isDefaultDisplay) {
Settings.Global.putInt(mService.mContext.getContentResolver(),
@@ -367,6 +373,7 @@
mFixedToUserRotation);
final boolean hasDensityOverride = settings.mForcedDensity != 0;
+ final boolean hasDensityOverrideRatio = settings.mForcedDensityRatio != 0.0f;
final boolean hasSizeOverride = settings.mForcedWidth != 0 && settings.mForcedHeight != 0;
dc.mIsDensityForced = hasDensityOverride;
dc.mIsSizeForced = hasSizeOverride;
@@ -378,6 +385,10 @@
final int height = hasSizeOverride ? settings.mForcedHeight : dc.mInitialDisplayHeight;
final int density = hasDensityOverride ? settings.mForcedDensity
: dc.getInitialDisplayDensity();
+ if (hasDensityOverrideRatio) {
+ dc.mExternalDisplayForcedDensityRatio = settings.mForcedDensityRatio;
+ }
+
dc.updateBaseDisplayMetrics(width, height, density, dc.mBaseDisplayPhysicalXDpi,
dc.mBaseDisplayPhysicalYDpi);
@@ -496,6 +507,13 @@
int mForcedWidth;
int mForcedHeight;
int mForcedDensity;
+ /**
+ * The ratio of the forced density to the initial density of the display. This is only
+ * saved for external displays, and used to make sure ratio between forced density and
+ * initial density persist when a resolution change happens. Ratio is updated when
+ * mForcedDensity is changed.
+ */
+ float mForcedDensityRatio;
@Nullable
@ForceScalingMode
Integer mForcedScalingMode;
@@ -561,6 +579,10 @@
mForcedDensity = other.mForcedDensity;
changed = true;
}
+ if (other.mForcedDensityRatio != mForcedDensityRatio) {
+ mForcedDensityRatio = other.mForcedDensityRatio;
+ changed = true;
+ }
if (!Objects.equals(other.mForcedScalingMode, mForcedScalingMode)) {
mForcedScalingMode = other.mForcedScalingMode;
changed = true;
@@ -649,6 +671,11 @@
mForcedDensity = delta.mForcedDensity;
changed = true;
}
+ if (delta.mForcedDensityRatio != 0
+ && delta.mForcedDensityRatio != mForcedDensityRatio) {
+ mForcedDensityRatio = delta.mForcedDensityRatio;
+ changed = true;
+ }
if (delta.mForcedScalingMode != null
&& !Objects.equals(delta.mForcedScalingMode, mForcedScalingMode)) {
mForcedScalingMode = delta.mForcedScalingMode;
@@ -713,6 +740,7 @@
&& mUserRotationMode == null
&& mUserRotation == null
&& mForcedWidth == 0 && mForcedHeight == 0 && mForcedDensity == 0
+ && mForcedDensityRatio == 0.0f
&& mForcedScalingMode == null
&& mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
&& mShouldShowWithInsecureKeyguard == null
@@ -736,6 +764,7 @@
&& mForcedHeight == that.mForcedHeight
&& mForcedDensity == that.mForcedDensity
&& mRemoveContentMode == that.mRemoveContentMode
+ && mForcedDensityRatio == that.mForcedDensityRatio
&& Objects.equals(mUserRotationMode, that.mUserRotationMode)
&& Objects.equals(mUserRotation, that.mUserRotation)
&& Objects.equals(mForcedScalingMode, that.mForcedScalingMode)
@@ -755,10 +784,11 @@
@Override
public int hashCode() {
return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth,
- mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
- mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mIsHomeSupported,
- mImePolicy, mFixedToUserRotation, mIgnoreOrientationRequest,
- mIgnoreDisplayCutout, mDontMoveToTop, mIgnoreActivitySizeRestrictions);
+ mForcedHeight, mForcedDensity, mForcedDensityRatio, mForcedScalingMode,
+ mRemoveContentMode, mShouldShowWithInsecureKeyguard,
+ mShouldShowSystemDecors, mIsHomeSupported, mImePolicy, mFixedToUserRotation,
+ mIgnoreOrientationRequest, mIgnoreDisplayCutout, mDontMoveToTop,
+ mIgnoreActivitySizeRestrictions);
}
@Override
@@ -770,6 +800,7 @@
+ ", mForcedWidth=" + mForcedWidth
+ ", mForcedHeight=" + mForcedHeight
+ ", mForcedDensity=" + mForcedDensity
+ + ", mForcedDensityRatio=" + mForcedDensityRatio
+ ", mForcedScalingMode=" + mForcedScalingMode
+ ", mRemoveContentMode=" + mRemoveContentMode
+ ", mShouldShowWithInsecureKeyguard=" + mShouldShowWithInsecureKeyguard
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 7135c3b..e7a1fdd 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -511,6 +511,8 @@
0 /* defaultValue */);
settingsEntry.mForcedDensity = getIntAttribute(parser, "forcedDensity",
0 /* defaultValue */);
+ settingsEntry.mForcedDensityRatio = parser.getAttributeFloat(null, "forcedDensityRatio",
+ 0.0f /* defaultValue */);
settingsEntry.mForcedScalingMode = getIntegerAttribute(parser, "forcedScalingMode",
null /* defaultValue */);
settingsEntry.mRemoveContentMode = getIntAttribute(parser, "removeContentMode",
@@ -599,6 +601,10 @@
if (settingsEntry.mForcedDensity != 0) {
out.attributeInt(null, "forcedDensity", settingsEntry.mForcedDensity);
}
+ if (settingsEntry.mForcedDensityRatio != 0.0f) {
+ out.attributeFloat(null, "forcedDensityRatio",
+ settingsEntry.mForcedDensityRatio);
+ }
if (settingsEntry.mForcedScalingMode != null) {
out.attributeInt(null, "forcedScalingMode",
settingsEntry.mForcedScalingMode);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f309372..e864ecf 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2794,13 +2794,7 @@
}
startHomeOnDisplay(mCurrentUser, reason, displayContent.getDisplayId());
- if (enableDisplayContentModeManagement()) {
- if (displayContent.isSystemDecorationsSupported()) {
- displayContent.getDisplayPolicy().notifyDisplayAddSystemDecorations();
- }
- } else {
- displayContent.getDisplayPolicy().notifyDisplayAddSystemDecorations();
- }
+ displayContent.getDisplayPolicy().notifyDisplayAddSystemDecorations();
}
@Override
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 63a8c86..78c6f90 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -77,6 +77,7 @@
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
+import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -257,6 +258,12 @@
/** Custom activity-level animation options and callbacks. */
private AnimationOptions mOverrideOptions;
+ /**
+ * Custom background color
+ */
+ @ColorInt
+ private int mOverrideBackgroundColor;
+
private IRemoteCallback mClientAnimationStartCallback = null;
private IRemoteCallback mClientAnimationFinishCallback = null;
@@ -1011,6 +1018,13 @@
}
/**
+ * Set background color for collecting transition.
+ */
+ void setOverrideBackgroundColor(@ColorInt int backgroundColor) {
+ mOverrideBackgroundColor = backgroundColor;
+ }
+
+ /**
* Call this when all known changes related to this transition have been applied. Until
* all participants have finished drawing, the transition can still collect participants.
*
@@ -2038,8 +2052,7 @@
if (container.asActivityRecord() != null
|| shouldApplyAnimOptionsToTask(container.asTask())) {
changes.get(i).setAnimationOptions(mOverrideOptions);
- // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions.
- changes.get(i).setBackgroundColor(mOverrideOptions.getBackgroundColor());
+ changes.get(i).setBackgroundColor(mOverrideBackgroundColor);
} else if (shouldApplyAnimOptionsToEmbeddedTf(container.asTaskFragment())) {
// We only override AnimationOptions because backgroundColor should be from
// TaskFragmentAnimationParams.
@@ -2495,7 +2508,12 @@
sb.append(" id=" + mSyncId);
sb.append(" type=" + transitTypeToString(mType));
sb.append(" flags=0x" + Integer.toHexString(mFlags));
- sb.append(" overrideAnimOptions=" + mOverrideOptions);
+ if (mOverrideOptions != null) {
+ sb.append(" overrideAnimOptions=" + mOverrideOptions);
+ }
+ if (mOverrideBackgroundColor != 0) {
+ sb.append(" overrideBackgroundColor=" + mOverrideBackgroundColor);
+ }
if (!mChanges.isEmpty()) {
sb.append(" c=[");
for (int i = 0; i < mChanges.size(); i++) {
@@ -3006,9 +3024,7 @@
final Rect parentBounds = parent.getBounds();
change.setEndRelOffset(bounds.left - parentBounds.left,
bounds.top - parentBounds.top);
- if (Flags.activityEmbeddingOverlayPresentationFlag()) {
- change.setEndParentSize(parentBounds.width(), parentBounds.height());
- }
+ change.setEndParentSize(parentBounds.width(), parentBounds.height());
int endRotation = target.getWindowConfiguration().getRotation();
if (activityRecord != null) {
// TODO(b/227427984): Shell needs to aware letterbox.
@@ -3059,18 +3075,14 @@
AnimationOptions animOptions = null;
if (activityRecord != null && animOptionsForActivityTransition != null) {
animOptions = animOptionsForActivityTransition;
- } else if (Flags.activityEmbeddingOverlayPresentationFlag()
- && isEmbeddedTaskFragment) {
+ } else if (isEmbeddedTaskFragment) {
final TaskFragmentAnimationParams params = taskFragment.getAnimationParams();
if (params.hasOverrideAnimation()) {
// Only set AnimationOptions if there's any animation override.
- // We use separated field for backgroundColor, and
- // AnimationOptions#backgroundColor will be removed in long term.
animOptions = AnimationOptions.makeCustomAnimOptions(
taskFragment.getTask().getBasePackageName(),
params.getOpenAnimationResId(), params.getChangeAnimationResId(),
- params.getCloseAnimationResId(), 0 /* backgroundColor */,
- false /* overrideTaskTransition */);
+ params.getCloseAnimationResId(), false /* overrideTaskTransition */);
animOptions.setUserId(taskFragment.getTask().mUserId);
}
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index ba7f364..11c5c93 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -23,6 +23,7 @@
import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -938,12 +939,19 @@
}
/** @see Transition#setOverrideAnimation */
- void setOverrideAnimation(TransitionInfo.AnimationOptions options, ActivityRecord r,
- @Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
+ void setOverrideAnimation(@NonNull TransitionInfo.AnimationOptions options,
+ @NonNull ActivityRecord r, @Nullable IRemoteCallback startCallback,
+ @Nullable IRemoteCallback finishCallback) {
if (mCollectingTransition == null) return;
mCollectingTransition.setOverrideAnimation(options, r, startCallback, finishCallback);
}
+ /** @see Transition#setOverrideBackgroundColor */
+ void setOverrideBackgroundColor(@ColorInt int backgroundColor) {
+ if (mCollectingTransition == null) return;
+ mCollectingTransition.setOverrideBackgroundColor(backgroundColor);
+ }
+
void setNoAnimation(WindowContainer wc) {
if (mCollectingTransition == null) return;
mCollectingTransition.setNoAnimation(wc);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 6e16d29..2cd860a 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -1294,7 +1294,7 @@
mInstrumentation.waitForIdleSync();
final var postScreenshot = mInstrumentation.getUiAutomation().takeScreenshot();
mDumpOnFailure.dumpOnFailure("post-getUiObject", postScreenshot);
- assertWithMessage("UiObject with " + bySelector + " was found").that(uiObject).isNull();
+ assertWithMessage("UiObject with " + bySelector + " was found").that(uiObject).isNotNull();
return uiObject;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 4b53f13..46bc70e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -383,6 +383,12 @@
// When we override new reasonable throttle values after init...
mCountDown = new CountDownLatch(8);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_COMPACT_THROTTLE_1,
Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -400,12 +406,6 @@
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_COMPACT_THROTTLE_6,
Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ,
- Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ,
- Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1), false);
assertThat(mCountDown.await(7, TimeUnit.SECONDS)).isTrue();
// Then those flags values are reflected in the compactor.
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 009ce88..d702cae 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -33,9 +33,6 @@
"test-apps/DisplayManagerTestApp/src/**/*.java",
],
- kotlincflags: [
- "-Werror",
- ],
static_libs: [
"a11ychecker",
"aatf",
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 02ed67b..cfd501a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -83,6 +83,7 @@
import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT;
+import static com.android.window.flags.Flags.FLAG_ENABLE_PERSISTING_DENSITY_SCALE_FOR_CONNECTED_DISPLAYS;
import static com.google.common.truth.Truth.assertThat;
@@ -2872,6 +2873,74 @@
assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
}
+ @EnableFlags(FLAG_ENABLE_PERSISTING_DENSITY_SCALE_FOR_CONNECTED_DISPLAYS)
+ @Test
+ public void testForcedDensityRatioSetForExternalDisplays_persistDensityScaleFlagEnabled() {
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.type = Display.TYPE_EXTERNAL;
+ final DisplayContent displayContent = createNewDisplay(displayInfo);
+ final int baseWidth = 1280;
+ final int baseHeight = 720;
+ final int baseDensity = 320;
+ final float baseXDpi = 60;
+ final float baseYDpi = 60;
+
+ displayContent.mInitialDisplayWidth = baseWidth;
+ displayContent.mInitialDisplayHeight = baseHeight;
+ displayContent.mInitialDisplayDensity = baseDensity;
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity, baseXDpi,
+ baseYDpi);
+
+ final int forcedDensity = 640;
+
+ // Verify that forcing the density is honored and the size doesn't change.
+ displayContent.setForcedDensity(forcedDensity, 0 /* userId */);
+ verifySizes(displayContent, baseWidth, baseHeight, forcedDensity);
+
+ // Verify that density ratio is set correctly.
+ assertEquals((float) forcedDensity / baseDensity,
+ displayContent.mExternalDisplayForcedDensityRatio, 0.01);
+ }
+
+ @EnableFlags(FLAG_ENABLE_PERSISTING_DENSITY_SCALE_FOR_CONNECTED_DISPLAYS)
+ @Test
+ public void testForcedDensityUpdateForExternalDisplays_persistDensityScaleFlagEnabled() {
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.type = Display.TYPE_EXTERNAL;
+ final DisplayContent displayContent = createNewDisplay(displayInfo);
+ final int baseWidth = 1280;
+ final int baseHeight = 720;
+ final int baseDensity = 320;
+ final float baseXDpi = 60;
+ final float baseYDpi = 60;
+
+ displayContent.mInitialDisplayWidth = baseWidth;
+ displayContent.mInitialDisplayHeight = baseHeight;
+ displayContent.mInitialDisplayDensity = baseDensity;
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity, baseXDpi,
+ baseYDpi);
+
+ final int forcedDensity = 640;
+
+ // Verify that forcing the density is honored and the size doesn't change.
+ displayContent.setForcedDensity(forcedDensity, 0 /* userId */);
+ verifySizes(displayContent, baseWidth, baseHeight, forcedDensity);
+
+ // Verify that density ratio is set correctly.
+ assertEquals((float) 2.0f,
+ displayContent.mExternalDisplayForcedDensityRatio, 0.001);
+
+
+ displayContent.mInitialDisplayDensity = 160;
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity, baseXDpi,
+ baseYDpi);
+
+ // Verify that forced density is updated based on the ratio.
+ assertEquals(320, displayContent.mBaseDisplayDensity);
+ }
+
@EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
@Test
public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index b1cad51..a57577a96 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -42,6 +42,7 @@
import android.annotation.NonNull;
import android.app.WindowConfiguration;
import android.content.ContentResolver;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.view.Display;
@@ -53,6 +54,7 @@
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -272,6 +274,23 @@
mSecondaryDisplay.mBaseDisplayDensity);
}
+ @EnableFlags(Flags.FLAG_ENABLE_PERSISTING_DENSITY_SCALE_FOR_CONNECTED_DISPLAYS)
+ @Test
+ public void testSetForcedDensityRatio() {
+ mDisplayWindowSettings.setForcedDensity(mSecondaryDisplay.getDisplayInfo(),
+ 300 /* density */, 0 /* userId */);
+ mDisplayWindowSettings.setForcedDensityRatio(mSecondaryDisplay.getDisplayInfo(),
+ 2.0f /* ratio */);
+ mDisplayWindowSettings.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+ assertEquals(mSecondaryDisplay.mInitialDisplayDensity * 2.0f,
+ mSecondaryDisplay.mBaseDisplayDensity, 0.01);
+
+ mWm.clearForcedDisplayDensityForUser(mSecondaryDisplay.getDisplayId(), 0 /* userId */);
+ assertEquals(mSecondaryDisplay.mInitialDisplayDensity,
+ mSecondaryDisplay.mBaseDisplayDensity);
+ }
+
@Test
public void testSetForcedScalingMode() {
mDisplayWindowSettings.setForcedScalingMode(mSecondaryDisplay,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index c0cb09f..5699c29 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2088,14 +2088,16 @@
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptions() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
- TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
+ final ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ final TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
.makeCustomAnimOptions("testPackage", Resources.ID_NULL,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
- Color.GREEN, false /* overrideTaskTransition */);
+ false /* overrideTaskTransition */);
mTransition.setOverrideAnimation(options, r, null /* startCallback */,
null /* finishCallback */);
+ final int expectedBackgroundColor = Color.GREEN;
+ mTransition.setOverrideBackgroundColor(expectedBackgroundColor);
mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
@@ -2115,12 +2117,13 @@
assertEquals("Activity change's AnimationOptions must be overridden.",
options, activityChange.getAnimationOptions());
assertEquals("Activity change's background color must be overridden.",
- options.getBackgroundColor(), activityChange.getBackgroundColor());
+ expectedBackgroundColor, activityChange.getBackgroundColor());
+
}
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_haveTaskFragmentAnimParams() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ final ActivityRecord r = initializeOverrideAnimationOptionsTest();
final TaskFragment embeddedTf = mTransition.mTargets.get(2).mContainer.asTaskFragment();
embeddedTf.setAnimationParams(new TaskFragmentAnimationParams.Builder()
@@ -2128,13 +2131,15 @@
.setOpenAnimationResId(0x12345678)
.build());
- TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
+ final TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
.makeCustomAnimOptions("testPackage", Resources.ID_NULL,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
- Color.GREEN, false /* overrideTaskTransition */);
+ false /* overrideTaskTransition */);
mTransition.setOverrideAnimation(options, r, null /* startCallback */,
null /* finishCallback */);
+ final int expectedBackgroundColor = Color.GREEN;
+ mTransition.setOverrideBackgroundColor(expectedBackgroundColor);
final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
final TransitionInfo.Change taskChange = mInfo.getChanges().get(1);
@@ -2147,7 +2152,7 @@
.makeCustomAnimOptions("testPackage", 0x12345678,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
- 0, false /* overrideTaskTransition */);
+ false /* overrideTaskTransition */);
embeddedTfChange.setAnimationOptions(expectedOptions);
mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
@@ -2163,19 +2168,21 @@
assertEquals("Activity change's AnimationOptions must be overridden.",
options, activityChange.getAnimationOptions());
assertEquals("Activity change's background color must be overridden.",
- options.getBackgroundColor(), activityChange.getBackgroundColor());
+ expectedBackgroundColor, activityChange.getBackgroundColor());
}
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptionsWithTaskOverride() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
- TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
+ final ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ final TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
.makeCustomAnimOptions("testPackage", Resources.ID_NULL,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
- Color.GREEN, true /* overrideTaskTransition */);
+ true /* overrideTaskTransition */);
mTransition.setOverrideAnimation(options, r, null /* startCallback */,
null /* finishCallback */);
+ final int expectedBackgroundColor = Color.GREEN;
+ mTransition.setOverrideBackgroundColor(expectedBackgroundColor);
mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
@@ -2189,7 +2196,7 @@
assertEquals("Task change's AnimationOptions must be overridden.",
options, taskChange.getAnimationOptions());
assertEquals("Task change's background color must be overridden.",
- options.getBackgroundColor(), taskChange.getBackgroundColor());
+ expectedBackgroundColor, taskChange.getBackgroundColor());
assertEquals("Embedded TF change's AnimationOptions must be overridden.",
options, embeddedTfChange.getAnimationOptions());
assertEquals("Embedded TF change's background color must be overridden.",
@@ -2197,7 +2204,7 @@
assertEquals("Activity change's AnimationOptions must be overridden.",
options, activityChange.getAnimationOptions());
assertEquals("Activity change's background color must be overridden.",
- options.getBackgroundColor(), activityChange.getBackgroundColor());
+ expectedBackgroundColor, activityChange.getBackgroundColor());
}
private ActivityRecord initializeOverrideAnimationOptionsTest() {
diff --git a/tests/Codegen/OWNERS b/tests/Codegen/OWNERS
index da723b3..e69de29 100644
--- a/tests/Codegen/OWNERS
+++ b/tests/Codegen/OWNERS
@@ -1 +0,0 @@
-eugenesusla@google.com
\ No newline at end of file
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 168141b..1f0bd61 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -19,9 +19,6 @@
"src/**/*.kt",
],
asset_dirs: ["assets"],
- kotlincflags: [
- "-Werror",
- ],
platform_apis: true,
certificate: "platform",
static_libs: [