• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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.server.input
18 
19 import android.content.Context
20 import android.content.ContextWrapper
21 import android.content.pm.ActivityInfo
22 import android.content.pm.ApplicationInfo
23 import android.content.pm.PackageManager
24 import android.content.pm.ResolveInfo
25 import android.content.pm.ServiceInfo
26 import android.hardware.input.InputManager
27 import android.hardware.input.KeyGlyphMap.KeyCombination
28 import android.os.Bundle
29 import android.os.test.TestLooper
30 import android.platform.test.annotations.EnableFlags
31 import android.platform.test.annotations.Presubmit
32 import android.platform.test.flag.junit.SetFlagsRule
33 import android.view.InputDevice
34 import android.view.KeyEvent
35 import androidx.test.core.app.ApplicationProvider
36 import com.android.hardware.input.Flags
37 import com.android.test.input.MockInputManagerRule
38 import com.android.test.input.R
39 import org.junit.Assert.assertEquals
40 import org.junit.Assert.assertNotNull
41 import org.junit.Assert.assertNull
42 import org.junit.Before
43 import org.junit.Rule
44 import org.junit.Test
45 import org.mockito.Mock
46 import org.mockito.Mockito
47 import org.mockito.junit.MockitoJUnit
48 
49 /**
50  * Tests for custom keyboard glyph map configuration.
51  *
52  * Build/Install/Run: atest InputTests:KeyboardGlyphManagerTests
53  */
54 @Presubmit
55 class KeyboardGlyphManagerTests {
56 
57     companion object {
58         const val DEVICE_ID = 1
59         const val VENDOR_ID = 0x1234
60         const val PRODUCT_ID = 0x3456
61         const val DEVICE_ID2 = 2
62         const val VENDOR_ID2 = 0x1235
63         const val PRODUCT_ID2 = 0x3457
64         const val PACKAGE_NAME = "KeyboardLayoutManagerTests"
65         const val RECEIVER_NAME = "DummyReceiver"
66     }
67 
68     @get:Rule val setFlagsRule = SetFlagsRule()
69     @get:Rule val mockitoRule = MockitoJUnit.rule()!!
70     @get:Rule val inputManagerRule = MockInputManagerRule()
71 
72     @Mock private lateinit var packageManager: PackageManager
73 
74     private lateinit var keyboardGlyphManager: KeyboardGlyphManager
75     private lateinit var context: Context
76     private lateinit var testLooper: TestLooper
77     private lateinit var keyboardDevice: InputDevice
78 
79     @Before
setupnull80     fun setup() {
81         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
82         testLooper = TestLooper()
83         keyboardGlyphManager = KeyboardGlyphManager(context, testLooper.looper)
84 
85         setupInputDevices()
86         setupBroadcastReceiver()
87         keyboardGlyphManager.systemRunning()
88         testLooper.dispatchAll()
89     }
90 
setupInputDevicesnull91     private fun setupInputDevices() {
92         val inputManager = InputManager(context)
93         Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
94             .thenReturn(inputManager)
95 
96         keyboardDevice = createKeyboard(DEVICE_ID, VENDOR_ID, PRODUCT_ID, 0, "", "")
97         Mockito.`when`(inputManagerRule.mock.inputDeviceIds)
98             .thenReturn(intArrayOf(DEVICE_ID, DEVICE_ID2))
99         Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
100 
101         val keyboardDevice2 = createKeyboard(DEVICE_ID2, VENDOR_ID2, PRODUCT_ID2, 0, "", "")
102         Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID2)).thenReturn(keyboardDevice2)
103     }
104 
setupBroadcastReceivernull105     private fun setupBroadcastReceiver() {
106         Mockito.`when`(context.packageManager).thenReturn(packageManager)
107 
108         val info = createMockReceiver()
109         Mockito.`when`(
110                 packageManager.queryBroadcastReceiversAsUser(
111                     Mockito.any(),
112                     Mockito.anyInt(),
113                     Mockito.anyInt(),
114                 )
115             )
116             .thenReturn(listOf(info))
117         Mockito.`when`(packageManager.getReceiverInfo(Mockito.any(), Mockito.anyInt()))
118             .thenReturn(info.activityInfo)
119 
120         val resources = context.resources
121         Mockito.`when`(
122                 packageManager.getResourcesForApplication(Mockito.any(ApplicationInfo::class.java))
123             )
124             .thenReturn(resources)
125     }
126 
createMockReceivernull127     private fun createMockReceiver(): ResolveInfo {
128         val info = ResolveInfo()
129         info.activityInfo = ActivityInfo()
130         info.activityInfo.packageName = PACKAGE_NAME
131         info.activityInfo.name = RECEIVER_NAME
132         info.activityInfo.applicationInfo = ApplicationInfo()
133         info.activityInfo.metaData = Bundle()
134         info.activityInfo.metaData.putInt(
135             InputManager.META_DATA_KEYBOARD_GLYPH_MAPS,
136             R.xml.keyboard_glyph_maps,
137         )
138         info.serviceInfo = ServiceInfo()
139         info.serviceInfo.packageName = PACKAGE_NAME
140         info.serviceInfo.name = RECEIVER_NAME
141         return info
142     }
143 
144     @Test
145     @EnableFlags(Flags.FLAG_KEYBOARD_GLYPH_MAP)
testGlyphMapsLoadednull146     fun testGlyphMapsLoaded() {
147         assertNotNull(
148             "Glyph map for test keyboard(deviceId=$DEVICE_ID) must exist",
149             keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID),
150         )
151         assertNotNull(
152             "Glyph map for test keyboard(deviceId=$DEVICE_ID2) must exist",
153             keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID2),
154         )
155         assertNull(
156             "Glyph map for non-existing keyboard must be null",
157             keyboardGlyphManager.getKeyGlyphMap(-2),
158         )
159     }
160 
161     @Test
162     @EnableFlags(Flags.FLAG_KEYBOARD_GLYPH_MAP)
testGlyphMapCorrectlyLoadednull163     fun testGlyphMapCorrectlyLoaded() {
164         val glyphMap = keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID)
165         // Test glyph map used in this test: {@see test_glyph_map.xml}
166         assertNotNull(glyphMap!!.getDrawableForKeycode(context, KeyEvent.KEYCODE_BACK))
167 
168         assertNotNull(glyphMap.getDrawableForModifier(context, KeyEvent.KEYCODE_META_LEFT))
169         assertNotNull(glyphMap.getDrawableForModifier(context, KeyEvent.KEYCODE_META_RIGHT))
170         assertNotNull(glyphMap.getDrawableForModifierState(context, KeyEvent.META_META_ON))
171 
172         val functionRowKeys = glyphMap.functionRowKeys
173         assertEquals(1, functionRowKeys.size)
174         assertEquals(KeyEvent.KEYCODE_EMOJI_PICKER, functionRowKeys[0])
175 
176         val hardwareShortcuts = glyphMap.hardwareShortcuts
177         assertEquals(2, hardwareShortcuts.size)
178         assertEquals(
179             KeyEvent.KEYCODE_BACK,
180             hardwareShortcuts[KeyCombination(KeyEvent.META_FUNCTION_ON, KeyEvent.KEYCODE_1)],
181         )
182         assertEquals(
183             KeyEvent.KEYCODE_HOME,
184             hardwareShortcuts[
185                 KeyCombination(
186                     KeyEvent.META_FUNCTION_ON or KeyEvent.META_META_ON,
187                     KeyEvent.KEYCODE_2,
188                 )],
189         )
190     }
191 }
192