1 /* 2 * Copyright (C) 2021 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 package com.android.launcher3.taskbar; 17 18 import static android.view.View.AccessibilityDelegate; 19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 20 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; 21 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; 22 23 import static com.android.launcher3.LauncherAnimUtils.ROTATION_DRAWABLE_PERCENT; 24 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; 25 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; 26 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.SYSUI_SURFACE_PROGRESS_INDEX; 27 import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode; 28 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y; 29 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK; 30 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME; 31 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH; 32 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS; 33 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_KEYGUARD; 34 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_SMALL_SCREEN; 35 import static com.android.launcher3.util.FlagDebugUtils.appendFlag; 36 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; 37 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; 38 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; 39 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED; 40 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; 41 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; 42 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; 43 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; 44 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; 45 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 46 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; 47 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING; 48 49 import android.animation.ArgbEvaluator; 50 import android.animation.ObjectAnimator; 51 import android.annotation.DrawableRes; 52 import android.annotation.IdRes; 53 import android.annotation.LayoutRes; 54 import android.content.pm.ActivityInfo.Config; 55 import android.content.res.ColorStateList; 56 import android.content.res.Resources; 57 import android.graphics.Color; 58 import android.graphics.Point; 59 import android.graphics.Rect; 60 import android.graphics.Region; 61 import android.graphics.Region.Op; 62 import android.graphics.drawable.AnimatedVectorDrawable; 63 import android.graphics.drawable.PaintDrawable; 64 import android.graphics.drawable.RotateDrawable; 65 import android.inputmethodservice.InputMethodService; 66 import android.os.Handler; 67 import android.util.Property; 68 import android.view.Gravity; 69 import android.view.MotionEvent; 70 import android.view.View; 71 import android.view.View.OnAttachStateChangeListener; 72 import android.view.View.OnClickListener; 73 import android.view.View.OnHoverListener; 74 import android.view.ViewGroup; 75 import android.view.ViewTreeObserver; 76 import android.view.WindowManager; 77 import android.widget.FrameLayout; 78 import android.widget.ImageView; 79 import android.widget.LinearLayout; 80 81 import com.android.launcher3.DeviceProfile; 82 import com.android.launcher3.LauncherAnimUtils; 83 import com.android.launcher3.R; 84 import com.android.launcher3.Utilities; 85 import com.android.launcher3.anim.AlphaUpdateListener; 86 import com.android.launcher3.anim.AnimatedFloat; 87 import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton; 88 import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory; 89 import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter; 90 import com.android.launcher3.util.DimensionUtils; 91 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; 92 import com.android.launcher3.util.MultiValueAlpha; 93 import com.android.launcher3.util.TouchController; 94 import com.android.launcher3.views.BaseDragLayer; 95 import com.android.systemui.shared.rotation.FloatingRotationButton; 96 import com.android.systemui.shared.rotation.RotationButton; 97 import com.android.systemui.shared.rotation.RotationButtonController; 98 import com.android.systemui.shared.system.QuickStepContract; 99 100 import java.io.PrintWriter; 101 import java.util.ArrayList; 102 import java.util.StringJoiner; 103 import java.util.function.IntPredicate; 104 105 /** 106 * Controller for managing nav bar buttons in taskbar 107 */ 108 public class NavbarButtonsViewController implements TaskbarControllers.LoggableTaskbarController { 109 110 private final Rect mTempRect = new Rect(); 111 112 private static final int FLAG_SWITCHER_SHOWING = 1 << 0; 113 private static final int FLAG_IME_VISIBLE = 1 << 1; 114 private static final int FLAG_ROTATION_BUTTON_VISIBLE = 1 << 2; 115 private static final int FLAG_A11Y_VISIBLE = 1 << 3; 116 private static final int FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE = 1 << 4; 117 private static final int FLAG_KEYGUARD_VISIBLE = 1 << 5; 118 private static final int FLAG_KEYGUARD_OCCLUDED = 1 << 6; 119 private static final int FLAG_DISABLE_HOME = 1 << 7; 120 private static final int FLAG_DISABLE_RECENTS = 1 << 8; 121 private static final int FLAG_DISABLE_BACK = 1 << 9; 122 private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10; 123 private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 11; 124 private static final int FLAG_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 12; 125 private static final int FLAG_SMALL_SCREEN = 1 << 13; 126 private static final int FLAG_SLIDE_IN_VIEW_VISIBLE = 1 << 14; 127 128 /** 129 * Flags where a UI could be over Taskbar surfaces, so the color override should be disabled. 130 */ 131 private static final int FLAGS_ON_BACKGROUND_COLOR_OVERRIDE_DISABLED = 132 FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_VOICE_INTERACTION_WINDOW_SHOWING; 133 134 private static final String NAV_BUTTONS_SEPARATE_WINDOW_TITLE = "Taskbar Nav Buttons"; 135 136 public static final int ALPHA_INDEX_IMMERSIVE_MODE = 0; 137 public static final int ALPHA_INDEX_KEYGUARD_OR_DISABLE = 1; 138 public static final int ALPHA_INDEX_SUW = 2; 139 private static final int NUM_ALPHA_CHANNELS = 3; 140 141 private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>(); 142 private final ArrayList<ImageView> mAllButtons = new ArrayList<>(); 143 private int mState; 144 145 private final TaskbarActivityContext mContext; 146 private final FrameLayout mNavButtonsView; 147 private final LinearLayout mNavButtonContainer; 148 // Used for IME+A11Y buttons 149 private final ViewGroup mEndContextualContainer; 150 private final ViewGroup mStartContextualContainer; 151 private final int mLightIconColor; 152 private final int mDarkIconColor; 153 /** Color to use for navigation bar buttons, if they are on on a Taskbar surface background. */ 154 private final int mOnBackgroundIconColor; 155 156 private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat( 157 this::updateNavButtonTranslationY); 158 private final AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay = new AnimatedFloat( 159 this::updateNavButtonTranslationY); 160 private final AnimatedFloat mTaskbarNavButtonTranslationYForIme = new AnimatedFloat( 161 this::updateNavButtonTranslationY); 162 private float mLastSetNavButtonTranslationY; 163 // Used for System UI state updates that should translate the nav button for in-app display. 164 private final AnimatedFloat mNavButtonInAppDisplayProgressForSysui = new AnimatedFloat( 165 this::updateNavButtonInAppDisplayProgressForSysui); 166 /** Expected nav button dark intensity communicated via the framework. */ 167 private final AnimatedFloat mTaskbarNavButtonDarkIntensity = new AnimatedFloat( 168 this::updateNavButtonColor); 169 /** {@code 1} if the Taskbar background color is fully opaque. */ 170 private final AnimatedFloat mOnTaskbarBackgroundNavButtonColorOverride = new AnimatedFloat( 171 this::updateNavButtonColor); 172 /** {@code 1} if a Taskbar slide in overlay is visible over Taskbar. */ 173 private final AnimatedFloat mSlideInViewVisibleNavButtonColorOverride = new AnimatedFloat( 174 this::updateNavButtonColor); 175 /** Disables the {@link #mOnBackgroundIconColor} override if {@code 0}. */ 176 private final AnimatedFloat mOnBackgroundNavButtonColorOverrideMultiplier = new AnimatedFloat( 177 this::updateNavButtonColor); 178 private final RotationButtonListener mRotationButtonListener = new RotationButtonListener(); 179 180 private final Rect mFloatingRotationButtonBounds = new Rect(); 181 182 // Initialized in init. 183 private TaskbarControllers mControllers; 184 private boolean mIsImeRenderingNavButtons; 185 private ImageView mA11yButton; 186 private int mSysuiStateFlags; 187 private ImageView mBackButton; 188 private ImageView mHomeButton; 189 private MultiValueAlpha mBackButtonAlpha; 190 private MultiValueAlpha mHomeButtonAlpha; 191 private FloatingRotationButton mFloatingRotationButton; 192 193 // Variables for moving nav buttons to a separate window above IME 194 private boolean mAreNavButtonsInSeparateWindow = false; 195 private BaseDragLayer<TaskbarActivityContext> mSeparateWindowParent; // Initialized in init. 196 private final ViewTreeObserver.OnComputeInternalInsetsListener mSeparateWindowInsetsComputer = 197 this::onComputeInsetsForSeparateWindow; 198 private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender(); 199 private ImageView mRecentsButton; 200 NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView)201 public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) { 202 mContext = context; 203 mNavButtonsView = navButtonsView; 204 mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons); 205 mEndContextualContainer = mNavButtonsView.findViewById(R.id.end_contextual_buttons); 206 mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons); 207 208 mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color); 209 mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color); 210 mOnBackgroundIconColor = Utilities.isDarkTheme(context) ? mLightIconColor : mDarkIconColor; 211 } 212 213 /** 214 * Initializes the controller 215 */ init(TaskbarControllers controllers)216 public void init(TaskbarControllers controllers) { 217 mControllers = controllers; 218 boolean isThreeButtonNav = mContext.isThreeButtonNav(); 219 DeviceProfile deviceProfile = mContext.getDeviceProfile(); 220 Resources resources = mContext.getResources(); 221 Point p = !mContext.isUserSetupComplete() 222 ? new Point(0, controllers.taskbarActivityContext.getSetupWindowHeight()) 223 : DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources, 224 TaskbarManager.isPhoneMode(deviceProfile)); 225 mNavButtonsView.getLayoutParams().height = p.y; 226 227 mIsImeRenderingNavButtons = 228 InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar(); 229 if (!mIsImeRenderingNavButtons) { 230 // IME switcher 231 View imeSwitcherButton = addButton(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH, 232 isThreeButtonNav ? mStartContextualContainer : mEndContextualContainer, 233 mControllers.navButtonController, R.id.ime_switcher); 234 mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton, 235 flags -> ((flags & FLAG_SWITCHER_SHOWING) != 0) 236 && ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0))); 237 } 238 239 mPropertyHolders.add(new StatePropertyHolder( 240 mControllers.taskbarViewController.getTaskbarIconAlpha() 241 .get(ALPHA_INDEX_KEYGUARD), 242 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 243 && (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0)); 244 245 mPropertyHolders.add(new StatePropertyHolder( 246 mControllers.taskbarViewController.getTaskbarIconAlpha() 247 .get(ALPHA_INDEX_SMALL_SCREEN), 248 flags -> (flags & FLAG_SMALL_SCREEN) == 0)); 249 250 mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController 251 .getKeyguardBgTaskbar(), flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0)); 252 253 // Force nav buttons (specifically back button) to be visible during setup wizard. 254 boolean isInSetup = !mContext.isUserSetupComplete(); 255 boolean isInKidsMode = mContext.isNavBarKidsModeActive(); 256 boolean alwaysShowButtons = isThreeButtonNav || isInSetup; 257 258 // Make sure to remove nav bar buttons translation when any of the following occur: 259 // - Notification shade is expanded 260 // - IME is showing (add separate translation for IME) 261 // - VoiceInteractionWindow (assistant) is showing 262 int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE 263 | FLAG_VOICE_INTERACTION_WINDOW_SHOWING; 264 mPropertyHolders.add(new StatePropertyHolder(mNavButtonInAppDisplayProgressForSysui, 265 flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE, 266 1, 0)); 267 // Center nav buttons in new height for IME. 268 float transForIme = (mContext.getDeviceProfile().taskbarHeight 269 - mControllers.taskbarInsetsController.getTaskbarHeightForIme()) / 2f; 270 // For gesture nav, nav buttons only show for IME anyway so keep them translated down. 271 float defaultButtonTransY = alwaysShowButtons ? 0 : transForIme; 272 mPropertyHolders.add(new StatePropertyHolder(mTaskbarNavButtonTranslationYForIme, 273 flags -> (flags & FLAG_IME_VISIBLE) != 0 && !isInKidsMode, AnimatedFloat.VALUE, 274 transForIme, defaultButtonTransY)); 275 276 // Start at 1 because relevant flags are unset at init. 277 mOnBackgroundNavButtonColorOverrideMultiplier.value = 1; 278 mPropertyHolders.add(new StatePropertyHolder( 279 mOnBackgroundNavButtonColorOverrideMultiplier, 280 flags -> (flags & FLAGS_ON_BACKGROUND_COLOR_OVERRIDE_DISABLED) == 0)); 281 282 mPropertyHolders.add(new StatePropertyHolder( 283 mSlideInViewVisibleNavButtonColorOverride, 284 flags -> (flags & FLAG_SLIDE_IN_VIEW_VISIBLE) != 0)); 285 286 if (alwaysShowButtons) { 287 initButtons(mNavButtonContainer, mEndContextualContainer, 288 mControllers.navButtonController); 289 updateButtonLayoutSpacing(); 290 updateStateForFlag(FLAG_SMALL_SCREEN, isPhoneButtonNavMode(mContext)); 291 292 mPropertyHolders.add(new StatePropertyHolder( 293 mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(), 294 flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0)); 295 296 // Rotation button 297 RotationButton rotationButton = new RotationButtonImpl( 298 addButton(mEndContextualContainer, R.id.rotate_suggestion, 299 R.layout.taskbar_contextual_button)); 300 rotationButton.hide(); 301 mControllers.rotationButtonController.setRotationButton(rotationButton, null); 302 } else { 303 mFloatingRotationButton = new FloatingRotationButton(mContext, 304 R.string.accessibility_rotate_button, 305 R.layout.rotate_suggestion, 306 R.id.rotate_suggestion, 307 R.dimen.floating_rotation_button_min_margin, 308 R.dimen.rounded_corner_content_padding, 309 R.dimen.floating_rotation_button_taskbar_left_margin, 310 R.dimen.floating_rotation_button_taskbar_bottom_margin, 311 R.dimen.floating_rotation_button_diameter, 312 R.dimen.key_button_ripple_max_width, 313 R.bool.floating_rotation_button_position_left); 314 mControllers.rotationButtonController.setRotationButton(mFloatingRotationButton, 315 mRotationButtonListener); 316 317 if (!mIsImeRenderingNavButtons) { 318 View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK, 319 mStartContextualContainer, mControllers.navButtonController, R.id.back); 320 imeDownButton.setRotation(Utilities.isRtl(resources) ? 90 : -90); 321 // Only show when IME is visible. 322 mPropertyHolders.add(new StatePropertyHolder(imeDownButton, 323 flags -> (flags & FLAG_IME_VISIBLE) != 0)); 324 } 325 } 326 327 applyState(); 328 mPropertyHolders.forEach(StatePropertyHolder::endAnimation); 329 330 // Initialize things needed to move nav buttons to separate window. 331 mSeparateWindowParent = new BaseDragLayer<TaskbarActivityContext>(mContext, null, 0) { 332 @Override 333 public void recreateControllers() { 334 mControllers = new TouchController[0]; 335 } 336 337 @Override 338 protected boolean canFindActiveController() { 339 // We don't have any controllers, but we don't want any floating views such as 340 // folder to intercept, either. This ensures nav buttons can always be pressed. 341 return false; 342 } 343 }; 344 mSeparateWindowParent.recreateControllers(); 345 } 346 initButtons(ViewGroup navContainer, ViewGroup endContainer, TaskbarNavButtonController navButtonController)347 private void initButtons(ViewGroup navContainer, ViewGroup endContainer, 348 TaskbarNavButtonController navButtonController) { 349 350 mBackButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK, 351 mNavButtonContainer, mControllers.navButtonController, R.id.back); 352 mBackButtonAlpha = new MultiValueAlpha(mBackButton, NUM_ALPHA_CHANNELS); 353 mBackButtonAlpha.setUpdateVisibility(true); 354 mPropertyHolders.add(new StatePropertyHolder( 355 mBackButtonAlpha.get(ALPHA_INDEX_KEYGUARD_OR_DISABLE), 356 flags -> { 357 // Show only if not disabled, and if not on the keyguard or otherwise only when 358 // the bouncer or a lockscreen app is showing above the keyguard 359 boolean showingOnKeyguard = (flags & FLAG_KEYGUARD_VISIBLE) == 0 || 360 (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0 || 361 (flags & FLAG_KEYGUARD_OCCLUDED) != 0; 362 return (flags & FLAG_DISABLE_BACK) == 0 363 && ((flags & FLAG_KEYGUARD_VISIBLE) == 0 || showingOnKeyguard); 364 })); 365 mPropertyHolders.add(new StatePropertyHolder(mBackButton, 366 flags -> (flags & FLAG_IME_VISIBLE) != 0, 367 ROTATION_DRAWABLE_PERCENT, 1f, 0f)); 368 // Translate back button to be at end/start of other buttons for keyguard 369 int navButtonSize = mContext.getResources().getDimensionPixelSize( 370 R.dimen.taskbar_nav_buttons_size); 371 boolean isRtl = Utilities.isRtl(mContext.getResources()); 372 mPropertyHolders.add(new StatePropertyHolder( 373 mBackButton, flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0 374 || (flags & FLAG_KEYGUARD_VISIBLE) != 0, 375 VIEW_TRANSLATE_X, navButtonSize * (isRtl ? -2 : 2), 0)); 376 377 // home button 378 mHomeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer, 379 navButtonController, R.id.home); 380 mHomeButtonAlpha = new MultiValueAlpha(mHomeButton, NUM_ALPHA_CHANNELS); 381 mHomeButtonAlpha.setUpdateVisibility(true); 382 mPropertyHolders.add( 383 new StatePropertyHolder(mHomeButtonAlpha.get( 384 ALPHA_INDEX_KEYGUARD_OR_DISABLE), 385 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && 386 (flags & FLAG_DISABLE_HOME) == 0)); 387 388 // Recents button 389 mRecentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS, 390 navContainer, navButtonController, R.id.recent_apps); 391 mHitboxExtender.init(mRecentsButton, mNavButtonsView, mContext.getDeviceProfile(), 392 () -> { 393 float[] recentsCoords = new float[2]; 394 getDescendantCoordRelativeToAncestor(mRecentsButton, mNavButtonsView, 395 recentsCoords, false); 396 return recentsCoords; 397 }, new Handler()); 398 mRecentsButton.setOnClickListener(v -> { 399 navButtonController.onButtonClick(BUTTON_RECENTS, v); 400 mHitboxExtender.onRecentsButtonClicked(); 401 }); 402 mPropertyHolders.add(new StatePropertyHolder(mRecentsButton, 403 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && (flags & FLAG_DISABLE_RECENTS) == 0 404 && !mContext.isNavBarKidsModeActive())); 405 406 // A11y button 407 mA11yButton = addButton(R.drawable.ic_sysbar_accessibility_button, BUTTON_A11Y, 408 endContainer, navButtonController, R.id.accessibility_button, 409 R.layout.taskbar_contextual_button); 410 mPropertyHolders.add(new StatePropertyHolder(mA11yButton, 411 flags -> (flags & FLAG_A11Y_VISIBLE) != 0 412 && (flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)); 413 } 414 parseSystemUiFlags(int sysUiStateFlags)415 private void parseSystemUiFlags(int sysUiStateFlags) { 416 mSysuiStateFlags = sysUiStateFlags; 417 boolean isImeVisible = (sysUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0; 418 boolean isImeSwitcherShowing = (sysUiStateFlags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0; 419 boolean a11yVisible = (sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 420 boolean isHomeDisabled = (sysUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0; 421 boolean isRecentsDisabled = (sysUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0; 422 boolean isBackDisabled = (sysUiStateFlags & SYSUI_STATE_BACK_DISABLED) != 0; 423 int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED 424 | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 425 boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0; 426 boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0; 427 boolean isVoiceInteractionWindowShowing = 428 (sysUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0; 429 430 // TODO(b/202218289) we're getting IME as not visible on lockscreen from system 431 updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible); 432 updateStateForFlag(FLAG_SWITCHER_SHOWING, isImeSwitcherShowing); 433 updateStateForFlag(FLAG_A11Y_VISIBLE, a11yVisible); 434 updateStateForFlag(FLAG_DISABLE_HOME, isHomeDisabled); 435 updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled); 436 updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled); 437 updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded); 438 updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive); 439 updateStateForFlag(FLAG_VOICE_INTERACTION_WINDOW_SHOWING, isVoiceInteractionWindowShowing); 440 441 if (mA11yButton != null) { 442 // Only used in 3 button 443 boolean a11yLongClickable = 444 (sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 445 mA11yButton.setLongClickable(a11yLongClickable); 446 updateButtonLayoutSpacing(); 447 } 448 } 449 updateStateForSysuiFlags(int systemUiStateFlags, boolean skipAnim)450 public void updateStateForSysuiFlags(int systemUiStateFlags, boolean skipAnim) { 451 if (systemUiStateFlags == mSysuiStateFlags) { 452 return; 453 } 454 parseSystemUiFlags(systemUiStateFlags); 455 applyState(); 456 if (skipAnim) { 457 mPropertyHolders.forEach(StatePropertyHolder::endAnimation); 458 } 459 } 460 461 /** 462 * @return {@code true} if A11y is showing in 3 button nav taskbar 463 */ isContextualButtonShowing()464 private boolean isContextualButtonShowing() { 465 return mContext.isThreeButtonNav() && (mState & FLAG_A11Y_VISIBLE) != 0; 466 } 467 468 /** 469 * Should be called when we need to show back button for bouncer 470 */ setBackForBouncer(boolean isBouncerVisible)471 public void setBackForBouncer(boolean isBouncerVisible) { 472 updateStateForFlag(FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE, isBouncerVisible); 473 applyState(); 474 } 475 476 /** 477 * Slightly misnamed, but should be called when keyguard OR AOD is showing. 478 * We consider keyguardVisible when it's showing bouncer OR is occlucded by another app 479 */ setKeyguardVisible(boolean isKeyguardVisible, boolean isKeyguardOccluded)480 public void setKeyguardVisible(boolean isKeyguardVisible, boolean isKeyguardOccluded) { 481 updateStateForFlag(FLAG_KEYGUARD_VISIBLE, isKeyguardVisible || isKeyguardOccluded); 482 updateStateForFlag(FLAG_KEYGUARD_OCCLUDED, isKeyguardOccluded); 483 applyState(); 484 } 485 486 /** {@code true} if a slide in view is currently visible over taskbar. */ setSlideInViewVisible(boolean isSlideInViewVisible)487 public void setSlideInViewVisible(boolean isSlideInViewVisible) { 488 updateStateForFlag(FLAG_SLIDE_IN_VIEW_VISIBLE, isSlideInViewVisible); 489 applyState(); 490 } 491 492 /** 493 * Returns true if IME bar is visible 494 */ isImeVisible()495 public boolean isImeVisible() { 496 return (mState & FLAG_IME_VISIBLE) != 0; 497 } 498 499 /** 500 * Returns true if the home button is disabled 501 */ isHomeDisabled()502 public boolean isHomeDisabled() { 503 return (mState & FLAG_DISABLE_HOME) != 0; 504 } 505 506 /** 507 * Returns true if the recents (overview) button is disabled 508 */ isRecentsDisabled()509 public boolean isRecentsDisabled() { 510 return (mState & FLAG_DISABLE_RECENTS) != 0; 511 } 512 513 /** 514 * Adds the bounds corresponding to all visible buttons to provided region 515 */ addVisibleButtonsRegion(BaseDragLayer<?> parent, Region outRegion)516 public void addVisibleButtonsRegion(BaseDragLayer<?> parent, Region outRegion) { 517 int count = mAllButtons.size(); 518 for (int i = 0; i < count; i++) { 519 View button = mAllButtons.get(i); 520 if (button.getVisibility() == View.VISIBLE) { 521 parent.getDescendantRectRelativeToSelf(button, mTempRect); 522 if (mHitboxExtender.extendedHitboxEnabled()) { 523 mTempRect.bottom += mContext.getDeviceProfile().getTaskbarOffsetY(); 524 } 525 outRegion.op(mTempRect, Op.UNION); 526 } 527 } 528 } 529 530 /** 531 * Returns multi-value alpha controller for back button. 532 */ getBackButtonAlpha()533 public MultiValueAlpha getBackButtonAlpha() { 534 return mBackButtonAlpha; 535 } 536 537 /** 538 * Returns multi-value alpha controller for home button. 539 */ getHomeButtonAlpha()540 public MultiValueAlpha getHomeButtonAlpha() { 541 return mHomeButtonAlpha; 542 } 543 544 /** 545 * Sets the AccessibilityDelegate for the home button. 546 */ setHomeButtonAccessibilityDelegate(AccessibilityDelegate accessibilityDelegate)547 public void setHomeButtonAccessibilityDelegate(AccessibilityDelegate accessibilityDelegate) { 548 if (mHomeButton == null) { 549 return; 550 } 551 mHomeButton.setAccessibilityDelegate(accessibilityDelegate); 552 } 553 554 /** 555 * Sets the AccessibilityDelegate for the back button. 556 */ setBackButtonAccessibilityDelegate(AccessibilityDelegate accessibilityDelegate)557 public void setBackButtonAccessibilityDelegate(AccessibilityDelegate accessibilityDelegate) { 558 if (mBackButton == null) { 559 return; 560 } 561 mBackButton.setAccessibilityDelegate(accessibilityDelegate); 562 } 563 564 /** Use to set the translationY for the all nav+contextual buttons */ getTaskbarNavButtonTranslationY()565 public AnimatedFloat getTaskbarNavButtonTranslationY() { 566 return mTaskbarNavButtonTranslationY; 567 } 568 569 /** Use to set the translationY for the all nav+contextual buttons when in Launcher */ getTaskbarNavButtonTranslationYForInAppDisplay()570 public AnimatedFloat getTaskbarNavButtonTranslationYForInAppDisplay() { 571 return mTaskbarNavButtonTranslationYForInAppDisplay; 572 } 573 574 /** Use to set the dark intensity for the all nav+contextual buttons */ getTaskbarNavButtonDarkIntensity()575 public AnimatedFloat getTaskbarNavButtonDarkIntensity() { 576 return mTaskbarNavButtonDarkIntensity; 577 } 578 579 /** Use to override the nav button color with {@link #mOnBackgroundIconColor}. */ getOnTaskbarBackgroundNavButtonColorOverride()580 public AnimatedFloat getOnTaskbarBackgroundNavButtonColorOverride() { 581 return mOnTaskbarBackgroundNavButtonColorOverride; 582 } 583 584 /** 585 * Does not call {@link #applyState()}. Don't forget to! 586 */ updateStateForFlag(int flag, boolean enabled)587 private void updateStateForFlag(int flag, boolean enabled) { 588 if (enabled) { 589 mState |= flag; 590 } else { 591 mState &= ~flag; 592 } 593 } 594 applyState()595 private void applyState() { 596 int count = mPropertyHolders.size(); 597 for (int i = 0; i < count; i++) { 598 mPropertyHolders.get(i).setState(mState); 599 } 600 } 601 updateNavButtonInAppDisplayProgressForSysui()602 private void updateNavButtonInAppDisplayProgressForSysui() { 603 TaskbarUIController uiController = mControllers.uiController; 604 if (uiController instanceof LauncherTaskbarUIController) { 605 ((LauncherTaskbarUIController) uiController).onTaskbarInAppDisplayProgressUpdate( 606 mNavButtonInAppDisplayProgressForSysui.value, SYSUI_SURFACE_PROGRESS_INDEX); 607 } 608 } 609 610 /** 611 * Sets the translationY of the nav buttons based on the current device state. 612 */ updateNavButtonTranslationY()613 public void updateNavButtonTranslationY() { 614 if (isPhoneButtonNavMode(mContext)) { 615 return; 616 } 617 final float normalTranslationY = mTaskbarNavButtonTranslationY.value; 618 final float imeAdjustmentTranslationY = mTaskbarNavButtonTranslationYForIme.value; 619 TaskbarUIController uiController = mControllers.uiController; 620 final float inAppDisplayAdjustmentTranslationY = 621 (uiController instanceof LauncherTaskbarUIController 622 && ((LauncherTaskbarUIController) uiController).shouldUseInAppLayout()) 623 ? mTaskbarNavButtonTranslationYForInAppDisplay.value : 0; 624 625 mLastSetNavButtonTranslationY = normalTranslationY 626 + imeAdjustmentTranslationY 627 + inAppDisplayAdjustmentTranslationY; 628 mNavButtonsView.setTranslationY(mLastSetNavButtonTranslationY); 629 } 630 updateNavButtonColor()631 private void updateNavButtonColor() { 632 final ArgbEvaluator argbEvaluator = ArgbEvaluator.getInstance(); 633 final int sysUiNavButtonIconColor = (int) argbEvaluator.evaluate( 634 mTaskbarNavButtonDarkIntensity.value, 635 mLightIconColor, 636 mDarkIconColor); 637 // Override the color from framework if nav buttons are over an opaque Taskbar surface. 638 final int iconColor = (int) argbEvaluator.evaluate( 639 mOnBackgroundNavButtonColorOverrideMultiplier.value 640 * Math.max( 641 mOnTaskbarBackgroundNavButtonColorOverride.value, 642 mSlideInViewVisibleNavButtonColorOverride.value), 643 sysUiNavButtonIconColor, 644 mOnBackgroundIconColor); 645 for (ImageView button : mAllButtons) { 646 button.setImageTintList(ColorStateList.valueOf(iconColor)); 647 } 648 } 649 addButton(@rawableRes int drawableId, @TaskbarButton int buttonType, ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id)650 protected ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType, 651 ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id) { 652 return addButton(drawableId, buttonType, parent, navButtonController, id, 653 R.layout.taskbar_nav_button); 654 } 655 addButton(@rawableRes int drawableId, @TaskbarButton int buttonType, ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id, @LayoutRes int layoutId)656 private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType, 657 ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id, 658 @LayoutRes int layoutId) { 659 ImageView buttonView = addButton(parent, id, layoutId); 660 buttonView.setImageResource(drawableId); 661 buttonView.setContentDescription(parent.getContext().getString( 662 navButtonController.getButtonContentDescription(buttonType))); 663 buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType, view)); 664 buttonView.setOnLongClickListener(view -> 665 navButtonController.onButtonLongClick(buttonType, view)); 666 return buttonView; 667 } 668 addButton(ViewGroup parent, @IdRes int id, @LayoutRes int layoutId)669 private ImageView addButton(ViewGroup parent, @IdRes int id, @LayoutRes int layoutId) { 670 ImageView buttonView = (ImageView) mContext.getLayoutInflater() 671 .inflate(layoutId, parent, false); 672 buttonView.setId(id); 673 parent.addView(buttonView); 674 mAllButtons.add(buttonView); 675 return buttonView; 676 } 677 isEventOverAnyItem(MotionEvent ev)678 public boolean isEventOverAnyItem(MotionEvent ev) { 679 return mFloatingRotationButtonBounds.contains((int) ev.getX(), (int) ev.getY()); 680 } 681 onConfigurationChanged(@onfig int configChanges)682 public void onConfigurationChanged(@Config int configChanges) { 683 if (mFloatingRotationButton != null) { 684 mFloatingRotationButton.onConfigurationChanged(configChanges); 685 } 686 if (!mContext.isUserSetupComplete()) { 687 handleSetupUi(); 688 } 689 updateButtonLayoutSpacing(); 690 } 691 handleSetupUi()692 private void handleSetupUi() { 693 // Since setup wizard only has back button enabled, it looks strange to be 694 // end-aligned, so start-align instead. 695 FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams) 696 mNavButtonContainer.getLayoutParams(); 697 Resources resources = mContext.getResources(); 698 DeviceProfile deviceProfile = mContext.getDeviceProfile(); 699 int setupMargin = resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin); 700 navButtonsLayoutParams.setMarginStart(setupMargin); 701 navButtonsLayoutParams.bottomMargin = !deviceProfile.isLandscape 702 ? 0 703 : setupMargin - 704 (resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2); 705 navButtonsLayoutParams.setMarginEnd(0); 706 navButtonsLayoutParams.gravity = Gravity.START; 707 mNavButtonsView.getLayoutParams().height = 708 mControllers.taskbarActivityContext.getSetupWindowHeight(); 709 mNavButtonContainer.setLayoutParams(navButtonsLayoutParams); 710 } 711 712 /** 713 * Adds the correct spacing to 3 button nav container depending on if device is in kids mode, 714 * setup wizard, or normal 3 button nav. 715 */ updateButtonLayoutSpacing()716 private void updateButtonLayoutSpacing() { 717 DeviceProfile dp = mContext.getDeviceProfile(); 718 Resources res = mContext.getResources(); 719 boolean isInSetup = !mContext.isUserSetupComplete(); 720 // TODO(b/244231596) we're getting the incorrect kidsMode value in small-screen 721 boolean isInKidsMode = mContext.isNavBarKidsModeActive(); 722 723 if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) { 724 boolean isThreeButtonNav = mContext.isThreeButtonNav(); 725 726 NavButtonLayoutter navButtonLayoutter = 727 NavButtonLayoutFactory.Companion.getUiLayoutter( 728 dp, mNavButtonsView, res, isInKidsMode, isInSetup, isThreeButtonNav, 729 TaskbarManager.isPhoneMode(dp)); 730 navButtonLayoutter.layoutButtons(dp, isContextualButtonShowing()); 731 return; 732 } 733 734 if (isInSetup) { 735 handleSetupUi(); 736 737 // Hide back button in SUW if keyboard is showing (IME draws its own back). 738 mPropertyHolders.add(new StatePropertyHolder( 739 mBackButtonAlpha.get(ALPHA_INDEX_SUW), 740 flags -> (flags & FLAG_IME_VISIBLE) == 0)); 741 } else if (isInKidsMode) { 742 int iconSize = res.getDimensionPixelSize( 743 R.dimen.taskbar_icon_size_kids); 744 int buttonWidth = res.getDimensionPixelSize( 745 R.dimen.taskbar_nav_buttons_width_kids); 746 int buttonHeight = res.getDimensionPixelSize( 747 R.dimen.taskbar_nav_buttons_height_kids); 748 int buttonRadius = res.getDimensionPixelSize( 749 R.dimen.taskbar_nav_buttons_corner_radius_kids); 750 int paddingleft = (buttonWidth - iconSize) / 2; 751 int paddingRight = paddingleft; 752 int paddingTop = (buttonHeight - iconSize) / 2; 753 int paddingBottom = paddingTop; 754 755 // Update icons 756 final RotateDrawable rotateDrawable = new RotateDrawable(); 757 rotateDrawable.setDrawable(mContext.getDrawable(R.drawable.ic_sysbar_back_kids)); 758 rotateDrawable.setFromDegrees(0f); 759 rotateDrawable.setToDegrees(-90f); 760 mBackButton.setImageDrawable(rotateDrawable); 761 mBackButton.setScaleType(ImageView.ScaleType.FIT_CENTER); 762 mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom); 763 764 mHomeButton.setImageDrawable( 765 mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids)); 766 mHomeButton.setScaleType(ImageView.ScaleType.FIT_CENTER); 767 mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom); 768 769 // Home button layout 770 LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams( 771 buttonWidth, 772 buttonHeight 773 ); 774 int homeButtonLeftMargin = res.getDimensionPixelSize( 775 R.dimen.taskbar_home_button_left_margin_kids); 776 homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0); 777 mHomeButton.setLayoutParams(homeLayoutparams); 778 779 // Back button layout 780 LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams( 781 buttonWidth, 782 buttonHeight 783 ); 784 int backButtonLeftMargin = res.getDimensionPixelSize( 785 R.dimen.taskbar_back_button_left_margin_kids); 786 backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0); 787 mBackButton.setLayoutParams(backLayoutParams); 788 789 // Button backgrounds 790 int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1); 791 PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha); 792 buttonBackground.setCornerRadius(buttonRadius); 793 mHomeButton.setBackground(buttonBackground); 794 mBackButton.setBackground(buttonBackground); 795 796 // Update alignment within taskbar 797 FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams) 798 mNavButtonContainer.getLayoutParams(); 799 navButtonsLayoutParams.setMarginStart( 800 navButtonsLayoutParams.getMarginEnd() / 2); 801 navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart()); 802 navButtonsLayoutParams.gravity = Gravity.CENTER; 803 mNavButtonContainer.requestLayout(); 804 805 mHomeButton.setOnLongClickListener(null); 806 } else if (mContext.isThreeButtonNav()) { 807 final RotateDrawable rotateDrawable = new RotateDrawable(); 808 rotateDrawable.setDrawable(mContext.getDrawable(R.drawable.ic_sysbar_back)); 809 rotateDrawable.setFromDegrees(0f); 810 rotateDrawable.setToDegrees(Utilities.isRtl(mContext.getResources()) ? 90f : -90f); 811 mBackButton.setImageDrawable(rotateDrawable); 812 813 // Setup normal 3 button 814 // Add spacing after the end of the last nav button 815 FrameLayout.LayoutParams navButtonParams = 816 (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams(); 817 navButtonParams.gravity = Gravity.END; 818 navButtonParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; 819 navButtonParams.height = MATCH_PARENT; 820 821 int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing); 822 int contextualWidth = mEndContextualContainer.getWidth(); 823 // If contextual buttons are showing, we check if the end margin is enough for the 824 // contextual button to be showing - if not, move the nav buttons over a smidge 825 if (isContextualButtonShowing() && navMarginEnd < contextualWidth) { 826 // Additional spacing, eat up half of space between last icon and nav button 827 navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2; 828 } 829 navButtonParams.setMarginEnd(navMarginEnd); 830 mNavButtonContainer.setLayoutParams(navButtonParams); 831 832 // Add the spaces in between the nav buttons 833 int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween); 834 for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) { 835 View navButton = mNavButtonContainer.getChildAt(i); 836 LinearLayout.LayoutParams buttonLayoutParams = 837 (LinearLayout.LayoutParams) navButton.getLayoutParams(); 838 buttonLayoutParams.weight = 0; 839 if (i == 0) { 840 buttonLayoutParams.setMarginEnd(spaceInBetween / 2); 841 } else if (i == mNavButtonContainer.getChildCount() - 1) { 842 buttonLayoutParams.setMarginStart(spaceInBetween / 2); 843 } else { 844 buttonLayoutParams.setMarginStart(spaceInBetween / 2); 845 buttonLayoutParams.setMarginEnd(spaceInBetween / 2); 846 } 847 } 848 } 849 850 } 851 onDestroy()852 public void onDestroy() { 853 mPropertyHolders.clear(); 854 mControllers.rotationButtonController.unregisterListeners(); 855 if (mFloatingRotationButton != null) { 856 mFloatingRotationButton.hide(); 857 } 858 859 moveNavButtonsBackToTaskbarWindow(); 860 mNavButtonContainer.removeAllViews(); 861 mEndContextualContainer.removeAllViews(); 862 mStartContextualContainer.removeAllViews(); 863 mAllButtons.clear(); 864 } 865 866 /** 867 * Moves mNavButtonsView from TaskbarDragLayer to a placeholder BaseDragLayer on a new window. 868 */ moveNavButtonsToNewWindow()869 public void moveNavButtonsToNewWindow() { 870 if (mAreNavButtonsInSeparateWindow) { 871 return; 872 } 873 874 if (mIsImeRenderingNavButtons) { 875 // IME is rendering the nav buttons, so we don't need to create a new layer for them. 876 return; 877 } 878 879 mSeparateWindowParent.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { 880 @Override 881 public void onViewAttachedToWindow(View view) { 882 mSeparateWindowParent.getViewTreeObserver().addOnComputeInternalInsetsListener( 883 mSeparateWindowInsetsComputer); 884 } 885 886 @Override 887 public void onViewDetachedFromWindow(View view) { 888 mSeparateWindowParent.removeOnAttachStateChangeListener(this); 889 mSeparateWindowParent.getViewTreeObserver().removeOnComputeInternalInsetsListener( 890 mSeparateWindowInsetsComputer); 891 } 892 }); 893 894 mAreNavButtonsInSeparateWindow = true; 895 mContext.getDragLayer().removeView(mNavButtonsView); 896 mSeparateWindowParent.addView(mNavButtonsView); 897 WindowManager.LayoutParams windowLayoutParams = mContext.createDefaultWindowLayoutParams( 898 TYPE_NAVIGATION_BAR_PANEL, NAV_BUTTONS_SEPARATE_WINDOW_TITLE); 899 mContext.addWindowView(mSeparateWindowParent, windowLayoutParams); 900 901 } 902 903 /** 904 * Moves mNavButtonsView from its temporary window and reattaches it to TaskbarDragLayer. 905 */ moveNavButtonsBackToTaskbarWindow()906 public void moveNavButtonsBackToTaskbarWindow() { 907 if (!mAreNavButtonsInSeparateWindow) { 908 return; 909 } 910 911 mAreNavButtonsInSeparateWindow = false; 912 mContext.removeWindowView(mSeparateWindowParent); 913 mSeparateWindowParent.removeView(mNavButtonsView); 914 mContext.getDragLayer().addView(mNavButtonsView); 915 } 916 onComputeInsetsForSeparateWindow(ViewTreeObserver.InternalInsetsInfo insetsInfo)917 private void onComputeInsetsForSeparateWindow(ViewTreeObserver.InternalInsetsInfo insetsInfo) { 918 addVisibleButtonsRegion(mSeparateWindowParent, insetsInfo.touchableRegion); 919 insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); 920 } 921 922 @Override dumpLogs(String prefix, PrintWriter pw)923 public void dumpLogs(String prefix, PrintWriter pw) { 924 pw.println(prefix + "NavbarButtonsViewController:"); 925 926 pw.println(prefix + "\tmState=" + getStateString(mState)); 927 pw.println(prefix + "\tmLightIconColor=" + Integer.toHexString(mLightIconColor)); 928 pw.println(prefix + "\tmDarkIconColor=" + Integer.toHexString(mDarkIconColor)); 929 pw.println(prefix + "\tmFloatingRotationButtonBounds=" + mFloatingRotationButtonBounds); 930 pw.println(prefix + "\tmSysuiStateFlags=" + QuickStepContract.getSystemUiStateString( 931 mSysuiStateFlags)); 932 pw.println(prefix + "\tLast set nav button translationY=" + mLastSetNavButtonTranslationY); 933 pw.println(prefix + "\t\tmTaskbarNavButtonTranslationY=" 934 + mTaskbarNavButtonTranslationY.value); 935 pw.println(prefix + "\t\tmTaskbarNavButtonTranslationYForInAppDisplay=" 936 + mTaskbarNavButtonTranslationYForInAppDisplay.value); 937 pw.println(prefix + "\t\tmTaskbarNavButtonTranslationYForIme=" 938 + mTaskbarNavButtonTranslationYForIme.value); 939 } 940 getStateString(int flags)941 private static String getStateString(int flags) { 942 StringJoiner str = new StringJoiner("|"); 943 appendFlag(str, flags, FLAG_SWITCHER_SHOWING, "FLAG_SWITCHER_SHOWING"); 944 appendFlag(str, flags, FLAG_IME_VISIBLE, "FLAG_IME_VISIBLE"); 945 appendFlag(str, flags, FLAG_ROTATION_BUTTON_VISIBLE, "FLAG_ROTATION_BUTTON_VISIBLE"); 946 appendFlag(str, flags, FLAG_A11Y_VISIBLE, "FLAG_A11Y_VISIBLE"); 947 appendFlag(str, flags, FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE, 948 "FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE"); 949 appendFlag(str, flags, FLAG_KEYGUARD_VISIBLE, "FLAG_KEYGUARD_VISIBLE"); 950 appendFlag(str, flags, FLAG_KEYGUARD_OCCLUDED, "FLAG_KEYGUARD_OCCLUDED"); 951 appendFlag(str, flags, FLAG_DISABLE_HOME, "FLAG_DISABLE_HOME"); 952 appendFlag(str, flags, FLAG_DISABLE_RECENTS, "FLAG_DISABLE_RECENTS"); 953 appendFlag(str, flags, FLAG_DISABLE_BACK, "FLAG_DISABLE_BACK"); 954 appendFlag(str, flags, FLAG_NOTIFICATION_SHADE_EXPANDED, 955 "FLAG_NOTIFICATION_SHADE_EXPANDED"); 956 appendFlag(str, flags, FLAG_SCREEN_PINNING_ACTIVE, "FLAG_SCREEN_PINNING_ACTIVE"); 957 appendFlag(str, flags, FLAG_VOICE_INTERACTION_WINDOW_SHOWING, 958 "FLAG_VOICE_INTERACTION_WINDOW_SHOWING"); 959 return str.toString(); 960 } 961 getTouchController()962 public TouchController getTouchController() { 963 return mHitboxExtender; 964 } 965 966 /** 967 * @param alignment 0 -> Taskbar, 1 -> Workspace 968 */ updateTaskbarAlignment(float alignment)969 public void updateTaskbarAlignment(float alignment) { 970 mHitboxExtender.onAnimationProgressToOverview(alignment); 971 } 972 973 private class RotationButtonListener implements RotationButton.RotationButtonUpdatesCallback { 974 @Override onVisibilityChanged(boolean isVisible)975 public void onVisibilityChanged(boolean isVisible) { 976 if (isVisible) { 977 mFloatingRotationButton.getCurrentView() 978 .getBoundsOnScreen(mFloatingRotationButtonBounds); 979 } else { 980 mFloatingRotationButtonBounds.setEmpty(); 981 } 982 } 983 } 984 985 private class RotationButtonImpl implements RotationButton { 986 987 private final ImageView mButton; 988 private AnimatedVectorDrawable mImageDrawable; 989 RotationButtonImpl(ImageView button)990 RotationButtonImpl(ImageView button) { 991 mButton = button; 992 } 993 994 @Override setRotationButtonController(RotationButtonController rotationButtonController)995 public void setRotationButtonController(RotationButtonController rotationButtonController) { 996 // TODO(b/187754252) UI polish, different icons based on light/dark context, etc 997 mImageDrawable = (AnimatedVectorDrawable) mButton.getContext() 998 .getDrawable(rotationButtonController.getIconResId()); 999 mButton.setImageDrawable(mImageDrawable); 1000 mButton.setContentDescription(mButton.getResources() 1001 .getString(R.string.accessibility_rotate_button)); 1002 mImageDrawable.setCallback(mButton); 1003 } 1004 1005 @Override getCurrentView()1006 public View getCurrentView() { 1007 return mButton; 1008 } 1009 1010 @Override show()1011 public boolean show() { 1012 mButton.setVisibility(View.VISIBLE); 1013 mState |= FLAG_ROTATION_BUTTON_VISIBLE; 1014 applyState(); 1015 return true; 1016 } 1017 1018 @Override hide()1019 public boolean hide() { 1020 mButton.setVisibility(View.GONE); 1021 mState &= ~FLAG_ROTATION_BUTTON_VISIBLE; 1022 applyState(); 1023 return true; 1024 } 1025 1026 @Override isVisible()1027 public boolean isVisible() { 1028 return mButton.getVisibility() == View.VISIBLE; 1029 } 1030 1031 @Override updateIcon(int lightIconColor, int darkIconColor)1032 public void updateIcon(int lightIconColor, int darkIconColor) { 1033 // TODO(b/187754252): UI Polish 1034 } 1035 1036 @Override setOnClickListener(OnClickListener onClickListener)1037 public void setOnClickListener(OnClickListener onClickListener) { 1038 mButton.setOnClickListener(onClickListener); 1039 } 1040 1041 @Override setOnHoverListener(OnHoverListener onHoverListener)1042 public void setOnHoverListener(OnHoverListener onHoverListener) { 1043 mButton.setOnHoverListener(onHoverListener); 1044 } 1045 1046 @Override getImageDrawable()1047 public AnimatedVectorDrawable getImageDrawable() { 1048 return mImageDrawable; 1049 } 1050 1051 @Override setDarkIntensity(float darkIntensity)1052 public void setDarkIntensity(float darkIntensity) { 1053 // TODO(b/187754252) UI polish 1054 } 1055 1056 @Override acceptRotationProposal()1057 public boolean acceptRotationProposal() { 1058 return mButton.isAttachedToWindow(); 1059 } 1060 } 1061 1062 private static class StatePropertyHolder { 1063 1064 private final float mEnabledValue, mDisabledValue; 1065 private final ObjectAnimator mAnimator; 1066 private final IntPredicate mEnableCondition; 1067 1068 private boolean mIsEnabled = true; 1069 StatePropertyHolder(View view, IntPredicate enableCondition)1070 StatePropertyHolder(View view, IntPredicate enableCondition) { 1071 this(view, enableCondition, LauncherAnimUtils.VIEW_ALPHA, 1, 0); 1072 mAnimator.addListener(new AlphaUpdateListener(view)); 1073 } 1074 StatePropertyHolder(MultiProperty alphaProperty, IntPredicate enableCondition)1075 StatePropertyHolder(MultiProperty alphaProperty, 1076 IntPredicate enableCondition) { 1077 this(alphaProperty, enableCondition, MULTI_PROPERTY_VALUE, 1, 0); 1078 } 1079 StatePropertyHolder(AnimatedFloat animatedFloat, IntPredicate enableCondition)1080 StatePropertyHolder(AnimatedFloat animatedFloat, IntPredicate enableCondition) { 1081 this(animatedFloat, enableCondition, AnimatedFloat.VALUE, 1, 0); 1082 } 1083 StatePropertyHolder(T target, IntPredicate enabledCondition, Property<T, Float> property, float enabledValue, float disabledValue)1084 <T> StatePropertyHolder(T target, IntPredicate enabledCondition, 1085 Property<T, Float> property, float enabledValue, float disabledValue) { 1086 mEnableCondition = enabledCondition; 1087 mEnabledValue = enabledValue; 1088 mDisabledValue = disabledValue; 1089 mAnimator = ObjectAnimator.ofFloat(target, property, enabledValue, disabledValue); 1090 } 1091 setState(int flags)1092 public void setState(int flags) { 1093 boolean isEnabled = mEnableCondition.test(flags); 1094 if (mIsEnabled != isEnabled) { 1095 mIsEnabled = isEnabled; 1096 mAnimator.cancel(); 1097 mAnimator.setFloatValues(mIsEnabled ? mEnabledValue : mDisabledValue); 1098 mAnimator.start(); 1099 } 1100 } 1101 endAnimation()1102 public void endAnimation() { 1103 if (mAnimator.isRunning()) { 1104 mAnimator.end(); 1105 } 1106 } 1107 } 1108 } 1109