The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2007-2008 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 5 | * use this file except in compliance with the License. You may obtain a copy of |
| 6 | * 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, WITHOUT |
| 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 13 | * License for the specific language governing permissions and limitations under |
| 14 | * the License. |
| 15 | */ |
| 16 | |
| 17 | package android.inputmethodservice; |
| 18 | |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 19 | import static java.lang.annotation.RetentionPolicy.SOURCE; |
| 20 | |
| 21 | import android.annotation.IntDef; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 22 | import android.app.Dialog; |
| 23 | import android.content.Context; |
Heemin Seog | 28097c4 | 2019-08-15 13:18:41 -0700 | [diff] [blame] | 24 | import android.content.pm.PackageManager; |
Tadashi G. Takaoka | 7bd6c20 | 2011-01-25 18:02:55 +0900 | [diff] [blame] | 25 | import android.graphics.Rect; |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 26 | import android.os.Debug; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 27 | import android.os.IBinder; |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 28 | import android.util.Log; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 29 | import android.view.Gravity; |
Dianne Hackborn | 83fe3f5 | 2009-09-12 23:38:30 -0700 | [diff] [blame] | 30 | import android.view.KeyEvent; |
Tadashi G. Takaoka | 7bd6c20 | 2011-01-25 18:02:55 +0900 | [diff] [blame] | 31 | import android.view.MotionEvent; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 32 | import android.view.WindowManager; |
| 33 | |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 34 | import java.lang.annotation.Retention; |
| 35 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 36 | /** |
| 37 | * A SoftInputWindow is a Dialog that is intended to be used for a top-level input |
| 38 | * method window. It will be displayed along the edge of the screen, moving |
| 39 | * the application user interface away from it so that the focused item is |
| 40 | * always visible. |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 41 | * @hide |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 42 | */ |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 43 | public class SoftInputWindow extends Dialog { |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 44 | private static final boolean DEBUG = false; |
| 45 | private static final String TAG = "SoftInputWindow"; |
| 46 | |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 47 | final String mName; |
| 48 | final Callback mCallback; |
| 49 | final KeyEvent.Callback mKeyEventCallback; |
Dianne Hackborn | 83fe3f5 | 2009-09-12 23:38:30 -0700 | [diff] [blame] | 50 | final KeyEvent.DispatcherState mDispatcherState; |
Dianne Hackborn | e30e02f | 2014-05-27 18:24:45 -0700 | [diff] [blame] | 51 | final int mWindowType; |
| 52 | final int mGravity; |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 53 | final boolean mTakesFocus; |
Heemin Seog | 28097c4 | 2019-08-15 13:18:41 -0700 | [diff] [blame] | 54 | final boolean mAutomotiveHideNavBarForKeyboard; |
Tadashi G. Takaoka | 7bd6c20 | 2011-01-25 18:02:55 +0900 | [diff] [blame] | 55 | private final Rect mBounds = new Rect(); |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 56 | |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 57 | @Retention(SOURCE) |
| 58 | @IntDef(value = {SoftInputWindowState.TOKEN_PENDING, SoftInputWindowState.TOKEN_SET, |
| 59 | SoftInputWindowState.SHOWN_AT_LEAST_ONCE, SoftInputWindowState.REJECTED_AT_LEAST_ONCE}) |
| 60 | private @interface SoftInputWindowState { |
| 61 | /** |
| 62 | * The window token is not set yet. |
| 63 | */ |
| 64 | int TOKEN_PENDING = 0; |
| 65 | /** |
| 66 | * The window token was set, but the window is not shown yet. |
| 67 | */ |
| 68 | int TOKEN_SET = 1; |
| 69 | /** |
| 70 | * The window was shown at least once. |
| 71 | */ |
| 72 | int SHOWN_AT_LEAST_ONCE = 2; |
| 73 | /** |
| 74 | * {@link android.view.WindowManager.BadTokenException} was sent when calling |
| 75 | * {@link Dialog#show()} at least once. |
| 76 | */ |
| 77 | int REJECTED_AT_LEAST_ONCE = 3; |
| 78 | /** |
| 79 | * The window is considered destroyed. Any incoming request should be ignored. |
| 80 | */ |
| 81 | int DESTROYED = 4; |
| 82 | } |
| 83 | |
| 84 | @SoftInputWindowState |
| 85 | private int mWindowState = SoftInputWindowState.TOKEN_PENDING; |
| 86 | |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 87 | public interface Callback { |
| 88 | public void onBackPressed(); |
| 89 | } |
| 90 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 91 | public void setToken(IBinder token) { |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 92 | switch (mWindowState) { |
| 93 | case SoftInputWindowState.TOKEN_PENDING: |
| 94 | // Normal scenario. Nothing to worry about. |
| 95 | WindowManager.LayoutParams lp = getWindow().getAttributes(); |
| 96 | lp.token = token; |
| 97 | getWindow().setAttributes(lp); |
| 98 | updateWindowState(SoftInputWindowState.TOKEN_SET); |
| 99 | return; |
| 100 | case SoftInputWindowState.TOKEN_SET: |
| 101 | case SoftInputWindowState.SHOWN_AT_LEAST_ONCE: |
| 102 | case SoftInputWindowState.REJECTED_AT_LEAST_ONCE: |
| 103 | throw new IllegalStateException("setToken can be called only once"); |
| 104 | case SoftInputWindowState.DESTROYED: |
| 105 | // Just ignore. Since there are multiple event queues from the token is issued |
| 106 | // in the system server to the timing when it arrives here, it can be delivered |
| 107 | // after the is already destroyed. No one should be blamed because of such an |
| 108 | // unfortunate but possible scenario. |
| 109 | Log.i(TAG, "Ignoring setToken() because window is already destroyed."); |
| 110 | return; |
| 111 | default: |
| 112 | throw new IllegalStateException("Unexpected state=" + mWindowState); |
| 113 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 114 | } |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 115 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 116 | /** |
Dianne Hackborn | 2c84cfc | 2011-10-31 15:39:59 -0700 | [diff] [blame] | 117 | * Create a SoftInputWindow that uses a custom style. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 118 | * |
| 119 | * @param context The Context in which the DockWindow should run. In |
| 120 | * particular, it uses the window manager and theme from this context |
| 121 | * to present its UI. |
| 122 | * @param theme A style resource describing the theme to use for the window. |
| 123 | * See <a href="{@docRoot}reference/available-resources.html#stylesandthemes">Style |
| 124 | * and Theme Resources</a> for more information about defining and |
| 125 | * using styles. This theme is applied on top of the current theme in |
| 126 | * <var>context</var>. If 0, the default dialog theme will be used. |
| 127 | */ |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 128 | public SoftInputWindow(Context context, String name, int theme, Callback callback, |
| 129 | KeyEvent.Callback keyEventCallback, KeyEvent.DispatcherState dispatcherState, |
Dianne Hackborn | e30e02f | 2014-05-27 18:24:45 -0700 | [diff] [blame] | 130 | int windowType, int gravity, boolean takesFocus) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 131 | super(context, theme); |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 132 | mName = name; |
| 133 | mCallback = callback; |
| 134 | mKeyEventCallback = keyEventCallback; |
Dianne Hackborn | 83fe3f5 | 2009-09-12 23:38:30 -0700 | [diff] [blame] | 135 | mDispatcherState = dispatcherState; |
Dianne Hackborn | e30e02f | 2014-05-27 18:24:45 -0700 | [diff] [blame] | 136 | mWindowType = windowType; |
| 137 | mGravity = gravity; |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 138 | mTakesFocus = takesFocus; |
Heemin Seog | 28097c4 | 2019-08-15 13:18:41 -0700 | [diff] [blame] | 139 | mAutomotiveHideNavBarForKeyboard = context.getResources().getBoolean( |
| 140 | com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 141 | initDockWindow(); |
| 142 | } |
| 143 | |
Dianne Hackborn | 83fe3f5 | 2009-09-12 23:38:30 -0700 | [diff] [blame] | 144 | @Override |
| 145 | public void onWindowFocusChanged(boolean hasFocus) { |
| 146 | super.onWindowFocusChanged(hasFocus); |
| 147 | mDispatcherState.reset(); |
| 148 | } |
| 149 | |
Tadashi G. Takaoka | 7bd6c20 | 2011-01-25 18:02:55 +0900 | [diff] [blame] | 150 | @Override |
| 151 | public boolean dispatchTouchEvent(MotionEvent ev) { |
| 152 | getWindow().getDecorView().getHitRect(mBounds); |
Jeff Brown | fe9f8ab | 2011-05-06 18:20:01 -0700 | [diff] [blame] | 153 | |
| 154 | if (ev.isWithinBoundsNoHistory(mBounds.left, mBounds.top, |
| 155 | mBounds.right - 1, mBounds.bottom - 1)) { |
| 156 | return super.dispatchTouchEvent(ev); |
| 157 | } else { |
| 158 | MotionEvent temp = ev.clampNoHistory(mBounds.left, mBounds.top, |
| 159 | mBounds.right - 1, mBounds.bottom - 1); |
| 160 | boolean handled = super.dispatchTouchEvent(temp); |
| 161 | temp.recycle(); |
| 162 | return handled; |
| 163 | } |
Tadashi G. Takaoka | 7bd6c20 | 2011-01-25 18:02:55 +0900 | [diff] [blame] | 164 | } |
| 165 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 166 | /** |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 167 | * Set which boundary of the screen the DockWindow sticks to. |
| 168 | * |
Andrew Solovay | 5c05ded | 2018-10-02 14:14:42 -0700 | [diff] [blame] | 169 | * @param gravity The boundary of the screen to stick. See {@link |
| 170 | * android.view.Gravity.LEFT}, {@link android.view.Gravity.TOP}, |
| 171 | * {@link android.view.Gravity.BOTTOM}, {@link |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 172 | * android.view.Gravity.RIGHT}. |
| 173 | */ |
| 174 | public void setGravity(int gravity) { |
| 175 | WindowManager.LayoutParams lp = getWindow().getAttributes(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 176 | lp.gravity = gravity; |
Dianne Hackborn | e30e02f | 2014-05-27 18:24:45 -0700 | [diff] [blame] | 177 | updateWidthHeight(lp); |
| 178 | getWindow().setAttributes(lp); |
| 179 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 180 | |
Dianne Hackborn | 20d9474 | 2014-05-29 18:35:45 -0700 | [diff] [blame] | 181 | public int getGravity() { |
| 182 | return getWindow().getAttributes().gravity; |
| 183 | } |
| 184 | |
Dianne Hackborn | e30e02f | 2014-05-27 18:24:45 -0700 | [diff] [blame] | 185 | private void updateWidthHeight(WindowManager.LayoutParams lp) { |
| 186 | if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) { |
| 187 | lp.width = WindowManager.LayoutParams.MATCH_PARENT; |
| 188 | lp.height = WindowManager.LayoutParams.WRAP_CONTENT; |
| 189 | } else { |
| 190 | lp.width = WindowManager.LayoutParams.WRAP_CONTENT; |
| 191 | lp.height = WindowManager.LayoutParams.MATCH_PARENT; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 192 | } |
| 193 | } |
| 194 | |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 195 | public boolean onKeyDown(int keyCode, KeyEvent event) { |
| 196 | if (mKeyEventCallback != null && mKeyEventCallback.onKeyDown(keyCode, event)) { |
| 197 | return true; |
| 198 | } |
| 199 | return super.onKeyDown(keyCode, event); |
| 200 | } |
| 201 | |
| 202 | public boolean onKeyLongPress(int keyCode, KeyEvent event) { |
| 203 | if (mKeyEventCallback != null && mKeyEventCallback.onKeyLongPress(keyCode, event)) { |
| 204 | return true; |
| 205 | } |
| 206 | return super.onKeyLongPress(keyCode, event); |
| 207 | } |
| 208 | |
| 209 | public boolean onKeyUp(int keyCode, KeyEvent event) { |
| 210 | if (mKeyEventCallback != null && mKeyEventCallback.onKeyUp(keyCode, event)) { |
| 211 | return true; |
| 212 | } |
| 213 | return super.onKeyUp(keyCode, event); |
| 214 | } |
| 215 | |
| 216 | public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { |
| 217 | if (mKeyEventCallback != null && mKeyEventCallback.onKeyMultiple(keyCode, count, event)) { |
| 218 | return true; |
| 219 | } |
| 220 | return super.onKeyMultiple(keyCode, count, event); |
| 221 | } |
| 222 | |
| 223 | public void onBackPressed() { |
| 224 | if (mCallback != null) { |
| 225 | mCallback.onBackPressed(); |
| 226 | } else { |
| 227 | super.onBackPressed(); |
| 228 | } |
| 229 | } |
| 230 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 231 | private void initDockWindow() { |
| 232 | WindowManager.LayoutParams lp = getWindow().getAttributes(); |
| 233 | |
Dianne Hackborn | e30e02f | 2014-05-27 18:24:45 -0700 | [diff] [blame] | 234 | lp.type = mWindowType; |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 235 | lp.setTitle(mName); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 236 | |
Dianne Hackborn | e30e02f | 2014-05-27 18:24:45 -0700 | [diff] [blame] | 237 | lp.gravity = mGravity; |
| 238 | updateWidthHeight(lp); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 239 | |
| 240 | getWindow().setAttributes(lp); |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 241 | |
| 242 | int windowSetFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; |
| 243 | int windowModFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 244 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 245 | WindowManager.LayoutParams.FLAG_DIM_BEHIND; |
| 246 | |
| 247 | if (!mTakesFocus) { |
| 248 | windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
| 249 | } else { |
| 250 | windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; |
| 251 | windowModFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; |
| 252 | } |
| 253 | |
Heemin Seog | 28097c4 | 2019-08-15 13:18:41 -0700 | [diff] [blame] | 254 | if (isAutomotive() && mAutomotiveHideNavBarForKeyboard) { |
| 255 | windowSetFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; |
| 256 | windowModFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; |
| 257 | } |
| 258 | |
Dianne Hackborn | c03c916 | 2014-05-02 10:45:59 -0700 | [diff] [blame] | 259 | getWindow().setFlags(windowSetFlags, windowModFlags); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 260 | } |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 261 | |
| 262 | @Override |
| 263 | public final void show() { |
| 264 | switch (mWindowState) { |
| 265 | case SoftInputWindowState.TOKEN_PENDING: |
| 266 | throw new IllegalStateException("Window token is not set yet."); |
| 267 | case SoftInputWindowState.TOKEN_SET: |
| 268 | case SoftInputWindowState.SHOWN_AT_LEAST_ONCE: |
| 269 | // Normal scenario. Nothing to worry about. |
| 270 | try { |
| 271 | super.show(); |
| 272 | updateWindowState(SoftInputWindowState.SHOWN_AT_LEAST_ONCE); |
| 273 | } catch (WindowManager.BadTokenException e) { |
| 274 | // Just ignore this exception. Since show() can be requested from other |
| 275 | // components such as the system and there could be multiple event queues before |
| 276 | // the request finally arrives here, the system may have already invalidated the |
| 277 | // window token attached to our window. In such a scenario, receiving |
| 278 | // BadTokenException here is an expected behavior. We just ignore it and update |
| 279 | // the state so that we do not touch this window later. |
| 280 | Log.i(TAG, "Probably the IME window token is already invalidated." |
| 281 | + " show() does nothing."); |
| 282 | updateWindowState(SoftInputWindowState.REJECTED_AT_LEAST_ONCE); |
| 283 | } |
| 284 | return; |
| 285 | case SoftInputWindowState.REJECTED_AT_LEAST_ONCE: |
| 286 | // Just ignore. In general we cannot completely avoid this kind of race condition. |
| 287 | Log.i(TAG, "Not trying to call show() because it was already rejected once."); |
| 288 | return; |
| 289 | case SoftInputWindowState.DESTROYED: |
| 290 | // Just ignore. In general we cannot completely avoid this kind of race condition. |
| 291 | Log.i(TAG, "Ignoring show() because the window is already destroyed."); |
| 292 | return; |
| 293 | default: |
| 294 | throw new IllegalStateException("Unexpected state=" + mWindowState); |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | final void dismissForDestroyIfNecessary() { |
| 299 | switch (mWindowState) { |
| 300 | case SoftInputWindowState.TOKEN_PENDING: |
| 301 | case SoftInputWindowState.TOKEN_SET: |
| 302 | // nothing to do because the window has never been shown. |
| 303 | updateWindowState(SoftInputWindowState.DESTROYED); |
| 304 | return; |
| 305 | case SoftInputWindowState.SHOWN_AT_LEAST_ONCE: |
| 306 | // Disable exit animation for the current IME window |
| 307 | // to avoid the race condition between the exit and enter animations |
| 308 | // when the current IME is being switched to another one. |
| 309 | try { |
| 310 | getWindow().setWindowAnimations(0); |
| 311 | dismiss(); |
| 312 | } catch (WindowManager.BadTokenException e) { |
| 313 | // Just ignore this exception. Since show() can be requested from other |
| 314 | // components such as the system and there could be multiple event queues before |
| 315 | // the request finally arrives here, the system may have already invalidated the |
| 316 | // window token attached to our window. In such a scenario, receiving |
| 317 | // BadTokenException here is an expected behavior. We just ignore it and update |
| 318 | // the state so that we do not touch this window later. |
| 319 | Log.i(TAG, "Probably the IME window token is already invalidated. " |
| 320 | + "No need to dismiss it."); |
| 321 | } |
| 322 | // Either way, consider that the window is destroyed. |
| 323 | updateWindowState(SoftInputWindowState.DESTROYED); |
| 324 | return; |
| 325 | case SoftInputWindowState.REJECTED_AT_LEAST_ONCE: |
| 326 | // Just ignore. In general we cannot completely avoid this kind of race condition. |
| 327 | Log.i(TAG, |
| 328 | "Not trying to dismiss the window because it is most likely unnecessary."); |
| 329 | // Anyway, consider that the window is destroyed. |
| 330 | updateWindowState(SoftInputWindowState.DESTROYED); |
| 331 | return; |
| 332 | case SoftInputWindowState.DESTROYED: |
| 333 | throw new IllegalStateException( |
| 334 | "dismissForDestroyIfNecessary can be called only once"); |
| 335 | default: |
| 336 | throw new IllegalStateException("Unexpected state=" + mWindowState); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | private void updateWindowState(@SoftInputWindowState int newState) { |
| 341 | if (DEBUG) { |
| 342 | if (mWindowState != newState) { |
| 343 | Log.d(TAG, "WindowState: " + stateToString(mWindowState) + " -> " |
| 344 | + stateToString(newState) + " @ " + Debug.getCaller()); |
| 345 | } |
| 346 | } |
| 347 | mWindowState = newState; |
| 348 | } |
| 349 | |
Heemin Seog | 28097c4 | 2019-08-15 13:18:41 -0700 | [diff] [blame] | 350 | private boolean isAutomotive() { |
| 351 | return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); |
| 352 | } |
| 353 | |
Yohei Yukawa | 13a9ffb | 2018-08-29 19:56:02 -0700 | [diff] [blame] | 354 | private static String stateToString(@SoftInputWindowState int state) { |
| 355 | switch (state) { |
| 356 | case SoftInputWindowState.TOKEN_PENDING: |
| 357 | return "TOKEN_PENDING"; |
| 358 | case SoftInputWindowState.TOKEN_SET: |
| 359 | return "TOKEN_SET"; |
| 360 | case SoftInputWindowState.SHOWN_AT_LEAST_ONCE: |
| 361 | return "SHOWN_AT_LEAST_ONCE"; |
| 362 | case SoftInputWindowState.REJECTED_AT_LEAST_ONCE: |
| 363 | return "REJECTED_AT_LEAST_ONCE"; |
| 364 | case SoftInputWindowState.DESTROYED: |
| 365 | return "DESTROYED"; |
| 366 | default: |
| 367 | throw new IllegalStateException("Unknown state=" + state); |
| 368 | } |
| 369 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 370 | } |