1 /*
2  * Copyright (C) 2018 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 com.android.internal.inputmethod;
18 
19 import android.annotation.Nullable;
20 import android.view.View;
21 import android.view.WindowManager;
22 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
23 import android.view.inputmethod.HandwritingGesture;
24 
25 import java.util.StringJoiner;
26 
27 /**
28  * Provides useful methods for debugging.
29  */
30 public final class InputMethodDebug {
31 
32     /**
33      * Not intended to be instantiated.
34      */
InputMethodDebug()35     private InputMethodDebug() {
36     }
37 
38     /**
39      * Converts {@link StartInputReason} to {@link String} for debug logging.
40      *
41      * @param reason integer constant for {@link StartInputReason}.
42      * @return {@link String} message corresponds for the given {@code reason}.
43      */
startInputReasonToString(@tartInputReason int reason)44     public static String startInputReasonToString(@StartInputReason int reason) {
45         switch (reason) {
46             case StartInputReason.UNSPECIFIED:
47                 return "UNSPECIFIED";
48             case StartInputReason.WINDOW_FOCUS_GAIN:
49                 return "WINDOW_FOCUS_GAIN";
50             case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY:
51                 return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
52             case StartInputReason.SCHEDULED_CHECK_FOCUS:
53                 return "SCHEDULED_CHECK_FOCUS";
54             case StartInputReason.APP_CALLED_RESTART_INPUT_API:
55                 return "APP_CALLED_RESTART_INPUT_API";
56             case StartInputReason.CHECK_FOCUS:
57                 return "CHECK_FOCUS";
58             case StartInputReason.BOUND_TO_IMMS:
59                 return "BOUND_TO_IMMS";
60             case StartInputReason.UNBOUND_FROM_IMMS:
61                 return "UNBOUND_FROM_IMMS";
62             case StartInputReason.ACTIVATED_BY_IMMS:
63                 return "ACTIVATED_BY_IMMS";
64             case StartInputReason.DEACTIVATED_BY_IMMS:
65                 return "DEACTIVATED_BY_IMMS";
66             case StartInputReason.SESSION_CREATED_BY_IME:
67                 return "SESSION_CREATED_BY_IME";
68             case StartInputReason.BOUND_ACCESSIBILITY_SESSION_TO_IMMS:
69                 return "BOUND_ACCESSIBILITY_SESSION_TO_IMMS";
70             default:
71                 return "Unknown=" + reason;
72         }
73     }
74 
75     /**
76      * Converts {@link UnbindReason} to {@link String} for debug logging.
77      *
78      * @param reason integer constant for {@link UnbindReason}.
79      * @return {@link String} message corresponds for the given {@code reason}.
80      */
unbindReasonToString(@nbindReason int reason)81     public static String unbindReasonToString(@UnbindReason int reason) {
82         switch (reason) {
83             case UnbindReason.UNSPECIFIED:
84                 return "UNSPECIFIED";
85             case UnbindReason.SWITCH_CLIENT:
86                 return "SWITCH_CLIENT";
87             case UnbindReason.SWITCH_IME:
88                 return "SWITCH_IME";
89             case UnbindReason.DISCONNECT_IME:
90                 return "DISCONNECT_IME";
91             case UnbindReason.NO_IME:
92                 return "NO_IME";
93             case UnbindReason.SWITCH_IME_FAILED:
94                 return "SWITCH_IME_FAILED";
95             case UnbindReason.SWITCH_USER:
96                 return "SWITCH_USER";
97             default:
98                 return "Unknown=" + reason;
99         }
100     }
101 
102     /**
103      * Converts {@link SoftInputModeFlags} to {@link String} for debug logging.
104      *
105      * @param softInputMode integer constant for {@link SoftInputModeFlags}.
106      * @return {@link String} message corresponds for the given {@code softInputMode}.
107      */
softInputModeToString(@oftInputModeFlags int softInputMode)108     public static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
109         final StringJoiner joiner = new StringJoiner("|");
110         final int state = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
111         final int adjust = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
112         final boolean isForwardNav =
113                 (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
114 
115         switch (state) {
116             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
117                 joiner.add("STATE_UNSPECIFIED");
118                 break;
119             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
120                 joiner.add("STATE_UNCHANGED");
121                 break;
122             case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
123                 joiner.add("STATE_HIDDEN");
124                 break;
125             case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
126                 joiner.add("STATE_ALWAYS_HIDDEN");
127                 break;
128             case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
129                 joiner.add("STATE_VISIBLE");
130                 break;
131             case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
132                 joiner.add("STATE_ALWAYS_VISIBLE");
133                 break;
134             default:
135                 joiner.add("STATE_UNKNOWN(" + state + ")");
136                 break;
137         }
138 
139         switch (adjust) {
140             case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
141                 joiner.add("ADJUST_UNSPECIFIED");
142                 break;
143             case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
144                 joiner.add("ADJUST_RESIZE");
145                 break;
146             case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN:
147                 joiner.add("ADJUST_PAN");
148                 break;
149             case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
150                 joiner.add("ADJUST_NOTHING");
151                 break;
152             default:
153                 joiner.add("ADJUST_UNKNOWN(" + adjust + ")");
154                 break;
155         }
156 
157         if (isForwardNav) {
158             // This is a special bit that is set by the system only during the window navigation.
159             joiner.add("IS_FORWARD_NAVIGATION");
160         }
161 
162         return joiner.setEmptyValue("(none)").toString();
163     }
164 
165     /**
166      * Converts {@link StartInputFlags} to {@link String} for debug logging.
167      *
168      * @param startInputFlags integer constant for {@link StartInputFlags}.
169      * @return {@link String} message corresponds for the given {@code startInputFlags}.
170      */
startInputFlagsToString(@tartInputFlags int startInputFlags)171     public static String startInputFlagsToString(@StartInputFlags int startInputFlags) {
172         final StringJoiner joiner = new StringJoiner("|");
173         if ((startInputFlags & StartInputFlags.VIEW_HAS_FOCUS) != 0) {
174             joiner.add("VIEW_HAS_FOCUS");
175         }
176         if ((startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
177             joiner.add("IS_TEXT_EDITOR");
178         }
179         if ((startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0) {
180             joiner.add("INITIAL_CONNECTION");
181         }
182 
183         return joiner.setEmptyValue("(none)").toString();
184     }
185 
186 
187     /**
188      * Converts {@link SoftInputShowHideReason} to {@link String} for history dump.
189      */
softInputDisplayReasonToString(@oftInputShowHideReason int reason)190     public static String softInputDisplayReasonToString(@SoftInputShowHideReason int reason) {
191         switch (reason) {
192             case SoftInputShowHideReason.NOT_SET:
193                 return "NOT_SET";
194             case SoftInputShowHideReason.SHOW_SOFT_INPUT:
195                 return "SHOW_SOFT_INPUT";
196             case SoftInputShowHideReason.ATTACH_NEW_INPUT:
197                 return "ATTACH_NEW_INPUT";
198             case SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME:
199                 return "SHOW_SOFT_INPUT_FROM_IME";
200             case SoftInputShowHideReason.HIDE_SOFT_INPUT:
201                 return "HIDE_SOFT_INPUT";
202             case SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME:
203                 return "HIDE_SOFT_INPUT_FROM_IME";
204             case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV:
205                 return "SHOW_AUTO_EDITOR_FORWARD_NAV";
206             case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV:
207                 return "SHOW_STATE_VISIBLE_FORWARD_NAV";
208             case SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE:
209                 return "SHOW_STATE_ALWAYS_VISIBLE";
210             case SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE:
211                 return "SHOW_SETTINGS_ON_CHANGE";
212             case SoftInputShowHideReason.HIDE_SWITCH_USER:
213                 return "HIDE_SWITCH_USER";
214             case SoftInputShowHideReason.HIDE_INVALID_USER:
215                 return "HIDE_INVALID_USER";
216             case SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW:
217                 return "HIDE_UNSPECIFIED_WINDOW";
218             case SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV:
219                 return "HIDE_STATE_HIDDEN_FORWARD_NAV";
220             case SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE:
221                 return "HIDE_ALWAYS_HIDDEN_STATE";
222             case SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND:
223                 return "HIDE_RESET_SHELL_COMMAND";
224             case SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE:
225                 return "HIDE_SETTINGS_ON_CHANGE";
226             case SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME:
227                 return "HIDE_POWER_BUTTON_GO_HOME";
228             case SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED:
229                 return "HIDE_DOCKED_STACK_ATTACHED";
230             case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION:
231                 return "HIDE_RECENTS_ANIMATION";
232             case SoftInputShowHideReason.HIDE_BUBBLES:
233                 return "HIDE_BUBBLES";
234             case SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR:
235                 return "HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR";
236             case SoftInputShowHideReason.HIDE_REMOVE_CLIENT:
237                 return "HIDE_REMOVE_CLIENT";
238             case SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY:
239                 return "SHOW_RESTORE_IME_VISIBILITY";
240             case SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT:
241                 return "SHOW_TOGGLE_SOFT_INPUT";
242             case SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT:
243                 return "HIDE_TOGGLE_SOFT_INPUT";
244             case SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API:
245                 return "SHOW_SOFT_INPUT_BY_INSETS_API";
246             case SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE:
247                 return "HIDE_DISPLAY_IME_POLICY_HIDE";
248             case SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API:
249                 return "HIDE_SOFT_INPUT_BY_INSETS_API";
250             case SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY:
251                 return "HIDE_SOFT_INPUT_BY_BACK_KEY";
252             case SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT:
253                 return "HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT";
254             case SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED:
255                 return "HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED";
256             case SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION:
257                 return "HIDE_SOFT_INPUT_IMM_DEPRECATION";
258             case SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR:
259                 return "HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR";
260             case SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS:
261                 return "SHOW_IME_SCREENSHOT_FROM_IMMS";
262             case SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS:
263                 return "REMOVE_IME_SCREENSHOT_FROM_IMMS";
264             case SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE:
265                 return "HIDE_WHEN_INPUT_TARGET_INVISIBLE";
266             case SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION:
267                 return "HIDE_SOFT_INPUT_CLOSE_CURRENT_SESSION";
268             case SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW:
269                 return "HIDE_SOFT_INPUT_FROM_VIEW";
270             case SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT:
271                 return "SHOW_SOFT_INPUT_LEGACY_DIRECT";
272             case SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT:
273                 return "HIDE_SOFT_INPUT_LEGACY_DIRECT";
274             case SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT:
275                 return "SHOW_WINDOW_LEGACY_DIRECT";
276             case SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT:
277                 return "HIDE_WINDOW_LEGACY_DIRECT";
278             case SoftInputShowHideReason.RESET_NEW_CONFIGURATION:
279                 return "RESET_NEW_CONFIGURATION";
280             case SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY:
281                 return "UPDATE_CANDIDATES_VIEW_VISIBILITY";
282             case SoftInputShowHideReason.CONTROLS_CHANGED:
283                 return "CONTROLS_CHANGED";
284             case SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED:
285                 return "DISPLAY_CONFIGURATION_CHANGED";
286             case SoftInputShowHideReason.DISPLAY_INSETS_CHANGED:
287                 return "DISPLAY_INSETS_CHANGED";
288             case SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED:
289                 return "DISPLAY_CONTROLS_CHANGED";
290             case SoftInputShowHideReason.UNBIND_CURRENT_METHOD:
291                 return "UNBIND_CURRENT_METHOD";
292             case SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED:
293                 return "HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED";
294             case SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL:
295                 return "HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL";
296             case SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT:
297                 return "SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT";
298             case SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION:
299                 return "SHOW_SOFT_INPUT_IMM_DEPRECATION";
300             case SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION:
301                 return "CONTROL_WINDOW_INSETS_ANIMATION";
302             default:
303                 return "Unknown=" + reason;
304         }
305     }
306 
307     /**
308      * Converts {@link HandwritingGesture.GestureTypeFlags} to {@link String} for debug logging.
309      *
310      * @param gestureTypeFlags integer constant for {@link HandwritingGesture.GestureTypeFlags}.
311      * @return {@link String} message corresponds for the given {@code gestureTypeFlags}.
312      */
handwritingGestureTypeFlagsToString( @andwritingGesture.GestureTypeFlags int gestureTypeFlags)313     public static String handwritingGestureTypeFlagsToString(
314             @HandwritingGesture.GestureTypeFlags int gestureTypeFlags) {
315         final StringJoiner joiner = new StringJoiner("|");
316         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_SELECT) != 0) {
317             joiner.add("SELECT");
318         }
319         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_SELECT_RANGE) != 0) {
320             joiner.add("SELECT_RANGE");
321         }
322         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_INSERT) != 0) {
323             joiner.add("INSERT");
324         }
325         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_DELETE) != 0) {
326             joiner.add("DELETE");
327         }
328         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_DELETE_RANGE) != 0) {
329             joiner.add("DELETE_RANGE");
330         }
331         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_REMOVE_SPACE) != 0) {
332             joiner.add("REMOVE_SPACE");
333         }
334         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_JOIN_OR_SPLIT) != 0) {
335             joiner.add("JOIN_OR_SPLIT");
336         }
337         return joiner.setEmptyValue("(none)").toString();
338     }
339 
340     /**
341      * Dumps the given {@link View} related to input method focus state for debugging.
342      */
dumpViewInfo(@ullable View view)343     public static String dumpViewInfo(@Nullable View view) {
344         if (view == null) {
345             return "null";
346         }
347         final StringBuilder sb = new StringBuilder();
348         sb.append(view);
349         sb.append(",focus=" + view.hasFocus());
350         sb.append(",windowFocus=" + view.hasWindowFocus());
351         sb.append(",window=" + view.getWindowToken());
352         sb.append(",displayId=" + view.getContext().getDisplayId());
353         sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
354         sb.append(",hasImeFocus=" + view.hasImeFocus());
355 
356         return sb.toString();
357     }
358 }
359