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.SHOW_SOFT_INPUT:
193                 return "SHOW_SOFT_INPUT";
194             case SoftInputShowHideReason.ATTACH_NEW_INPUT:
195                 return "ATTACH_NEW_INPUT";
196             case SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME:
197                 return "SHOW_SOFT_INPUT_FROM_IME";
198             case SoftInputShowHideReason.HIDE_SOFT_INPUT:
199                 return "HIDE_SOFT_INPUT";
200             case SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME:
201                 return "HIDE_SOFT_INPUT_FROM_IME";
202             case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV:
203                 return "SHOW_AUTO_EDITOR_FORWARD_NAV";
204             case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV:
205                 return "SHOW_STATE_VISIBLE_FORWARD_NAV";
206             case SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE:
207                 return "SHOW_STATE_ALWAYS_VISIBLE";
208             case SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE:
209                 return "SHOW_SETTINGS_ON_CHANGE";
210             case SoftInputShowHideReason.HIDE_SWITCH_USER:
211                 return "HIDE_SWITCH_USER";
212             case SoftInputShowHideReason.HIDE_INVALID_USER:
213                 return "HIDE_INVALID_USER";
214             case SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW:
215                 return "HIDE_UNSPECIFIED_WINDOW";
216             case SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV:
217                 return "HIDE_STATE_HIDDEN_FORWARD_NAV";
218             case SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE:
219                 return "HIDE_ALWAYS_HIDDEN_STATE";
220             case SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND:
221                 return "HIDE_RESET_SHELL_COMMAND";
222             case SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE:
223                 return "HIDE_SETTINGS_ON_CHANGE";
224             case SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME:
225                 return "HIDE_POWER_BUTTON_GO_HOME";
226             case SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED:
227                 return "HIDE_DOCKED_STACK_ATTACHED";
228             case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION:
229                 return "HIDE_RECENTS_ANIMATION";
230             case SoftInputShowHideReason.HIDE_BUBBLES:
231                 return "HIDE_BUBBLES";
232             case SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR:
233                 return "HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR";
234             case SoftInputShowHideReason.HIDE_REMOVE_CLIENT:
235                 return "HIDE_REMOVE_CLIENT";
236             case SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY:
237                 return "SHOW_RESTORE_IME_VISIBILITY";
238             case SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT:
239                 return "SHOW_TOGGLE_SOFT_INPUT";
240             case SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT:
241                 return "HIDE_TOGGLE_SOFT_INPUT";
242             case SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API:
243                 return "SHOW_SOFT_INPUT_BY_INSETS_API";
244             case SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE:
245                 return "HIDE_DISPLAY_IME_POLICY_HIDE";
246             case SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API:
247                 return "HIDE_SOFT_INPUT_BY_INSETS_API";
248             case SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY:
249                 return "HIDE_SOFT_INPUT_BY_BACK_KEY";
250             case SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT:
251                 return "HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT";
252             case SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED:
253                 return "HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED";
254             case SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION:
255                 return "HIDE_SOFT_INPUT_IMM_DEPRECATION";
256             case SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR:
257                 return "HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR";
258             case SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS:
259                 return "SHOW_IME_SCREENSHOT_FROM_IMMS";
260             case SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS:
261                 return "REMOVE_IME_SCREENSHOT_FROM_IMMS";
262             case SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE:
263                 return "HIDE_WHEN_INPUT_TARGET_INVISIBLE";
264             default:
265                 return "Unknown=" + reason;
266         }
267     }
268 
269     /**
270      * Converts {@link HandwritingGesture.GestureTypeFlags} to {@link String} for debug logging.
271      *
272      * @param gestureTypeFlags integer constant for {@link HandwritingGesture.GestureTypeFlags}.
273      * @return {@link String} message corresponds for the given {@code gestureTypeFlags}.
274      */
handwritingGestureTypeFlagsToString( @andwritingGesture.GestureTypeFlags int gestureTypeFlags)275     public static String handwritingGestureTypeFlagsToString(
276             @HandwritingGesture.GestureTypeFlags int gestureTypeFlags) {
277         final StringJoiner joiner = new StringJoiner("|");
278         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_SELECT) != 0) {
279             joiner.add("SELECT");
280         }
281         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_SELECT_RANGE) != 0) {
282             joiner.add("SELECT_RANGE");
283         }
284         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_INSERT) != 0) {
285             joiner.add("INSERT");
286         }
287         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_DELETE) != 0) {
288             joiner.add("DELETE");
289         }
290         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_DELETE_RANGE) != 0) {
291             joiner.add("DELETE_RANGE");
292         }
293         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_REMOVE_SPACE) != 0) {
294             joiner.add("REMOVE_SPACE");
295         }
296         if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_JOIN_OR_SPLIT) != 0) {
297             joiner.add("JOIN_OR_SPLIT");
298         }
299         return joiner.setEmptyValue("(none)").toString();
300     }
301 
302     /**
303      * Dumps the given {@link View} related to input method focus state for debugging.
304      */
dumpViewInfo(@ullable View view)305     public static String dumpViewInfo(@Nullable View view) {
306         if (view == null) {
307             return "null";
308         }
309         final StringBuilder sb = new StringBuilder();
310         sb.append(view);
311         sb.append(",focus=" + view.hasFocus());
312         sb.append(",windowFocus=" + view.hasWindowFocus());
313         sb.append(",window=" + view.getWindowToken());
314         sb.append(",displayId=" + view.getContext().getDisplayId());
315         sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
316         sb.append(",hasImeFocus=" + view.hasImeFocus());
317 
318         return sb.toString();
319     }
320 }
321