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