1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.keyguard.domain.interactor 18 19 import android.media.AudioManager 20 import android.view.KeyEvent 21 import com.android.settingslib.volume.data.repository.AudioRepository 22 import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.lifecycle.ExclusiveActivatable 25 import com.android.systemui.telephony.domain.interactor.TelephonyInteractor 26 import javax.inject.Inject 27 28 /** Handle media key events while on keyguard or bouncer. */ 29 @SysUISingleton 30 class KeyguardMediaKeyInteractor 31 @Inject 32 constructor( 33 private val telephonyInteractor: TelephonyInteractor, 34 private val audioRepository: AudioRepository, 35 ) : ExclusiveActivatable() { 36 37 /** 38 * Allows the media keys to work when the keyguard is showing. Forwards the relevant media keys 39 * to [AudioManager]. 40 * 41 * @param event The key event 42 * @return whether the event was consumed as a media key. 43 */ processMediaKeyEventnull44 fun processMediaKeyEvent(event: KeyEvent): Boolean { 45 if (ComposeBouncerFlags.isUnexpectedlyInLegacyMode()) { 46 return false 47 } 48 val keyCode = event.keyCode 49 if (event.action == KeyEvent.ACTION_DOWN) { 50 when (keyCode) { 51 KeyEvent.KEYCODE_MEDIA_PLAY, 52 KeyEvent.KEYCODE_MEDIA_PAUSE, 53 KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> { 54 /* Suppress PLAY/PAUSE toggle when phone is ringing or 55 * in-call to avoid music playback */ 56 // suppress key event 57 return telephonyInteractor.isInCall.value 58 } 59 60 KeyEvent.KEYCODE_MUTE, 61 KeyEvent.KEYCODE_HEADSETHOOK, 62 KeyEvent.KEYCODE_MEDIA_STOP, 63 KeyEvent.KEYCODE_MEDIA_NEXT, 64 KeyEvent.KEYCODE_MEDIA_PREVIOUS, 65 KeyEvent.KEYCODE_MEDIA_REWIND, 66 KeyEvent.KEYCODE_MEDIA_RECORD, 67 KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, 68 KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK -> { 69 audioRepository.dispatchMediaKeyEvent(event) 70 return true 71 } 72 73 KeyEvent.KEYCODE_VOLUME_UP, 74 KeyEvent.KEYCODE_VOLUME_DOWN, 75 KeyEvent.KEYCODE_VOLUME_MUTE -> return false 76 } 77 } else if (event.action == KeyEvent.ACTION_UP) { 78 when (keyCode) { 79 KeyEvent.KEYCODE_MUTE, 80 KeyEvent.KEYCODE_HEADSETHOOK, 81 KeyEvent.KEYCODE_MEDIA_PLAY, 82 KeyEvent.KEYCODE_MEDIA_PAUSE, 83 KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 84 KeyEvent.KEYCODE_MEDIA_STOP, 85 KeyEvent.KEYCODE_MEDIA_NEXT, 86 KeyEvent.KEYCODE_MEDIA_PREVIOUS, 87 KeyEvent.KEYCODE_MEDIA_REWIND, 88 KeyEvent.KEYCODE_MEDIA_RECORD, 89 KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, 90 KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK -> { 91 audioRepository.dispatchMediaKeyEvent(event) 92 return true 93 } 94 } 95 } 96 return false 97 } 98 onActivatednull99 override suspend fun onActivated(): Nothing { 100 // Collect to keep this flow hot for this interactor. 101 telephonyInteractor.isInCall.collect {} 102 } 103 } 104