Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d925e4a0 authored by Justin Weir's avatar Justin Weir
Browse files

Add key event handling to Flexiglass

Extracted the key event handling methods from
NotificationShadeWindowView's InteractionEventHandler into a new
class named WindowRootViewKeyEventHandler and moved the key handling
methods in NotificationShadeWindowView up into WindowRootView, the
parent class it shares with SceneWindowRootView.

Test: updated and ran affected tests
Test: manually verified with flag both on and off
Fixes: 399431426
Flag: com.android.systemui.scene_container
Change-Id: I7d2210429c8f1b2b06c42670245d2cc8d88dc3c4
parent d280057b
Loading
Loading
Loading
Loading
+69 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.scene.ui.view

import android.view.KeyEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.fakeFalsingCollector
import com.android.systemui.keyevent.domain.interactor.mockSysUIKeyEventHandler
import com.android.systemui.kosmos.runTest
import com.android.systemui.testKosmos
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
class WindowRootViewKeyEventHandlerTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val underTest: WindowRootViewKeyEventHandler = kosmos.windowRootViewKeyEventHandler

    @Test
    fun dispatchKeyEvent_forwardsDispatchKeyEvent() =
        kosmos.runTest {
            val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
            underTest.dispatchKeyEvent(keyEvent)
            verify(mockSysUIKeyEventHandler).dispatchKeyEvent(keyEvent)
        }

    @Test
    fun dispatchKeyEventPreIme_forwardsDispatchKeyEventPreIme() =
        kosmos.runTest {
            val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
            underTest.dispatchKeyEventPreIme(keyEvent)
            verify(mockSysUIKeyEventHandler).dispatchKeyEventPreIme(keyEvent)
        }

    @Test
    fun interceptMediaKey_forwardsInterceptMediaKey() =
        kosmos.runTest {
            val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)
            underTest.interceptMediaKey(keyEvent)
            verify(mockSysUIKeyEventHandler).interceptMediaKey(keyEvent)
        }

    @Test
    fun collectKeyEvent_forwardsCollectKeyEvent() =
        kosmos.runTest {
            val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A)
            underTest.collectKeyEvent(keyEvent)
            assertEquals(keyEvent, fakeFalsingCollector.lastKeyEvent)
        }
}
+5 −4
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootViewKeyEventHandler
import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractorPassThrough
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
@@ -69,7 +70,6 @@ import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.window.ui.viewmodel.WindowRootViewModel
@@ -85,12 +85,12 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock

@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@@ -175,13 +175,14 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
        featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
        testScope = TestScope()
        val falsingCollector = FalsingCollectorFake()
        controller =
            NotificationShadeWindowViewController(
                blurUtils,
                windowRootViewModelFactory,
                choreographer,
                lockscreenShadeTransitionController,
                FalsingCollectorFake(),
                falsingCollector,
                statusBarStateController,
                dockManager,
                notificationShadeDepthController,
@@ -212,7 +213,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
                NotificationLaunchAnimationInteractor(NotificationLaunchAnimationRepository()),
                featureFlags,
                FakeSystemClock(),
                Mockito.mock(SysUIKeyEventHandler::class.java),
                WindowRootViewKeyEventHandler({ mock<SysUIKeyEventHandler>() }, falsingCollector),
                quickSettingsController,
                primaryBouncerInteractor,
                alternateBouncerInteractor,
+2 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootVi
        sceneDataSourceDelegator: SceneDataSourceDelegator,
        qsSceneAdapter: Provider<QSSceneAdapter>,
        sceneJankMonitorFactory: SceneJankMonitor.Factory,
        windowRootViewKeyEventHandler: WindowRootViewKeyEventHandler,
    ) {
        setLayoutInsetsController(layoutInsetController)
        SceneWindowRootViewBinder.bind(
@@ -53,6 +54,7 @@ class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootVi
            qsSceneAdapter = qsSceneAdapter,
            sceneJankMonitorFactory = sceneJankMonitorFactory,
        )
        setWindowRootViewKeyEventHandler(windowRootViewKeyEventHandler)
    }

    override fun setVisibility(visibility: Int) {
+24 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.Context
import android.util.AttributeSet
import android.util.Pair
import android.view.DisplayCutout
import android.view.KeyEvent
import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
@@ -37,6 +38,11 @@ open class WindowRootView(context: Context, attrs: AttributeSet?) : FrameLayout(
    private var rightInset = 0

    private var previousInsets: WindowInsets? = null
    private lateinit var windowRootViewKeyEventHandler: WindowRootViewKeyEventHandler

    fun setWindowRootViewKeyEventHandler(wrvkeh: WindowRootViewKeyEventHandler) {
        windowRootViewKeyEventHandler = wrvkeh
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
@@ -137,6 +143,24 @@ open class WindowRootView(context: Context, attrs: AttributeSet?) : FrameLayout(
        return parent.let { it !is View || it.id == android.R.id.content }
    }

    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        windowRootViewKeyEventHandler.collectKeyEvent(event)

        if (windowRootViewKeyEventHandler.interceptMediaKey(event)) {
            return true
        }

        if (super.dispatchKeyEvent(event)) {
            return true
        }

        return windowRootViewKeyEventHandler.dispatchKeyEvent(event)
    }

    override fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
        return windowRootViewKeyEventHandler.dispatchKeyEventPreIme(event) ?: false
    }

    /** Controller responsible for calculating insets for the shade window. */
    interface LayoutInsetsController {

+49 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.scene.ui.view

import android.view.KeyEvent
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import dagger.Lazy
import javax.inject.Inject

@SysUISingleton
class WindowRootViewKeyEventHandler
@Inject
constructor(
    val sysUIKeyEventHandlerLazy: Lazy<SysUIKeyEventHandler>,
    val falsingCollector: FalsingCollector,
) {
    fun dispatchKeyEvent(event: KeyEvent): Boolean {
        return sysUIKeyEventHandlerLazy.get().dispatchKeyEvent(event)
    }

    fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
        return sysUIKeyEventHandlerLazy.get().dispatchKeyEventPreIme(event)
    }

    fun interceptMediaKey(event: KeyEvent): Boolean {
        return sysUIKeyEventHandlerLazy.get().interceptMediaKey(event)
    }

    /** Collects the KeyEvent without intercepting it. */
    fun collectKeyEvent(event: KeyEvent) {
        falsingCollector.onKeyEvent(event)
    }
}
Loading