• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.qs
18 
19 import android.view.KeyEvent
20 import android.view.View
21 import androidx.core.util.Consumer
22 
23 /**
24  * Listens for left and right arrow keys pressed while focus is on the view.
25  *
26  * Key press is treated as correct when its full lifecycle happened on the view: first
27  * [KeyEvent.ACTION_DOWN] was performed, view didn't lose focus in the meantime and then
28  * [KeyEvent.ACTION_UP] was performed with the same [KeyEvent.getKeyCode]
29  */
30 class LeftRightArrowPressedListener private constructor() :
31     View.OnKeyListener, View.OnFocusChangeListener {
32 
33     private var lastKeyCode: Int? = 0
34     private var listener: Consumer<Int>? = null
35 
setArrowKeyPressedListenernull36     fun setArrowKeyPressedListener(arrowPressedListener: Consumer<Int>) {
37         listener = arrowPressedListener
38     }
39 
onKeynull40     override fun onKey(view: View, keyCode: Int, keyEvent: KeyEvent): Boolean {
41         if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
42             // only scroll on ACTION_UP as we don't handle longpressing for now. Still we need
43             // to intercept even ACTION_DOWN otherwise keyboard focus will be moved before we
44             // have a chance to intercept ACTION_UP.
45             if (keyEvent.action == KeyEvent.ACTION_UP && keyCode == lastKeyCode) {
46                 listener?.accept(keyCode)
47                 lastKeyCode = null
48             } else if (keyEvent.repeatCount == 0) {
49                 // we only read key events that are NOT coming from long pressing because that also
50                 // causes reading ACTION_DOWN event (with repeated count > 0) when moving focus with
51                 // arrow from another sibling view
52                 lastKeyCode = keyCode
53             }
54             return true
55         }
56         return false
57     }
58 
onFocusChangenull59     override fun onFocusChange(view: View, hasFocus: Boolean) {
60         // resetting lastKeyCode so we get fresh cleared state on focus
61         if (hasFocus) {
62             lastKeyCode = null
63         }
64     }
65 
66     companion object {
67         @JvmStatic
createAndRegisterListenerForViewnull68         fun createAndRegisterListenerForView(view: View): LeftRightArrowPressedListener {
69             val listener = LeftRightArrowPressedListener()
70             view.setOnKeyListener(listener)
71             view.onFocusChangeListener = listener
72             return listener
73         }
74     }
75 }
76