Merge "Fix ScreenshotShelfView gesture handling" into 24D1-dev
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index ef1d87d..a1dd415 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -40,6 +40,7 @@
*/
interface ScreenshotActionsProvider {
fun onScrollChipReady(onClick: Runnable)
+ fun onScrollChipInvalidated()
fun setCompletedScreenshot(result: ScreenshotSavedResult)
/**
@@ -67,6 +68,8 @@
@Assisted val requestId: String,
@Assisted val actionExecutor: ActionExecutor,
) : ScreenshotActionsProvider {
+ private var addedScrollChip = false
+ private var onScrollClick: Runnable? = null
private var pendingAction: ((ScreenshotSavedResult) -> Unit)? = null
private var result: ScreenshotSavedResult? = null
@@ -122,18 +125,26 @@
}
override fun onScrollChipReady(onClick: Runnable) {
- viewModel.addAction(
- ActionButtonAppearance(
- AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_scroll),
- context.resources.getString(R.string.screenshot_scroll_label),
- context.resources.getString(R.string.screenshot_scroll_label),
- ),
- showDuringEntrance = true,
- ) {
- onClick.run()
+ onScrollClick = onClick
+ if (!addedScrollChip) {
+ viewModel.addAction(
+ ActionButtonAppearance(
+ AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_scroll),
+ context.resources.getString(R.string.screenshot_scroll_label),
+ context.resources.getString(R.string.screenshot_scroll_label),
+ ),
+ showDuringEntrance = true,
+ ) {
+ onScrollClick?.run()
+ }
+ addedScrollChip = true
}
}
+ override fun onScrollChipInvalidated() {
+ onScrollClick = null
+ }
+
override fun setCompletedScreenshot(result: ScreenshotSavedResult) {
if (this.result != null) {
Log.e(TAG, "Got a second completed screenshot for existing request!")
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 0217fc2..dfd742c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -591,7 +591,11 @@
if (mConfigChanges.applyNewConfig(mContext.getResources())) {
// Hide the scroll chip until we know it's available in this
// orientation
- mViewProxy.hideScrollChip();
+ if (screenshotShelfUi2()) {
+ mActionsProvider.onScrollChipInvalidated();
+ } else {
+ mViewProxy.hideScrollChip();
+ }
// Delay scroll capture eval a bit to allow the underlying activity
// to set up in the new orientation.
mScreenshotHandler.postDelayed(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index 853e50a..896c3bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -37,6 +37,7 @@
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@RunWith(AndroidTestingRunner::class)
@@ -111,6 +112,47 @@
assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_CHOOSER)
}
+ @Test
+ fun scrollChipClicked_callsOnClick() = runTest {
+ actionsProvider = createActionsProvider()
+
+ val onScrollClick = mock<Runnable>()
+ val numActions = viewModel.actions.value.size
+ actionsProvider.onScrollChipReady(onScrollClick)
+ viewModel.actions.value[numActions].onClicked!!.invoke()
+
+ verify(onScrollClick).run()
+ }
+
+ @Test
+ fun scrollChipClicked_afterInvalidate_doesNothing() = runTest {
+ actionsProvider = createActionsProvider()
+
+ val onScrollClick = mock<Runnable>()
+ val numActions = viewModel.actions.value.size
+ actionsProvider.onScrollChipReady(onScrollClick)
+ actionsProvider.onScrollChipInvalidated()
+ viewModel.actions.value[numActions].onClicked!!.invoke()
+
+ verify(onScrollClick, never()).run()
+ }
+
+ @Test
+ fun scrollChipClicked_afterUpdate_runsNewAction() = runTest {
+ actionsProvider = createActionsProvider()
+
+ val onScrollClick = mock<Runnable>()
+ val onScrollClick2 = mock<Runnable>()
+ val numActions = viewModel.actions.value.size
+ actionsProvider.onScrollChipReady(onScrollClick)
+ actionsProvider.onScrollChipInvalidated()
+ actionsProvider.onScrollChipReady(onScrollClick2)
+ viewModel.actions.value[numActions].onClicked!!.invoke()
+
+ verify(onScrollClick2).run()
+ verify(onScrollClick, never()).run()
+ }
+
private fun createActionsProvider(): ScreenshotActionsProvider {
return DefaultScreenshotActionsProvider(
context,
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index e155126..cd7f73c 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -423,7 +423,6 @@
// Searching previous
final ActivityRecord prevActivity = currentTask.getActivity((below) -> !below.finishing,
currentActivity, false /*includeBoundary*/, true /*traverseTopToBottom*/);
-
final TaskFragment currTF = currentActivity.getTaskFragment();
if (currTF != null && currTF.asTask() == null) {
// The currentActivity is embedded, search for the candidate previous activities.
@@ -432,13 +431,34 @@
outPrevActivities.add(prevActivity);
return true;
}
- if (currTF.getAdjacentTaskFragment() != null) {
- // The two TFs are adjacent (visually displayed side-by-side), search if any
- // activity below the lowest one
- // If companion, those two TF will be closed together.
- if (currTF.getCompanionTaskFragment() != null) {
+ if (currTF.getAdjacentTaskFragment() == null) {
+ final TaskFragment nextTF = findNextTaskFragment(currentTask, currTF);
+ if (isSecondCompanionToFirst(currTF, nextTF)) {
+ // TF is isStacked, search bottom activity from companion TF.
+ //
+ // Sample hierarchy: search for underPrevious if any.
+ // Current TF
+ // Companion TF (bottomActivityInCompanion)
+ // Bottom Activity not inside companion TF (underPrevious)
+ // find bottom activity in Companion TF.
+ final ActivityRecord bottomActivityInCompanion = nextTF.getActivity(
+ (below) -> !below.finishing, false /* traverseTopToBottom */);
+ final ActivityRecord underPrevious = currentTask.getActivity(
+ (below) -> !below.finishing, bottomActivityInCompanion,
+ false /*includeBoundary*/, true /*traverseTopToBottom*/);
+ if (underPrevious != null) {
+ outPrevActivities.add(underPrevious);
+ addPreviousAdjacentActivityIfExist(underPrevious, outPrevActivities);
+ }
+ return true;
+ }
+ } else {
+ // If adjacent TF has companion to current TF, those two TF will be closed together.
+ final TaskFragment adjacentTF = currTF.getAdjacentTaskFragment();
+ if (isSecondCompanionToFirst(currTF, adjacentTF)) {
+ // The two TFs are adjacent (visually displayed side-by-side), search if any
+ // activity below the lowest one.
final WindowContainer commonParent = currTF.getParent();
- final TaskFragment adjacentTF = currTF.getAdjacentTaskFragment();
final TaskFragment lowerTF = commonParent.mChildren.indexOf(currTF)
< commonParent.mChildren.indexOf(adjacentTF)
? currTF : adjacentTF;
@@ -452,25 +472,6 @@
// Unable to predict if no companion, it can only close current activity and make
// prev Activity full screened.
return false;
- } else if (currTF.getCompanionTaskFragment() != null) {
- // TF is isStacked, search bottom activity from companion TF.
- //
- // Sample hierarchy: search for underPrevious if any.
- // Current TF
- // Companion TF (bottomActivityInCompanion)
- // Bottom Activity not inside companion TF (underPrevious)
- final TaskFragment companionTF = currTF.getCompanionTaskFragment();
- // find bottom activity in Companion TF.
- final ActivityRecord bottomActivityInCompanion = companionTF.getActivity(
- (below) -> !below.finishing, false /* traverseTopToBottom */);
- final ActivityRecord underPrevious = currentTask.getActivity(
- (below) -> !below.finishing, bottomActivityInCompanion,
- false /*includeBoundary*/, true /*traverseTopToBottom*/);
- if (underPrevious != null) {
- outPrevActivities.add(underPrevious);
- addPreviousAdjacentActivityIfExist(underPrevious, outPrevActivities);
- }
- return true;
}
}
@@ -485,6 +486,24 @@
return true;
}
+ private static TaskFragment findNextTaskFragment(@NonNull Task currentTask,
+ @NonNull TaskFragment topTF) {
+ final int topIndex = currentTask.mChildren.indexOf(topTF);
+ if (topIndex <= 0) {
+ return null;
+ }
+ final WindowContainer next = currentTask.mChildren.get(topIndex - 1);
+ return next.asTaskFragment();
+ }
+
+ /**
+ * Whether the second TF has set companion to first TF.
+ * When set, the second TF will be removed by organizer if the first TF is removed.
+ */
+ private static boolean isSecondCompanionToFirst(TaskFragment first, TaskFragment second) {
+ return second != null && second.getCompanionTaskFragment() == first;
+ }
+
private static void addPreviousAdjacentActivityIfExist(@NonNull ActivityRecord prevActivity,
@NonNull ArrayList<ActivityRecord> outPrevActivities) {
final TaskFragment prevTF = prevActivity.getTaskFragment();
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index b75869c..5517e70 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1373,7 +1373,8 @@
}
final boolean shouldShowLetterboxUi =
- (mActivityRecord.isInLetterboxAnimation() || isSurfaceVisible(mainWindow))
+ (mActivityRecord.isInLetterboxAnimation() || mActivityRecord.isVisible()
+ || mActivityRecord.isVisibleRequested())
&& mainWindow.areAppWindowBoundsLetterboxed()
// Check for FLAG_SHOW_WALLPAPER explicitly instead of using
// WindowContainer#showWallpaper because the later will return true when this
@@ -1385,12 +1386,6 @@
return shouldShowLetterboxUi;
}
- @VisibleForTesting
- boolean isSurfaceVisible(WindowState mainWindow) {
- return mainWindow.isOnScreen() && (mActivityRecord.isVisible()
- || mActivityRecord.isVisibleRequested());
- }
-
private Color getLetterboxBackgroundColor() {
final WindowState w = mActivityRecord.findMainWindow();
if (w == null || w.isLetterboxedForDisplayCutout()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index c29547f..9b2a880 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -291,13 +291,22 @@
assertTrue(predictable);
outPrevActivities.clear();
- // Stacked + companion => predict for previous task
+ // Stacked + top companion to bottom but bottom didn't => predict for previous activity
tf2.setCompanionTaskFragment(tf1);
predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr,
outPrevActivities);
+ assertTrue(outPrevActivities.contains(prevAr));
+ assertTrue(predictable);
+ tf2.setCompanionTaskFragment(null);
+ outPrevActivities.clear();
+
+ // Stacked + next companion to top => predict for previous task
+ tf1.setCompanionTaskFragment(tf2);
+ predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr,
+ outPrevActivities);
assertTrue(outPrevActivities.isEmpty());
assertTrue(predictable);
- tf2.setCompanionTaskFragment(null);
+ tf1.setCompanionTaskFragment(null);
// Adjacent + no companion => unable to predict
// TF1 | TF2
@@ -314,11 +323,13 @@
// Adjacent + companion => predict for previous task
tf1.setCompanionTaskFragment(tf2);
- tf2.setCompanionTaskFragment(tf1);
predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr,
outPrevActivities);
assertTrue(outPrevActivities.isEmpty());
assertTrue(predictable);
+ tf1.setCompanionTaskFragment(null);
+
+ tf2.setCompanionTaskFragment(tf1);
predictable = BackNavigationController.getAnimatablePrevActivities(task, prevAr,
outPrevActivities);
assertTrue(outPrevActivities.isEmpty());
@@ -361,18 +372,27 @@
tf3.setAdjacentTaskFragment(null);
final TaskFragment tf4 = createTaskFragmentWithActivity(task);
- // Stacked + companion => predict for previous activity below companion.
+ // Stacked + next companion to top => predict for previous activity below companion.
// Tf4
// TF3
// TF2
// TF1
- tf4.setCompanionTaskFragment(tf3);
tf3.setCompanionTaskFragment(tf4);
topAr = tf4.getTopMostActivity();
predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr,
outPrevActivities);
assertTrue(outPrevActivities.contains(tf2.getTopMostActivity()));
assertTrue(predictable);
+ outPrevActivities.clear();
+ tf3.setCompanionTaskFragment(null);
+
+ // Stacked + top companion to next but next one didn't => predict for previous activity.
+ tf4.setCompanionTaskFragment(tf3);
+ topAr = tf4.getTopMostActivity();
+ predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr,
+ outPrevActivities);
+ assertTrue(outPrevActivities.contains(tf3.getTopMostActivity()));
+ assertTrue(predictable);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 6806f6a..0b760b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -609,7 +609,8 @@
doReturn(false).when(mActivity).isInLetterboxAnimation();
assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
- doReturn(false).when(mainWindow).isOnScreen();
+ doReturn(false).when(mActivity).isVisibleRequested();
+ doReturn(false).when(mActivity).isVisible();
assertEquals(0, mController.getRoundedCornersRadius(mainWindow));
doReturn(true).when(mActivity).isInLetterboxAnimation();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 36c1c8c..bf15bc8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -909,8 +909,7 @@
assertEquals(window, mActivity.findMainWindow());
spyOn(mActivity.mLetterboxUiController);
- doReturn(true).when(mActivity.mLetterboxUiController)
- .isSurfaceVisible(any());
+ doReturn(true).when(mActivity).isVisibleRequested();
assertTrue(mActivity.mLetterboxUiController.shouldShowLetterboxUi(
mActivity.findMainWindow()));
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 88acbab..25d8a3d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -13112,39 +13112,41 @@
})
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @Nullable ServiceState getServiceState(@IncludeLocationData int includeLocationData) {
- return getServiceStateForSubscriber(getSubId(),
+ return getServiceStateForSlot(SubscriptionManager.getSlotIndex(getSubId()),
includeLocationData != INCLUDE_LOCATION_DATA_FINE,
includeLocationData == INCLUDE_LOCATION_DATA_NONE);
}
/**
- * Returns the service state information on specified subscription. Callers require
- * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+ * Returns the service state information on specified SIM slot.
*
- * May return {@code null} when the subscription is inactive or when there was an error
+ * May return {@code null} when the {@code slotIndex} is invalid or when there was an error
* communicating with the phone process.
+ *
+ * @param slotIndex of phone whose service state is returned
* @param renounceFineLocationAccess Set this to true if the caller would not like to receive
* location related information which will be sent if the caller already possess
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission
* @param renounceCoarseLocationAccess Set this to true if the caller would not like to
* receive location related information which will be sent if the caller already possess
* {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions.
+ * @return Service state on specified SIM slot.
*/
- private ServiceState getServiceStateForSubscriber(int subId,
- boolean renounceFineLocationAccess,
+ private ServiceState getServiceStateForSlot(int slotIndex, boolean renounceFineLocationAccess,
boolean renounceCoarseLocationAccess) {
try {
ITelephony service = getITelephony();
if (service != null) {
- return service.getServiceStateForSubscriber(subId, renounceFineLocationAccess,
- renounceCoarseLocationAccess, getOpPackageName(), getAttributionTag());
+ return service.getServiceStateForSlot(slotIndex,
+ renounceFineLocationAccess, renounceCoarseLocationAccess,
+ getOpPackageName(), getAttributionTag());
}
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
+ Log.e(TAG, "Error calling ITelephony#getServiceStateForSlot", e);
} catch (NullPointerException e) {
AnomalyReporter.reportAnomaly(
UUID.fromString("e2bed88e-def9-476e-bd71-3e572a8de6d1"),
- "getServiceStateForSubscriber " + subId + " NPE");
+ "getServiceStateForSlot " + slotIndex + " NPE");
}
return null;
}
@@ -13159,7 +13161,35 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public ServiceState getServiceStateForSubscriber(int subId) {
- return getServiceStateForSubscriber(subId, false, false);
+ return getServiceStateForSlot(
+ SubscriptionManager.getSlotIndex(subId), false, false);
+ }
+
+ /**
+ * Returns the service state information on specified SIM slot.
+ *
+ * If you want continuous updates of service state info, register a {@link TelephonyCallback}
+ * that implements {@link TelephonyCallback.ServiceStateListener} through
+ * {@link #registerTelephonyCallback}.
+ *
+ * May return {@code null} when the {@code slotIndex} is invalid or when there was an error
+ * communicating with the phone process
+ *
+ * See {@link #getActiveModemCount()} to get the total number of slots
+ * that are active on the device.
+ *
+ * @param slotIndex of phone whose service state is returned
+ * @return ServiceState on specified SIM slot.
+ *
+ * @hide
+ */
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ })
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+ public @Nullable ServiceState getServiceStateForSlot(int slotIndex) {
+ return getServiceStateForSlot(slotIndex, false, false);
}
/**
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 0bb5fd5..47f53f3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -1015,13 +1015,27 @@
* @hide
*/
public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3;
+ /**
+ * Datagram type indicating that the datagram to be sent or received is of type SOS message and
+ * is the last message to emergency service provider indicating still needs help.
+ * @hide
+ */
+ public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP = 4;
+ /**
+ * Datagram type indicating that the datagram to be sent or received is of type SOS message and
+ * is the last message to emergency service provider indicating no more help is needed.
+ * @hide
+ */
+ public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5;
/** @hide */
@IntDef(prefix = "DATAGRAM_TYPE_", value = {
DATAGRAM_TYPE_UNKNOWN,
DATAGRAM_TYPE_SOS_MESSAGE,
DATAGRAM_TYPE_LOCATION_SHARING,
- DATAGRAM_TYPE_KEEP_ALIVE
+ DATAGRAM_TYPE_KEEP_ALIVE,
+ DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP,
+ DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED
})
@Retention(RetentionPolicy.SOURCE)
public @interface DatagramType {}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f591f40..ccc68bc 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1399,19 +1399,18 @@
oneway void requestModemActivityInfo(in ResultReceiver result);
/**
- * Get the service state on specified subscription
- * @param subId Subscription id
+ * Get the service state on specified SIM slot.
+ * @param slotIndex of phone whose service state is returned
* @param renounceFineLocationAccess Set this to true if the caller would not like to
* receive fine location related information
* @param renounceCoarseLocationAccess Set this to true if the caller would not like to
* receive coarse location related information
* @param callingPackage The package making the call
* @param callingFeatureId The feature in the package
- * @return Service state on specified subscription.
+ * @return Service state on specified SIM slot.
*/
- ServiceState getServiceStateForSubscriber(int subId, boolean renounceFineLocationAccess,
- boolean renounceCoarseLocationAccess,
- String callingPackage, String callingFeatureId);
+ ServiceState getServiceStateForSlot(int slotIndex, boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess, String callingPackage, String callingFeatureId);
/**
* Returns the URI for the per-account voicemail ringtone set in Phone settings.