1 /* 2 * Copyright (C) 2012 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.NonNull; 20 import android.os.LocaleList; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 24 import java.util.HashMap; 25 import java.util.Map; 26 import java.util.Objects; 27 28 /** 29 * Describes a keyboard layout. 30 * 31 * @hide 32 */ 33 public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayout> { 34 35 /** Undefined keyboard layout */ 36 public static final String LAYOUT_TYPE_UNDEFINED = "undefined"; 37 38 /** Qwerty-based keyboard layout */ 39 public static final String LAYOUT_TYPE_QWERTY = "qwerty"; 40 41 /** Qwertz-based keyboard layout */ 42 public static final String LAYOUT_TYPE_QWERTZ = "qwertz"; 43 44 /** Azerty-based keyboard layout */ 45 public static final String LAYOUT_TYPE_AZERTY = "azerty"; 46 47 /** Dvorak keyboard layout */ 48 public static final String LAYOUT_TYPE_DVORAK = "dvorak"; 49 50 /** Colemak keyboard layout */ 51 public static final String LAYOUT_TYPE_COLEMAK = "colemak"; 52 53 /** Workman keyboard layout */ 54 public static final String LAYOUT_TYPE_WORKMAN = "workman"; 55 56 /** Turkish-F keyboard layout */ 57 public static final String LAYOUT_TYPE_TURKISH_F = "turkish_f"; 58 59 /** Turkish-Q keyboard layout */ 60 public static final String LAYOUT_TYPE_TURKISH_Q = "turkish_q"; 61 62 /** Keyboard layout that has been enhanced with a large number of extra characters */ 63 public static final String LAYOUT_TYPE_EXTENDED = "extended"; 64 65 private final String mDescriptor; 66 private final String mLabel; 67 private final String mCollection; 68 private final int mPriority; 69 @NonNull 70 private final LocaleList mLocales; 71 private final LayoutType mLayoutType; 72 private final int mVendorId; 73 private final int mProductId; 74 75 /** Currently supported Layout types in the KCM files */ 76 public enum LayoutType { 77 UNDEFINED(0, LAYOUT_TYPE_UNDEFINED), 78 QWERTY(1, LAYOUT_TYPE_QWERTY), 79 QWERTZ(2, LAYOUT_TYPE_QWERTZ), 80 AZERTY(3, LAYOUT_TYPE_AZERTY), 81 DVORAK(4, LAYOUT_TYPE_DVORAK), 82 COLEMAK(5, LAYOUT_TYPE_COLEMAK), 83 WORKMAN(6, LAYOUT_TYPE_WORKMAN), 84 TURKISH_F(7, LAYOUT_TYPE_TURKISH_F), 85 TURKISH_Q(8, LAYOUT_TYPE_TURKISH_Q), 86 EXTENDED(9, LAYOUT_TYPE_EXTENDED); 87 88 private final int mValue; 89 private final String mName; 90 private static final Map<Integer, LayoutType> VALUE_TO_ENUM_MAP = new HashMap<>(); 91 private static final Map<String, LayoutType> NAME_TO_ENUM_MAP = new HashMap<>(); 92 static { 93 for (LayoutType type : LayoutType.values()) { VALUE_TO_ENUM_MAP.put(type.mValue, type)94 VALUE_TO_ENUM_MAP.put(type.mValue, type); NAME_TO_ENUM_MAP.put(type.mName, type)95 NAME_TO_ENUM_MAP.put(type.mName, type); 96 } 97 } 98 of(int value)99 private static LayoutType of(int value) { 100 return VALUE_TO_ENUM_MAP.getOrDefault(value, UNDEFINED); 101 } 102 LayoutType(int value, String name)103 LayoutType(int value, String name) { 104 this.mValue = value; 105 this.mName = name; 106 } 107 getValue()108 private int getValue() { 109 return mValue; 110 } 111 getName()112 private String getName() { 113 return mName; 114 } 115 116 /** 117 * Returns enum value for provided layout type 118 * @param layoutName name of the layout type 119 * @return int value corresponding to the LayoutType enum that matches the layout name. 120 * (LayoutType.UNDEFINED if no match found) 121 */ getLayoutTypeEnumValue(String layoutName)122 public static int getLayoutTypeEnumValue(String layoutName) { 123 return NAME_TO_ENUM_MAP.getOrDefault(layoutName, UNDEFINED).getValue(); 124 } 125 126 /** 127 * Returns name for provided layout type enum value 128 * @param enumValue value representation for LayoutType enum 129 * @return Layout name corresponding to the enum value (LAYOUT_TYPE_UNDEFINED if not found) 130 */ getLayoutNameFromValue(int enumValue)131 public static String getLayoutNameFromValue(int enumValue) { 132 return VALUE_TO_ENUM_MAP.getOrDefault(enumValue, UNDEFINED).getName(); 133 } 134 } 135 136 @NonNull 137 public static final Parcelable.Creator<KeyboardLayout> CREATOR = new Parcelable.Creator<>() { 138 public KeyboardLayout createFromParcel(Parcel source) { 139 return new KeyboardLayout(source); 140 } 141 public KeyboardLayout[] newArray(int size) { 142 return new KeyboardLayout[size]; 143 } 144 }; 145 KeyboardLayout(String descriptor, String label, String collection, int priority, LocaleList locales, int layoutValue, int vid, int pid)146 public KeyboardLayout(String descriptor, String label, String collection, int priority, 147 LocaleList locales, int layoutValue, int vid, int pid) { 148 mDescriptor = descriptor; 149 mLabel = label; 150 mCollection = collection; 151 mPriority = priority; 152 mLocales = locales; 153 mLayoutType = LayoutType.of(layoutValue); 154 mVendorId = vid; 155 mProductId = pid; 156 } 157 KeyboardLayout(Parcel source)158 private KeyboardLayout(Parcel source) { 159 mDescriptor = source.readString(); 160 mLabel = source.readString(); 161 mCollection = source.readString(); 162 mPriority = source.readInt(); 163 mLocales = LocaleList.CREATOR.createFromParcel(source); 164 mLayoutType = LayoutType.of(source.readInt()); 165 mVendorId = source.readInt(); 166 mProductId = source.readInt(); 167 } 168 169 /** 170 * Gets the keyboard layout descriptor, which can be used to retrieve 171 * the keyboard layout again later using 172 * {@link InputManager#getKeyboardLayout(String)}. 173 * 174 * @return The keyboard layout descriptor. 175 */ getDescriptor()176 public String getDescriptor() { 177 return mDescriptor; 178 } 179 180 /** 181 * Gets the keyboard layout descriptive label to show in the user interface. 182 * @return The keyboard layout descriptive label. 183 */ getLabel()184 public String getLabel() { 185 return mLabel; 186 } 187 188 /** 189 * Gets the name of the collection to which the keyboard layout belongs. This is 190 * the label of the broadcast receiver or application that provided the keyboard layout. 191 * @return The keyboard layout collection name. 192 */ getCollection()193 public String getCollection() { 194 return mCollection; 195 } 196 197 /** 198 * Gets the locales that this keyboard layout is intended for. 199 * This may be empty if a locale has not been assigned to this keyboard layout. 200 * @return The keyboard layout's intended locale. 201 */ getLocales()202 public LocaleList getLocales() { 203 return mLocales; 204 } 205 206 /** 207 * Gets the layout type that this keyboard layout is intended for. 208 * This may be "undefined" if a layoutType has not been assigned to this keyboard layout. 209 * @return The keyboard layout's intended layout type. 210 */ getLayoutType()211 public String getLayoutType() { 212 return mLayoutType.getName(); 213 } 214 215 /** 216 * Gets the vendor ID of the hardware device this keyboard layout is intended for. 217 * Returns -1 if this is not specific to any piece of hardware. 218 * @return The hardware vendor ID of the keyboard layout's intended device. 219 */ getVendorId()220 public int getVendorId() { 221 return mVendorId; 222 } 223 224 /** 225 * Gets the product ID of the hardware device this keyboard layout is intended for. 226 * Returns -1 if this is not specific to any piece of hardware. 227 * @return The hardware product ID of the keyboard layout's intended device. 228 */ getProductId()229 public int getProductId() { 230 return mProductId; 231 } 232 233 @Override describeContents()234 public int describeContents() { 235 return 0; 236 } 237 238 @Override writeToParcel(Parcel dest, int flags)239 public void writeToParcel(Parcel dest, int flags) { 240 dest.writeString(mDescriptor); 241 dest.writeString(mLabel); 242 dest.writeString(mCollection); 243 dest.writeInt(mPriority); 244 mLocales.writeToParcel(dest, 0); 245 dest.writeInt(mLayoutType.getValue()); 246 dest.writeInt(mVendorId); 247 dest.writeInt(mProductId); 248 } 249 250 @Override compareTo(KeyboardLayout another)251 public int compareTo(KeyboardLayout another) { 252 // Note that these arguments are intentionally flipped since you want higher priority 253 // keyboards to be listed before lower priority keyboards. 254 int result = Integer.compare(another.mPriority, mPriority); 255 if (result == 0) { 256 result = Integer.compare(mLayoutType.mValue, another.mLayoutType.mValue); 257 } 258 if (result == 0) { 259 result = mLabel.compareToIgnoreCase(another.mLabel); 260 } 261 if (result == 0) { 262 result = mCollection.compareToIgnoreCase(another.mCollection); 263 } 264 return result; 265 } 266 267 @Override toString()268 public String toString() { 269 String collectionString = mCollection.isEmpty() ? "" : " - " + mCollection; 270 return "KeyboardLayout " + mLabel + collectionString 271 + ", descriptor: " + mDescriptor 272 + ", priority: " + mPriority 273 + ", locales: " + mLocales.toString() 274 + ", layout type: " + mLayoutType.getName() 275 + ", vendorId: " + mVendorId 276 + ", productId: " + mProductId; 277 } 278 279 /** 280 * Check if the provided layout type is supported/valid. 281 * 282 * @param layoutName name of layout type 283 * @return {@code true} if the provided layout type is supported/valid. 284 */ isLayoutTypeValid(@onNull String layoutName)285 public static boolean isLayoutTypeValid(@NonNull String layoutName) { 286 Objects.requireNonNull(layoutName, "Provided layout name should not be null"); 287 for (LayoutType layoutType : LayoutType.values()) { 288 if (layoutName.equals(layoutType.getName())) { 289 return true; 290 } 291 } 292 // Layout doesn't match any supported layout types 293 return false; 294 } 295 } 296