1 /* 2 * Copyright (C) 2022 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 android.inputmethodservice; 18 19 import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR; 20 import static android.view.WindowInsets.Type.captionBar; 21 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; 22 23 import android.animation.ValueAnimator; 24 import android.annotation.FloatRange; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.app.StatusBarManager; 28 import android.graphics.Color; 29 import android.graphics.Insets; 30 import android.graphics.Rect; 31 import android.graphics.Region; 32 import android.inputmethodservice.navigationbar.NavigationBarFrame; 33 import android.inputmethodservice.navigationbar.NavigationBarView; 34 import android.view.Gravity; 35 import android.view.LayoutInflater; 36 import android.view.View; 37 import android.view.ViewGroup; 38 import android.view.ViewParent; 39 import android.view.ViewTreeObserver; 40 import android.view.Window; 41 import android.view.WindowInsets; 42 import android.view.WindowInsetsController.Appearance; 43 import android.view.animation.Interpolator; 44 import android.view.animation.PathInterpolator; 45 import android.widget.FrameLayout; 46 47 import com.android.internal.inputmethod.InputMethodNavButtonFlags; 48 49 import java.util.Objects; 50 51 /** 52 * This class hides details behind {@link InputMethodService#canImeRenderGesturalNavButtons()} from 53 * {@link InputMethodService}. 54 * 55 * <p>All the package-private methods are no-op when 56 * {@link InputMethodService#canImeRenderGesturalNavButtons()} returns {@code false}.</p> 57 */ 58 final class NavigationBarController { 59 60 private interface Callback { 61 updateInsets(@onNull InputMethodService.Insets originalInsets)62 default void updateInsets(@NonNull InputMethodService.Insets originalInsets) { 63 } 64 updateTouchableInsets(@onNull InputMethodService.Insets originalInsets, @NonNull ViewTreeObserver.InternalInsetsInfo dest)65 default void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets, 66 @NonNull ViewTreeObserver.InternalInsetsInfo dest) { 67 } 68 onSoftInputWindowCreated(@onNull SoftInputWindow softInputWindow)69 default void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) { 70 } 71 onViewInitialized()72 default void onViewInitialized() { 73 } 74 onWindowShown()75 default void onWindowShown() { 76 } 77 onDestroy()78 default void onDestroy() { 79 } 80 onNavButtonFlagsChanged(@nputMethodNavButtonFlags int navButtonFlags)81 default void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) { 82 } 83 isShown()84 default boolean isShown() { 85 return false; 86 } 87 toDebugString()88 default String toDebugString() { 89 return "No-op implementation"; 90 } 91 92 Callback NOOP = new Callback() { 93 }; 94 } 95 96 private final Callback mImpl; 97 NavigationBarController(@onNull InputMethodService inputMethodService)98 NavigationBarController(@NonNull InputMethodService inputMethodService) { 99 mImpl = InputMethodService.canImeRenderGesturalNavButtons() 100 ? new Impl(inputMethodService) : Callback.NOOP; 101 } 102 103 /** 104 * Update the given insets to be at least as big as the IME navigation bar, when visible. 105 * 106 * @param originalInsets the insets to check and modify to include the IME navigation bar. 107 */ updateInsets(@onNull InputMethodService.Insets originalInsets)108 void updateInsets(@NonNull InputMethodService.Insets originalInsets) { 109 mImpl.updateInsets(originalInsets); 110 } 111 updateTouchableInsets(@onNull InputMethodService.Insets originalInsets, @NonNull ViewTreeObserver.InternalInsetsInfo dest)112 void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets, 113 @NonNull ViewTreeObserver.InternalInsetsInfo dest) { 114 mImpl.updateTouchableInsets(originalInsets, dest); 115 } 116 onSoftInputWindowCreated(@onNull SoftInputWindow softInputWindow)117 void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) { 118 mImpl.onSoftInputWindowCreated(softInputWindow); 119 } 120 onViewInitialized()121 void onViewInitialized() { 122 mImpl.onViewInitialized(); 123 } 124 onWindowShown()125 void onWindowShown() { 126 mImpl.onWindowShown(); 127 } 128 onDestroy()129 void onDestroy() { 130 mImpl.onDestroy(); 131 } 132 onNavButtonFlagsChanged(@nputMethodNavButtonFlags int navButtonFlags)133 void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) { 134 mImpl.onNavButtonFlagsChanged(navButtonFlags); 135 } 136 137 /** 138 * Returns whether the IME navigation bar is currently shown. 139 */ isShown()140 boolean isShown() { 141 return mImpl.isShown(); 142 } 143 toDebugString()144 String toDebugString() { 145 return mImpl.toDebugString(); 146 } 147 148 private static final class Impl implements Callback, Window.DecorCallback { 149 private static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700; 150 151 // Copied from com.android.systemui.animation.Interpolators#LEGACY_DECELERATE 152 private static final Interpolator LEGACY_DECELERATE = 153 new PathInterpolator(0f, 0f, 0.2f, 1f); 154 155 @NonNull 156 private final InputMethodService mService; 157 158 private boolean mDestroyed = false; 159 160 private boolean mImeDrawsImeNavBar; 161 162 @Nullable 163 private NavigationBarFrame mNavigationBarFrame; 164 @Nullable 165 Insets mLastInsets; 166 167 private boolean mShouldShowImeSwitcherWhenImeIsShown; 168 169 @Appearance 170 private int mAppearance; 171 172 @FloatRange(from = 0.0f, to = 1.0f) 173 private float mDarkIntensity; 174 175 @Nullable 176 private ValueAnimator mTintAnimator; 177 178 private boolean mDrawLegacyNavigationBarBackground; 179 180 private final Rect mTempRect = new Rect(); 181 private final int[] mTempPos = new int[2]; 182 Impl(@onNull InputMethodService inputMethodService)183 Impl(@NonNull InputMethodService inputMethodService) { 184 mService = inputMethodService; 185 } 186 187 @Nullable getSystemInsets()188 private Insets getSystemInsets() { 189 if (mService.mWindow == null) { 190 return null; 191 } 192 final View decorView = mService.mWindow.getWindow().getDecorView(); 193 if (decorView == null) { 194 return null; 195 } 196 final WindowInsets windowInsets = decorView.getRootWindowInsets(); 197 if (windowInsets == null) { 198 return null; 199 } 200 final Insets stableBarInsets = 201 windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()); 202 return Insets.min(windowInsets.getInsets(WindowInsets.Type.systemBars() 203 | WindowInsets.Type.displayCutout()), stableBarInsets); 204 } 205 installNavigationBarFrameIfNecessary()206 private void installNavigationBarFrameIfNecessary() { 207 if (!mImeDrawsImeNavBar) { 208 return; 209 } 210 if (mNavigationBarFrame != null) { 211 return; 212 } 213 final View rawDecorView = mService.mWindow.getWindow().getDecorView(); 214 if (!(rawDecorView instanceof ViewGroup)) { 215 return; 216 } 217 final ViewGroup decorView = (ViewGroup) rawDecorView; 218 mNavigationBarFrame = decorView.findViewByPredicate( 219 NavigationBarFrame.class::isInstance); 220 final Insets systemInsets = getSystemInsets(); 221 if (mNavigationBarFrame == null) { 222 mNavigationBarFrame = new NavigationBarFrame(mService); 223 LayoutInflater.from(mService).inflate( 224 com.android.internal.R.layout.input_method_navigation_bar, 225 mNavigationBarFrame); 226 if (systemInsets != null) { 227 decorView.addView(mNavigationBarFrame, new FrameLayout.LayoutParams( 228 ViewGroup.LayoutParams.MATCH_PARENT, 229 systemInsets.bottom, Gravity.BOTTOM)); 230 mLastInsets = systemInsets; 231 } else { 232 decorView.addView(mNavigationBarFrame); 233 } 234 final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate( 235 NavigationBarView.class::isInstance); 236 if (navigationBarView != null) { 237 // TODO(b/213337792): Support InputMethodService#setBackDisposition(). 238 // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary. 239 final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT 240 | (mShouldShowImeSwitcherWhenImeIsShown 241 ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN 242 : 0); 243 navigationBarView.setNavigationIconHints(hints); 244 } 245 } else { 246 mNavigationBarFrame.setLayoutParams(new FrameLayout.LayoutParams( 247 ViewGroup.LayoutParams.MATCH_PARENT, systemInsets.bottom, Gravity.BOTTOM)); 248 mLastInsets = systemInsets; 249 } 250 251 if (mDrawLegacyNavigationBarBackground) { 252 mNavigationBarFrame.setBackgroundColor(Color.BLACK); 253 } else { 254 mNavigationBarFrame.setBackground(null); 255 } 256 257 setIconTintInternal(calculateTargetDarkIntensity(mAppearance, 258 mDrawLegacyNavigationBarBackground)); 259 260 if (ENABLE_HIDE_IME_CAPTION_BAR) { 261 mNavigationBarFrame.setOnApplyWindowInsetsListener((view, insets) -> { 262 if (mNavigationBarFrame != null) { 263 boolean visible = insets.isVisible(captionBar()); 264 mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.GONE); 265 } 266 return view.onApplyWindowInsets(insets); 267 }); 268 } 269 } 270 uninstallNavigationBarFrameIfNecessary()271 private void uninstallNavigationBarFrameIfNecessary() { 272 if (mNavigationBarFrame == null) { 273 return; 274 } 275 final ViewParent parent = mNavigationBarFrame.getParent(); 276 if (parent instanceof ViewGroup) { 277 ((ViewGroup) parent).removeView(mNavigationBarFrame); 278 } 279 if (ENABLE_HIDE_IME_CAPTION_BAR) { 280 mNavigationBarFrame.setOnApplyWindowInsetsListener(null); 281 } 282 mNavigationBarFrame = null; 283 } 284 285 @Override updateInsets(@onNull InputMethodService.Insets originalInsets)286 public void updateInsets(@NonNull InputMethodService.Insets originalInsets) { 287 if (!mImeDrawsImeNavBar || mNavigationBarFrame == null 288 || mNavigationBarFrame.getVisibility() != View.VISIBLE 289 || mService.isFullscreenMode()) { 290 return; 291 } 292 293 final int[] loc = new int[2]; 294 mNavigationBarFrame.getLocationInWindow(loc); 295 if (originalInsets.contentTopInsets > loc[1]) { 296 originalInsets.contentTopInsets = loc[1]; 297 } 298 if (originalInsets.visibleTopInsets > loc[1]) { 299 originalInsets.visibleTopInsets = loc[1]; 300 } 301 } 302 303 @Override updateTouchableInsets(@onNull InputMethodService.Insets originalInsets, @NonNull ViewTreeObserver.InternalInsetsInfo dest)304 public void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets, 305 @NonNull ViewTreeObserver.InternalInsetsInfo dest) { 306 if (!mImeDrawsImeNavBar || mNavigationBarFrame == null) { 307 return; 308 } 309 310 final Insets systemInsets = getSystemInsets(); 311 if (systemInsets != null) { 312 final Window window = mService.mWindow.getWindow(); 313 final View decor = window.getDecorView(); 314 315 // If the extract view is shown, everything is touchable, so no need to update 316 // touchable insets, but we still update normal insets below. 317 if (!mService.isExtractViewShown()) { 318 Region touchableRegion = null; 319 final View inputFrame = mService.mInputFrame; 320 switch (originalInsets.touchableInsets) { 321 case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME: 322 if (inputFrame.getVisibility() == View.VISIBLE) { 323 inputFrame.getLocationInWindow(mTempPos); 324 mTempRect.set(mTempPos[0], mTempPos[1], 325 mTempPos[0] + inputFrame.getWidth(), 326 mTempPos[1] + inputFrame.getHeight()); 327 touchableRegion = new Region(mTempRect); 328 } 329 break; 330 case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: 331 if (inputFrame.getVisibility() == View.VISIBLE) { 332 inputFrame.getLocationInWindow(mTempPos); 333 mTempRect.set(mTempPos[0], originalInsets.contentTopInsets, 334 mTempPos[0] + inputFrame.getWidth(), 335 mTempPos[1] + inputFrame.getHeight()); 336 touchableRegion = new Region(mTempRect); 337 } 338 break; 339 case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: 340 if (inputFrame.getVisibility() == View.VISIBLE) { 341 inputFrame.getLocationInWindow(mTempPos); 342 mTempRect.set(mTempPos[0], originalInsets.visibleTopInsets, 343 mTempPos[0] + inputFrame.getWidth(), 344 mTempPos[1] + inputFrame.getHeight()); 345 touchableRegion = new Region(mTempRect); 346 } 347 break; 348 case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: 349 touchableRegion = new Region(); 350 touchableRegion.set(originalInsets.touchableRegion); 351 break; 352 } 353 // Hereafter "mTempRect" means a navigation bar rect. 354 mTempRect.set(decor.getLeft(), decor.getBottom() - systemInsets.bottom, 355 decor.getRight(), decor.getBottom()); 356 if (touchableRegion == null) { 357 touchableRegion = new Region(mTempRect); 358 } else { 359 touchableRegion.union(mTempRect); 360 } 361 362 dest.touchableRegion.set(touchableRegion); 363 dest.setTouchableInsets( 364 ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 365 } 366 367 // TODO(b/215443343): See if we can use View#OnLayoutChangeListener(). 368 // TODO(b/215443343): See if we can replace DecorView#mNavigationColorViewState.view 369 boolean zOrderChanged = false; 370 if (decor instanceof ViewGroup) { 371 ViewGroup decorGroup = (ViewGroup) decor; 372 final View navbarBackgroundView = window.getNavigationBarBackgroundView(); 373 zOrderChanged = navbarBackgroundView != null 374 && decorGroup.indexOfChild(navbarBackgroundView) 375 > decorGroup.indexOfChild(mNavigationBarFrame); 376 } 377 final boolean insetChanged = !Objects.equals(systemInsets, mLastInsets); 378 if (zOrderChanged || insetChanged) { 379 scheduleRelayout(); 380 } 381 } 382 } 383 scheduleRelayout()384 private void scheduleRelayout() { 385 // Capture the current frame object in case the object is replaced or cleared later. 386 final NavigationBarFrame frame = mNavigationBarFrame; 387 frame.post(() -> { 388 if (mDestroyed) { 389 return; 390 } 391 if (!frame.isAttachedToWindow()) { 392 return; 393 } 394 final Window window = mService.mWindow.getWindow(); 395 if (window == null) { 396 return; 397 } 398 final View decor = window.peekDecorView(); 399 if (decor == null) { 400 return; 401 } 402 if (!(decor instanceof ViewGroup)) { 403 return; 404 } 405 final ViewGroup decorGroup = (ViewGroup) decor; 406 final Insets currentSystemInsets = getSystemInsets(); 407 if (!Objects.equals(currentSystemInsets, mLastInsets)) { 408 frame.setLayoutParams(new FrameLayout.LayoutParams( 409 ViewGroup.LayoutParams.MATCH_PARENT, 410 currentSystemInsets.bottom, Gravity.BOTTOM)); 411 mLastInsets = currentSystemInsets; 412 } 413 final View navbarBackgroundView = 414 window.getNavigationBarBackgroundView(); 415 if (navbarBackgroundView != null 416 && decorGroup.indexOfChild(navbarBackgroundView) 417 > decorGroup.indexOfChild(frame)) { 418 decorGroup.bringChildToFront(frame); 419 } 420 }); 421 } 422 423 @Override onSoftInputWindowCreated(@onNull SoftInputWindow softInputWindow)424 public void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) { 425 final Window window = softInputWindow.getWindow(); 426 mAppearance = window.getSystemBarAppearance(); 427 window.setDecorCallback(this); 428 } 429 430 @Override onViewInitialized()431 public void onViewInitialized() { 432 if (mDestroyed) { 433 return; 434 } 435 installNavigationBarFrameIfNecessary(); 436 } 437 438 @Override onDestroy()439 public void onDestroy() { 440 if (mDestroyed) { 441 return; 442 } 443 if (mTintAnimator != null) { 444 mTintAnimator.cancel(); 445 mTintAnimator = null; 446 } 447 mDestroyed = true; 448 } 449 450 @Override onWindowShown()451 public void onWindowShown() { 452 if (mDestroyed || !mImeDrawsImeNavBar || mNavigationBarFrame == null) { 453 return; 454 } 455 final Insets systemInsets = getSystemInsets(); 456 if (systemInsets != null) { 457 if (!Objects.equals(systemInsets, mLastInsets)) { 458 mNavigationBarFrame.setLayoutParams(new NavigationBarFrame.LayoutParams( 459 ViewGroup.LayoutParams.MATCH_PARENT, 460 systemInsets.bottom, Gravity.BOTTOM)); 461 mLastInsets = systemInsets; 462 } 463 final Window window = mService.mWindow.getWindow(); 464 View rawDecorView = window.getDecorView(); 465 if (rawDecorView instanceof ViewGroup) { 466 final ViewGroup decor = (ViewGroup) rawDecorView; 467 final View navbarBackgroundView = window.getNavigationBarBackgroundView(); 468 if (navbarBackgroundView != null 469 && decor.indexOfChild(navbarBackgroundView) 470 > decor.indexOfChild(mNavigationBarFrame)) { 471 decor.bringChildToFront(mNavigationBarFrame); 472 } 473 } 474 if (!ENABLE_HIDE_IME_CAPTION_BAR) { 475 mNavigationBarFrame.setVisibility(View.VISIBLE); 476 } 477 } 478 } 479 480 @Override onNavButtonFlagsChanged(@nputMethodNavButtonFlags int navButtonFlags)481 public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) { 482 if (mDestroyed) { 483 return; 484 } 485 486 final boolean imeDrawsImeNavBar = 487 (navButtonFlags & InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR) != 0; 488 final boolean shouldShowImeSwitcherWhenImeIsShown = 489 (navButtonFlags & InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN) 490 != 0; 491 492 mImeDrawsImeNavBar = imeDrawsImeNavBar; 493 final boolean prevShouldShowImeSwitcherWhenImeIsShown = 494 mShouldShowImeSwitcherWhenImeIsShown; 495 mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown; 496 497 if (ENABLE_HIDE_IME_CAPTION_BAR) { 498 mService.mWindow.getWindow().getDecorView().getWindowInsetsController() 499 .setImeCaptionBarInsetsHeight(getImeCaptionBarHeight()); 500 } 501 502 if (imeDrawsImeNavBar) { 503 installNavigationBarFrameIfNecessary(); 504 if (mNavigationBarFrame == null) { 505 return; 506 } 507 if (mShouldShowImeSwitcherWhenImeIsShown 508 == prevShouldShowImeSwitcherWhenImeIsShown) { 509 return; 510 } 511 final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate( 512 NavigationBarView.class::isInstance); 513 if (navigationBarView == null) { 514 return; 515 } 516 final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT 517 | (shouldShowImeSwitcherWhenImeIsShown 518 ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0); 519 navigationBarView.setNavigationIconHints(hints); 520 } else { 521 uninstallNavigationBarFrameIfNecessary(); 522 } 523 } 524 525 @Override onSystemBarAppearanceChanged(@ppearance int appearance)526 public void onSystemBarAppearanceChanged(@Appearance int appearance) { 527 if (mDestroyed) { 528 return; 529 } 530 531 mAppearance = appearance; 532 533 if (mNavigationBarFrame == null) { 534 return; 535 } 536 537 final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance, 538 mDrawLegacyNavigationBarBackground); 539 540 if (mTintAnimator != null) { 541 mTintAnimator.cancel(); 542 } 543 mTintAnimator = ValueAnimator.ofFloat(mDarkIntensity, targetDarkIntensity); 544 mTintAnimator.addUpdateListener( 545 animation -> setIconTintInternal((Float) animation.getAnimatedValue())); 546 mTintAnimator.setDuration(DEFAULT_COLOR_ADAPT_TRANSITION_TIME); 547 mTintAnimator.setStartDelay(0); 548 mTintAnimator.setInterpolator(LEGACY_DECELERATE); 549 mTintAnimator.start(); 550 } 551 setIconTintInternal(float darkIntensity)552 private void setIconTintInternal(float darkIntensity) { 553 mDarkIntensity = darkIntensity; 554 if (mNavigationBarFrame == null) { 555 return; 556 } 557 final NavigationBarView navigationBarView = 558 mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance); 559 if (navigationBarView == null) { 560 return; 561 } 562 navigationBarView.setDarkIntensity(darkIntensity); 563 } 564 565 @FloatRange(from = 0.0f, to = 1.0f) calculateTargetDarkIntensity(@ppearance int appearance, boolean drawLegacyNavigationBarBackground)566 private static float calculateTargetDarkIntensity(@Appearance int appearance, 567 boolean drawLegacyNavigationBarBackground) { 568 final boolean lightNavBar = !drawLegacyNavigationBarBackground 569 && (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0; 570 return lightNavBar ? 1.0f : 0.0f; 571 } 572 573 @Override onDrawLegacyNavigationBarBackgroundChanged( boolean drawLegacyNavigationBarBackground)574 public boolean onDrawLegacyNavigationBarBackgroundChanged( 575 boolean drawLegacyNavigationBarBackground) { 576 if (mDestroyed) { 577 return false; 578 } 579 580 if (drawLegacyNavigationBarBackground != mDrawLegacyNavigationBarBackground) { 581 mDrawLegacyNavigationBarBackground = drawLegacyNavigationBarBackground; 582 if (mNavigationBarFrame != null) { 583 if (mDrawLegacyNavigationBarBackground) { 584 mNavigationBarFrame.setBackgroundColor(Color.BLACK); 585 } else { 586 mNavigationBarFrame.setBackground(null); 587 } 588 scheduleRelayout(); 589 } 590 onSystemBarAppearanceChanged(mAppearance); 591 } 592 return drawLegacyNavigationBarBackground; 593 } 594 595 /** 596 * Returns the height of the IME caption bar if this should be shown, or {@code 0} instead. 597 */ getImeCaptionBarHeight()598 private int getImeCaptionBarHeight() { 599 return mImeDrawsImeNavBar 600 ? mService.getResources().getDimensionPixelSize( 601 com.android.internal.R.dimen.navigation_bar_frame_height) 602 : 0; 603 } 604 605 @Override isShown()606 public boolean isShown() { 607 return mNavigationBarFrame != null 608 && mNavigationBarFrame.getVisibility() == View.VISIBLE; 609 } 610 611 @Override toDebugString()612 public String toDebugString() { 613 return "{mImeDrawsImeNavBar=" + mImeDrawsImeNavBar 614 + " mNavigationBarFrame=" + mNavigationBarFrame 615 + " mShouldShowImeSwitcherWhenImeIsShown=" 616 + mShouldShowImeSwitcherWhenImeIsShown 617 + " mAppearance=0x" + Integer.toHexString(mAppearance) 618 + " mDarkIntensity=" + mDarkIntensity 619 + " mDrawLegacyNavigationBarBackground=" + mDrawLegacyNavigationBarBackground 620 + "}"; 621 } 622 } 623 } 624