diff options
14 files changed, 234 insertions, 53 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index fe3d4f6b39bf..07efad89010a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4380,7 +4380,7 @@ modes dimensions {@link config_minPercentageMultiWindowSupportWidth} the device supports to determine if the activity can be shown in multi windowing modes. --> - <integer name="config_respectsActivityMinWidthHeightMultiWindow">0</integer> + <integer name="config_respectsActivityMinWidthHeightMultiWindow">-1</integer> <!-- This value is only used when the device checks activity min height to determine if it can be shown in multi windowing modes. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 74cce68f270b..dcc2d93060c9 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -377,8 +377,16 @@ class TaskContainer { @Nullable TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) { - return getContainer(container -> container.hasAppearedActivity(activityToken) - || container.hasPendingAppearedActivity(activityToken)); + // When the new activity is launched to the topmost TF because the source activity + // was in that TF, and the source activity is finished before resolving the new activity, + // we will try to see if the new activity match a rule with the split activities below. + // If matched, it can be reparented. + final TaskFragmentContainer taskFragmentContainer + = getContainer(container -> container.hasPendingAppearedActivity(activityToken)); + if (taskFragmentContainer != null) { + return taskFragmentContainer; + } + return getContainer(container -> container.hasAppearedActivity(activityToken)); } @Nullable diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java index 7fab371cb790..bc4916a607a3 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java @@ -535,7 +535,8 @@ public class TaskFragmentContainerTest { // container1. container2.setInfo(mTransaction, mInfo); - assertTrue(container2.hasActivity(mActivity.getActivityToken())); + assertTrue(container1.hasActivity(mActivity.getActivityToken())); + assertFalse(container2.hasActivity(mActivity.getActivityToken())); // When the pending appeared record is removed from container1, we respect the appeared // record in container2. container1.removePendingAppearedActivity(mActivity.getActivityToken()); diff --git a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml index 045b975a854e..462a49ccb1eb 100644 --- a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml +++ b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml @@ -99,11 +99,11 @@ </LinearLayout> - <FrameLayout + + <LinearLayout android:minHeight="@dimen/letterbox_restart_dialog_button_height" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" - style="?android:attr/buttonBarButtonStyle" android:layout_gravity="end"> <Button @@ -133,7 +133,7 @@ android:text="@string/letterbox_restart_restart" android:contentDescription="@string/letterbox_restart_restart"/> - </FrameLayout> + </LinearLayout> </LinearLayout> diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java index 31f89960836b..ef4c3ef0d321 100644 --- a/media/java/android/media/projection/MediaProjection.java +++ b/media/java/android/media/projection/MediaProjection.java @@ -16,6 +16,8 @@ package android.media.projection; +import static android.view.Display.DEFAULT_DISPLAY; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.compat.CompatChanges; @@ -29,6 +31,7 @@ import android.hardware.display.VirtualDisplayConfig; import android.os.Build; import android.os.Handler; import android.os.RemoteException; +import android.os.UserManager; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; @@ -70,6 +73,7 @@ public final class MediaProjection { private final DisplayManager mDisplayManager; @NonNull private final Map<Callback, CallbackRecord> mCallbacks = new ArrayMap<>(); + private final int mDisplayId; /** @hide */ public MediaProjection(Context context, IMediaProjection impl) { @@ -88,6 +92,11 @@ public final class MediaProjection { throw new RuntimeException("Failed to start media projection", e); } mDisplayManager = displayManager; + + final UserManager userManager = context.getSystemService(UserManager.class); + mDisplayId = userManager.isVisibleBackgroundUsersSupported() + ? userManager.getMainDisplayIdAssignedToUser() + : DEFAULT_DISPLAY; } /** @@ -156,6 +165,7 @@ public final class MediaProjection { if (surface != null) { builder.setSurface(surface); } + builder.setDisplayIdToMirror(mDisplayId); return createVirtualDisplay(builder, callback, handler); } @@ -234,6 +244,7 @@ public final class MediaProjection { if (surface != null) { builder.setSurface(surface); } + builder.setDisplayIdToMirror(mDisplayId); return createVirtualDisplay(builder, callback, handler); } diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index 6776f611559c..33650d91e6a3 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -735,10 +735,15 @@ static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, j } static status_t attachAndQeueuGraphicBuffer(JNIEnv* env, JNIImageWriterContext *ctx, - sp<Surface> surface, sp<GraphicBuffer> gb, jlong timestampNs, jint dataSpace, + sp<GraphicBuffer> gb, jlong timestampNs, jint dataSpace, jint left, jint top, jint right, jint bottom, jint transform, jint scalingMode) { status_t res = OK; // Step 1. Attach Image + sp<Surface> surface = ctx->getProducer(); + if (surface == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", + "Producer surface is null, ImageWriter seems already closed"); + } res = surface->attachBuffer(gb.get()); if (res != OK) { ALOGE("Attach image failed: %s (%d)", strerror(-res), res); @@ -835,7 +840,6 @@ static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nat return -1; } - sp<Surface> surface = ctx->getProducer(); if (isFormatOpaque(ctx->getBufferFormat()) != isFormatOpaque(nativeHalFormat)) { jniThrowException(env, "java/lang/IllegalStateException", "Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa"); @@ -851,7 +855,7 @@ static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nat return -1; } - return attachAndQeueuGraphicBuffer(env, ctx, surface, buffer->mGraphicBuffer, timestampNs, + return attachAndQeueuGraphicBuffer(env, ctx, buffer->mGraphicBuffer, timestampNs, dataSpace, left, top, right, bottom, transform, scalingMode); } @@ -866,7 +870,6 @@ static jint ImageWriter_attachAndQueueGraphicBuffer(JNIEnv* env, jobject thiz, j return -1; } - sp<Surface> surface = ctx->getProducer(); if (isFormatOpaque(ctx->getBufferFormat()) != isFormatOpaque(nativeHalFormat)) { jniThrowException(env, "java/lang/IllegalStateException", "Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa"); @@ -880,7 +883,8 @@ static jint ImageWriter_attachAndQueueGraphicBuffer(JNIEnv* env, jobject thiz, j "Trying to attach an invalid graphic buffer"); return -1; } - return attachAndQeueuGraphicBuffer(env, ctx, surface, graphicBuffer, timestampNs, + + return attachAndQeueuGraphicBuffer(env, ctx, graphicBuffer, timestampNs, dataSpace, left, top, right, bottom, transform, scalingMode); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt index 1ec78742f0dd..4d81317b7755 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat as assertThatRepository @@ -81,7 +82,7 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() { assertThat(values) .containsExactly( - null, // LOCKSCREEN -> AOD does not have any specific surface visibility. + null // LOCKSCREEN -> AOD does not have any specific surface visibility. ) transitionRepository.sendTransitionStep( @@ -118,6 +119,53 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() { } @Test + fun draggingToPrimaryBouncerUpdateIsSent() = + testScope.runTest { + underTest.start() + transitionRepository.sendTransitionSteps( + from = KeyguardState.OFF, + to = KeyguardState.LOCKSCREEN, + testScope, + ) + + val steps by collectValues(transitionRepository.transitions) + + shadeRepository.setLegacyShadeExpansion(0f) + shadeRepository.setLegacyShadeTracking(true) + keyguardRepository.setKeyguardDismissible(false) + keyguardRepository.setStatusBarState(KEYGUARD) + runCurrent() + + // User starts dragging up + shadeRepository.setLegacyShadeExpansion(0.1f) + runCurrent() + + assertThatRepository(transitionRepository) + .startedTransition( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.PRIMARY_BOUNCER, + ) + + // FakeKeyguardRepository doesn't send the step, so do that + transitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.PRIMARY_BOUNCER, + value = 0f, + ) + ) + runCurrent() + + // Update is sent + shadeRepository.setLegacyShadeExpansion(0.2f) + runCurrent() + + assertThatRepository(transitionRepository) + .updatedTransition(value = 1f, state = TransitionState.RUNNING) + } + + @Test @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionsToGone_whenDismissFlingWhileDismissable_flagEnabled() = testScope.runTest { @@ -132,10 +180,7 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() { runCurrent() assertThatRepository(transitionRepository) - .startedTransition( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, - ) + .startedTransition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE) } @Test @@ -184,15 +229,12 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() { true, ActivityManager.RunningTaskInfo().apply { topActivityType = WindowConfiguration.ACTIVITY_TYPE_STANDARD - } + }, ) runCurrent() assertThatRepository(transitionRepository) - .startedTransition( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - ) + .startedTransition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.OCCLUDED) } @Test @@ -207,14 +249,11 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() { true, ActivityManager.RunningTaskInfo().apply { topActivityType = WindowConfiguration.ACTIVITY_TYPE_DREAM - } + }, ) runCurrent() assertThatRepository(transitionRepository) - .startedTransition( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DREAMING, - ) + .startedTransition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.DREAMING) } } diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java index 76df9c96c801..fb00d6e16dcc 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java @@ -75,6 +75,9 @@ import javax.inject.Named; * touches are consumed. */ public class TouchMonitor { + // An incrementing id used to identify the touch monitor instance. + private static int sNextInstanceId = 0; + private final Logger mLogger; // This executor is used to protect {@code mActiveTouchSessions} from being modified // concurrently. Any operation that adds or removes values should use this executor. @@ -138,7 +141,7 @@ public class TouchMonitor { completer.set(predecessor); } - if (mActiveTouchSessions.isEmpty()) { + if (mActiveTouchSessions.isEmpty() && mInitialized) { if (mStopMonitoringPending) { stopMonitoring(false); } else { @@ -271,7 +274,7 @@ public class TouchMonitor { @Override public void onDestroy(LifecycleOwner owner) { - stopMonitoring(true); + destroy(); } }; @@ -279,6 +282,11 @@ public class TouchMonitor { * When invoked, instantiates a new {@link InputSession} to monitor touch events. */ private void startMonitoring() { + if (!mInitialized) { + mLogger.w("attempting to startMonitoring when not initialized"); + return; + } + mLogger.i("startMonitoring(): monitoring started"); stopMonitoring(true); @@ -587,7 +595,7 @@ public class TouchMonitor { mDisplayHelper = displayHelper; mWindowManagerService = windowManagerService; mConfigurationInteractor = configurationInteractor; - mLoggingName = loggingName + ":TouchMonitor"; + mLoggingName = loggingName + ":TouchMonitor[" + sNextInstanceId++ + "]"; mLogger = new Logger(logBuffer, mLoggingName); } @@ -613,7 +621,8 @@ public class TouchMonitor { */ public void destroy() { if (!mInitialized) { - throw new IllegalStateException("TouchMonitor not initialized"); + // In the case that we've already been destroyed, this is a no-op + return; } stopMonitoring(true); diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt index ec0322736f4b..cafa74faf1a1 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt @@ -26,6 +26,7 @@ import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.CommunalAppWidgetHostView import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.dagger.qualifiers.UiBackground +import java.util.concurrent.Executor import javax.inject.Inject import kotlin.coroutines.CoroutineContext @@ -34,6 +35,7 @@ class WidgetViewFactory @Inject constructor( @UiBackground private val uiBgContext: CoroutineContext, + @UiBackground private val uiBgExecutor: Executor, private val appWidgetHost: CommunalAppWidgetHost, private val interactionHandler: WidgetInteractionHandler, private val listenerFactory: AppWidgetHostListenerDelegate.Factory, @@ -44,8 +46,11 @@ constructor( size: SizeF, ): CommunalAppWidgetHostView = withContext("$TAG#createWidget", uiBgContext) { - val view = CommunalAppWidgetHostView(context, interactionHandler) - view.setAppWidget(model.appWidgetId, model.providerInfo) + val view = + CommunalAppWidgetHostView(context, interactionHandler).apply { + setExecutor(uiBgExecutor) + setAppWidget(model.appWidgetId, model.providerInfo) + } // Instead of setting the view as the listener directly, we wrap the view in a delegate // which ensures the callbacks always get called on the main thread. appWidgetHost.setListener(model.appWidgetId, listenerFactory.create(view)) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index cd5daf9a99f0..1e9541e1923e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -210,13 +210,18 @@ constructor( } else { TransitionState.RUNNING } - transitionRepository.updateTransition( - id, - // This maps the shadeExpansion to a much faster curve, to match - // the existing logic - 1f - MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, shadeExpansion), - nextState, - ) + + // startTransition below will issue the CANCELED directly + if (nextState != TransitionState.CANCELED) { + transitionRepository.updateTransition( + id, + // This maps the shadeExpansion to a much faster curve, to match + // the existing logic + 1f - + MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, shadeExpansion), + nextState, + ) + } if ( nextState == TransitionState.CANCELED || @@ -234,11 +239,12 @@ constructor( ownerName = name, from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.LOCKSCREEN, + modeOnCanceled = TransitionModeOnCanceled.REVERSE, animator = getDefaultAnimatorForTransitionsToState( KeyguardState.LOCKSCREEN ) - .apply { duration = 0 }, + .apply { duration = 100L }, ) ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index 5ea9e6ae0a70..301ab2bcdd65 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -311,10 +311,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi mConfig = MobileMappings.Config.readConfig(mContext); mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId); mSubIdTelephonyManagerMap.put(mDefaultDataSubId, mTelephonyManager); - InternetTelephonyCallback telephonyCallback = - new InternetTelephonyCallback(mDefaultDataSubId); - mSubIdTelephonyCallbackMap.put(mDefaultDataSubId, telephonyCallback); - mTelephonyManager.registerTelephonyCallback(mExecutor, telephonyCallback); + registerInternetTelephonyCallback(mTelephonyManager, mDefaultDataSubId); // Listen the connectivity changes mConnectivityManager.registerDefaultNetworkCallback(mConnectivityManagerNetworkCallback); mCanConfigWifi = canConfigWifi; @@ -346,6 +343,23 @@ public class InternetDialogController implements AccessPointController.AccessPoi mCallback = null; } + /** + * This is to generate and register the new callback to Telephony for uncached subscription id, + * then cache it. Telephony also cached this callback into + * {@link com.android.server.TelephonyRegistry}, so if subscription id and callback were cached + * already, it shall do nothing to avoid registering redundant callback to Telephony. + */ + private void registerInternetTelephonyCallback( + TelephonyManager telephonyManager, int subId) { + if (mSubIdTelephonyCallbackMap.containsKey(subId)) { + // Avoid to generate and register unnecessary callback to Telephony. + return; + } + InternetTelephonyCallback telephonyCallback = new InternetTelephonyCallback(subId); + mSubIdTelephonyCallbackMap.put(subId, telephonyCallback); + telephonyManager.registerTelephonyCallback(mExecutor, telephonyCallback); + } + boolean isAirplaneModeEnabled() { return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0; } @@ -673,9 +687,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi int subId = subInfo.getSubscriptionId(); if (mSubIdTelephonyManagerMap.get(subId) == null) { TelephonyManager secondaryTm = mTelephonyManager.createForSubscriptionId(subId); - InternetTelephonyCallback telephonyCallback = new InternetTelephonyCallback(subId); - secondaryTm.registerTelephonyCallback(mExecutor, telephonyCallback); - mSubIdTelephonyCallbackMap.put(subId, telephonyCallback); + registerInternetTelephonyCallback(secondaryTm, subId); mSubIdTelephonyManagerMap.put(subId, secondaryTm); } return subId; @@ -1351,6 +1363,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi if (DEBUG) { Log.d(TAG, "DDS: defaultDataSubId:" + defaultDataSubId); } + if (SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)) { // clean up old defaultDataSubId TelephonyCallback oldCallback = mSubIdTelephonyCallbackMap.get(mDefaultDataSubId); @@ -1366,9 +1379,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi // create for new defaultDataSubId mTelephonyManager = mTelephonyManager.createForSubscriptionId(defaultDataSubId); mSubIdTelephonyManagerMap.put(defaultDataSubId, mTelephonyManager); - InternetTelephonyCallback newCallback = new InternetTelephonyCallback(defaultDataSubId); - mSubIdTelephonyCallbackMap.put(defaultDataSubId, newCallback); - mTelephonyManager.registerTelephonyCallback(mHandler::post, newCallback); + registerInternetTelephonyCallback(mTelephonyManager, defaultDataSubId); mCallback.onSubscriptionsChanged(defaultDataSubId); } mDefaultDataSubId = defaultDataSubId; diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java index aa8c6b7a8a5f..e160ff17a6ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.content.res.Configuration; @@ -643,6 +644,46 @@ public class TouchMonitorTest extends SysuiTestCase { environment.verifyInputSessionDispose(); } + @Test + public void testSessionPopAfterDestroy() { + final TouchHandler touchHandler = createTouchHandler(); + + final Environment environment = new Environment(Stream.of(touchHandler) + .collect(Collectors.toCollection(HashSet::new)), mKosmos); + + final InputEvent initialEvent = Mockito.mock(InputEvent.class); + environment.publishInputEvent(initialEvent); + + // Ensure session started + final InputChannelCompat.InputEventListener eventListener = + registerInputEventListener(touchHandler); + + // First event will be missed since we register after the execution loop, + final InputEvent event = Mockito.mock(InputEvent.class); + environment.publishInputEvent(event); + verify(eventListener).onInputEvent(eq(event)); + + final ArgumentCaptor<TouchHandler.TouchSession> touchSessionArgumentCaptor = + ArgumentCaptor.forClass(TouchHandler.TouchSession.class); + + verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture()); + + environment.updateLifecycle(Lifecycle.State.DESTROYED); + + // Check to make sure the input session is now disposed. + environment.verifyInputSessionDispose(); + + clearInvocations(environment.mInputFactory); + + // Pop the session + touchSessionArgumentCaptor.getValue().pop(); + + environment.executeAll(); + + // Ensure no input sessions were created due to the session reset. + verifyNoMoreInteractions(environment.mInputFactory); + } + @Test public void testPilfering() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java index eea02eec7099..2f8f45cb0197 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -887,6 +888,34 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { } @Test + public void getActiveAutoSwitchNonDdsSubId_registerCallbackForExistedSubId_notRegister() { + mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true); + + // Adds non DDS subId + SubscriptionInfo info = mock(SubscriptionInfo.class); + doReturn(SUB_ID2).when(info).getSubscriptionId(); + doReturn(false).when(info).isOpportunistic(); + when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info); + + mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); + + // 1st time is onStart(), 2nd time is getActiveAutoSwitchNonDdsSubId() + verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any()); + assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size() == 2); + + // Adds non DDS subId again + doReturn(SUB_ID2).when(info).getSubscriptionId(); + doReturn(false).when(info).isOpportunistic(); + when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info); + + mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); + + // Does not add due to cached subInfo in mSubIdTelephonyCallbackMap. + verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any()); + assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size() == 2); + } + + @Test public void getMobileNetworkSummary() { mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true); Resources res1 = mock(Resources.class); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt index 450f08f1fe3f..11f0c19ffa67 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt @@ -21,17 +21,20 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled -import com.android.systemui.util.mockito.any +import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.FailureMetadata import com.google.common.truth.Subject import com.google.common.truth.Truth import com.google.common.truth.Truth.assertAbout +import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertEquals import org.junit.Assert.fail import org.mockito.Mockito import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor /** [Subject] used to make assertions about a [Mockito.spy] KeyguardTransitionRepository. */ class KeyguardTransitionRepositorySpySubject @@ -89,6 +92,20 @@ private constructor( } } + /** + * Asserts that we started a transition to the given state, optionally verifying additional + * params. + */ + suspend fun updatedTransition(value: Float, state: TransitionState) { + val valueCaptor = argumentCaptor<Float>() + val stateCaptor = argumentCaptor<TransitionState>() + + verify(repository).updateTransition(any(), valueCaptor.capture(), stateCaptor.capture()) + + assertThat(value).isEqualTo(valueCaptor.firstValue) + assertThat(state).isEqualTo(stateCaptor.firstValue) + } + /** Verifies that [KeyguardTransitionRepository.startTransition] was never called. */ suspend fun noTransitionsStarted() { verify(repository, never()).startTransition(any()) |