diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index de0f7522f36d6..411d157fa9278 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -458,8 +458,7 @@ public class Dialog implements DialogInterface, Window.Callback, && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { // Add onBackPressed as default back behavior. mDefaultBackCallback = this::onBackPressed; - getOnBackInvokedDispatcher().registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_DEFAULT, mDefaultBackCallback); + getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback); mDefaultBackCallback = null; } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 19bbcff0bd79d..ded7e785ca756 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -5514,13 +5514,17 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 210) { final SettingsState secureSettings = getSecureSettingsLocked(userId); - final int defaultValueVibrateIconEnabled = getContext().getResources() - .getInteger(R.integer.def_statusBarVibrateIconEnabled); - secureSettings.insertSettingOverrideableByRestoreLocked( - Secure.STATUS_BAR_SHOW_VIBRATE_ICON, - String.valueOf(defaultValueVibrateIconEnabled), - null /* tag */, true /* makeDefault */, - SettingsState.SYSTEM_PACKAGE_NAME); + final Setting currentSetting = secureSettings.getSettingLocked( + Secure.STATUS_BAR_SHOW_VIBRATE_ICON); + if (currentSetting.isNull()) { + final int defaultValueVibrateIconEnabled = getContext().getResources() + .getInteger(R.integer.def_statusBarVibrateIconEnabled); + secureSettings.insertSettingOverrideableByRestoreLocked( + Secure.STATUS_BAR_SHOW_VIBRATE_ICON, + String.valueOf(defaultValueVibrateIconEnabled), + null /* tag */, true /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } currentVersion = 211; } // vXXX: Add new settings above this point. diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index 0d9af31639f2e..3dec5138de956 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -55,6 +55,7 @@ import android.view.WindowInsetsController.Appearance; import android.view.WindowInsetsController.Behavior; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.android.internal.statusbar.LetterboxDetails; import com.android.internal.view.AppearanceRegion; @@ -125,7 +126,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, private final DisplayManager mDisplayManager; private Context mWindowContext; private ScreenPinningNotify mScreenPinningNotify; - private int mNavigationMode; + private int mNavigationMode = -1; private final Consumer mPipListener; /** @@ -217,8 +218,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, parseCurrentSysuiState(); mCommandQueue.addCallback(this); mOverviewProxyService.addCallback(this); - mEdgeBackGestureHandler.onNavigationModeChanged( - mNavigationModeController.addListener(this)); + onNavigationModeChanged(mNavigationModeController.addListener(this)); mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); mNavBarHelper.init(); mEdgeBackGestureHandler.onNavBarAttached(); @@ -492,6 +492,11 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, !QuickStepContract.isGesturalMode(mNavigationMode)); } + @VisibleForTesting + int getNavigationMode() { + return mNavigationMode; + } + @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println("TaskbarDelegate (displayId=" + mDisplayId + "):"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt index 9da94ce968c7e..4133802189a51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt @@ -119,6 +119,9 @@ constructor( // Don't apply the filter to (non-promoted) group summaries // - summary will be pruned if necessary, depending on if children are filtered entry.parent?.summary == entry -> false + // Check that the entry satisfies certain characteristics that would bypass the + // filter + shouldIgnoreUnseenCheck(entry) -> false else -> true }.also { hasFiltered -> hasFilteredAnyNotifs = hasFilteredAnyNotifs || hasFiltered } @@ -134,6 +137,13 @@ constructor( keyguardNotificationVisibilityProvider.shouldHideNotification(entry) } + private fun shouldIgnoreUnseenCheck(entry: NotificationEntry): Boolean = + when { + entry.isMediaNotification -> true + entry.sbn.isOngoing -> true + else -> false + } + // TODO(b/206118999): merge this class with SensitiveContentCoordinator which also depends on // these same updates private fun setupInvalidateNotifListCallbacks() {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt new file mode 100644 index 0000000000000..d9e3f8fbf1468 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt @@ -0,0 +1,16 @@ +package com.android.systemui.statusbar.notification.fsi + +class FsiDebug { + + companion object { + private const val debugTag = "FsiDebug" + private const val debug = true + + fun log(s: Any) { + if (!debug) { + return + } + android.util.Log.d(debugTag, "$s") + } + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt new file mode 100644 index 0000000000000..d9ede44faa2e1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 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.stylus + +import android.hardware.input.InputManager +import android.os.Handler +import android.util.ArrayMap +import android.view.InputDevice +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import java.util.concurrent.CopyOnWriteArrayList +import javax.inject.Inject + +/** + * A class which keeps track of InputDevice events related to stylus devices, and notifies + * registered callbacks of stylus events. + */ +@SysUISingleton +class StylusManager +@Inject +constructor( + private val inputManager: InputManager, + @Background private val handler: Handler, +) : InputManager.InputDeviceListener { + + private val stylusCallbacks: CopyOnWriteArrayList = CopyOnWriteArrayList() + // This map should only be accessed on the handler + private val inputDeviceAddressMap: MutableMap = ArrayMap() + + /** + * Starts listening to InputManager InputDevice events. Will also load the InputManager snapshot + * at time of starting. + */ + fun startListener() { + addExistingStylusToMap() + inputManager.registerInputDeviceListener(this, handler) + } + + /** Registers a StylusCallback to listen to stylus events. */ + fun registerCallback(callback: StylusCallback) { + stylusCallbacks.add(callback) + } + + /** Unregisters a StylusCallback. If StylusCallback is not registered, is a no-op. */ + fun unregisterCallback(callback: StylusCallback) { + stylusCallbacks.remove(callback) + } + + override fun onInputDeviceAdded(deviceId: Int) { + val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return + if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return + + // TODO(b/257936830): get address once input api available + val btAddress: String? = null + inputDeviceAddressMap[deviceId] = btAddress + executeStylusCallbacks { cb -> cb.onStylusAdded(deviceId) } + + if (btAddress != null) { + executeStylusCallbacks { cb -> cb.onStylusBluetoothConnected(deviceId, btAddress) } + } + } + + override fun onInputDeviceChanged(deviceId: Int) { + val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return + if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return + + // TODO(b/257936830): get address once input api available + val currAddress: String? = null + val prevAddress: String? = inputDeviceAddressMap[deviceId] + inputDeviceAddressMap[deviceId] = currAddress + + if (prevAddress == null && currAddress != null) { + executeStylusCallbacks { cb -> cb.onStylusBluetoothConnected(deviceId, currAddress) } + } + + if (prevAddress != null && currAddress == null) { + executeStylusCallbacks { cb -> cb.onStylusBluetoothDisconnected(deviceId, prevAddress) } + } + } + + override fun onInputDeviceRemoved(deviceId: Int) { + if (!inputDeviceAddressMap.contains(deviceId)) return + + val btAddress: String? = inputDeviceAddressMap[deviceId] + inputDeviceAddressMap.remove(deviceId) + if (btAddress != null) { + executeStylusCallbacks { cb -> cb.onStylusBluetoothDisconnected(deviceId, btAddress) } + } + executeStylusCallbacks { cb -> cb.onStylusRemoved(deviceId) } + } + + private fun executeStylusCallbacks(run: (cb: StylusCallback) -> Unit) { + stylusCallbacks.forEach(run) + } + + private fun addExistingStylusToMap() { + for (deviceId: Int in inputManager.inputDeviceIds) { + val device: InputDevice = inputManager.getInputDevice(deviceId) ?: continue + if (device.supportsSource(InputDevice.SOURCE_STYLUS)) { + // TODO(b/257936830): get address once input api available + inputDeviceAddressMap[deviceId] = null + } + } + } + + /** Callback interface to receive events from the StylusManager. */ + interface StylusCallback { + fun onStylusAdded(deviceId: Int) {} + fun onStylusRemoved(deviceId: Int) {} + fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {} + fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) {} + } + + companion object { + private val TAG = StylusManager::class.simpleName.orEmpty() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt new file mode 100644 index 0000000000000..1742c6994246f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt @@ -0,0 +1,93 @@ +package com.android.systemui.navigationbar + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager +import com.android.systemui.model.SysUiState +import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler +import com.android.systemui.recents.OverviewProxyService +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.phone.AutoHideController +import com.android.systemui.statusbar.phone.LightBarController +import com.android.systemui.statusbar.phone.LightBarTransitionsController +import com.android.wm.shell.back.BackAnimation +import com.android.wm.shell.pip.Pip +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.any +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.util.Optional + +@SmallTest +class TaskbarDelegateTest : SysuiTestCase() { + val DISPLAY_ID = 0; + val MODE_GESTURE = 0; + val MODE_THREE_BUTTON = 1; + + private lateinit var mTaskbarDelegate: TaskbarDelegate + @Mock + lateinit var mEdgeBackGestureHandlerFactory : EdgeBackGestureHandler.Factory + @Mock + lateinit var mEdgeBackGestureHandler : EdgeBackGestureHandler + @Mock + lateinit var mLightBarControllerFactory : LightBarTransitionsController.Factory + @Mock + lateinit var mLightBarTransitionController: LightBarTransitionsController + @Mock + lateinit var mCommandQueue: CommandQueue + @Mock + lateinit var mOverviewProxyService: OverviewProxyService + @Mock + lateinit var mNavBarHelper: NavBarHelper + @Mock + lateinit var mNavigationModeController: NavigationModeController + @Mock + lateinit var mSysUiState: SysUiState + @Mock + lateinit var mDumpManager: DumpManager + @Mock + lateinit var mAutoHideController: AutoHideController + @Mock + lateinit var mLightBarController: LightBarController + @Mock + lateinit var mOptionalPip: Optional + @Mock + lateinit var mBackAnimation: BackAnimation + @Mock + lateinit var mCurrentSysUiState: NavBarHelper.CurrentSysuiState + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + `when`(mEdgeBackGestureHandlerFactory.create(context)).thenReturn(mEdgeBackGestureHandler) + `when`(mLightBarControllerFactory.create(any())).thenReturn(mLightBarTransitionController) + `when`(mNavBarHelper.currentSysuiState).thenReturn(mCurrentSysUiState) + `when`(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState) + mTaskbarDelegate = TaskbarDelegate(context, mEdgeBackGestureHandlerFactory, + mLightBarControllerFactory) + mTaskbarDelegate.setDependencies(mCommandQueue, mOverviewProxyService, mNavBarHelper, + mNavigationModeController, mSysUiState, mDumpManager, mAutoHideController, + mLightBarController, mOptionalPip, mBackAnimation) + } + + @Test + fun navigationModeInitialized() { + `when`(mNavigationModeController.addListener(any())).thenReturn(MODE_THREE_BUTTON) + assert(mTaskbarDelegate.navigationMode == -1) + mTaskbarDelegate.init(DISPLAY_ID) + assert(mTaskbarDelegate.navigationMode == MODE_THREE_BUTTON) + } + + @Test + fun navigationModeInitialized_notifyEdgeBackHandler() { + `when`(mNavigationModeController.addListener(any())).thenReturn(MODE_GESTURE) + mTaskbarDelegate.init(DISPLAY_ID) + verify(mEdgeBackGestureHandler, times(1)).onNavigationModeChanged(MODE_GESTURE) + } +} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt index 7f73856bff896..5f19fac8666a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator +import android.app.Notification import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -33,6 +34,7 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionHe import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.withArgCaptor @@ -104,6 +106,50 @@ class KeyguardCoordinatorTest : SysuiTestCase() { } } + @Test + fun unseenFilterDoesNotSuppressSeenOngoingNotifWhileKeyguardShowing() { + whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) + + // GIVEN: Keyguard is not showing, and an ongoing notification is present + keyguardRepository.setKeyguardShowing(false) + runKeyguardCoordinatorTest { + val fakeEntry = NotificationEntryBuilder() + .setNotification(Notification.Builder(mContext).setOngoing(true).build()) + .build() + collectionListener.onEntryAdded(fakeEntry) + + // WHEN: The keyguard is now showing + keyguardRepository.setKeyguardShowing(true) + testScheduler.runCurrent() + + // THEN: The notification is recognized as "ongoing" and is not filtered out. + assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse() + } + } + + @Test + fun unseenFilterDoesNotSuppressSeenMediaNotifWhileKeyguardShowing() { + whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) + + // GIVEN: Keyguard is not showing, and a media notification is present + keyguardRepository.setKeyguardShowing(false) + runKeyguardCoordinatorTest { + val fakeEntry = NotificationEntryBuilder().build().apply { + row = mock().apply { + whenever(isMediaRow).thenReturn(true) + } + } + collectionListener.onEntryAdded(fakeEntry) + + // WHEN: The keyguard is now showing + keyguardRepository.setKeyguardShowing(true) + testScheduler.runCurrent() + + // THEN: The notification is recognized as "media" and is not filtered out. + assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse() + } + } + @Test fun unseenFilterUpdatesSeenProviderWhenSuppressing() { whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt new file mode 100644 index 0000000000000..e6ec20e0ea734 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2022 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.stylus + +import android.hardware.input.InputManager +import android.os.Handler +import android.testing.AndroidTestingRunner +import android.view.InputDevice +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.* +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +@Ignore("b/257936830 until bt APIs") +class StylusManagerTest : SysuiTestCase() { + @Mock lateinit var inputManager: InputManager + + @Mock lateinit var stylusDevice: InputDevice + + @Mock lateinit var btStylusDevice: InputDevice + + @Mock lateinit var otherDevice: InputDevice + + @Mock lateinit var handler: Handler + + @Mock lateinit var stylusCallback: StylusManager.StylusCallback + + @Mock lateinit var otherStylusCallback: StylusManager.StylusCallback + + private lateinit var stylusManager: StylusManager + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(handler.post(any())).thenAnswer { + (it.arguments[0] as Runnable).run() + true + } + + stylusManager = StylusManager(inputManager, handler) + + stylusManager.registerCallback(stylusCallback) + + whenever(otherDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(false) + whenever(stylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true) + whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true) + + // whenever(stylusDevice.bluetoothAddress).thenReturn(null) + // whenever(btStylusDevice.bluetoothAddress).thenReturn(STYLUS_BT_ADDRESS) + + whenever(inputManager.getInputDevice(OTHER_DEVICE_ID)).thenReturn(otherDevice) + whenever(inputManager.getInputDevice(STYLUS_DEVICE_ID)).thenReturn(stylusDevice) + whenever(inputManager.getInputDevice(BT_STYLUS_DEVICE_ID)).thenReturn(btStylusDevice) + whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(STYLUS_DEVICE_ID)) + } + + @Test + fun startListener_registersInputDeviceListener() { + stylusManager.startListener() + + verify(inputManager, times(1)).registerInputDeviceListener(stylusManager, handler) + } + + @Test + fun onInputDeviceAdded_multipleRegisteredCallbacks_callsAll() { + stylusManager.registerCallback(otherStylusCallback) + + stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID) + + verify(stylusCallback, times(1)).onStylusAdded(STYLUS_DEVICE_ID) + verifyNoMoreInteractions(stylusCallback) + verify(otherStylusCallback, times(1)).onStylusAdded(STYLUS_DEVICE_ID) + verifyNoMoreInteractions(otherStylusCallback) + } + + @Test + fun onInputDeviceAdded_stylus_callsCallbacksOnStylusAdded() { + stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID) + + verify(stylusCallback, times(1)).onStylusAdded(STYLUS_DEVICE_ID) + verifyNoMoreInteractions(stylusCallback) + } + + @Test + fun onInputDeviceAdded_btStylus_callsCallbacksWithAddress() { + stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) + + inOrder(stylusCallback).let { + it.verify(stylusCallback, times(1)).onStylusAdded(BT_STYLUS_DEVICE_ID) + it.verify(stylusCallback, times(1)) + .onStylusBluetoothConnected(BT_STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS) + } + } + + @Test + fun onInputDeviceAdded_notStylus_doesNotCallCallbacks() { + stylusManager.onInputDeviceAdded(OTHER_DEVICE_ID) + + verifyNoMoreInteractions(stylusCallback) + } + + @Test + fun onInputDeviceChanged_multipleRegisteredCallbacks_callsAll() { + stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID) + // whenever(stylusDevice.bluetoothAddress).thenReturn(STYLUS_BT_ADDRESS) + stylusManager.registerCallback(otherStylusCallback) + + stylusManager.onInputDeviceChanged(STYLUS_DEVICE_ID) + + verify(stylusCallback, times(1)) + .onStylusBluetoothConnected(STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS) + verify(otherStylusCallback, times(1)) + .onStylusBluetoothConnected(STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS) + } + + @Test + fun onInputDeviceChanged_stylusNewBtConnection_callsCallbacks() { + stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID) + // whenever(stylusDevice.bluetoothAddress).thenReturn(STYLUS_BT_ADDRESS) + + stylusManager.onInputDeviceChanged(STYLUS_DEVICE_ID) + + verify(stylusCallback, times(1)) + .onStylusBluetoothConnected(STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS) + } + + @Test + fun onInputDeviceChanged_stylusLostBtConnection_callsCallbacks() { + stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) + // whenever(btStylusDevice.bluetoothAddress).thenReturn(null) + + stylusManager.onInputDeviceChanged(BT_STYLUS_DEVICE_ID) + + verify(stylusCallback, times(1)) + .onStylusBluetoothDisconnected(BT_STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS) + } + + @Test + fun onInputDeviceChanged_btConnection_stylusAlreadyBtConnected_onlyCallsListenersOnce() { + stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) + + stylusManager.onInputDeviceChanged(BT_STYLUS_DEVICE_ID) + + verify(stylusCallback, times(1)) + .onStylusBluetoothConnected(BT_STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS) + } + + @Test + fun onInputDeviceChanged_noBtConnection_stylusNeverBtConnected_doesNotCallCallbacks() { + stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID) + + stylusManager.onInputDeviceChanged(STYLUS_DEVICE_ID) + + verify(stylusCallback, never()).onStylusBluetoothDisconnected(any(), any()) + } + + @Test + fun onInputDeviceRemoved_multipleRegisteredCallbacks_callsAll() { + stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID) + stylusManager.registerCallback(otherStylusCallback) + + stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID) + + verify(stylusCallback, times(1)).onStylusRemoved(STYLUS_DEVICE_ID) + verify(otherStylusCallback, times(1)).onStylusRemoved(STYLUS_DEVICE_ID) + } + + @Test + fun onInputDeviceRemoved_stylus_callsCallbacks() { + stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID) + + stylusManager.onInputDeviceRemoved(STYLUS_DEVICE_ID) + + verify(stylusCallback, times(1)).onStylusRemoved(STYLUS_DEVICE_ID) + verify(stylusCallback, never()).onStylusBluetoothDisconnected(any(), any()) + } + + @Test + fun onInputDeviceRemoved_btStylus_callsCallbacks() { + stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) + + stylusManager.onInputDeviceRemoved(BT_STYLUS_DEVICE_ID) + + inOrder(stylusCallback).let { + it.verify(stylusCallback, times(1)) + .onStylusBluetoothDisconnected(BT_STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS) + it.verify(stylusCallback, times(1)).onStylusRemoved(BT_STYLUS_DEVICE_ID) + } + } + + companion object { + private const val OTHER_DEVICE_ID = 0 + private const val STYLUS_DEVICE_ID = 1 + private const val BT_STYLUS_DEVICE_ID = 2 + + private const val STYLUS_BT_ADDRESS = "SOME:ADDRESS" + } +} diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 3a49d8627c23a..d328fd7cacbb5 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -379,6 +379,10 @@ public final class DreamManagerService extends SystemService { return false; } + if (!dreamsEnabledForUser(ActivityManager.getCurrentUser())) { + return false; + } + if ((mWhenToDream & DREAM_ON_CHARGE) == DREAM_ON_CHARGE) { return mIsCharging; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 6d5da3254eac1..85a2a5d0e0ebf 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1049,6 +1049,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { return; } + // Make sure the device locks. Unfortunately, this has the side-effect of briefly revealing + // the lock screen before the dream appears. Note that this locking behavior needs to + // happen regardless of whether we end up dreaming (below) or not. + // TODO(b/261662912): Find a better way to lock the device that doesn't result in jank. + lockNow(null); + + // Don't dream if the user isn't user zero. + // TODO(b/261907079): Move this check to DreamManagerService#canStartDreamingInternal(). + if (ActivityManager.getCurrentUser() != UserHandle.USER_SYSTEM) { + noDreamAction.run(); + return; + } + final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal(); if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) { noDreamAction.run();