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

Commit 3eb4b11d authored by Beverly's avatar Beverly
Browse files

Create new interactor for a global key events observer

KeyEventInteractor: can observe ALL key events, regardless
of whether the key event has or will be handled. This should
only be used to observe key event states.

SysUIKeyEventHandler: routes key events that haven't already been
handled by the system, that are going through NSWVC's interaction
handler. If SysUI decides to handle the key event, then the key
event won't be sent to other consumers (ie: apps).

Test: atest SysUIKeyEventHandlerTest KeyEventInteractortest
Bug: 301047491
Change-Id: I5ca503b4e20d3198098272622841e44b7b80ed07
parent 7f6c431c
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -56,6 +56,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.keyboard.KeyboardModule;
import com.android.systemui.keyboard.KeyboardModule;
import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.dagger.MonitorLog;
@@ -181,6 +182,7 @@ import javax.inject.Named;
        FalsingModule.class,
        FalsingModule.class,
        FlagsModule.class,
        FlagsModule.class,
        FooterActionsModule.class,
        FooterActionsModule.class,
        KeyEventRepositoryModule.class,
        KeyboardModule.class,
        KeyboardModule.class,
        KeyguardBlueprintModule.class,
        KeyguardBlueprintModule.class,
        LetterboxModule.class,
        LetterboxModule.class,
+56 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 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.keyevent.data.repository

import android.view.KeyEvent
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.CommandQueue
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow

/** Defines interface for classes that encapsulate application state for key event presses. */
interface KeyEventRepository {
    /** Observable for whether the power button key is pressed/down or not. */
    val isPowerButtonDown: Flow<Boolean>
}

@SysUISingleton
class KeyEventRepositoryImpl
@Inject
constructor(
    val commandQueue: CommandQueue,
) : KeyEventRepository {
    override val isPowerButtonDown: Flow<Boolean> = conflatedCallbackFlow {
        val callback =
            object : CommandQueue.Callbacks {
                override fun handleSystemKey(event: KeyEvent) {
                    if (event.keyCode == KeyEvent.KEYCODE_POWER) {
                        trySendWithFailureLogging(event.isDown, TAG, "updated isPowerButtonDown")
                    }
                }
            }
        commandQueue.addCallback(callback)
        awaitClose { commandQueue.removeCallback(callback) }
    }

    companion object {
        private const val TAG = "KeyEventRepositoryImpl"
    }
}
+25 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 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.keyevent.data.repository

import dagger.Binds
import dagger.Module

@Module
interface KeyEventRepositoryModule {
    @Binds fun keyEventRepository(impl: KeyEventRepositoryImpl): KeyEventRepository
}
+7 −39
Original line number Original line Diff line number Diff line
@@ -13,55 +13,23 @@
 * See the License for the specific language governing permissions and
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * limitations under the License.
 */
 */

package com.android.systemui.keyevent.domain.interactor
package com.android.systemui.keyevent.domain.interactor


import android.view.KeyEvent
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
import com.android.systemui.keyevent.data.repository.KeyEventRepository
import javax.inject.Inject
import javax.inject.Inject


/**
/**
 * Sends key events to the appropriate interactors and then acts upon key events that haven't
 * Business logic for all key event state. This includes all key events, regardless of whether
 * already been handled but should be handled by SystemUI.
 * they've been handled or not by a consumer.
 *
 * For key events that SysUI wants to properly handle, see [SysUIKeyEventHandler].
 */
 */
@SysUISingleton
@SysUISingleton
class KeyEventInteractor
class KeyEventInteractor
@Inject
@Inject
constructor(
constructor(
    private val backActionInteractor: BackActionInteractor,
    repository: KeyEventRepository,
    private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor,
) {
) {
    fun dispatchKeyEvent(event: KeyEvent): Boolean {
    val isPowerButtonDown = repository.isPowerButtonDown
        if (keyguardKeyEventInteractor.dispatchKeyEvent(event)) {
            return true
        }

        when (event.keyCode) {
            KeyEvent.KEYCODE_BACK -> {
                if (event.handleAction()) {
                    backActionInteractor.onBackRequested()
                }
                return true
            }
        }
        return false
    }

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

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

    companion object {
        // Most actions shouldn't be handled on the down event and instead handled on subsequent
        // key events like ACTION_UP.
        fun KeyEvent.handleAction(): Boolean {
            return action != KeyEvent.ACTION_DOWN
        }
    }
}
}
+69 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 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.keyevent.domain.interactor

import android.view.KeyEvent
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
import javax.inject.Inject

/**
 * Sends key events to the appropriate interactors and then acts upon key events that haven't
 * already been handled but should be handled by SystemUI.
 *
 * To observe any key event states, see [KeyEventInteractor].
 */
@SysUISingleton
class SysUIKeyEventHandler
@Inject
constructor(
    private val backActionInteractor: BackActionInteractor,
    private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor,
) {
    fun dispatchKeyEvent(event: KeyEvent): Boolean {
        if (keyguardKeyEventInteractor.dispatchKeyEvent(event)) {
            return true
        }

        when (event.keyCode) {
            KeyEvent.KEYCODE_BACK -> {
                if (event.handleAction()) {
                    backActionInteractor.onBackRequested()
                }
                return true
            }
        }
        return false
    }

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

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

    companion object {
        // Most actions shouldn't be handled on the down event and instead handled on subsequent
        // key events like ACTION_UP.
        fun KeyEvent.handleAction(): Boolean {
            return action != KeyEvent.ACTION_DOWN
        }
    }
}
Loading