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