• 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 android.hardware.input;
18 
19 import android.annotation.DrawableRes;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.pm.ActivityInfo;
25 import android.content.pm.PackageManager;
26 import android.content.res.Resources;
27 import android.graphics.drawable.Drawable;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.util.Log;
31 import android.util.SparseIntArray;
32 import android.view.KeyEvent;
33 
34 import java.util.Arrays;
35 import java.util.HashMap;
36 import java.util.Map;
37 import java.util.Objects;
38 
39 /**
40  * This class provides access to device specific key glyphs, modifier glyphs and device specific
41  * shortcuts and keys
42  *
43  * @hide
44  */
45 public final class KeyGlyphMap implements Parcelable {
46     private static final String TAG = "KeyGlyphMap";
47 
48     @NonNull
49     private final ComponentName mComponentName;
50     @NonNull
51     private final SparseIntArray mKeyGlyphs;
52     @NonNull
53     private final SparseIntArray mModifierGlyphs;
54     @NonNull
55     private final int[] mFunctionRowKeys;
56     @NonNull
57     private final Map<KeyCombination, Integer> mHardwareShortcuts;
58 
59     public static final @NonNull Parcelable.Creator<KeyGlyphMap> CREATOR =
60             new Parcelable.Creator<>() {
61                 public KeyGlyphMap createFromParcel(Parcel in) {
62                     return new KeyGlyphMap(in);
63                 }
64 
65                 public KeyGlyphMap[] newArray(int size) {
66                     return new KeyGlyphMap[size];
67                 }
68             };
69 
KeyGlyphMap(@onNull ComponentName componentName, @NonNull SparseIntArray keyGlyphs, @NonNull SparseIntArray modifierGlyphs, @NonNull int[] functionRowKeys, @NonNull Map<KeyCombination, Integer> hardwareShortcuts)70     public KeyGlyphMap(@NonNull ComponentName componentName,
71             @NonNull SparseIntArray keyGlyphs, @NonNull SparseIntArray modifierGlyphs,
72             @NonNull int[] functionRowKeys,
73             @NonNull Map<KeyCombination, Integer> hardwareShortcuts) {
74         mComponentName = componentName;
75         mKeyGlyphs = keyGlyphs;
76         mModifierGlyphs = modifierGlyphs;
77         mFunctionRowKeys = functionRowKeys;
78         mHardwareShortcuts = hardwareShortcuts;
79     }
80 
KeyGlyphMap(Parcel in)81     public KeyGlyphMap(Parcel in) {
82         mComponentName = in.readParcelable(getClass().getClassLoader(), ComponentName.class);
83         mKeyGlyphs = in.readSparseIntArray();
84         mModifierGlyphs = in.readSparseIntArray();
85         mFunctionRowKeys = new int[in.readInt()];
86         in.readIntArray(mFunctionRowKeys);
87         mHardwareShortcuts = new HashMap<>(in.readInt());
88         in.readMap(mHardwareShortcuts, getClass().getClassLoader(), KeyCombination.class,
89                 Integer.class);
90     }
91 
92     @Override
writeToParcel(@onNull Parcel dest, int flags)93     public void writeToParcel(@NonNull Parcel dest, int flags) {
94         dest.writeParcelable(mComponentName, 0);
95         dest.writeSparseIntArray(mKeyGlyphs);
96         dest.writeSparseIntArray(mModifierGlyphs);
97         dest.writeInt(mFunctionRowKeys.length);
98         dest.writeIntArray(mFunctionRowKeys);
99         dest.writeInt(mHardwareShortcuts.size());
100         dest.writeMap(mHardwareShortcuts);
101     }
102 
103     @Override
describeContents()104     public int describeContents() {
105         return 0;
106     }
107 
108     /**
109      * Defines a key combination that includes a keycode and modifier state.
110      */
111     public static class KeyCombination implements Parcelable {
112         private final int mModifierState;
113         private final int mKeycode;
114 
KeyCombination(int modifierState, int keycode)115         public KeyCombination(int modifierState, int keycode) {
116             this.mModifierState = modifierState;
117             this.mKeycode = keycode;
118         }
119 
KeyCombination(Parcel in)120         public KeyCombination(Parcel in) {
121             this(in.readInt(), in.readInt());
122         }
123 
124         public static final Creator<KeyCombination> CREATOR = new Creator<>() {
125             @Override
126             public KeyCombination createFromParcel(Parcel in) {
127                 return new KeyCombination(in);
128             }
129 
130             @Override
131             public KeyCombination[] newArray(int size) {
132                 return new KeyCombination[size];
133             }
134         };
135 
getModifierState()136         public int getModifierState() {
137             return mModifierState;
138         }
139 
getKeycode()140         public int getKeycode() {
141             return mKeycode;
142         }
143 
144         @Override
describeContents()145         public int describeContents() {
146             return 0;
147         }
148 
149         @Override
writeToParcel(@ndroidx.annotation.NonNull Parcel dest, int flags)150         public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) {
151             dest.writeInt(mModifierState);
152             dest.writeInt(mKeycode);
153         }
154 
155         @Override
equals(Object o)156         public boolean equals(Object o) {
157             if (this == o) return true;
158             if (!(o instanceof KeyCombination that)) return false;
159             return mModifierState == that.mModifierState && mKeycode == that.mKeycode;
160         }
161 
162         @Override
hashCode()163         public int hashCode() {
164             return Objects.hash(mModifierState, mKeycode);
165         }
166     }
167 
168     /**
169      * Returns keycodes generated from the functional row defined for the keyboard.
170      */
getFunctionRowKeys()171     public int[] getFunctionRowKeys() {
172         return mFunctionRowKeys;
173     }
174 
175     /**
176      * Returns hardware defined shortcuts that are handled in the firmware of a particular
177      * keyboard (e.g. Fn+Backspace = Back, etc.)
178      *
179      * @return a map of (modifier + key) combinations to keycode mappings that are handled by the
180      * device hardware/firmware.
181      */
getHardwareShortcuts()182     public Map<KeyCombination, Integer> getHardwareShortcuts() {
183         return mHardwareShortcuts;
184     }
185 
186     /**
187      * Provides the drawable resource for the glyph for a keycode.
188      * Returns null if not available.
189      */
190     @Nullable
getDrawableForKeycode(Context context, int keycode)191     public Drawable getDrawableForKeycode(Context context, int keycode) {
192         return getDrawable(context, mKeyGlyphs.get(keycode, 0));
193     }
194 
195     /**
196      * Provides the drawable resource for the glyph for a modifier key.
197      * Returns null if not available.
198      */
199     @Nullable
getDrawableForModifier(Context context, int modifierKeycode)200     public Drawable getDrawableForModifier(Context context, int modifierKeycode) {
201         int modifier = switch (modifierKeycode) {
202             case KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_META_RIGHT -> KeyEvent.META_META_ON;
203             case KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT -> KeyEvent.META_CTRL_ON;
204             case KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT -> KeyEvent.META_ALT_ON;
205             case KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SHIFT_RIGHT ->
206                     KeyEvent.META_SHIFT_ON;
207             case KeyEvent.KEYCODE_FUNCTION -> KeyEvent.META_FUNCTION_ON;
208             case KeyEvent.KEYCODE_SYM -> KeyEvent.META_SYM_ON;
209             case KeyEvent.KEYCODE_CAPS_LOCK -> KeyEvent.META_CAPS_LOCK_ON;
210             case KeyEvent.KEYCODE_NUM_LOCK -> KeyEvent.META_NUM_LOCK_ON;
211             case KeyEvent.KEYCODE_SCROLL_LOCK -> KeyEvent.META_SCROLL_LOCK_ON;
212             default -> 0;
213         };
214         return getDrawable(context, mModifierGlyphs.get(modifier, 0));
215     }
216 
217     /**
218      * Provides the drawable resource for the glyph for a modifier state (e.g. META_META_ON).
219      * Returns null if not available.
220      */
221     @Nullable
getDrawableForModifierState(Context context, int modifierState)222     public Drawable getDrawableForModifierState(Context context, int modifierState) {
223         return getDrawable(context, mModifierGlyphs.get(modifierState, 0));
224     }
225 
226     @Nullable
getDrawable(Context context, @DrawableRes int drawableRes)227     private Drawable getDrawable(Context context, @DrawableRes int drawableRes) {
228         PackageManager pm = context.getPackageManager();
229         try {
230             ActivityInfo receiver = pm.getReceiverInfo(mComponentName,
231                     PackageManager.GET_META_DATA
232                             | PackageManager.MATCH_DIRECT_BOOT_AWARE
233                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
234             Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
235             return resources.getDrawable(drawableRes, null);
236         } catch (PackageManager.NameNotFoundException ignored) {
237             Log.e(TAG, "Package name not found for " + mComponentName);
238         } catch (Resources.NotFoundException ignored) {
239             Log.e(TAG, "Resource not found for " + mComponentName);
240         }
241         return null;
242     }
243 
244     @Override
toString()245     public String toString() {
246         return "KeyGlyphMap{"
247                 + "mComponentName=" + mComponentName
248                 + ", mKeyGlyphs=" + mKeyGlyphs
249                 + ", mModifierGlyphs=" + mModifierGlyphs
250                 + ", mFunctionRowKeys=" + Arrays.toString(mFunctionRowKeys)
251                 + ", mHardwareShortcuts=" + mHardwareShortcuts
252                 + '}';
253     }
254 }
255