- SysUI removes SYSUI_STATE_IME_SHOWING when starting a gesture from an app, but because unstashing has implications on the gesture transition (e.g. clips the bottom of the app), we defer handling the ime hiding until the gesture settles. Repurposed the flow that swaps the taskbar background during the gesture to support this case as well. - Delay the unstash when IME is closing, to align with the end of the IME exit transition - Remove TaskbarViewController.ALPHA_INDEX_IME now that we stash when IME is opening, since stashing already hides the taskbar icons - Also support passing a starting progress to the stashed handle reveal animation, to allow it to be reversed when cancelled. For example, when returning to an app that has IME showing, we first start unstashing because we're in an app, but then we get the signal that IME is attached so we stash again almost immediately (within a frame or two). Test: In both 3 button and fully gestural, open a keyboard in an app, ensure taskbar gets out of the way and then reappears at the end when the keyboard is dismissed Bug: 202511986 Change-Id: I93c298a98ba369ea6310466ff3f802231c582687
583 lines
25 KiB
Java
583 lines
25 KiB
Java
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.android.launcher3.taskbar;
|
|
|
|
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
|
|
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
|
|
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
|
|
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
|
|
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
|
|
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
|
|
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_KEYGUARD;
|
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
|
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
|
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
|
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
|
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
|
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
|
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
|
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
|
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
|
|
|
|
import android.animation.ArgbEvaluator;
|
|
import android.animation.ObjectAnimator;
|
|
import android.annotation.DrawableRes;
|
|
import android.annotation.IdRes;
|
|
import android.annotation.LayoutRes;
|
|
import android.content.res.ColorStateList;
|
|
import android.graphics.Rect;
|
|
import android.graphics.Region;
|
|
import android.graphics.Region.Op;
|
|
import android.graphics.drawable.AnimatedVectorDrawable;
|
|
import android.util.Property;
|
|
import android.view.Gravity;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.View.OnClickListener;
|
|
import android.view.View.OnHoverListener;
|
|
import android.view.ViewGroup;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.ImageView;
|
|
|
|
import com.android.launcher3.LauncherAnimUtils;
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.Utilities;
|
|
import com.android.launcher3.anim.AlphaUpdateListener;
|
|
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
|
|
import com.android.launcher3.util.MultiValueAlpha;
|
|
import com.android.launcher3.util.Themes;
|
|
import com.android.quickstep.AnimatedFloat;
|
|
import com.android.systemui.shared.rotation.FloatingRotationButton;
|
|
import com.android.systemui.shared.rotation.RotationButton;
|
|
import com.android.systemui.shared.rotation.RotationButtonController;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.function.IntPredicate;
|
|
|
|
/**
|
|
* Controller for managing nav bar buttons in taskbar
|
|
*/
|
|
public class NavbarButtonsViewController {
|
|
|
|
private final Rect mTempRect = new Rect();
|
|
|
|
private static final int FLAG_SWITCHER_SUPPORTED = 1 << 0;
|
|
private static final int FLAG_IME_VISIBLE = 1 << 1;
|
|
private static final int FLAG_ROTATION_BUTTON_VISIBLE = 1 << 2;
|
|
private static final int FLAG_A11Y_VISIBLE = 1 << 3;
|
|
private static final int FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE = 1 << 4;
|
|
private static final int FLAG_KEYGUARD_VISIBLE = 1 << 5;
|
|
private static final int FLAG_KEYGUARD_OCCLUDED = 1 << 6;
|
|
private static final int FLAG_DISABLE_HOME = 1 << 7;
|
|
private static final int FLAG_DISABLE_RECENTS = 1 << 8;
|
|
private static final int FLAG_DISABLE_BACK = 1 << 9;
|
|
private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10;
|
|
|
|
private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
|
|
|
|
private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>();
|
|
private final ArrayList<ImageView> mAllButtons = new ArrayList<>();
|
|
private int mState;
|
|
|
|
private final TaskbarActivityContext mContext;
|
|
private final FrameLayout mNavButtonsView;
|
|
private final ViewGroup mNavButtonContainer;
|
|
// Used for IME+A11Y buttons
|
|
private final ViewGroup mEndContextualContainer;
|
|
private final ViewGroup mStartContextualContainer;
|
|
private final int mLightIconColor;
|
|
private final int mDarkIconColor;
|
|
|
|
private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
|
|
this::updateNavButtonTranslationY);
|
|
private final AnimatedFloat mNavButtonTranslationYMultiplier = new AnimatedFloat(
|
|
this::updateNavButtonTranslationY);
|
|
private final AnimatedFloat mTaskbarNavButtonDarkIntensity = new AnimatedFloat(
|
|
this::updateNavButtonDarkIntensity);
|
|
private final AnimatedFloat mNavButtonDarkIntensityMultiplier = new AnimatedFloat(
|
|
this::updateNavButtonDarkIntensity);
|
|
private final RotationButtonListener mRotationButtonListener = new RotationButtonListener();
|
|
|
|
private final Rect mFloatingRotationButtonBounds = new Rect();
|
|
|
|
// Initialized in init.
|
|
private TaskbarControllers mControllers;
|
|
private View mA11yButton;
|
|
private int mSysuiStateFlags;
|
|
private View mBackButton;
|
|
private FloatingRotationButton mFloatingRotationButton;
|
|
|
|
public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
|
|
mContext = context;
|
|
mNavButtonsView = navButtonsView;
|
|
mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
|
|
mEndContextualContainer = mNavButtonsView.findViewById(R.id.end_contextual_buttons);
|
|
mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
|
|
|
|
mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color);
|
|
mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color);
|
|
}
|
|
|
|
/**
|
|
* Initializes the controller
|
|
*/
|
|
public void init(TaskbarControllers controllers) {
|
|
mControllers = controllers;
|
|
mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
|
|
mNavButtonTranslationYMultiplier.value = 1;
|
|
|
|
boolean isThreeButtonNav = mContext.isThreeButtonNav();
|
|
// IME switcher
|
|
View imeSwitcherButton = addButton(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH,
|
|
isThreeButtonNav ? mStartContextualContainer : mEndContextualContainer,
|
|
mControllers.navButtonController, R.id.ime_switcher);
|
|
mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton,
|
|
flags -> ((flags & MASK_IME_SWITCHER_VISIBLE) == MASK_IME_SWITCHER_VISIBLE)
|
|
&& ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)));
|
|
|
|
mPropertyHolders.add(new StatePropertyHolder(
|
|
mControllers.taskbarViewController.getTaskbarIconAlpha()
|
|
.getProperty(ALPHA_INDEX_KEYGUARD),
|
|
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, MultiValueAlpha.VALUE, 1, 0));
|
|
|
|
mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
|
|
.getKeyguardBgTaskbar(),
|
|
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, AnimatedFloat.VALUE, 1, 0));
|
|
|
|
// Make sure to remove nav bar buttons translation when notification shade is expanded.
|
|
mPropertyHolders.add(new StatePropertyHolder(mNavButtonTranslationYMultiplier,
|
|
flags -> (flags & FLAG_NOTIFICATION_SHADE_EXPANDED) != 0, AnimatedFloat.VALUE,
|
|
0, 1));
|
|
|
|
// Force nav buttons (specifically back button) to be visible during setup wizard.
|
|
boolean isInSetup = !mContext.isUserSetupComplete();
|
|
if (isThreeButtonNav || isInSetup) {
|
|
initButtons(mNavButtonContainer, mEndContextualContainer,
|
|
mControllers.navButtonController);
|
|
|
|
if (isInSetup) {
|
|
// Since setup wizard only has back button enabled, it looks strange to be
|
|
// end-aligned, so start-align instead.
|
|
FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
|
|
mNavButtonContainer.getLayoutParams();
|
|
navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd());
|
|
navButtonsLayoutParams.setMarginEnd(0);
|
|
navButtonsLayoutParams.gravity = Gravity.START;
|
|
mNavButtonContainer.requestLayout();
|
|
|
|
if (!isThreeButtonNav) {
|
|
// Tint all the nav buttons since there's no taskbar background in SUW.
|
|
for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
|
|
if (!(mNavButtonContainer.getChildAt(i) instanceof ImageView)) continue;
|
|
ImageView button = (ImageView) mNavButtonContainer.getChildAt(i);
|
|
button.setImageTintList(ColorStateList.valueOf(Themes.getAttrColor(
|
|
button.getContext(), android.R.attr.textColorPrimary)));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Animate taskbar background when any of these flags are enabled
|
|
int flagsToShowBg = FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE
|
|
| FLAG_NOTIFICATION_SHADE_EXPANDED;
|
|
mPropertyHolders.add(new StatePropertyHolder(
|
|
mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
|
|
flags -> (flags & flagsToShowBg) != 0, AnimatedFloat.VALUE, 1, 0));
|
|
|
|
// Rotation button
|
|
RotationButton rotationButton = new RotationButtonImpl(
|
|
addButton(mEndContextualContainer, R.id.rotate_suggestion,
|
|
R.layout.taskbar_contextual_button));
|
|
rotationButton.hide();
|
|
mControllers.rotationButtonController.setRotationButton(rotationButton, null);
|
|
} else {
|
|
mFloatingRotationButton = new FloatingRotationButton(mContext,
|
|
R.string.accessibility_rotate_button,
|
|
R.layout.rotate_suggestion,
|
|
R.id.rotate_suggestion,
|
|
R.dimen.floating_rotation_button_min_margin,
|
|
R.dimen.rounded_corner_content_padding,
|
|
R.dimen.floating_rotation_button_taskbar_left_margin,
|
|
R.dimen.floating_rotation_button_taskbar_bottom_margin,
|
|
R.dimen.floating_rotation_button_diameter,
|
|
R.dimen.key_button_ripple_max_width);
|
|
mControllers.rotationButtonController.setRotationButton(mFloatingRotationButton,
|
|
mRotationButtonListener);
|
|
|
|
View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
|
|
mStartContextualContainer, mControllers.navButtonController, R.id.back);
|
|
imeDownButton.setRotation(Utilities.isRtl(mContext.getResources()) ? 90 : -90);
|
|
// Rotate when Ime visible
|
|
mPropertyHolders.add(new StatePropertyHolder(imeDownButton,
|
|
flags -> (flags & FLAG_IME_VISIBLE) != 0));
|
|
}
|
|
|
|
applyState();
|
|
mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
|
|
}
|
|
|
|
private void initButtons(ViewGroup navContainer, ViewGroup endContainer,
|
|
TaskbarNavButtonController navButtonController) {
|
|
|
|
mBackButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
|
|
mNavButtonContainer, mControllers.navButtonController, R.id.back);
|
|
mPropertyHolders.add(new StatePropertyHolder(mBackButton,
|
|
flags -> (flags & FLAG_DISABLE_BACK) == 0));
|
|
boolean isRtl = Utilities.isRtl(mContext.getResources());
|
|
mPropertyHolders.add(new StatePropertyHolder(
|
|
mBackButton, flags -> (flags & FLAG_IME_VISIBLE) != 0, View.ROTATION,
|
|
isRtl ? 90 : -90, 0));
|
|
// Hide when keyguard is showing, show when bouncer or lock screen app is showing
|
|
mPropertyHolders.add(new StatePropertyHolder(mBackButton,
|
|
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 ||
|
|
(flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0 ||
|
|
(flags & FLAG_KEYGUARD_OCCLUDED) != 0));
|
|
// Translate back button to be at end/start of other buttons for keyguard
|
|
int navButtonSize = mContext.getResources().getDimensionPixelSize(
|
|
R.dimen.taskbar_nav_buttons_size);
|
|
mPropertyHolders.add(new StatePropertyHolder(
|
|
mBackButton, flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0
|
|
|| (flags & FLAG_KEYGUARD_VISIBLE) != 0,
|
|
VIEW_TRANSLATE_X, navButtonSize * (isRtl ? -2 : 2), 0));
|
|
|
|
|
|
// home and recents buttons
|
|
View homeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
|
|
navButtonController, R.id.home);
|
|
mPropertyHolders.add(new StatePropertyHolder(homeButton,
|
|
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
|
|
(flags & FLAG_DISABLE_HOME) == 0));
|
|
View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
|
|
navContainer, navButtonController, R.id.recent_apps);
|
|
mPropertyHolders.add(new StatePropertyHolder(recentsButton,
|
|
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
|
|
(flags & FLAG_DISABLE_RECENTS) == 0));
|
|
|
|
// A11y button
|
|
mA11yButton = addButton(R.drawable.ic_sysbar_accessibility_button, BUTTON_A11Y,
|
|
endContainer, navButtonController, R.id.accessibility_button,
|
|
R.layout.taskbar_contextual_button);
|
|
mPropertyHolders.add(new StatePropertyHolder(mA11yButton,
|
|
flags -> (flags & FLAG_A11Y_VISIBLE) != 0
|
|
&& (flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0));
|
|
}
|
|
|
|
private void parseSystemUiFlags(int sysUiStateFlags) {
|
|
mSysuiStateFlags = sysUiStateFlags;
|
|
boolean isImeVisible = (sysUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
|
|
boolean isImeSwitcherShowing = (sysUiStateFlags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0;
|
|
boolean a11yVisible = (sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
|
|
boolean isHomeDisabled = (sysUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
|
|
boolean isRecentsDisabled = (sysUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
|
|
boolean isBackDisabled = (sysUiStateFlags & SYSUI_STATE_BACK_DISABLED) != 0;
|
|
int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
|
|
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
|
|
boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0;
|
|
|
|
// TODO(b/202218289) we're getting IME as not visible on lockscreen from system
|
|
updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
|
|
updateStateForFlag(FLAG_SWITCHER_SUPPORTED, isImeSwitcherShowing);
|
|
updateStateForFlag(FLAG_A11Y_VISIBLE, a11yVisible);
|
|
updateStateForFlag(FLAG_DISABLE_HOME, isHomeDisabled);
|
|
updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled);
|
|
updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled);
|
|
updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded);
|
|
|
|
if (mA11yButton != null) {
|
|
// Only used in 3 button
|
|
boolean a11yLongClickable =
|
|
(sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
|
|
mA11yButton.setLongClickable(a11yLongClickable);
|
|
}
|
|
}
|
|
|
|
public void updateStateForSysuiFlags(int systemUiStateFlags, boolean skipAnim) {
|
|
if (systemUiStateFlags == mSysuiStateFlags) {
|
|
return;
|
|
}
|
|
parseSystemUiFlags(systemUiStateFlags);
|
|
applyState();
|
|
if (skipAnim) {
|
|
mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Should be called when we need to show back button for bouncer
|
|
*/
|
|
public void setBackForBouncer(boolean isBouncerVisible) {
|
|
updateStateForFlag(FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE, isBouncerVisible);
|
|
applyState();
|
|
}
|
|
|
|
/**
|
|
* Slightly misnamed, but should be called when keyguard OR AOD is showing.
|
|
* We consider keyguardVisible when it's showing bouncer OR is occlucded by another app
|
|
*/
|
|
public void setKeyguardVisible(boolean isKeyguardVisible, boolean isKeyguardOccluded) {
|
|
updateStateForFlag(FLAG_KEYGUARD_VISIBLE, isKeyguardVisible || isKeyguardOccluded);
|
|
updateStateForFlag(FLAG_KEYGUARD_OCCLUDED, isKeyguardOccluded);
|
|
applyState();
|
|
}
|
|
|
|
/**
|
|
* Returns true if IME bar is visible
|
|
*/
|
|
public boolean isImeVisible() {
|
|
return (mState & FLAG_IME_VISIBLE) != 0;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the home button is disabled
|
|
*/
|
|
public boolean isHomeDisabled() {
|
|
return (mState & FLAG_DISABLE_HOME) != 0;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the recents (overview) button is disabled
|
|
*/
|
|
public boolean isRecentsDisabled() {
|
|
return (mState & FLAG_DISABLE_RECENTS) != 0;
|
|
}
|
|
|
|
/**
|
|
* Adds the bounds corresponding to all visible buttons to provided region
|
|
*/
|
|
public void addVisibleButtonsRegion(TaskbarDragLayer parent, Region outRegion) {
|
|
int count = mAllButtons.size();
|
|
for (int i = 0; i < count; i++) {
|
|
View button = mAllButtons.get(i);
|
|
if (button.getVisibility() == View.VISIBLE) {
|
|
parent.getDescendantRectRelativeToSelf(button, mTempRect);
|
|
outRegion.op(mTempRect, Op.UNION);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Use to set the translationY for the all nav+contextual buttons */
|
|
public AnimatedFloat getTaskbarNavButtonTranslationY() {
|
|
return mTaskbarNavButtonTranslationY;
|
|
}
|
|
|
|
/** Use to set the dark intensity for the all nav+contextual buttons */
|
|
public AnimatedFloat getTaskbarNavButtonDarkIntensity() {
|
|
return mTaskbarNavButtonDarkIntensity;
|
|
}
|
|
|
|
/** Use to determine whether to use the dark intensity requested by the underlying app */
|
|
public AnimatedFloat getNavButtonDarkIntensityMultiplier() {
|
|
return mNavButtonDarkIntensityMultiplier;
|
|
}
|
|
|
|
/**
|
|
* Does not call {@link #applyState()}. Don't forget to!
|
|
*/
|
|
private void updateStateForFlag(int flag, boolean enabled) {
|
|
if (enabled) {
|
|
mState |= flag;
|
|
} else {
|
|
mState &= ~flag;
|
|
}
|
|
}
|
|
|
|
private void applyState() {
|
|
int count = mPropertyHolders.size();
|
|
for (int i = 0; i < count; i++) {
|
|
mPropertyHolders.get(i).setState(mState);
|
|
}
|
|
}
|
|
|
|
private void updateNavButtonTranslationY() {
|
|
mNavButtonsView.setTranslationY(mTaskbarNavButtonTranslationY.value
|
|
* mNavButtonTranslationYMultiplier.value);
|
|
}
|
|
|
|
private void updateNavButtonDarkIntensity() {
|
|
float darkIntensity = mTaskbarNavButtonDarkIntensity.value
|
|
* mNavButtonDarkIntensityMultiplier.value;
|
|
int iconColor = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightIconColor,
|
|
mDarkIconColor);
|
|
for (ImageView button : mAllButtons) {
|
|
button.setImageTintList(ColorStateList.valueOf(iconColor));
|
|
}
|
|
}
|
|
|
|
private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType,
|
|
ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id) {
|
|
return addButton(drawableId, buttonType, parent, navButtonController, id,
|
|
R.layout.taskbar_nav_button);
|
|
}
|
|
|
|
private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType,
|
|
ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id,
|
|
@LayoutRes int layoutId) {
|
|
ImageView buttonView = addButton(parent, id, layoutId);
|
|
buttonView.setImageResource(drawableId);
|
|
buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType));
|
|
buttonView.setOnLongClickListener(view ->
|
|
navButtonController.onButtonLongClick(buttonType));
|
|
return buttonView;
|
|
}
|
|
|
|
private ImageView addButton(ViewGroup parent, @IdRes int id, @LayoutRes int layoutId) {
|
|
ImageView buttonView = (ImageView) mContext.getLayoutInflater()
|
|
.inflate(layoutId, parent, false);
|
|
buttonView.setId(id);
|
|
parent.addView(buttonView);
|
|
mAllButtons.add(buttonView);
|
|
return buttonView;
|
|
}
|
|
|
|
public boolean isEventOverAnyItem(MotionEvent ev) {
|
|
return mFloatingRotationButtonBounds.contains((int) ev.getX(), (int) ev.getY());
|
|
}
|
|
|
|
public void onDestroy() {
|
|
mPropertyHolders.clear();
|
|
mControllers.rotationButtonController.unregisterListeners();
|
|
if (mFloatingRotationButton != null) {
|
|
mFloatingRotationButton.hide();
|
|
}
|
|
}
|
|
|
|
private class RotationButtonListener implements RotationButton.RotationButtonUpdatesCallback {
|
|
@Override
|
|
public void onVisibilityChanged(boolean isVisible) {
|
|
if (isVisible) {
|
|
mFloatingRotationButton.getCurrentView()
|
|
.getBoundsOnScreen(mFloatingRotationButtonBounds);
|
|
} else {
|
|
mFloatingRotationButtonBounds.setEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class RotationButtonImpl implements RotationButton {
|
|
|
|
private final ImageView mButton;
|
|
private AnimatedVectorDrawable mImageDrawable;
|
|
|
|
RotationButtonImpl(ImageView button) {
|
|
mButton = button;
|
|
}
|
|
|
|
@Override
|
|
public void setRotationButtonController(RotationButtonController rotationButtonController) {
|
|
// TODO(b/187754252) UI polish, different icons based on light/dark context, etc
|
|
mImageDrawable = (AnimatedVectorDrawable) mButton.getContext()
|
|
.getDrawable(rotationButtonController.getIconResId());
|
|
mButton.setImageDrawable(mImageDrawable);
|
|
mButton.setContentDescription(mButton.getResources()
|
|
.getString(R.string.accessibility_rotate_button));
|
|
mImageDrawable.setCallback(mButton);
|
|
}
|
|
|
|
@Override
|
|
public View getCurrentView() {
|
|
return mButton;
|
|
}
|
|
|
|
@Override
|
|
public boolean show() {
|
|
mButton.setVisibility(View.VISIBLE);
|
|
mState |= FLAG_ROTATION_BUTTON_VISIBLE;
|
|
applyState();
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean hide() {
|
|
mButton.setVisibility(View.GONE);
|
|
mState &= ~FLAG_ROTATION_BUTTON_VISIBLE;
|
|
applyState();
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean isVisible() {
|
|
return mButton.getVisibility() == View.VISIBLE;
|
|
}
|
|
|
|
@Override
|
|
public void updateIcon(int lightIconColor, int darkIconColor) {
|
|
// TODO(b/187754252): UI Polish
|
|
}
|
|
|
|
@Override
|
|
public void setOnClickListener(OnClickListener onClickListener) {
|
|
mButton.setOnClickListener(onClickListener);
|
|
}
|
|
|
|
@Override
|
|
public void setOnHoverListener(OnHoverListener onHoverListener) {
|
|
mButton.setOnHoverListener(onHoverListener);
|
|
}
|
|
|
|
@Override
|
|
public AnimatedVectorDrawable getImageDrawable() {
|
|
return mImageDrawable;
|
|
}
|
|
|
|
@Override
|
|
public void setDarkIntensity(float darkIntensity) {
|
|
// TODO(b/187754252) UI polish
|
|
}
|
|
|
|
@Override
|
|
public boolean acceptRotationProposal() {
|
|
return mButton.isAttachedToWindow();
|
|
}
|
|
}
|
|
|
|
private static class StatePropertyHolder {
|
|
|
|
private final float mEnabledValue, mDisabledValue;
|
|
private final ObjectAnimator mAnimator;
|
|
private final IntPredicate mEnableCondition;
|
|
|
|
private boolean mIsEnabled = true;
|
|
|
|
StatePropertyHolder(View view, IntPredicate enableCondition) {
|
|
this(view, enableCondition, LauncherAnimUtils.VIEW_ALPHA, 1, 0);
|
|
mAnimator.addListener(new AlphaUpdateListener(view));
|
|
}
|
|
|
|
<T> StatePropertyHolder(T target, IntPredicate enabledCondition,
|
|
Property<T, Float> property, float enabledValue, float disabledValue) {
|
|
mEnableCondition = enabledCondition;
|
|
mEnabledValue = enabledValue;
|
|
mDisabledValue = disabledValue;
|
|
mAnimator = ObjectAnimator.ofFloat(target, property, enabledValue, disabledValue);
|
|
}
|
|
|
|
public void setState(int flags) {
|
|
boolean isEnabled = mEnableCondition.test(flags);
|
|
if (mIsEnabled != isEnabled) {
|
|
mIsEnabled = isEnabled;
|
|
mAnimator.cancel();
|
|
mAnimator.setFloatValues(mIsEnabled ? mEnabledValue : mDisabledValue);
|
|
mAnimator.start();
|
|
}
|
|
}
|
|
|
|
public void endAnimation() {
|
|
if (mAnimator.isRunning()) {
|
|
mAnimator.end();
|
|
}
|
|
}
|
|
}
|
|
}
|