diff options
9 files changed, 132 insertions, 13 deletions
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index bd3a5e4fe67c..558bae727415 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5307,6 +5307,12 @@ <!-- Content description of the expand button icon in the notification when expanded.--> <string name="expand_button_content_description_expanded">Collapse</string> + <!-- A11y announcement when a view is collapsed. --> + <string name="content_description_collapsed">Collapsed</string> + + <!-- A11y announcement when a view is expanded. --> + <string name="content_description_expanded">Expanded</string> + <!-- Accessibility action description on the expand button. --> <string name="expand_action_accessibility">toggle expansion</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3c1a42feef25..dc2f74bb76fe 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3826,6 +3826,9 @@ <java-symbol type="string" name="expand_button_content_description_collapsed" /> <java-symbol type="string" name="expand_button_content_description_expanded" /> + <java-symbol type="string" name="content_description_collapsed" /> + <java-symbol type="string" name="content_description_expanded" /> + <!-- Colon separated list of package names that should be granted Notification Listener access --> <java-symbol type="string" name="config_defaultListenerAccessPackages" /> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8fa4a9e3932f..79435885f410 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1748,6 +1748,9 @@ <!-- Notification: Snooze panel: Snooze undo button label. [CHAR LIMIT=50]--> <string name="snooze_undo">Undo</string> + <!-- Notification: Snooze panel: Snooze undo content description for a11y. [CHAR LIMIT=NONE]--> + <string name="snooze_undo_content_description">Undo notification snooze</string> + <!-- Notification: Snooze panel: message indicating how long the notification was snoozed for. [CHAR LIMIT=100]--> <string name="snoozed_for_time">Snoozed for <xliff:g id="time_amount" example="15 minutes">%1$s</xliff:g></string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index f094102ad88f..d4ac19556b0a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -40,7 +40,15 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapApps(TransitionInfo info, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) { - return wrap(info, t, leashMap, new TransitionUtil.LeafTaskFilter()); + // LeafTaskFilter is order-dependent, so the same object needs to be used for all Change + // objects. That's why it's constructed here and captured by the lambda instead of building + // a new one ad hoc every time. + TransitionUtil.LeafTaskFilter taskFilter = new TransitionUtil.LeafTaskFilter(); + return wrap(info, t, leashMap, (change) -> { + // Intra-task activity -> activity transitions should be categorized as apps. + if (change.getActivityComponent() != null) return true; + return taskFilter.test(change); + }); } /** @@ -53,8 +61,12 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapNonApps(TransitionInfo info, boolean wallpapers, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) { - return wrap(info, t, leashMap, (change) -> (wallpapers - ? TransitionUtil.isWallpaper(change) : TransitionUtil.isNonApp(change))); + return wrap(info, t, leashMap, (change) -> { + // Intra-task activity -> activity transitions should be categorized as apps. + if (change.getActivityComponent() != null) return false; + return wallpapers + ? TransitionUtil.isWallpaper(change) : TransitionUtil.isNonApp(change); + }); } private static RemoteAnimationTarget[] wrap(TransitionInfo info, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java index 3443da19895e..99a6f6a59bd0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java @@ -45,13 +45,15 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; + import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.res.R; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; +import com.android.systemui.res.R; import java.util.ArrayList; import java.util.List; @@ -77,6 +79,9 @@ public class NotificationSnooze extends LinearLayout private static final LogMaker UNDO_LOG = new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE) .setType(MetricsEvent.TYPE_ACTION); + + private static final String PARAGRAPH_SEPARATOR = "\u2029"; + private NotificationGuts mGutsContainer; private NotificationSwipeActionHelper mSnoozeListener; private StatusBarNotification mSbn; @@ -111,8 +116,7 @@ public class NotificationSnooze extends LinearLayout } @VisibleForTesting - SnoozeOption getDefaultOption() - { + SnoozeOption getDefaultOption() { return mDefaultOption; } @@ -130,6 +134,8 @@ public class NotificationSnooze extends LinearLayout mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default); mUndoButton = (TextView) findViewById(R.id.undo); mUndoButton.setOnClickListener(this); + mUndoButton.setContentDescription( + getContext().getString(R.string.snooze_undo_content_description)); mExpandButton = (ImageView) findViewById(R.id.expand_button); mDivider = findViewById(R.id.divider); mDivider.setAlpha(0f); @@ -163,6 +169,46 @@ public class NotificationSnooze extends LinearLayout info.addAction(action); } } + + mSnoozeView.setAccessibilityDelegate(new AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + // Replace "Double tap to activate" prompt with "Double tap to expand/collapse" + AccessibilityAction customClick = new AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLICK, getExpandActionString()); + info.addAction(customClick); + } + }); + } + + /** + * Update the content description of the snooze view based on the snooze option and whether the + * snooze options are expanded or not. + * For example, this will be something like "Collapsed\u2029Snooze for 1 hour". The paragraph + * separator is added to introduce a break in speech, to match what TalkBack does by default + * when you e.g. press on a notification. + */ + private void updateContentDescription() { + mSnoozeView.setContentDescription( + getExpandStateString() + PARAGRAPH_SEPARATOR + mSelectedOptionText.getText()); + } + + /** Returns "collapse" if the snooze options are expanded, or "expand" otherwise. */ + @NonNull + private String getExpandActionString() { + return mContext.getString(mExpanded + ? com.android.internal.R.string.expand_button_content_description_expanded + : com.android.internal.R.string.expand_button_content_description_collapsed); + } + + + /** Returns "expanded" if the snooze options are expanded, or "collapsed" otherwise. */ + @NonNull + private String getExpandStateString() { + return mContext.getString( + (mExpanded ? com.android.internal.R.string.content_description_expanded + : com.android.internal.R.string.content_description_collapsed)); } @Override @@ -179,6 +225,8 @@ public class NotificationSnooze extends LinearLayout if (so.getAccessibilityAction() != null && so.getAccessibilityAction().getId() == action) { setSelected(so, true); + mSnoozeView.sendAccessibilityEvent( + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); return true; } } @@ -290,11 +338,10 @@ public class NotificationSnooze extends LinearLayout int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification : com.android.internal.R.drawable.ic_expand_notification; mExpandButton.setImageResource(drawableId); - mExpandButton.setContentDescription(mContext.getString(show - ? com.android.internal.R.string.expand_button_content_description_expanded - : com.android.internal.R.string.expand_button_content_description_collapsed)); + mExpandButton.setContentDescription(getExpandActionString()); if (mExpanded != show) { mExpanded = show; + updateContentDescription(); animateSnoozeOptions(show); if (mGutsContainer != null) { mGutsContainer.onHeightChanged(); @@ -335,8 +382,11 @@ public class NotificationSnooze extends LinearLayout } private void setSelected(SnoozeOption option, boolean userAction) { - mSelectedOption = option; - mSelectedOptionText.setText(option.getConfirmation()); + if (option != mSelectedOption) { + mSelectedOption = option; + mSelectedOptionText.setText(option.getConfirmation()); + updateContentDescription(); + } showSnoozeOptions(false); hideSelectedOption(); if (userAction) { diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 4e7a9bd2881a..f4e995719b6d 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -303,8 +303,15 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } else { if (DEBUG) appendLog("non-freeform-task-display-area"); } + final boolean isUpdatingExistingTaskWindowingMode = task != null + && task.getRequestedOverrideWindowingMode() != WINDOWING_MODE_UNDEFINED + && launchMode != task.getRequestedOverrideWindowingMode(); + if (DEBUG && isUpdatingExistingTaskWindowingMode) { + appendLog("updating-existing-task-windowing-mode"); + } // If launch mode matches display windowing mode, let it inherit from display. outParams.mWindowingMode = launchMode == suggestedDisplayArea.getWindowingMode() + && !isUpdatingExistingTaskWindowingMode ? WINDOWING_MODE_UNDEFINED : launchMode; if (phase == PHASE_WINDOWING_MODE) { diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java index 4c487a70390d..ba7297790dc9 100644 --- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java +++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java @@ -367,8 +367,11 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider, // TODO(b/312397262): consider virtual displays cases synchronized (mLock) { if (mIsDualDisplayBlockingEnabled - && !mExternalDisplaysConnected.get(displayId, false) - && mDisplayManager.getDisplay(displayId).getType() == TYPE_EXTERNAL) { + && !mExternalDisplaysConnected.get(displayId, false)) { + var display = mDisplayManager.getDisplay(displayId); + if (display == null || display.getType() != TYPE_EXTERNAL) { + return; + } mExternalDisplaysConnected.put(displayId, true); // Only update the supported state when going from 0 external display to 1 diff --git a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java index ddf4a089e76e..04cebab107ee 100644 --- a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java +++ b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java @@ -591,6 +591,20 @@ public final class FoldableDeviceStateProviderTest { } @Test + public void testOnDisplayAddedWithNullDisplayDoesNotThrowNPE() { + createProvider( + createConfig( + /* identifier= */ 1, /* name= */ "ONE", + /* flags= */0, (c) -> true, + FoldableDeviceStateProvider::hasNoConnectedExternalDisplay) + ); + + when(mDisplayManager.getDisplay(1)).thenReturn(null); + // This call should not throw NPE. + mProvider.onDisplayAdded(1); + } + + @Test public void hasNoConnectedDisplay_afterExternalDisplayAddedAndRemoved_returnsTrue() { createProvider( createConfig( diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 07cfbf094e5d..16f963fd6c3e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -628,6 +628,27 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { } @Test + public void testLaunchWindowingModeUpdatesExistingTask() { + final TestDisplayContent freeformDisplay = createNewDisplayContent( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea(); + ActivityRecord activity = createSourceActivity(freeformDisplay); + final Task task = activity.getTask(); + task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + + assertEquals(RESULT_CONTINUE, + new CalculateRequestBuilder() + .setTask(task) + .setOptions(options) + .calculate()); + + assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode); + } + + @Test public void testBoundsInOptionsInfersFreeformWithResizeableActivity() { final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchBounds(new Rect(0, 0, 100, 100)); |