下面列出了怎么用android.view.KeyCharacterMap的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* 获取导航栏高度
*
* @param context
* @return
*/
public static int getNavigationBarHeight(Context context) {
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if (!hasMenuKey && !hasBackKey) {
//没有物理按钮(虚拟导航栏)
print("没有物理按钮(虚拟导航栏)");
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
//获取NavigationBar的高度
int height = resources.getDimensionPixelSize(resourceId);
return height;
} else {
print("有物理导航栏,小米非全面屏");
//有物理导航栏,小米非全面屏
return 0;
}
}
/**
* Sends the key events corresponding to the text to the app being
* instrumented.
*
* @param text The text to be sent.
*/
public void sendStringSync(String text) {
if (text == null) {
return;
}
KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray());
if (events != null) {
for (int i = 0; i < events.length; i++) {
// We have to change the time of an event before injecting it because
// all KeyEvents returned by KeyCharacterMap.getEvents() have the same
// time stamp and the system rejects too old events. Hence, it is
// possible for an event to become stale before it is injected if it
// takes too long to inject the preceding ones.
sendKeySync(KeyEvent.changeTimeRepeat(events[i], SystemClock.uptimeMillis(), 0));
}
}
}
/**
* Send keys and blocks until the first specified accessibility event.
*
* Most key presses will cause some UI change to occur. If the device is busy, this will
* block until the device begins to process the key press at which point the call returns
* and normal wait for idle processing may begin. If no events are detected for the
* timeout period specified, the call will return anyway with false.
*
* @param keyCode
* @param metaState
* @param eventType
* @param timeout
* @return true if events is received, otherwise false.
*/
public boolean sendKeyAndWaitForEvent(final int keyCode, final int metaState,
final int eventType, long timeout) {
Runnable command = new Runnable() {
@Override
public void run() {
final long eventTime = SystemClock.uptimeMillis();
KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN,
keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
InputDevice.SOURCE_KEYBOARD);
if (injectEventSync(downEvent)) {
KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP,
keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
InputDevice.SOURCE_KEYBOARD);
injectEventSync(upEvent);
}
}
};
return runAndWaitForEvents(command, new WaitForAnyEventPredicate(eventType), timeout)
!= null;
}
public boolean sendKey(int keyCode, int metaState) {
if (DEBUG) {
Log.d(LOG_TAG, "sendKey (" + keyCode + ", " + metaState + ")");
}
final long eventTime = SystemClock.uptimeMillis();
KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN,
keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
InputDevice.SOURCE_KEYBOARD);
if (injectEventSync(downEvent)) {
KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP,
keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
InputDevice.SOURCE_KEYBOARD);
if(injectEventSync(upEvent)) {
return true;
}
}
return false;
}
/**
* Gets the shortcut intent for a given keycode+modifier. Make sure you
* strip whatever modifier is used for invoking shortcuts (for example,
* if 'Sym+A' should invoke a shortcut on 'A', you should strip the
* 'Sym' bit from the modifiers before calling this method.
* <p>
* This will first try an exact match (with modifiers), and then try a
* match without modifiers (primary character on a key).
*
* @param kcm The key character map of the device on which the key was pressed.
* @param keyCode The key code.
* @param metaState The meta state, omitting any modifiers that were used
* to invoke the shortcut.
* @return The intent that matches the shortcut, or null if not found.
*/
public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
ShortcutInfo shortcut = null;
// If the Shift key is pressed, then search for the shift shortcuts.
boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON;
SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mShortcuts;
// First try the exact keycode (with modifiers).
int shortcutChar = kcm.get(keyCode, metaState);
if (shortcutChar != 0) {
shortcut = shortcutMap.get(shortcutChar);
}
// Next try the primary character on that key.
if (shortcut == null) {
shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
if (shortcutChar != 0) {
shortcut = shortcutMap.get(shortcutChar);
}
}
return (shortcut != null) ? shortcut.intent : null;
}
/**
* Send keys and blocks until the first specified accessibility event.
*
* Most key presses will cause some UI change to occur. If the device is busy, this will
* block until the device begins to process the key press at which point the call returns
* and normal wait for idle processing may begin. If no events are detected for the
* timeout period specified, the call will return anyway with false.
*
* @param keyCode
* @param metaState
* @param eventType
* @param timeout
* @return true if events is received, otherwise false.
*/
public boolean sendKeyAndWaitForEvent(final int keyCode, final int metaState, final int eventType, long timeout) {
Runnable command = new Runnable() {
@Override
public void run() {
final long eventTime = SystemClock.uptimeMillis();
KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD);
if (injectEventSync(downEvent)) {
KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD);
injectEventSync(upEvent);
}
}
};
return runAndWaitForEvents(command, new WaitForAnyEventPredicate(eventType), timeout) != null;
}
/**
* Sends the key events corresponding to the text to the app being
* instrumented.
*
* @param text The text to be sent.
*/
public void sendStringSync(String text) {
if (text == null) {
return;
}
KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray());
if (events != null) {
for (int i = 0; i < events.length; i++) {
// We have to change the time of an event before injecting it because
// all KeyEvents returned by KeyCharacterMap.getEvents() have the same
// time stamp and the system rejects too old events. Hence, it is
// possible for an event to become stale before it is injected if it
// takes too long to inject the preceding ones.
sendKeySync(KeyEvent.changeTimeRepeat(events[i], SystemClock.uptimeMillis(), 0));
}
}
}
private KeyListener getKeyListener(KeyEvent event) {
KeyCharacterMap kmap = event.getKeyCharacterMap();
int kind = kmap.getKeyboardType();
if (kind == KeyCharacterMap.ALPHA) {
return QwertyKeyListener.getInstance(mAutoText, mAutoCap);
} else if (kind == KeyCharacterMap.NUMERIC) {
return MultiTapKeyListener.getInstance(mAutoText, mAutoCap);
} else if (kind == KeyCharacterMap.FULL
|| kind == KeyCharacterMap.SPECIAL_FUNCTION) {
// We consider special function keyboards full keyboards as a workaround for
// devices that do not have built-in keyboards. Applications may try to inject
// key events using the built-in keyboard device id which may be configured as
// a special function keyboard using a default key map. Ideally, as of Honeycomb,
// these applications should be modified to use KeyCharacterMap.VIRTUAL_KEYBOARD.
return QwertyKeyListener.getInstanceForFullKeyboard();
}
return NullKeyListener.getInstance();
}
private static long release(long state, int what, long mask,
long pressed, long released, long used, KeyEvent event) {
switch (event.getKeyCharacterMap().getModifierBehavior()) {
case KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED:
if ((state & used) != 0) {
state &= ~mask;
} else if ((state & pressed) != 0) {
state |= what | released;
}
break;
default:
state &= ~mask;
break;
}
return state;
}
/**
* Check if there is a software navigation bar(including the navigation bar in the screen).
*
* @return True if there is a software navigation bar
*/
private boolean hasSoftNavigationBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
final DisplayMetrics realDisplayMetrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getRealMetrics(realDisplayMetrics);
return realDisplayMetrics.heightPixels > mMetrics.heightPixels || realDisplayMetrics.widthPixels > mMetrics.widthPixels;
}
// old device check flow
// Navigation bar exists (config_showNavigationBar is true, or both the menu key and the back key are not exists)
final Context context = getContext();
final Resources resources = context.getResources();
final boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
final boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
final int showNavigationBarResId = resources.getIdentifier("config_showNavigationBar", "bool", "android");
final boolean hasNavigationBarConfig = showNavigationBarResId != 0 && resources.getBoolean(showNavigationBarResId);
return hasNavigationBarConfig || (!hasMenuKey && !hasBackKey);
}
@Test
public void injectKeyEventUpWithNoDown() throws Exception {
ActivityScenario<SendActivity> scenario = ActivityScenario.launch(SendActivity.class);
scenario.onActivity(
sendActivity -> {
View view = sendActivity.findViewById(R.id.send_data_edit_text);
assertTrue(view.requestFocus());
latch.countDown();
});
assertTrue("Timed out!", latch.await(10, TimeUnit.SECONDS));
KeyCharacterMap keyCharacterMap = UiControllerImpl.getKeyCharacterMap();
KeyEvent[] events = keyCharacterMap.getEvents("a".toCharArray());
assertTrue(injector.injectKeyEvent(events[1]));
}
/**
* Return whether the navigation bar visible.
*
* @return {@code true}: yes<br>{@code false}: no
*/
public static boolean isSupportNavBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
WindowManager wm = (WindowManager) UtilsApp.getApp().getSystemService(Context.WINDOW_SERVICE);
if (wm == null) return false;
Display display = wm.getDefaultDisplay();
Point size = new Point();
Point realSize = new Point();
display.getSize(size);
display.getRealSize(realSize);
return realSize.y != size.y || realSize.x != size.x;
}
boolean menu = ViewConfiguration.get(UtilsApp.getApp()).hasPermanentMenuKey();
boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
return !menu && !back;
}
/**
* 判断是否支持 Navigation Bar
* @return {@code true} yes, {@code false} no
*/
public static boolean isSupportNavBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
WindowManager windowManager = AppUtils.getWindowManager();
if (windowManager == null) return false;
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
Point realSize = new Point();
display.getSize(size);
display.getRealSize(realSize);
return realSize.y != size.y || realSize.x != size.x;
}
boolean menu = ViewConfiguration.get(DevUtils.getContext()).hasPermanentMenuKey();
boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
return !menu && !back;
}
/**
* @param context
* @return 是否存在导航栏
*/
public static boolean isNavigationBarShow(Context context) {
if (!(context instanceof Activity)) {
return false;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Display display = ((Activity) (context)).getWindowManager().getDefaultDisplay();
Point size = new Point();
Point realSize = new Point();
display.getSize(size);
display.getRealSize(realSize);
return realSize.x != size.x;
} else {
boolean menu = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if (menu || back) {
return false;
} else {
return true;
}
}
}
/**
* Send keys and blocks until the first specified accessibility event.
*
* Most key presses will cause some UI change to occur. If the device is
* busy, this will block until the device begins to process the key press at
* which point the call returns and normal wait for idle processing may
* begin. If no events are detected for the timeout period specified, the
* call will return anyway with false.
*
* @param keyCode
* @param metaState
* @param eventType
* @param timeout
* @return true if events is received, otherwise false.
*/
public boolean sendKeyAndWaitForEvent(final int keyCode,
final int metaState, final int eventType, long timeout) {
Runnable command = new Runnable() {
@Override
public void run() {
final long eventTime = SystemClock.uptimeMillis();
KeyEvent downEvent = new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, keyCode, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
InputDevice.SOURCE_KEYBOARD);
if (injectEventSync(downEvent)) {
KeyEvent upEvent = new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_UP, keyCode, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
InputDevice.SOURCE_KEYBOARD);
injectEventSync(upEvent);
}
}
};
return runAndWaitForEvents(command, new WaitForAnyEventPredicate(
eventType), timeout) != null;
}
@Override
protected AppiumResponse safeHandle(IHttpRequest request) {
final KeyCodeModel model = toModel(request, KeyCodeModel.class);
final int keyCode = model.keycode;
int metaState = model.metastate == null ? 0 : model.metastate;
int flags = model.flags == null ? 0 : model.flags;
final long downTime = SystemClock.uptimeMillis();
final InteractionController interactionController = UiAutomatorBridge.getInstance().getInteractionController();
boolean isSuccessful = interactionController.injectEventSync(new KeyEvent(downTime, downTime,
KeyEvent.ACTION_DOWN, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD,
0, flags));
// https://android.googlesource.com/platform/frameworks/base.git/+/9d83b4783c33f1fafc43f367503e129e5a5047fa%5E%21/#F0
isSuccessful &= interactionController.injectEventSync(new KeyEvent(downTime, SystemClock.uptimeMillis(),
KeyEvent.ACTION_DOWN, keyCode, 1, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD,
0, flags | KeyEvent.FLAG_LONG_PRESS));
isSuccessful &= interactionController.injectEventSync(new KeyEvent(downTime, SystemClock.uptimeMillis(),
KeyEvent.ACTION_UP, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD,
0, flags));
if (!isSuccessful) {
throw new InvalidElementStateException("Cannot inject long press event for key code " + keyCode);
}
return new AppiumResponse(getSessionId(request));
}
/**
* Send a dtmf signal to a call
*
* @param callId the call to send the signal
* @param keyCode the keyCode to send (android style)
* @return
*/
public int sendDtmf(int callId, int keyCode) throws SameThreadException {
if (!created) {
return -1;
}
String keyPressed = "";
// Since some device (xoom...) are apparently buggy with key character
// map loading...
// we have to do crappy thing here
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
keyPressed = Integer.toString(keyCode - KeyEvent.KEYCODE_0);
} else if (keyCode == KeyEvent.KEYCODE_POUND) {
keyPressed = "#";
} else if (keyCode == KeyEvent.KEYCODE_STAR) {
keyPressed = "*";
} else {
// Fallback... should never be there if using visible dialpad, but
// possible using keyboard
KeyCharacterMap km = KeyCharacterMap.load(KeyCharacterMap.NUMERIC);
keyPressed = Integer.toString(km.getNumber(keyCode));
}
return sendDtmf(callId, keyPressed);
}
public static int getNavBarHeight(Context context) {
int result = 0;
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if(!hasMenuKey && !hasBackKey) {
//The device has a navigation bar
Resources resources = context.getResources();
int orientation = context.getResources().getConfiguration().orientation;
int resourceId;
if (isTablet(context)){
resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape", "dimen", "android");
} else {
resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_width", "dimen", "android");
}
if (resourceId > 0) {
return context.getResources().getDimensionPixelSize(resourceId);
}
}
return result;
}
/**
* 虚拟操作拦(home等)是否显示
*/
public static boolean isNavigationBarShow(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Display display = activity.getWindowManager().getDefaultDisplay();
Point size = new Point();
Point realSize = new Point();
display.getSize(size);
display.getRealSize(realSize);
return realSize.y != size.y;
} else {
boolean menu = ViewConfiguration.get(activity).hasPermanentMenuKey();
boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if (menu || back) {
return false;
} else {
return true;
}
}
}
private void sendKeyEventsToTarget(int character) {
if (viewRootImpl == null && canGetViewRootImpl) {
getViewRootImpl();
}
KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD).getEvents(new
char[]{(char) character});
try {
Method method = viewRootImpl.getClass().getDeclaredMethod("dispatchKeyFromIme",
KeyEvent.class);
method.setAccessible(true);
if (events != null) {
final int N = events.length;
for (int i = 0;
i < N;
i++) {
KeyEvent event = events[i];
event = KeyEvent.changeFlags(event, event.getFlags() | KeyEvent
.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
method.invoke(viewRootImpl, event);
}
}
} catch (Exception e) {
e.printStackTrace();
LogUtil.e(TAG, "can not dispatch input event");
}
}
public void handleMessage(Message m) {
switch (m.what) {
case MSG_INJECT_KEY:
final long eventTime = SystemClock.uptimeMillis();
final InputManager inputManager = (InputManager)
XposedHelpers.callStaticMethod(InputManager.class, "getInstance");
int flags = KeyEvent.FLAG_FROM_SYSTEM;
XposedHelpers.callMethod(inputManager, "injectInputEvent",
new KeyEvent(eventTime - 50, eventTime - 50, KeyEvent.ACTION_DOWN, m.arg1, 0,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, flags, InputDevice.SOURCE_UNKNOWN), 0);
XposedHelpers.callMethod(inputManager, "injectInputEvent",
new KeyEvent(eventTime - 50, eventTime - 25, KeyEvent.ACTION_UP, m.arg1, 0,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, flags, InputDevice.SOURCE_UNKNOWN), 0);
break;
}
}
private boolean hasSystemNavigationBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Display d = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
DisplayMetrics realDisplayMetrics = new DisplayMetrics();
d.getRealMetrics(realDisplayMetrics);
int realHeight = realDisplayMetrics.heightPixels;
int realWidth = realDisplayMetrics.widthPixels;
DisplayMetrics displayMetrics = new DisplayMetrics();
d.getMetrics(displayMetrics);
int displayHeight = displayMetrics.heightPixels;
int displayWidth = displayMetrics.widthPixels;
return (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0;
} else {
boolean hasMenuKey = ViewConfiguration.get(this).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
return !hasMenuKey && !hasBackKey;
}
}
/**
* Return whether the navigation bar visible.
*
* @return {@code true}: yes<br>{@code false}: no
*/
public static boolean isSupportNavBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
WindowManager wm = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);
if (wm == null) return false;
Display display = wm.getDefaultDisplay();
Point size = new Point();
Point realSize = new Point();
display.getSize(size);
display.getRealSize(realSize);
return realSize.y != size.y || realSize.x != size.x;
}
boolean menu = ViewConfiguration.get(Utils.getApp()).hasPermanentMenuKey();
boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
return !menu && !back;
}
/**
* Get whether navigation bar is visible.
*
* @param context Context
* @return true: visible, false: invisible
*/
public static boolean navigationBarVisible(@NonNull Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
Point realSize = new Point();
display.getRealSize(realSize);
return realSize.y != size.y;
}else {
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if(hasMenuKey || hasBackKey) {
return false;
} else {
return true;
}
}
}
/**
* Sends the key events corresponding to the text to the app being
* instrumented.
*
* @param text The text to be sent.
*/
public void sendStringSync(String text) {
if (text == null) {
return;
}
KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray());
if (events != null) {
for (int i = 0; i < events.length; i++) {
sendKeySync(events[i]);
}
}
}
void sendKeyEventWithKeyCode(int keyCode, int flags) {
long eventTime = System.currentTimeMillis();
translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, keyCode, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
flags));
translateAndSendNativeEvents(new KeyEvent(System.currentTimeMillis(), eventTime,
KeyEvent.ACTION_UP, keyCode, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
flags));
}
/**
* Get the keycode value for AM and PM in the current language.
*/
private int getAmOrPmKeyCode(int amOrPm) {
// Cache the codes.
if (mAmKeyCode == -1 || mPmKeyCode == -1) {
// Find the first character in the AM/PM text that is unique.
KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
char amChar;
char pmChar;
for (int i = 0; i < Math.max(mAmText.length(), mPmText.length()); i++) {
amChar = mAmText.toLowerCase(Locale.getDefault()).charAt(i);
pmChar = mPmText.toLowerCase(Locale.getDefault()).charAt(i);
if (amChar != pmChar) {
KeyEvent[] events = kcm.getEvents(new char[] { amChar, pmChar });
// There should be 4 events: a down and up for both AM and PM.
if (events != null && events.length == 4) {
mAmKeyCode = events[0].getKeyCode();
mPmKeyCode = events[2].getKeyCode();
} else {
Log.e(TAG, "Unable to find keycodes for AM and PM.");
}
break;
}
}
}
if (amOrPm == AM) {
return mAmKeyCode;
} else if (amOrPm == PM) {
return mPmKeyCode;
}
return -1;
}
private void sendCurrentText() {
if (!mDummyMode) {
return;
}
Editable content = getEditable();
if (content != null) {
final int N = content.length();
if (N == 0) {
return;
}
if (N == 1) {
// If it's 1 character, we have a chance of being
// able to generate normal key events...
if (mKeyCharacterMap == null) {
mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
}
char[] chars = new char[1];
content.getChars(0, 1, chars, 0);
KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
if (events != null) {
for (int i=0; i<events.length; i++) {
if (DEBUG) Log.v(TAG, "Sending: " + events[i]);
sendKeyEvent(events[i]);
}
content.clear();
return;
}
}
// Otherwise, revert to the special key event containing
// the actual characters.
KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(),
content.toString(), KeyCharacterMap.VIRTUAL_KEYBOARD, 0);
sendKeyEvent(event);
content.clear();
}
}
void sendKeyEventWithKeyCode(int keyCode, int flags) {
long eventTime = System.currentTimeMillis();
translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, keyCode, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
flags));
translateAndSendNativeEvents(new KeyEvent(System.currentTimeMillis(), eventTime,
KeyEvent.ACTION_UP, keyCode, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
flags));
}
public static boolean isNavMenuExist(Context context) {
//通过判断设备是否有返回键、菜单键(不是虚拟键,是手机屏幕外的按键)来确定是否有navigation bar
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if (!hasMenuKey && !hasBackKey) {
// 做任何你需要做的,这个设备有一个导航栏
return true;
}
return false;
}