termux-app
198 строк · 10.2 Кб
1package com.termux.shared.view;2
3import android.app.Activity;4import android.content.Context;5import android.content.res.Configuration;6import android.inputmethodservice.InputMethodService;7import android.os.Build;8import android.view.View;9import android.view.WindowInsets;10import android.view.WindowManager;11import android.view.inputmethod.InputMethodManager;12
13import androidx.annotation.NonNull;14import androidx.annotation.RequiresApi;15import androidx.core.view.WindowInsetsCompat;16
17import com.termux.shared.logger.Logger;18
19public class KeyboardUtils {20
21private static final String LOG_TAG = "KeyboardUtils";22
23public static void setSoftKeyboardVisibility(@NonNull final Runnable showSoftKeyboardRunnable, final Activity activity, final View view, final boolean visible) {24if (visible) {25// A Runnable with a delay is used, otherwise soft keyboard may not automatically open26// on some devices, but still may fail27view.postDelayed(showSoftKeyboardRunnable, 500);28} else {29view.removeCallbacks(showSoftKeyboardRunnable);30hideSoftKeyboard(activity, view);31}32}33
34/**35* Toggle the soft keyboard. The {@link InputMethodManager#SHOW_FORCED} is passed as
36* {@code showFlags} so that keyboard is forcefully shown if it needs to be enabled.
37*
38* This is also important for soft keyboard to be shown when a hardware keyboard is connected, and
39* user has disabled the {@code Show on-screen keyboard while hardware keyboard is connected} toggle
40* in Android "Language and Input" settings but the current soft keyboard app overrides the
41* default implementation of {@link InputMethodService#onEvaluateInputViewShown()} and returns
42* {@code true}.
43*/
44public static void toggleSoftKeyboard(final Context context) {45if (context == null) return;46InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);47if (inputMethodManager != null)48inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);49}50
51/**52* Show the soft keyboard. The {@code 0} value is passed as {@code flags} so that keyboard is
53* forcefully shown.
54*
55* This is also important for soft keyboard to be shown on app startup when a hardware keyboard
56* is connected, and user has disabled the {@code Show on-screen keyboard while hardware keyboard
57* is connected} toggle in Android "Language and Input" settings but the current soft keyboard app
58* overrides the default implementation of {@link InputMethodService#onEvaluateInputViewShown()}
59* and returns {@code true}.
60* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:frameworks/base/core/java/android/inputmethodservice/InputMethodService.java;l=1751
61*
62* Also check {@link InputMethodService#onShowInputRequested(int, boolean)} which must return
63* {@code true}, which can be done by failing its {@code ((flags&InputMethod.SHOW_EXPLICIT) == 0)}
64* check by passing {@code 0} as {@code flags}.
65* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:frameworks/base/core/java/android/inputmethodservice/InputMethodService.java;l=2022
66*/
67public static void showSoftKeyboard(final Context context, final View view) {68if (context == null || view == null) return;69InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);70if (inputMethodManager != null)71inputMethodManager.showSoftInput(view, 0);72}73
74public static void hideSoftKeyboard(final Context context, final View view) {75if (context == null || view == null) return;76InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);77if (inputMethodManager != null)78inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);79}80
81public static void disableSoftKeyboard(final Activity activity, final View view) {82if (activity == null || view == null) return;83hideSoftKeyboard(activity, view);84setDisableSoftKeyboardFlags(activity);85}86
87public static void setDisableSoftKeyboardFlags(final Activity activity) {88if (activity != null && activity.getWindow() != null)89activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);90}91
92public static void clearDisableSoftKeyboardFlags(final Activity activity) {93if (activity != null && activity.getWindow() != null)94activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);95}96
97public static boolean areDisableSoftKeyboardFlagsSet(final Activity activity) {98if (activity == null || activity.getWindow() == null) return false;99return (activity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0;100}101
102public static void setSoftKeyboardAlwaysHiddenFlags(final Activity activity) {103if (activity != null && activity.getWindow() != null)104activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);105}106
107public static void setSoftInputModeAdjustResize(final Activity activity) {108// TODO: The flag is deprecated for API 30 and WindowInset API should be used109// https://developer.android.com/reference/android/view/WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE110// https://medium.com/androiddevelopers/animating-your-keyboard-fb776a8fb66d111// https://stackoverflow.com/a/65194077/14686958112if (activity != null && activity.getWindow() != null)113activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);114}115
116/**117* Check if soft keyboard is visible.
118* Does not work on android 7 but does on android 11 avd.
119*
120* @param activity The Activity of the root view for which the visibility should be checked.
121* @return Returns {@code true} if soft keyboard is visible, otherwise {@code false}.
122*/
123@RequiresApi(api = Build.VERSION_CODES.M)124public static boolean isSoftKeyboardVisible(final Activity activity) {125if (activity != null && activity.getWindow() != null) {126WindowInsets insets = activity.getWindow().getDecorView().getRootWindowInsets();127if (insets != null) {128WindowInsetsCompat insetsCompat = WindowInsetsCompat.toWindowInsetsCompat(insets);129if (insetsCompat.isVisible(WindowInsetsCompat.Type.ime())) {130Logger.logVerbose(LOG_TAG, "Soft keyboard visible");131return true;132}133}134}135
136Logger.logVerbose(LOG_TAG, "Soft keyboard not visible");137return false;138}139
140/**141* Check if hardware keyboard is connected.
142* Based on default implementation of {@link InputMethodService#onEvaluateInputViewShown()}.
143*
144* https://developer.android.com/guide/topics/resources/providing-resources#ImeQualifier
145*
146* @param context The Context for operations.
147* @return Returns {@code true} if device has hardware keys for text input or an external hardware
148* keyboard is connected, otherwise {@code false}.
149*/
150public static boolean isHardKeyboardConnected(final Context context) {151if (context == null) return false;152
153Configuration config = context.getResources().getConfiguration();154return config.keyboard != Configuration.KEYBOARD_NOKEYS155|| config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;156}157
158/**159* Check if soft keyboard should be disabled based on user configuration.
160*
161* @param context The Context for operations.
162* @return Returns {@code true} if device has soft keyboard should be disabled, otherwise {@code false}.
163*/
164public static boolean shouldSoftKeyboardBeDisabled(final Context context, final boolean isSoftKeyboardEnabled, final boolean isSoftKeyboardEnabledOnlyIfNoHardware) {165// If soft keyboard is disabled by user regardless of hardware keyboard166if (!isSoftKeyboardEnabled) {167return true;168} else {169/*170* Currently, for this case, soft keyboard will be disabled on Termux app startup and
171* when switching back from another app. Soft keyboard can be temporarily enabled in
172* show/hide soft keyboard toggle behaviour with keyboard toggle buttons and will continue
173* to work when tapping on terminal view for opening and back button for closing, until
174* Termux app is switched to another app. After returning back, keyboard will be disabled
175* until toggle is pressed again.
176* This may also be helpful for the Lineage OS bug where if "Show soft keyboard" toggle
177* in "Language and Input" is disabled and Termux is started without a hardware keyboard
178* in landscape mode, and then the keyboard is connected and phone is rotated to portrait
179* mode and then keyboard is toggled with Termux keyboard toggle buttons, then a blank
180* space is shown in-place of the soft keyboard. Its likely related to
181* WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE which pushes up the view when
182* keyboard is opened instead of the keyboard opening on top of the view (hiding stuff).
183* If the "Show soft keyboard" toggle was disabled, then this resizing shouldn't happen.
184* But it seems resizing does happen, but keyboard is never opened since its not supposed to.
185* https://github.com/termux/termux-app/issues/1995#issuecomment-837080079
186*/
187// If soft keyboard is disabled by user only if hardware keyboard is connected188if(isSoftKeyboardEnabledOnlyIfNoHardware) {189boolean isHardKeyboardConnected = KeyboardUtils.isHardKeyboardConnected(context);190Logger.logVerbose(LOG_TAG, "Hardware keyboard connected=" + isHardKeyboardConnected);191return isHardKeyboardConnected;192} else {193return false;194}195}196}197
198}
199