Merge "Log KeyboardConfigured atom" into udc-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b85e32f712
@@ -73,7 +73,7 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo
|
||||
private final int mProductId;
|
||||
|
||||
/** Currently supported Layout types in the KCM files */
|
||||
private enum LayoutType {
|
||||
public enum LayoutType {
|
||||
UNDEFINED(0, LAYOUT_TYPE_UNDEFINED),
|
||||
QWERTY(1, LAYOUT_TYPE_QWERTY),
|
||||
QWERTZ(2, LAYOUT_TYPE_QWERTZ),
|
||||
@@ -88,9 +88,11 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo
|
||||
private final int mValue;
|
||||
private final String mName;
|
||||
private static final Map<Integer, LayoutType> VALUE_TO_ENUM_MAP = new HashMap<>();
|
||||
private static final Map<String, LayoutType> NAME_TO_ENUM_MAP = new HashMap<>();
|
||||
static {
|
||||
for (LayoutType type : LayoutType.values()) {
|
||||
VALUE_TO_ENUM_MAP.put(type.mValue, type);
|
||||
NAME_TO_ENUM_MAP.put(type.mName, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +112,25 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo
|
||||
private String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum value for provided layout type
|
||||
* @param layoutName name of the layout type
|
||||
* @return int value corresponding to the LayoutType enum that matches the layout name.
|
||||
* (LayoutType.UNDEFINED if no match found)
|
||||
*/
|
||||
public static int getLayoutTypeEnumValue(String layoutName) {
|
||||
return NAME_TO_ENUM_MAP.getOrDefault(layoutName, UNDEFINED).getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name for provided layout type enum value
|
||||
* @param enumValue value representation for LayoutType enum
|
||||
* @return Layout name corresponding to the enum value (LAYOUT_TYPE_UNDEFINED if not found)
|
||||
*/
|
||||
public static String getLayoutNameFromValue(int enumValue) {
|
||||
return VALUE_TO_ENUM_MAP.getOrDefault(enumValue, UNDEFINED).getName();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package com.android.server.input;
|
||||
|
||||
import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE;
|
||||
import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER;
|
||||
import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
|
||||
|
||||
import android.annotation.AnyThread;
|
||||
import android.annotation.MainThread;
|
||||
import android.annotation.NonNull;
|
||||
@@ -67,6 +71,8 @@ import com.android.internal.inputmethod.InputMethodSubtypeHandle;
|
||||
import com.android.internal.messages.nano.SystemMessageProto;
|
||||
import com.android.internal.notification.SystemNotificationChannels;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.server.input.KeyboardMetricsCollector.KeyboardConfigurationEvent;
|
||||
import com.android.server.input.KeyboardMetricsCollector.LayoutSelectionCriteria;
|
||||
import com.android.server.inputmethod.InputMethodManagerInternal;
|
||||
|
||||
import libcore.io.Streams;
|
||||
@@ -118,7 +124,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
// This cache stores "best-matched" layouts so that we don't need to run the matching
|
||||
// algorithm repeatedly.
|
||||
@GuardedBy("mKeyboardLayoutCache")
|
||||
private final Map<String, String> mKeyboardLayoutCache = new ArrayMap<>();
|
||||
private final Map<String, KeyboardLayoutInfo> mKeyboardLayoutCache = new ArrayMap<>();
|
||||
private final Object mImeInfoLock = new Object();
|
||||
@Nullable
|
||||
@GuardedBy("mImeInfoLock")
|
||||
@@ -194,7 +200,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
setCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier(), layout);
|
||||
}
|
||||
}
|
||||
config.setCurrentLayout(layout);
|
||||
config.setCurrentLayout(
|
||||
new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER));
|
||||
if (layout == null) {
|
||||
// In old settings show notification always until user manually selects a
|
||||
// layout in the settings.
|
||||
@@ -205,18 +212,19 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
final InputDeviceIdentifier identifier = inputDevice.getIdentifier();
|
||||
final String key = getLayoutDescriptor(identifier);
|
||||
Set<String> selectedLayouts = new HashSet<>();
|
||||
for (ImeInfo imeInfo : getImeInfoListForLayoutMapping()) {
|
||||
List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping();
|
||||
List<KeyboardLayoutInfo> layoutInfoList = new ArrayList<>();
|
||||
boolean hasMissingLayout = false;
|
||||
for (ImeInfo imeInfo : imeInfoList) {
|
||||
// Check if the layout has been previously configured
|
||||
String layout = getKeyboardLayoutForInputDeviceInternal(identifier,
|
||||
new ImeInfo(imeInfo.mUserId, imeInfo.mImeSubtypeHandle,
|
||||
imeInfo.mImeSubtype));
|
||||
if (layout == null) {
|
||||
// If even one layout not configured properly, we need to ask user to configure
|
||||
// the keyboard properly from the Settings.
|
||||
selectedLayouts.clear();
|
||||
break;
|
||||
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
|
||||
imeInfo);
|
||||
boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
|
||||
if (!noLayoutFound) {
|
||||
selectedLayouts.add(layoutInfo.mDescriptor);
|
||||
}
|
||||
selectedLayouts.add(layout);
|
||||
layoutInfoList.add(layoutInfo);
|
||||
hasMissingLayout |= noLayoutFound;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
@@ -225,24 +233,38 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
+ selectedLayouts);
|
||||
}
|
||||
|
||||
// If even one layout not configured properly, we need to ask user to configure
|
||||
// the keyboard properly from the Settings.
|
||||
if (hasMissingLayout) {
|
||||
selectedLayouts.clear();
|
||||
}
|
||||
|
||||
config.setConfiguredLayouts(selectedLayouts);
|
||||
|
||||
// Update current layout: If there is a change then need to reload.
|
||||
synchronized (mImeInfoLock) {
|
||||
String layout = getKeyboardLayoutForInputDeviceInternal(
|
||||
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
|
||||
inputDevice.getIdentifier(), mCurrentImeInfo);
|
||||
if (!Objects.equals(layout, config.getCurrentLayout())) {
|
||||
config.setCurrentLayout(layout);
|
||||
if (!Objects.equals(layoutInfo, config.getCurrentLayout())) {
|
||||
config.setCurrentLayout(layoutInfo);
|
||||
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (mDataStore) {
|
||||
try {
|
||||
boolean isFirstConfiguration = !mDataStore.hasInputDeviceEntry(key);
|
||||
if (mDataStore.setSelectedKeyboardLayouts(key, selectedLayouts)) {
|
||||
// Need to show the notification only if layout selection changed
|
||||
// from the previous configuration
|
||||
needToShowNotification = true;
|
||||
|
||||
// Logging keyboard configuration data to statsd only if the
|
||||
// configuration changed from the previous configuration. Currently
|
||||
// only logging for New Settings UI where we are using IME to decide
|
||||
// the layout information.
|
||||
logKeyboardConfigurationEvent(inputDevice, imeInfoList, layoutInfoList,
|
||||
isFirstConfiguration);
|
||||
}
|
||||
} finally {
|
||||
mDataStore.saveIfNeeded();
|
||||
@@ -252,8 +274,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
if (needToShowNotification) {
|
||||
maybeUpdateNotification();
|
||||
}
|
||||
// TODO (b/280421650): Implement logging statements using KeyboardMetricsCollector
|
||||
// for KeyboardConfigured atom
|
||||
}
|
||||
|
||||
private String getDefaultKeyboardLayout(final InputDevice inputDevice) {
|
||||
@@ -403,7 +423,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
|
||||
@AnyThread
|
||||
@Nullable
|
||||
public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
|
||||
public KeyboardLayout getKeyboardLayout(@NonNull String keyboardLayoutDescriptor) {
|
||||
Objects.requireNonNull(keyboardLayoutDescriptor,
|
||||
"keyboardLayoutDescriptor must not be null");
|
||||
|
||||
@@ -751,8 +771,9 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
String keyboardLayoutDescriptor;
|
||||
if (useNewSettingsUi()) {
|
||||
synchronized (mImeInfoLock) {
|
||||
keyboardLayoutDescriptor = getKeyboardLayoutForInputDeviceInternal(identifier,
|
||||
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
|
||||
mCurrentImeInfo);
|
||||
keyboardLayoutDescriptor = layoutInfo == null ? null : layoutInfo.mDescriptor;
|
||||
}
|
||||
} else {
|
||||
keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
|
||||
@@ -789,13 +810,13 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
return null;
|
||||
}
|
||||
InputMethodSubtypeHandle subtypeHandle = InputMethodSubtypeHandle.of(imeInfo, imeSubtype);
|
||||
String layout = getKeyboardLayoutForInputDeviceInternal(identifier,
|
||||
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
|
||||
new ImeInfo(userId, subtypeHandle, imeSubtype));
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "getKeyboardLayoutForInputDevice() " + identifier.toString() + ", userId : "
|
||||
+ userId + ", subtypeHandle = " + subtypeHandle + " -> " + layout);
|
||||
+ userId + ", subtypeHandle = " + subtypeHandle + " -> " + layoutInfo);
|
||||
}
|
||||
return layout;
|
||||
return layoutInfo != null ? layoutInfo.mDescriptor : null;
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@@ -926,11 +947,11 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
for (int i = 0; i < mConfiguredKeyboards.size(); i++) {
|
||||
InputDevice inputDevice = Objects.requireNonNull(
|
||||
getInputDevice(mConfiguredKeyboards.keyAt(i)));
|
||||
String layout = getKeyboardLayoutForInputDeviceInternal(inputDevice.getIdentifier(),
|
||||
mCurrentImeInfo);
|
||||
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
|
||||
inputDevice.getIdentifier(), mCurrentImeInfo);
|
||||
KeyboardConfiguration config = mConfiguredKeyboards.valueAt(i);
|
||||
if (!Objects.equals(layout, config.getCurrentLayout())) {
|
||||
config.setCurrentLayout(layout);
|
||||
if (!Objects.equals(layoutInfo, config.getCurrentLayout())) {
|
||||
config.setCurrentLayout(layoutInfo);
|
||||
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
|
||||
return;
|
||||
}
|
||||
@@ -939,39 +960,40 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getKeyboardLayoutForInputDeviceInternal(InputDeviceIdentifier identifier,
|
||||
@Nullable ImeInfo imeInfo) {
|
||||
private KeyboardLayoutInfo getKeyboardLayoutForInputDeviceInternal(
|
||||
InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) {
|
||||
InputDevice inputDevice = getInputDevice(identifier);
|
||||
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
|
||||
return null;
|
||||
}
|
||||
String key = createLayoutKey(identifier, imeInfo);
|
||||
String layout;
|
||||
synchronized (mDataStore) {
|
||||
layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key);
|
||||
}
|
||||
if (layout == null) {
|
||||
synchronized (mKeyboardLayoutCache) {
|
||||
// Check Auto-selected layout cache to see if layout had been previously selected
|
||||
if (mKeyboardLayoutCache.containsKey(key)) {
|
||||
layout = mKeyboardLayoutCache.get(key);
|
||||
} else {
|
||||
// NOTE: This list is already filtered based on IME Script code
|
||||
KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal(
|
||||
identifier, imeInfo);
|
||||
// Call auto-matching algorithm to find the best matching layout
|
||||
layout = getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo,
|
||||
layoutList);
|
||||
mKeyboardLayoutCache.put(key, layout);
|
||||
}
|
||||
String layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key);
|
||||
if (layout != null) {
|
||||
return new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (mKeyboardLayoutCache) {
|
||||
// Check Auto-selected layout cache to see if layout had been previously selected
|
||||
if (mKeyboardLayoutCache.containsKey(key)) {
|
||||
return mKeyboardLayoutCache.get(key);
|
||||
} else {
|
||||
// NOTE: This list is already filtered based on IME Script code
|
||||
KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal(
|
||||
identifier, imeInfo);
|
||||
// Call auto-matching algorithm to find the best matching layout
|
||||
KeyboardLayoutInfo layoutInfo =
|
||||
getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo, layoutList);
|
||||
mKeyboardLayoutCache.put(key, layoutInfo);
|
||||
return layoutInfo;
|
||||
}
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getDefaultKeyboardLayoutBasedOnImeInfo(InputDevice inputDevice,
|
||||
@Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) {
|
||||
private static KeyboardLayoutInfo getDefaultKeyboardLayoutBasedOnImeInfo(
|
||||
InputDevice inputDevice, @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) {
|
||||
Arrays.sort(layoutList);
|
||||
|
||||
// Check <VendorID, ProductID> matching for explicitly declared custom KCM files.
|
||||
@@ -984,7 +1006,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
+ "vendor and product Ids. " + inputDevice.getIdentifier()
|
||||
+ " : " + layout.getDescriptor());
|
||||
}
|
||||
return layout.getDescriptor();
|
||||
return new KeyboardLayoutInfo(layout.getDescriptor(),
|
||||
LAYOUT_SELECTION_CRITERIA_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1001,7 +1024,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
+ "HW information (Language tag and Layout type). "
|
||||
+ inputDevice.getIdentifier() + " : " + layoutDesc);
|
||||
}
|
||||
return layoutDesc;
|
||||
return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1023,7 +1046,10 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
+ "IME locale matching. " + inputDevice.getIdentifier() + " : "
|
||||
+ layoutDesc);
|
||||
}
|
||||
return layoutDesc;
|
||||
if (layoutDesc != null) {
|
||||
return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1229,6 +1255,26 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void logKeyboardConfigurationEvent(@NonNull InputDevice inputDevice,
|
||||
@NonNull List<ImeInfo> imeInfoList, @NonNull List<KeyboardLayoutInfo> layoutInfoList,
|
||||
boolean isFirstConfiguration) {
|
||||
if (imeInfoList.isEmpty() || layoutInfoList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
KeyboardConfigurationEvent.Builder configurationEventBuilder =
|
||||
new KeyboardConfigurationEvent.Builder(inputDevice).setIsFirstTimeConfiguration(
|
||||
isFirstConfiguration);
|
||||
for (int i = 0; i < imeInfoList.size(); i++) {
|
||||
KeyboardLayoutInfo layoutInfo = layoutInfoList.get(i);
|
||||
boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
|
||||
configurationEventBuilder.addLayoutSelection(imeInfoList.get(i).mImeSubtype,
|
||||
noLayoutFound ? null : getKeyboardLayout(layoutInfo.mDescriptor),
|
||||
noLayoutFound ? LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
|
||||
: layoutInfo.mSelectionCriteria);
|
||||
}
|
||||
KeyboardMetricsCollector.logKeyboardConfiguredAtom(configurationEventBuilder.build());
|
||||
}
|
||||
|
||||
private boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_EXISTING_DEVICES:
|
||||
@@ -1411,7 +1457,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
|
||||
// If null, it means no layout is selected for the device.
|
||||
@Nullable
|
||||
private String mCurrentLayout;
|
||||
private KeyboardLayoutInfo mCurrentLayout;
|
||||
|
||||
private boolean hasConfiguredLayouts() {
|
||||
return mConfiguredLayouts != null && !mConfiguredLayouts.isEmpty();
|
||||
@@ -1427,15 +1473,42 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getCurrentLayout() {
|
||||
private KeyboardLayoutInfo getCurrentLayout() {
|
||||
return mCurrentLayout;
|
||||
}
|
||||
|
||||
private void setCurrentLayout(String currentLayout) {
|
||||
private void setCurrentLayout(KeyboardLayoutInfo currentLayout) {
|
||||
mCurrentLayout = currentLayout;
|
||||
}
|
||||
}
|
||||
|
||||
private static class KeyboardLayoutInfo {
|
||||
@Nullable
|
||||
private final String mDescriptor;
|
||||
@LayoutSelectionCriteria
|
||||
private final int mSelectionCriteria;
|
||||
|
||||
private KeyboardLayoutInfo(@Nullable String descriptor,
|
||||
@LayoutSelectionCriteria int selectionCriteria) {
|
||||
mDescriptor = descriptor;
|
||||
mSelectionCriteria = selectionCriteria;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof KeyboardLayoutInfo) {
|
||||
return Objects.equals(mDescriptor, ((KeyboardLayoutInfo) obj).mDescriptor)
|
||||
&& mSelectionCriteria == ((KeyboardLayoutInfo) obj).mSelectionCriteria;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * mSelectionCriteria + mDescriptor.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
private interface KeyboardLayoutVisitor {
|
||||
void visitKeyboardLayout(Resources resources,
|
||||
int keyboardLayoutResId, KeyboardLayout layout);
|
||||
|
||||
@@ -19,16 +19,25 @@ package com.android.server.input;
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.hardware.input.KeyboardLayout;
|
||||
import android.icu.util.ULocale;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.InputDevice;
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.KeyboardConfiguredProto.KeyboardLayoutConfig;
|
||||
import com.android.internal.os.KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Collect Keyboard metrics
|
||||
@@ -36,6 +45,30 @@ import java.util.List;
|
||||
public final class KeyboardMetricsCollector {
|
||||
private static final String TAG = "KeyboardMetricCollector";
|
||||
|
||||
// To enable these logs, run: 'adb shell setprop log.tag.KeyboardMetricCollector DEBUG'
|
||||
// (requires restart)
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = {
|
||||
LAYOUT_SELECTION_CRITERIA_USER,
|
||||
LAYOUT_SELECTION_CRITERIA_DEVICE,
|
||||
LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
|
||||
})
|
||||
public @interface LayoutSelectionCriteria {}
|
||||
|
||||
/** Manual selection by user */
|
||||
public static final int LAYOUT_SELECTION_CRITERIA_USER = 0;
|
||||
|
||||
/** Auto-detection based on device provided language tag and layout type */
|
||||
public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 1;
|
||||
|
||||
/** Auto-detection based on IME provided language tag and layout type */
|
||||
public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 2;
|
||||
|
||||
@VisibleForTesting
|
||||
static final String DEFAULT_LAYOUT = "Default";
|
||||
|
||||
/**
|
||||
* Log keyboard system shortcuts for the proto
|
||||
* {@link com.android.os.input.KeyboardSystemsEventReported}
|
||||
@@ -43,120 +76,233 @@ public final class KeyboardMetricsCollector {
|
||||
*/
|
||||
public static void logKeyboardSystemsEventReportedAtom(InputDevice inputDevice,
|
||||
int keyboardSystemEvent, int[] keyCode, int modifierState) {
|
||||
int vendor_id = inputDevice.getVendorId();
|
||||
int product_id = inputDevice.getProductId();
|
||||
int vendorId = inputDevice.getVendorId();
|
||||
int productId = inputDevice.getProductId();
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
|
||||
vendor_id, product_id, keyboardSystemEvent, keyCode, modifierState);
|
||||
vendorId, productId, keyboardSystemEvent, keyCode, modifierState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to log the KeyboardConfigured
|
||||
* {@link com.android.os.input.KeyboardConfigured} atom
|
||||
*
|
||||
* @param inputDevice Input device
|
||||
* @param keyboardLayoutConfigurations List of keyboard configurations
|
||||
* @param isFirstTimeConfiguration Whether keyboard is configured for the first time
|
||||
* @param event {@link KeyboardConfigurationEvent} contains information about keyboard
|
||||
* configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the
|
||||
* configuration event to log.
|
||||
*/
|
||||
public static void logKeyboardConfiguredAtom(InputDevice inputDevice,
|
||||
List<KeyboardLayoutConfiguration> keyboardLayoutConfigurations,
|
||||
boolean isFirstTimeConfiguration) {
|
||||
int vendor_id = inputDevice.getVendorId();
|
||||
int product_id = inputDevice.getProductId();
|
||||
|
||||
public static void logKeyboardConfiguredAtom(KeyboardConfigurationEvent event) {
|
||||
// Creating proto to log nested field KeyboardLayoutConfig in atom
|
||||
ProtoOutputStream proto = new ProtoOutputStream();
|
||||
|
||||
for (KeyboardLayoutConfiguration keyboardLayoutConfiguration :
|
||||
keyboardLayoutConfigurations) {
|
||||
addKeyboardLayoutConfigurationToProto(proto, keyboardLayoutConfiguration);
|
||||
for (LayoutConfiguration layoutConfiguration : event.getLayoutConfigurations()) {
|
||||
addKeyboardLayoutConfigurationToProto(proto, layoutConfiguration);
|
||||
}
|
||||
// Push the atom to Statsd
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_CONFIGURED,
|
||||
isFirstTimeConfiguration, vendor_id, product_id, proto.getBytes());
|
||||
event.isFirstConfiguration(), event.getVendorId(), event.getProductId(),
|
||||
proto.getBytes());
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Logging Keyboard configuration event: " + event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the KeyboardLayoutConfig proto which is a repeated proto
|
||||
* in the RepeatedKeyboardLayoutConfig proto with values from the
|
||||
* {@link KeyboardLayoutConfiguration} class
|
||||
* {@link LayoutConfiguration} class
|
||||
* The proto definitions can be found at:
|
||||
* "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto"
|
||||
*
|
||||
* @param proto Representing the nested proto RepeatedKeyboardLayoutConfig
|
||||
* @param keyboardLayoutConfiguration Class containing the fields for populating the
|
||||
* @param layoutConfiguration Class containing the fields for populating the
|
||||
* KeyboardLayoutConfig proto
|
||||
*/
|
||||
private static void addKeyboardLayoutConfigurationToProto(ProtoOutputStream proto,
|
||||
KeyboardLayoutConfiguration keyboardLayoutConfiguration) {
|
||||
LayoutConfiguration layoutConfiguration) {
|
||||
// Start a new KeyboardLayoutConfig proto.
|
||||
long keyboardLayoutConfigToken = proto.start(
|
||||
RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG);
|
||||
proto.write(KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG,
|
||||
keyboardLayoutConfiguration.getKeyboardLanguageTag());
|
||||
layoutConfiguration.keyboardLanguageTag);
|
||||
proto.write(KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE,
|
||||
keyboardLayoutConfiguration.getKeyboardLayoutType());
|
||||
layoutConfiguration.keyboardLayoutType);
|
||||
proto.write(KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME,
|
||||
keyboardLayoutConfiguration.getKeyboardLayoutName());
|
||||
layoutConfiguration.keyboardLayoutName);
|
||||
proto.write(KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA,
|
||||
keyboardLayoutConfiguration.getLayoutSelectionCriteria());
|
||||
layoutConfiguration.layoutSelectionCriteria);
|
||||
proto.end(keyboardLayoutConfigToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Java class representing the proto KeyboardLayoutConfig defined in
|
||||
* "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto"
|
||||
* Class representing the proto KeyboardLayoutConfig defined in
|
||||
* "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto
|
||||
*
|
||||
* @see com.android.os.input.KeyboardConfigured
|
||||
*/
|
||||
public static class KeyboardLayoutConfiguration {
|
||||
// KeyboardLayoutType in "frameworks/base/core/res/res/values/attrs.xml"
|
||||
// contains mapping for enums to int
|
||||
int mKeyboardLayoutType;
|
||||
String mKeyboardLanguageTag;
|
||||
KeyboardLayout mKeyboardLayout;
|
||||
@LayoutSelectionCriteria int mLayoutSelectionCriteria;
|
||||
public static class KeyboardConfigurationEvent {
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = {
|
||||
LAYOUT_SELECTION_CRITERIA_USER,
|
||||
LAYOUT_SELECTION_CRITERIA_DEVICE,
|
||||
LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
|
||||
})
|
||||
public @interface LayoutSelectionCriteria {}
|
||||
private final InputDevice mInputDevice;
|
||||
private final boolean mIsFirstConfiguration;
|
||||
private final List<LayoutConfiguration> mLayoutConfigurations;
|
||||
|
||||
/** Manual selection by user */
|
||||
public static final int LAYOUT_SELECTION_CRITERIA_USER = 0;
|
||||
|
||||
/** Auto-detection based on device provided language tag and layout type */
|
||||
public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 1;
|
||||
|
||||
/** Auto-detection based on IME provided language tag and layout type */
|
||||
public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 2;
|
||||
|
||||
KeyboardLayoutConfiguration(int keyboardLayoutType,
|
||||
String keyboardLanguageTag,
|
||||
KeyboardLayout keyboardLayout,
|
||||
@LayoutSelectionCriteria int layoutSelectionCriteria) {
|
||||
mKeyboardLayoutType = keyboardLayoutType;
|
||||
mKeyboardLanguageTag = keyboardLanguageTag;
|
||||
mKeyboardLayout = keyboardLayout;
|
||||
mLayoutSelectionCriteria = layoutSelectionCriteria;
|
||||
}
|
||||
int getKeyboardLayoutType() {
|
||||
return mKeyboardLayoutType;
|
||||
private KeyboardConfigurationEvent(InputDevice inputDevice, boolean isFirstConfiguration,
|
||||
List<LayoutConfiguration> layoutConfigurations) {
|
||||
mInputDevice = inputDevice;
|
||||
mIsFirstConfiguration = isFirstConfiguration;
|
||||
mLayoutConfigurations = layoutConfigurations;
|
||||
}
|
||||
|
||||
String getKeyboardLanguageTag() {
|
||||
return mKeyboardLanguageTag;
|
||||
public int getVendorId() {
|
||||
return mInputDevice.getVendorId();
|
||||
}
|
||||
|
||||
String getKeyboardLayoutName() {
|
||||
return mKeyboardLayout.getLabel();
|
||||
public int getProductId() {
|
||||
return mInputDevice.getProductId();
|
||||
}
|
||||
|
||||
@LayoutSelectionCriteria int getLayoutSelectionCriteria() {
|
||||
return mLayoutSelectionCriteria;
|
||||
public boolean isFirstConfiguration() {
|
||||
return mIsFirstConfiguration;
|
||||
}
|
||||
|
||||
public List<LayoutConfiguration> getLayoutConfigurations() {
|
||||
return mLayoutConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InputDevice = {VendorId = " + Integer.toHexString(getVendorId())
|
||||
+ ", ProductId = " + Integer.toHexString(getProductId())
|
||||
+ "}, isFirstConfiguration = " + mIsFirstConfiguration
|
||||
+ ", LayoutConfigurations = " + mLayoutConfigurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class to help create {@link KeyboardConfigurationEvent}.
|
||||
*/
|
||||
public static class Builder {
|
||||
@NonNull
|
||||
private final InputDevice mInputDevice;
|
||||
private boolean mIsFirstConfiguration;
|
||||
private final List<InputMethodSubtype> mImeSubtypeList = new ArrayList<>();
|
||||
private final List<KeyboardLayout> mSelectedLayoutList = new ArrayList<>();
|
||||
private final List<Integer> mLayoutSelectionCriteriaList = new ArrayList<>();
|
||||
|
||||
public Builder(@NonNull InputDevice inputDevice) {
|
||||
Objects.requireNonNull(inputDevice, "InputDevice provided should not be null");
|
||||
mInputDevice = inputDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this is the first time this keyboard is configured.
|
||||
*/
|
||||
public Builder setIsFirstTimeConfiguration(boolean isFirstTimeConfiguration) {
|
||||
mIsFirstConfiguration = isFirstTimeConfiguration;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds keyboard layout configuration info for a particular IME subtype language
|
||||
*/
|
||||
public Builder addLayoutSelection(@NonNull InputMethodSubtype imeSubtype,
|
||||
@Nullable KeyboardLayout selectedLayout,
|
||||
@LayoutSelectionCriteria int layoutSelectionCriteria) {
|
||||
Objects.requireNonNull(imeSubtype, "IME subtype provided should not be null");
|
||||
if (!isValidSelectionCriteria(layoutSelectionCriteria)) {
|
||||
throw new IllegalStateException("Invalid layout selection criteria");
|
||||
}
|
||||
mImeSubtypeList.add(imeSubtype);
|
||||
mSelectedLayoutList.add(selectedLayout);
|
||||
mLayoutSelectionCriteriaList.add(layoutSelectionCriteria);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link KeyboardConfigurationEvent} from the provided information
|
||||
*/
|
||||
public KeyboardConfigurationEvent build() {
|
||||
int size = mImeSubtypeList.size();
|
||||
if (size == 0) {
|
||||
throw new IllegalStateException("Should have at least one configuration");
|
||||
}
|
||||
List<LayoutConfiguration> configurationList = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
KeyboardLayout selectedLayout = mSelectedLayoutList.get(i);
|
||||
@LayoutSelectionCriteria int layoutSelectionCriteria =
|
||||
mLayoutSelectionCriteriaList.get(i);
|
||||
InputMethodSubtype imeSubtype = mImeSubtypeList.get(i);
|
||||
String keyboardLanguageTag;
|
||||
String keyboardLayoutStringType;
|
||||
if (layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE) {
|
||||
keyboardLanguageTag = mInputDevice.getKeyboardLanguageTag();
|
||||
keyboardLayoutStringType = mInputDevice.getKeyboardLayoutType();
|
||||
} else {
|
||||
ULocale pkLocale = imeSubtype.getPhysicalKeyboardHintLanguageTag();
|
||||
keyboardLanguageTag = pkLocale != null ? pkLocale.toLanguageTag()
|
||||
: imeSubtype.getCanonicalizedLanguageTag();
|
||||
keyboardLayoutStringType = imeSubtype.getPhysicalKeyboardHintLayoutType();
|
||||
}
|
||||
// Sanitize null values
|
||||
String keyboardLayoutName =
|
||||
selectedLayout == null ? DEFAULT_LAYOUT : selectedLayout.getLabel();
|
||||
keyboardLanguageTag = keyboardLanguageTag == null ? "" : keyboardLanguageTag;
|
||||
int keyboardLayoutType = KeyboardLayout.LayoutType.getLayoutTypeEnumValue(
|
||||
keyboardLayoutStringType);
|
||||
|
||||
configurationList.add(
|
||||
new LayoutConfiguration(keyboardLayoutType, keyboardLanguageTag,
|
||||
keyboardLayoutName, layoutSelectionCriteria));
|
||||
}
|
||||
return new KeyboardConfigurationEvent(mInputDevice, mIsFirstConfiguration,
|
||||
configurationList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class LayoutConfiguration {
|
||||
// This should match enum values defined in "frameworks/base/core/res/res/values/attrs.xml"
|
||||
public final int keyboardLayoutType;
|
||||
public final String keyboardLanguageTag;
|
||||
public final String keyboardLayoutName;
|
||||
@LayoutSelectionCriteria
|
||||
public final int layoutSelectionCriteria;
|
||||
|
||||
private LayoutConfiguration(int keyboardLayoutType, String keyboardLanguageTag,
|
||||
String keyboardLayoutName, @LayoutSelectionCriteria int layoutSelectionCriteria) {
|
||||
this.keyboardLayoutType = keyboardLayoutType;
|
||||
this.keyboardLanguageTag = keyboardLanguageTag;
|
||||
this.keyboardLayoutName = keyboardLayoutName;
|
||||
this.layoutSelectionCriteria = layoutSelectionCriteria;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{keyboardLanguageTag = " + keyboardLanguageTag + " keyboardLayoutType = "
|
||||
+ KeyboardLayout.LayoutType.getLayoutNameFromValue(keyboardLayoutType)
|
||||
+ " keyboardLayoutName = " + keyboardLayoutName + " layoutSelectionCriteria = "
|
||||
+ getStringForSelectionCriteria(layoutSelectionCriteria) + "}";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getStringForSelectionCriteria(
|
||||
@LayoutSelectionCriteria int layoutSelectionCriteria) {
|
||||
switch (layoutSelectionCriteria) {
|
||||
case LAYOUT_SELECTION_CRITERIA_USER:
|
||||
return "LAYOUT_SELECTION_CRITERIA_USER";
|
||||
case LAYOUT_SELECTION_CRITERIA_DEVICE:
|
||||
return "LAYOUT_SELECTION_CRITERIA_DEVICE";
|
||||
case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD:
|
||||
return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD";
|
||||
default:
|
||||
return "INVALID_CRITERIA";
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidSelectionCriteria(int layoutSelectionCriteria) {
|
||||
return layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER
|
||||
|| layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE
|
||||
|| layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,6 +101,10 @@ final class PersistentDataStore {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasInputDeviceEntry(String inputDeviceDescriptor) {
|
||||
return getInputDeviceState(inputDeviceDescriptor) != null;
|
||||
}
|
||||
|
||||
public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
|
||||
InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
|
||||
if (state == null) {
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.input
|
||||
|
||||
import android.hardware.input.KeyboardLayout
|
||||
import android.icu.util.ULocale
|
||||
import android.platform.test.annotations.Presubmit
|
||||
import android.view.InputDevice
|
||||
import android.view.inputmethod.InputMethodSubtype
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
private fun createKeyboard(
|
||||
deviceId: Int,
|
||||
vendorId: Int,
|
||||
productId: Int,
|
||||
languageTag: String?,
|
||||
layoutType: String?
|
||||
): InputDevice =
|
||||
InputDevice.Builder()
|
||||
.setId(deviceId)
|
||||
.setName("Device $deviceId")
|
||||
.setDescriptor("descriptor $deviceId")
|
||||
.setSources(InputDevice.SOURCE_KEYBOARD)
|
||||
.setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
|
||||
.setExternal(true)
|
||||
.setVendorId(vendorId)
|
||||
.setProductId(productId)
|
||||
.setKeyboardLanguageTag(languageTag)
|
||||
.setKeyboardLayoutType(layoutType)
|
||||
.build()
|
||||
|
||||
private fun createImeSubtype(
|
||||
imeSubtypeId: Int,
|
||||
languageTag: String,
|
||||
layoutType: String
|
||||
): InputMethodSubtype =
|
||||
InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(imeSubtypeId)
|
||||
.setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType).build()
|
||||
|
||||
/**
|
||||
* Tests for {@link KeyboardMetricsCollector}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest FrameworksServicesTests:KeyboardMetricsCollectorTests
|
||||
*/
|
||||
@Presubmit
|
||||
class KeyboardMetricsCollectorTests {
|
||||
|
||||
companion object {
|
||||
const val DEVICE_ID = 1
|
||||
const val DEFAULT_VENDOR_ID = 123
|
||||
const val DEFAULT_PRODUCT_ID = 456
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateKeyboardConfigurationEvent_throwsExceptionWithoutAnyLayoutConfiguration() {
|
||||
assertThrows(IllegalStateException::class.java) {
|
||||
KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
|
||||
createKeyboard(
|
||||
DEVICE_ID,
|
||||
DEFAULT_VENDOR_ID,
|
||||
DEFAULT_PRODUCT_ID,
|
||||
null,
|
||||
null
|
||||
)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateKeyboardConfigurationEvent_throwsExceptionWithInvalidLayoutSelectionCriteria() {
|
||||
assertThrows(IllegalStateException::class.java) {
|
||||
KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
|
||||
createKeyboard(
|
||||
DEVICE_ID,
|
||||
DEFAULT_VENDOR_ID,
|
||||
DEFAULT_PRODUCT_ID,
|
||||
null,
|
||||
null
|
||||
)
|
||||
).addLayoutSelection(createImeSubtype(1, "en-US", "qwerty"), null, 123).build()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateKeyboardConfigurationEvent_withMultipleConfigurations() {
|
||||
val builder = KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
|
||||
createKeyboard(
|
||||
DEVICE_ID,
|
||||
DEFAULT_VENDOR_ID,
|
||||
DEFAULT_PRODUCT_ID,
|
||||
"de-CH",
|
||||
"qwertz"
|
||||
)
|
||||
)
|
||||
val event = builder.addLayoutSelection(
|
||||
createImeSubtype(1, "en-US", "qwerty"),
|
||||
KeyboardLayout(null, "English(US)(Qwerty)", null, 0, null, 0, 0, 0),
|
||||
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
|
||||
).addLayoutSelection(
|
||||
createImeSubtype(2, "en-US", "azerty"),
|
||||
null,
|
||||
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
|
||||
).addLayoutSelection(
|
||||
createImeSubtype(3, "en-US", "qwerty"),
|
||||
KeyboardLayout(null, "German", null, 0, null, 0, 0, 0),
|
||||
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
|
||||
).setIsFirstTimeConfiguration(true).build()
|
||||
|
||||
assertEquals(
|
||||
"KeyboardConfigurationEvent should pick vendor ID from provided InputDevice",
|
||||
DEFAULT_VENDOR_ID,
|
||||
event.vendorId
|
||||
)
|
||||
assertEquals(
|
||||
"KeyboardConfigurationEvent should pick product ID from provided InputDevice",
|
||||
DEFAULT_PRODUCT_ID,
|
||||
event.productId
|
||||
)
|
||||
assertTrue(event.isFirstConfiguration)
|
||||
|
||||
assertEquals(
|
||||
"KeyboardConfigurationEvent should contain 3 configurations provided",
|
||||
3,
|
||||
event.layoutConfigurations.size
|
||||
)
|
||||
assertExpectedLayoutConfiguration(
|
||||
event.layoutConfigurations[0],
|
||||
"en-US",
|
||||
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
|
||||
"English(US)(Qwerty)",
|
||||
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
|
||||
)
|
||||
assertExpectedLayoutConfiguration(
|
||||
event.layoutConfigurations[1],
|
||||
"en-US",
|
||||
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"),
|
||||
KeyboardMetricsCollector.DEFAULT_LAYOUT,
|
||||
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
|
||||
)
|
||||
assertExpectedLayoutConfiguration(
|
||||
event.layoutConfigurations[2],
|
||||
"de-CH",
|
||||
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
|
||||
"German",
|
||||
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
|
||||
)
|
||||
}
|
||||
|
||||
private fun assertExpectedLayoutConfiguration(
|
||||
configuration: KeyboardMetricsCollector.LayoutConfiguration,
|
||||
expectedLanguageTag: String,
|
||||
expectedLayoutType: Int,
|
||||
expectedSelectedLayout: String,
|
||||
expectedLayoutSelectionCriteria: Int
|
||||
) {
|
||||
assertEquals(expectedLanguageTag, configuration.keyboardLanguageTag)
|
||||
assertEquals(expectedLayoutType, configuration.keyboardLayoutType)
|
||||
assertEquals(expectedSelectedLayout, configuration.keyboardLayoutName)
|
||||
assertEquals(expectedLayoutSelectionCriteria, configuration.layoutSelectionCriteria)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user