diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml index d32c11514f..d61a8952a7 100644 --- a/quickstep/res/layout/taskbar.xml +++ b/quickstep/res/layout/taskbar.xml @@ -13,12 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. --> - + android:layout_height="wrap_content" + android:clipChildren="false"> + + + android:gravity="center_vertical" + android:layout_gravity="start"/> - - - - - - - + android:gravity="center_vertical" + android:layout_gravity="end"/> + \ No newline at end of file diff --git a/quickstep/res/layout/taskbar_nav_button.xml b/quickstep/res/layout/taskbar_nav_button.xml new file mode 100644 index 0000000000..985f928ca7 --- /dev/null +++ b/quickstep/res/layout/taskbar_nav_button.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index d9c33aeeee..d8899a6e8e 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -148,11 +148,9 @@ 60dp - 44dp 48dp 54dp - - 8dp 16dp 16dp + 48dp diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 2a86e81f8b..8a7d01b71e 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -350,14 +350,6 @@ public abstract class BaseQuickstepLauncher extends Launcher ? new float[] {1, 1} : new float[] {1.1f, NO_OFFSET}; } - @Override - public float getNormalTaskbarScale() { - if (mTaskbarUIController != null) { - return mTaskbarUIController.getTaskbarScaleOnHome(); - } - return super.getNormalTaskbarScale(); - } - @Override public void onDragLayerHierarchyChanged() { onLauncherStateOrFocusChanged(); diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index 14b0c5dd62..c7c25670ce 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -85,7 +85,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements I mSampleHotseat = findViewById(R.id.sample_prediction); DeviceProfile grid = mActivityContext.getDeviceProfile(); - Rect padding = grid.getHotseatLayoutPadding(); + Rect padding = grid.getHotseatLayoutPadding(getContext()); mSampleHotseat.getLayoutParams().height = grid.cellHeightPx; mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1); diff --git a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java deleted file mode 100644 index 86ac39f5a2..0000000000 --- a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 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.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 android.annotation.DrawableRes; -import android.view.View; -import android.widget.ImageView; - -import com.android.launcher3.R; -import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton; -import com.android.launcher3.taskbar.contextual.RotationContextButton; - -/** - * Creates Buttons for Taskbar for 3 button nav. - * Can add animations and state management for buttons in this class as things progress. - */ -public class ButtonProvider { - - private final int mMarginLeftRight; - private final TaskbarActivityContext mContext; - - public ButtonProvider(TaskbarActivityContext context) { - mContext = context; - mMarginLeftRight = context.getResources() - .getDimensionPixelSize(R.dimen.taskbar_icon_spacing); - } - - public View getBack() { - // Back button - return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK); - } - - public View getDown() { - // Ime down button - return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK); - } - - public View getHome() { - // Home button - return getButtonForDrawable(R.drawable.ic_sysbar_home, BUTTON_HOME); - } - - public View getRecents() { - // Recents button - return getButtonForDrawable(R.drawable.ic_sysbar_recent, BUTTON_RECENTS); - } - - public View getImeSwitcher() { - // IME Switcher Button - return getButtonForDrawable(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH); - } - - public RotationContextButton getContextualRotation() { - // Rotation suggestion button - return new RotationContextButton(mContext); - } - - private View getButtonForDrawable(@DrawableRes int drawableId, @TaskbarButton int buttonType) { - ImageView buttonView = new ImageView(mContext); - buttonView.setImageResource(drawableId); - buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect); - buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0); - buttonView.setOnClickListener(view -> mContext.onNavigationButtonClick(buttonType)); - return buttonView; - } - -} diff --git a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java deleted file mode 100644 index d581302e62..0000000000 --- a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 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 android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.RelativeLayout; - -import com.android.launcher3.views.ActivityContext; - -public class ImeBarView extends RelativeLayout { - - private View mImeView; - - public ImeBarView(Context context) { - this(context, null); - } - - public ImeBarView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ImeBarView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public void init(ButtonProvider buttonProvider) { - // TODO (b/187966005), maybe need to replace ime switcher button with - // RotationContextButton when device rotates - ActivityContext context = getActivityContext(); - RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams( - context.getDeviceProfile().iconSizePx, - context.getDeviceProfile().iconSizePx - ); - RelativeLayout.LayoutParams downParams = new RelativeLayout.LayoutParams(imeParams); - - imeParams.addRule(ALIGN_PARENT_END); - imeParams.setMarginEnd(context.getDeviceProfile().iconSizePx); - downParams.setMarginStart(context.getDeviceProfile().iconSizePx); - downParams.addRule(ALIGN_PARENT_START); - - // Down Arrow - View downView = buttonProvider.getDown(); - downView.setLayoutParams(downParams); - downView.setRotation(-90); - addView(downView); - - // IME switcher button - mImeView = buttonProvider.getImeSwitcher(); - mImeView.setLayoutParams(imeParams); - addView(mImeView); - } - - public void setImeSwitcherVisibility(boolean show) { - mImeView.setVisibility(show ? VISIBLE : GONE); - } - - private T getActivityContext() { - return ActivityContext.lookupContext(getContext()); - } -} diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index f124de79ba..79af7cc5dd 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -15,42 +15,42 @@ */ package com.android.launcher3.taskbar; +import static com.android.launcher3.anim.Interpolators.LINEAR; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.graphics.Rect; -import android.graphics.RectF; import android.view.MotionEvent; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.BaseQuickstepLauncher; -import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState; import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; -import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.anim.PendingAnimation; -import com.android.launcher3.states.StateAnimationConfig; +import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; +import com.android.quickstep.AnimatedFloat; + /** * A data source which integrates with a Launcher instance - * TODO: Rename to have Launcher prefix */ - public class LauncherTaskbarUIController extends TaskbarUIController { private final BaseQuickstepLauncher mLauncher; private final TaskbarStateHandler mTaskbarStateHandler; - private final TaskbarAnimationController mTaskbarAnimationController; private final TaskbarHotseatController mHotseatController; private final TaskbarActivityContext mContext; final TaskbarDragLayer mTaskbarDragLayer; final TaskbarView mTaskbarView; + private AnimatedFloat mTaskBarAlpha; + private AlphaProperty mIconAlphaForHome; private @Nullable Animator mAnimator; private boolean mIsAnimatingToLauncher; - private ContextualRotationNotifier mContextualRotationNotifier; public LauncherTaskbarUIController( BaseQuickstepLauncher launcher, TaskbarActivityContext context) { @@ -60,20 +60,19 @@ public class LauncherTaskbarUIController extends TaskbarUIController { mLauncher = launcher; mTaskbarStateHandler = mLauncher.getTaskbarStateHandler(); - mTaskbarAnimationController = new TaskbarAnimationController(mLauncher, - createTaskbarAnimationControllerCallbacks()); mHotseatController = new TaskbarHotseatController( mLauncher, mTaskbarView::updateHotseatItems); + } @Override - protected void onCreate(ContextualRotationNotifier notifier) { - mContextualRotationNotifier = notifier; - mTaskbarStateHandler.setAnimationController(mTaskbarAnimationController); - mTaskbarAnimationController.init(); + protected void init(AnimatedFloat taskBarAlpha, AlphaProperty iconAlphaForLauncherState, + AlphaProperty iconAlphaForHome) { + mTaskBarAlpha = taskBarAlpha; + mIconAlphaForHome = iconAlphaForHome; + mTaskbarStateHandler.setAnimationController(iconAlphaForLauncherState); mHotseatController.init(); setTaskbarViewVisible(!mLauncher.hasBeenResumed()); - alignRealHotseatWithTaskbar(); mLauncher.setTaskbarUIController(this); } @@ -83,9 +82,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { // End this first, in case it relies on properties that are about to be cleaned up. mAnimator.end(); } - mContextualRotationNotifier = null; mTaskbarStateHandler.setAnimationController(null); - mTaskbarAnimationController.cleanup(); mHotseatController.cleanup(); setTaskbarViewVisible(true); mLauncher.getHotseat().setIconsAlpha(1f); @@ -97,46 +94,6 @@ public class LauncherTaskbarUIController extends TaskbarUIController { return !mIsAnimatingToLauncher; } - private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() { - return new TaskbarAnimationControllerCallbacks() { - @Override - public void updateTaskbarBackgroundAlpha(float alpha) { - mTaskbarDragLayer.setTaskbarBackgroundAlpha(alpha); - } - - @Override - public void updateTaskbarVisibilityAlpha(float alpha) { - mTaskbarView.setAlpha(alpha); - if (mContextualRotationNotifier != null) { - mContextualRotationNotifier.onTaskbarVisibilityChanged(alpha == 1); - } - } - - @Override - public void updateImeBarVisibilityAlpha(float alpha) { - mTaskbarDragLayer.updateImeBarVisibilityAlpha(alpha); - } - - @Override - public void updateTaskbarScale(float scale) { - mTaskbarView.setScaleX(scale); - mTaskbarView.setScaleY(scale); - } - - @Override - public void updateTaskbarTranslationY(float translationY) { - if (translationY < 0) { - // Resize to accommodate the max translation we'll reach. - mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize - + mLauncher.getHotseat().getTaskbarOffsetY()); - } else { - mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize); - } - mTaskbarView.setTranslationY(translationY); - } - }; - } - /** * Should be called from onResume() and onPause(), and animates the Taskbar accordingly. */ @@ -146,7 +103,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { mAnimator.cancel(); } if (isResumed) { - mAnimator = createAnimToLauncher(null, duration); + mAnimator = createAnimToLauncher(mLauncher.getStateManager().getState(), duration); } else { mAnimator = createAnimToApp(duration); } @@ -162,20 +119,19 @@ public class LauncherTaskbarUIController extends TaskbarUIController { /** * Create Taskbar animation when going from an app to Launcher. * @param toState If known, the state we will end up in when reaching Launcher. + * TODO: Move this and createAnimToApp to TaskbarStateHandler using the BACKGROUND state */ - public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) { + public Animator createAnimToLauncher(@NonNull LauncherState toState, long duration) { PendingAnimation anim = new PendingAnimation(duration); - anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(0, duration)); - if (toState != null) { - mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim); - } + mTaskbarStateHandler.setState(toState, anim); + + anim.setFloat(mTaskBarAlpha, AnimatedFloat.VALUE, 0, LINEAR); + mTaskbarView.alignIconsWithLauncher(mLauncher.getDeviceProfile(), anim); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { mIsAnimatingToLauncher = true; - mTaskbarView.setHolesAllowedInLayout(true); - mTaskbarView.updateHotseatItemsVisibility(); } @Override @@ -190,27 +146,17 @@ public class LauncherTaskbarUIController extends TaskbarUIController { private Animator createAnimToApp(long duration) { PendingAnimation anim = new PendingAnimation(duration); - anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(1, duration)); + anim.setFloat(mTaskBarAlpha, AnimatedFloat.VALUE, 1, LINEAR); + anim.addListener(AnimatorListeners.forEndCallback(mTaskbarView.resetIconPosition(anim))); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - mTaskbarView.updateHotseatItemsVisibility(); setTaskbarViewVisible(true); } - - @Override - public void onAnimationEnd(Animator animation) { - mTaskbarView.setHolesAllowedInLayout(false); - } }); return anim.buildAnim(); } - @Override - protected void onImeVisible(TaskbarDragLayer containerView, boolean isVisible) { - mTaskbarAnimationController.animateToVisibilityForIme(isVisible ? 0 : 1); - } - /** * Should be called when one or more items in the Hotseat have changed. */ @@ -230,55 +176,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController { return mContext.getDragController().isDragging(); } - /** - * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat. - */ - @Override - public void alignRealHotseatWithTaskbar() { - Rect hotseatBounds = new Rect(); - DeviceProfile grid = mLauncher.getDeviceProfile(); - int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize; - int taskbarOffset = mLauncher.getHotseat().getTaskbarOffsetY(); - int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset; - int hotseatBottomDiff = taskbarOffset; - - RectF hotseatBoundsF = mTaskbarView.getHotseatBounds(); - Utilities.scaleRectFAboutPivot(hotseatBoundsF, getTaskbarScaleOnHome(), - mTaskbarView.getPivotX(), mTaskbarView.getPivotY()); - hotseatBoundsF.round(hotseatBounds); - mLauncher.getHotseat().setPadding(hotseatBounds.left, - hotseatBounds.top + hotseatTopDiff, - mTaskbarView.getWidth() - hotseatBounds.right, - mTaskbarView.getHeight() - hotseatBounds.bottom + hotseatBottomDiff); - } - - /** - * Returns the ratio of the taskbar icon size on home vs in an app. - */ - public float getTaskbarScaleOnHome() { - DeviceProfile inAppDp = mContext.getDeviceProfile(); - DeviceProfile onHomeDp = mLauncher.getDeviceProfile(); - return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx; - } - void setTaskbarViewVisible(boolean isVisible) { - mTaskbarView.setIconsVisibility(isVisible); + mIconAlphaForHome.setValue(isVisible ? 1 : 0); mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f); } - - /** - * Contains methods that TaskbarAnimationController can call to interface with - * TaskbarController. - */ - protected interface TaskbarAnimationControllerCallbacks { - void updateTaskbarBackgroundAlpha(float alpha); - void updateTaskbarVisibilityAlpha(float alpha); - void updateImeBarVisibilityAlpha(float alpha); - void updateTaskbarScale(float scale); - void updateTaskbarTranslationY(float translationY); - } - - public interface ContextualRotationNotifier { - void onTaskbarVisibilityChanged(boolean showing); - } } diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonUIController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonUIController.java new file mode 100644 index 0000000000..1281b2e38f --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonUIController.java @@ -0,0 +1,316 @@ +/* + * 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.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 android.animation.ObjectAnimator; +import android.annotation.DrawableRes; +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.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.taskbar.contextual.RotationButton; +import com.android.launcher3.taskbar.contextual.RotationButtonController; +import com.android.launcher3.util.MultiValueAlpha; +import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; +import com.android.quickstep.AnimatedFloat; + +import java.util.ArrayList; +import java.util.function.IntPredicate; + +/** + * Controller for managing nav bar buttons in taskbar + */ +public class NavbarButtonUIController { + + 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 MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE; + + private final ArrayList mPropertyHolders = new ArrayList<>(); + private final ArrayList mAllButtons = new ArrayList<>(); + private int mState; + + private final TaskbarActivityContext mContext; + + public NavbarButtonUIController(TaskbarActivityContext context) { + mContext = context; + } + + /** + * Initializes the controller + */ + public void init(TaskbarDragLayer dragLayer, + TaskbarNavButtonController navButtonController, + RotationButtonController rotationButtonController, + AnimatedFloat taskbarBackgroundAlpha, AlphaProperty taskbarIconAlpha) { + FrameLayout buttonController = dragLayer.findViewById(R.id.navbuttons_view); + buttonController.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize; + + if (mContext.canShowNavButtons()) { + ViewGroup startContainer = buttonController.findViewById(R.id.start_nav_buttons); + ViewGroup endContainer = buttonController.findViewById(R.id.end_nav_buttons); + + initButtons(startContainer, endContainer, navButtonController); + + // Animate taskbar background when IME shows + mPropertyHolders.add(new StatePropertyHolder(taskbarBackgroundAlpha, + flags -> (flags & FLAG_IME_VISIBLE) == 0, + AnimatedFloat.VALUE, 0, 1)); + mPropertyHolders.add(new StatePropertyHolder( + taskbarIconAlpha, flags -> (flags & FLAG_IME_VISIBLE) == 0, + MultiValueAlpha.VALUE, 1, 0)); + + // Rotation button + RotationButton rotationButton = new RotationButtonImpl(addButton(endContainer)); + rotationButton.hide(); + rotationButtonController.setRotationButton(rotationButton); + } else { + rotationButtonController.setRotationButton(new RotationButton() { }); + } + + applyState(); + mPropertyHolders.forEach(StatePropertyHolder::endAnimation); + } + + private void initButtons(ViewGroup startContainer, ViewGroup endContainer, + TaskbarNavButtonController navButtonController) { + + View backButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK, + startContainer, navButtonController); + // Rotate when Ime visible + mPropertyHolders.add(new StatePropertyHolder(backButton, + flags -> (flags & FLAG_IME_VISIBLE) == 0, View.ROTATION, 0, + Utilities.isRtl(mContext.getResources()) ? 90 : -90)); + + // home and recents buttons + View homeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, startContainer, + navButtonController); + mPropertyHolders.add(new StatePropertyHolder(homeButton, + flags -> (flags & FLAG_IME_VISIBLE) == 0)); + View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS, + startContainer, navButtonController); + mPropertyHolders.add(new StatePropertyHolder(recentsButton, + flags -> (flags & FLAG_IME_VISIBLE) == 0)); + + // IME switcher + View imeSwitcherButton = addButton(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH, + endContainer, navButtonController); + mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton, + flags -> ((flags & MASK_IME_SWITCHER_VISIBLE) == MASK_IME_SWITCHER_VISIBLE) + && ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0))); + } + + /** + * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly. + */ + public void setImeIsVisible(boolean isImeVisible) { + if (isImeVisible) { + mState |= FLAG_IME_VISIBLE; + } else { + mState &= ~FLAG_IME_VISIBLE; + } + applyState(); + } + + /** + * Returns true if IME bar is visible + */ + public boolean isImeVisible() { + return (mState & FLAG_IME_VISIBLE) != 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); + } + } + } + + /** + * Sets if ime switcher is visible or not when ime is visible + */ + public void setImeSwitcherVisible(boolean imeSwitcherVisible) { + if (imeSwitcherVisible) { + mState |= FLAG_SWITCHER_SUPPORTED; + } else { + mState &= ~FLAG_SWITCHER_SUPPORTED; + } + applyState(); + } + + private void applyState() { + int count = mPropertyHolders.size(); + for (int i = 0; i < count; i++) { + mPropertyHolders.get(i).setState(mState); + } + } + + private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType, + ViewGroup parent, TaskbarNavButtonController navButtonController) { + ImageView buttonView = addButton(parent); + buttonView.setImageResource(drawableId); + buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType)); + return buttonView; + } + + private ImageView addButton(ViewGroup parent) { + ImageView buttonView = (ImageView) mContext.getLayoutInflater() + .inflate(R.layout.taskbar_nav_button, parent, false); + parent.addView(buttonView); + mAllButtons.add(buttonView); + return buttonView; + } + + 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); + mImageDrawable.setCallback(mButton); + } + + @Override + public View getCurrentView() { + return mButton; + } + + @Override + public void show() { + mButton.setVisibility(View.VISIBLE); + mState |= FLAG_ROTATION_BUTTON_VISIBLE; + applyState(); + } + + @Override + public void hide() { + mButton.setVisibility(View.GONE); + mState &= ~FLAG_ROTATION_BUTTON_VISIBLE; + applyState(); + } + + @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)); + } + + StatePropertyHolder(T target, IntPredicate enabledCondition, + Property 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(); + } + } + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index d51506cd6c..5f7dce5fac 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.pm.LauncherApps; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.inputmethodservice.InputMethodService; import android.os.Process; import android.os.SystemProperties; import android.util.Log; @@ -53,10 +54,12 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton; import com.android.launcher3.taskbar.contextual.RotationButtonController; import com.android.launcher3.touch.ItemClickHandler; +import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Themes; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.views.ActivityContext; +import com.android.quickstep.AnimatedFloat; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.systemui.shared.recents.model.Task; @@ -76,11 +79,16 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ private static final String WINDOW_TITLE = "Taskbar"; + private static final int ALPHA_INDEX_HOME = 0; + private static final int ALPHA_INDEX_LAUNCHER_STATE = 1; + private static final int ALPHA_INDEX_IME = 2; + private final DeviceProfile mDeviceProfile; private final LayoutInflater mLayoutInflater; private final TaskbarDragLayer mDragLayer; private final TaskbarIconController mIconController; private final TaskbarDragController mDragController; + private final NavbarButtonUIController mNavbarButtonUIController; private final WindowManager mWindowManager; private WindowManager.LayoutParams mWindowLayoutParams; @@ -89,7 +97,6 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ private int mLastRequestedNonFullscreenHeight; private final SysUINavigationMode.Mode mNavMode; - private final SystemTaskbarNotificationManager mSystemTaskbarNotificationManager; private final TaskbarNavButtonController mNavButtonController; private final RotationButtonController mRotationButtonController; @@ -101,43 +108,18 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ private final View.OnClickListener mOnTaskbarIconClickListener; private final View.OnLongClickListener mOnTaskbarIconLongClickListener; - private final TaskbarManager.SystemTaskbarNotifier mSystemTaskbarNotifier = - new TaskbarManager.SystemTaskbarNotifier() { - @Override - public void updateImeStatus(int displayId, int vis, int backDisposition, - boolean showImeSwitcher) { - /* - * When in 3 button nav, sysui flags don't get called since we prevent - * sysui nav bar from instantiating at all, which is what's responsible for - * sending sysui state flags over. - */ - mIconController.updateImeStatus(displayId, vis, showImeSwitcher); - } + // Alpha property for task bar + private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha); + private final AnimatedFloat mBgNavbar = new AnimatedFloat(this::updateBackgroundAlpha); - @Override - public void onRotationProposal(int rotation, boolean isValid) { - mRotationButtonController.onRotationProposal(rotation, isValid); - } - - @Override - public void disable(int displayId, int state1, int state2, boolean animate) { - mRotationButtonController.onDisable2FlagChanged(state2); - } - - @Override - public void onSystemBarAttributesChanged(int displayId, int behavior) { - mRotationButtonController.onBehaviorChanged(displayId, behavior); - } - }; + private final MultiValueAlpha mTaskbarIconAlpha; public TaskbarActivityContext(Context windowContext, DeviceProfile dp, - TaskbarNavButtonController buttonController, - SystemTaskbarNotificationManager systemTaskbarNotificationManager) { + TaskbarNavButtonController buttonController) { super(windowContext, Themes.getActivityThemeRes(windowContext)); mDeviceProfile = dp; mNavButtonController = buttonController; mNavMode = SysUINavigationMode.getMode(windowContext); - mSystemTaskbarNotificationManager = systemTaskbarNotificationManager; mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", () -> getPackageManager().isSafeMode()); @@ -146,23 +128,26 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ mOnTaskbarIconClickListener = this::onTaskbarIconClicked; float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size); + mDeviceProfile.updateIconSize(1, getResources()); float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx; mDeviceProfile.updateIconSize(iconScale, getResources()); mLayoutInflater = LayoutInflater.from(this).cloneInContext(this); mDragLayer = (TaskbarDragLayer) mLayoutInflater .inflate(R.layout.taskbar, null, false); - mRotationButtonController = new RotationButtonController(this, R.color.popup_color_primary_light, R.color.popup_color_primary_light); - mIconController = new TaskbarIconController(this, mDragLayer, - mRotationButtonController); + mNavbarButtonUIController = new NavbarButtonUIController(this); + mIconController = new TaskbarIconController(this, mDragLayer, mNavbarButtonUIController); Display display = windowContext.getDisplay(); Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY ? windowContext.getApplicationContext() : windowContext.getApplicationContext().createDisplayContext(display); mWindowManager = c.getSystemService(WindowManager.class); + + mTaskbarIconAlpha = new MultiValueAlpha(mDragLayer.findViewById(R.id.taskbar_view), 3); + mTaskbarIconAlpha.setUpdateVisibility(true); } public void init() { @@ -187,12 +172,12 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT } ); - mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener, - mNavMode); + mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener); + mNavbarButtonUIController.init(mDragLayer, mNavButtonController, mRotationButtonController, + mBgNavbar, mTaskbarIconAlpha.getProperty(ALPHA_INDEX_IME)); mWindowManager.addView(mDragLayer, mWindowLayoutParams); - if (mNavMode == Mode.THREE_BUTTONS) { - mSystemTaskbarNotificationManager - .registerSystemTaskbarNotifications(mSystemTaskbarNotifier); + if (canShowNavButtons()) { + mRotationButtonController.init(); } } @@ -232,10 +217,8 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ mUIController.onDestroy(); mUIController = uiController; mIconController.setUIController(mUIController); - mUIController.onCreate(mRotationButtonController::onTaskBarVisibilityChange); - if (mNavMode == Mode.THREE_BUTTONS) { - mRotationButtonController.init(); - } + mUIController.init(mBgTaskbar, mTaskbarIconAlpha.getProperty(ALPHA_INDEX_LAUNCHER_STATE), + mTaskbarIconAlpha.getProperty(ALPHA_INDEX_HOME)); } /** @@ -244,12 +227,8 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ public void onDestroy() { setUIController(TaskbarUIController.DEFAULT); mIconController.onDestroy(); + mRotationButtonController.onDestroy(); mWindowManager.removeViewImmediate(mDragLayer); - if (mNavMode == Mode.THREE_BUTTONS) { - mSystemTaskbarNotificationManager.removeSystemTaskbarNotifications( - mSystemTaskbarNotifier); - mRotationButtonController.cleanup(); - } } void onNavigationButtonClick(@TaskbarButton int buttonType) { @@ -261,6 +240,36 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ */ public void setImeIsVisible(boolean isImeVisible) { mIconController.setImeIsVisible(isImeVisible); + mNavbarButtonUIController.setImeIsVisible(isImeVisible); + } + + /** + * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from + * instantiating at all, which is what's responsible for sending sysui state flags over. + * + * @param vis IME visibility flag + */ + public void updateImeStatus(int displayId, int vis, boolean showImeSwitcher) { + if (displayId != getDisplayId() || !canShowNavButtons()) { + return; + } + mNavbarButtonUIController.setImeSwitcherVisible(showImeSwitcher); + setImeIsVisible((vis & InputMethodService.IME_VISIBLE) != 0); + } + + public void onRotationProposal(int rotation, boolean isValid) { + mRotationButtonController.onRotationProposal(rotation, isValid); + } + + public void disable(int displayId, int state1, int state2, boolean animate) { + if (displayId != getDisplayId()) { + return; + } + mRotationButtonController.onDisable2FlagChanged(state2); + } + + public void onSystemBarAttributesChanged(int displayId, int behavior) { + mRotationButtonController.onBehaviorChanged(displayId, behavior); } /** @@ -316,7 +325,9 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ }); } else if (tag instanceof WorkspaceItemInfo) { WorkspaceItemInfo info = (WorkspaceItemInfo) tag; - if (!(info.isDisabled() && ItemClickHandler.handleDisabledItemClicked(info, this))) { + if (info.isDisabled()) { + ItemClickHandler.handleDisabledItemClicked(info, this); + } else { Intent intent = new Intent(info.getIntent()) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { @@ -352,4 +363,9 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ AbstractFloatingView.closeAllOpenViews(this); } + + private void updateBackgroundAlpha() { + mDragLayer.setTaskbarBackgroundAlpha(Math.max(mBgNavbar.value, mBgTaskbar.value)); + } + } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java deleted file mode 100644 index e20ddf88ce..0000000000 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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.LauncherState.TASKBAR; - -import android.animation.Animator; - -import com.android.launcher3.BaseQuickstepLauncher; -import com.android.launcher3.Utilities; -import com.android.launcher3.taskbar.LauncherTaskbarUIController.TaskbarAnimationControllerCallbacks; -import com.android.quickstep.AnimatedFloat; -import com.android.quickstep.SystemUiProxy; -import com.android.systemui.shared.system.QuickStepContract; - -/** - * Works with TaskbarController to update the TaskbarView's visual properties based on factors such - * as LauncherState, whether Launcher is in the foreground, etc. - */ -public class TaskbarAnimationController { - - private static final long IME_VISIBILITY_ALPHA_DURATION = 120; - - private final BaseQuickstepLauncher mLauncher; - private final TaskbarAnimationControllerCallbacks mTaskbarCallbacks; - - // Background alpha. - private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat( - this::onTaskbarBackgroundAlphaChanged); - - // Overall visibility. - private final AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat( - this::updateVisibilityAlpha); - private final AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat( - this::updateVisibilityAlphaForIme); - - // Scale. - private final AnimatedFloat mTaskbarScaleForLauncherState = new AnimatedFloat( - this::updateScale); - - // TranslationY. - private final AnimatedFloat mTaskbarTranslationYForLauncherState = new AnimatedFloat( - this::updateTranslationY); - - public TaskbarAnimationController(BaseQuickstepLauncher launcher, - TaskbarAnimationControllerCallbacks taskbarCallbacks) { - mLauncher = launcher; - mTaskbarCallbacks = taskbarCallbacks; - } - - protected void init() { - mTaskbarBackgroundAlpha.updateValue(mLauncher.hasBeenResumed() ? 0f : 1f); - boolean isVisibleForLauncherState = (mLauncher.getStateManager().getState() - .getVisibleElements(mLauncher) & TASKBAR) != 0; - mTaskbarVisibilityAlphaForLauncherState.updateValue(isVisibleForLauncherState ? 1f : 0f); - boolean isImeVisible = (SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags() - & QuickStepContract.SYSUI_STATE_IME_SHOWING) != 0; - mTaskbarVisibilityAlphaForIme.updateValue(isImeVisible ? 0f : 1f); - - onTaskbarBackgroundAlphaChanged(); - updateVisibilityAlpha(); - } - - protected void cleanup() { - setNavBarButtonAlpha(1f); - } - - protected AnimatedFloat getTaskbarVisibilityForLauncherState() { - return mTaskbarVisibilityAlphaForLauncherState; - } - - protected AnimatedFloat getTaskbarScaleForLauncherState() { - return mTaskbarScaleForLauncherState; - } - - protected AnimatedFloat getTaskbarTranslationYForLauncherState() { - return mTaskbarTranslationYForLauncherState; - } - - protected Animator createAnimToBackgroundAlpha(float toAlpha, long duration) { - return mTaskbarBackgroundAlpha.animateToValue(mTaskbarBackgroundAlpha.value, toAlpha) - .setDuration(duration); - } - - protected void animateToVisibilityForIme(float toAlpha) { - mTaskbarVisibilityAlphaForIme.animateToValue(mTaskbarVisibilityAlphaForIme.value, toAlpha) - .setDuration(IME_VISIBILITY_ALPHA_DURATION).start(); - } - - private void onTaskbarBackgroundAlphaChanged() { - mTaskbarCallbacks.updateTaskbarBackgroundAlpha(mTaskbarBackgroundAlpha.value); - updateVisibilityAlpha(); - updateScale(); - updateTranslationY(); - } - - private void updateVisibilityAlpha() { - // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the - // assumption being that Taskbar should always be visible regardless of the current - // LauncherState if Launcher is paused. - float alphaDueToIme = mTaskbarVisibilityAlphaForIme.value; - float alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value, - mTaskbarVisibilityAlphaForLauncherState.value); - float taskbarAlpha = alphaDueToLauncher * alphaDueToIme; - mTaskbarCallbacks.updateTaskbarVisibilityAlpha(taskbarAlpha); - - // Make the nav bar invisible if taskbar is visible. - setNavBarButtonAlpha(1f - taskbarAlpha); - } - - private void updateVisibilityAlphaForIme() { - updateVisibilityAlpha(); - float taskbarAlphaDueToIme = mTaskbarVisibilityAlphaForIme.value; - mTaskbarCallbacks.updateImeBarVisibilityAlpha(1f - taskbarAlphaDueToIme); - } - - private void updateScale() { - // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the - // assumption being that Taskbar should always be at scale 1f regardless of the current - // LauncherState if Launcher is paused. - float scale = mTaskbarScaleForLauncherState.value; - scale = Utilities.mapRange(mTaskbarBackgroundAlpha.value, scale, 1f); - mTaskbarCallbacks.updateTaskbarScale(scale); - } - - private void updateTranslationY() { - // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the - // assumption being that Taskbar should always be at translationY 0f regardless of the - // current LauncherState if Launcher is paused. - float translationY = mTaskbarTranslationYForLauncherState.value; - translationY = Utilities.mapRange(mTaskbarBackgroundAlpha.value, translationY, 0f); - mTaskbarCallbacks.updateTaskbarTranslationY(translationY); - } - - private void setNavBarButtonAlpha(float navBarAlpha) { - SystemUiProxy.INSTANCE.get(mLauncher).setNavBarButtonAlpha(navBarAlpha, false); - } -} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index 855c50728b..4294eb565f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -15,9 +15,6 @@ */ package com.android.launcher3.taskbar; -import static android.view.View.INVISIBLE; -import static android.view.View.VISIBLE; - import android.content.ClipData; import android.content.ClipDescription; import android.content.Intent; @@ -89,7 +86,7 @@ public class TaskbarDragController extends DragController { startInternalDrag(btv); - btv.setVisibility(INVISIBLE); + btv.setAlpha(0); }); return true; } @@ -294,16 +291,9 @@ public class TaskbarDragController extends DragController { mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin); mTaskbarBackgroundPaint = new Paint(); mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background)); + mTaskbarBackgroundPaint.setAlpha(0); recreateControllers(); } @@ -109,12 +110,6 @@ public class TaskbarDragLayer extends BaseDragLayer { return true; } - public void updateImeBarVisibilityAlpha(float alpha) { - if (mControllerCallbacks != null) { - mControllerCallbacks.updateImeBarVisibilityAlpha(alpha); - } - } - @Override public void onViewRemoved(View child) { super.onViewRemoved(child); @@ -139,7 +134,6 @@ public class TaskbarDragLayer extends BaseDragLayer { return boundingBox; } - /** * Sets the alpha of the background color behind all the Taskbar contents. * @param alpha 0 is fully transparent, 1 is fully opaque. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java index 549e26cb22..b36b829b7a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java @@ -15,14 +15,9 @@ */ package com.android.launcher3.taskbar; -import static android.view.View.GONE; -import static android.view.View.VISIBLE; - import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME; import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION; -import android.graphics.Rect; -import android.inputmethodservice.InputMethodService; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; @@ -31,8 +26,6 @@ import androidx.annotation.NonNull; import com.android.launcher3.R; import com.android.launcher3.anim.AlphaUpdateListener; -import com.android.launcher3.taskbar.contextual.RotationButtonController; -import com.android.quickstep.SysUINavigationMode; import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo; /** @@ -40,42 +33,28 @@ import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo; */ public class TaskbarIconController { - private final Rect mTempRect = new Rect(); - private final TaskbarActivityContext mActivity; private final TaskbarDragLayer mDragLayer; + private final NavbarButtonUIController mNavbarButtonUIController; private final TaskbarView mTaskbarView; - private final ImeBarView mImeBarView; - private final RotationButtonController mRotationButtonController; @NonNull private TaskbarUIController mUIController = TaskbarUIController.DEFAULT; TaskbarIconController(TaskbarActivityContext activity, TaskbarDragLayer dragLayer, - RotationButtonController rotationButtonController) { + NavbarButtonUIController navbarButtonUIController) { mActivity = activity; mDragLayer = dragLayer; + mNavbarButtonUIController = navbarButtonUIController; mTaskbarView = mDragLayer.findViewById(R.id.taskbar_view); - mImeBarView = mDragLayer.findViewById(R.id.ime_bar_view); - mRotationButtonController = rotationButtonController; } - public void init(OnClickListener clickListener, OnLongClickListener longClickListener, - SysUINavigationMode.Mode navMode) { - mDragLayer.addOnLayoutChangeListener((v, a, b, c, d, e, f, g, h) -> - mUIController.alignRealHotseatWithTaskbar()); - - ButtonProvider buttonProvider = new ButtonProvider(mActivity); - mImeBarView.init(buttonProvider); - mTaskbarView.init(new TaskbarViewCallbacks(), clickListener, longClickListener, - buttonProvider); + public void init(OnClickListener clickListener, OnLongClickListener longClickListener) { + mTaskbarView.init(clickListener, longClickListener); mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize; mDragLayer.init(new TaskbarDragLayerCallbacks(), mTaskbarView); - if (mActivity.canShowNavButtons()) { - mRotationButtonController.setRotationButton(mTaskbarView.getContextualRotationButton()); - } } public void onDestroy() { @@ -86,27 +65,11 @@ public class TaskbarIconController { mUIController = uiController; } - /** - * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from - * instantiating at all, which is what's responsible for sending sysui state flags over. - * - * @param vis IME visibility flag - */ - public void updateImeStatus(int displayId, int vis, boolean showImeSwitcher) { - if (displayId != mActivity.getDisplayId() || !mActivity.canShowNavButtons()) { - return; - } - - mImeBarView.setImeSwitcherVisibility(showImeSwitcher); - setImeIsVisible((vis & InputMethodService.IME_VISIBLE) != 0); - } - /** * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly. */ public void setImeIsVisible(boolean isImeVisible) { mTaskbarView.setTouchesEnabled(!isImeVisible); - mUIController.onImeVisible(mDragLayer, isImeVisible); } /** @@ -122,7 +85,7 @@ public class TaskbarIconController { if (mDragLayer.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) { // Let touches pass through us. insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); - } else if (mImeBarView.getVisibility() == VISIBLE) { + } else if (mNavbarButtonUIController.isImeVisible()) { insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME); } else if (!mUIController.isTaskbarTouchable()) { // Let touches pass through us. @@ -131,17 +94,8 @@ public class TaskbarIconController { // Buttons are visible, take over the full taskbar area insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME); } else { - if (mTaskbarView.mSystemButtonContainer.getVisibility() == VISIBLE) { - mDragLayer.getDescendantRectRelativeToSelf( - mTaskbarView.mSystemButtonContainer, mTempRect); - insetsInfo.touchableRegion.set(mTempRect); - } - if (mTaskbarView.mContextualButtonContainer.getVisibility() == VISIBLE) { - mDragLayer.getDescendantRectRelativeToSelf( - mTaskbarView.mContextualButtonContainer, mTempRect); - insetsInfo.touchableRegion.union(mTempRect); - } - + mNavbarButtonUIController.addVisibleButtonsRegion( + mDragLayer, insetsInfo.touchableRegion); insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); } @@ -160,32 +114,11 @@ public class TaskbarIconController { // Ensure no other children present (like Folders, etc) for (int i = 0; i < count; i++) { View v = mDragLayer.getChildAt(i); - if (!((v instanceof TaskbarView) || (v instanceof ImeBarView))) { + if (!(v instanceof TaskbarView)) { return; } } mActivity.setTaskbarWindowFullscreen(false); } - - public void updateImeBarVisibilityAlpha(float alpha) { - if (!mActivity.canShowNavButtons()) { - // TODO Remove sysui IME bar for gesture nav as well - return; - } - mImeBarView.setAlpha(alpha); - mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE); - } - } - - /** - * Callbacks for {@link TaskbarView} to interact with the icon controller - */ - public class TaskbarViewCallbacks { - /** - * Returns whether no other controller is currently handling the given View's visibility. - */ - public boolean canUpdateViewVisibility(View child) { - return !mActivity.getDragController().isDraggingView(child); - } } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index b9acee8160..36bccee464 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -40,14 +40,11 @@ import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.TouchInteractionService; -import java.util.ArrayList; -import java.util.List; - /** * Class to manage taskbar lifecycle */ public class TaskbarManager implements DisplayController.DisplayInfoChangeListener, - SysUINavigationMode.NavigationModeChangeListener, SystemTaskbarNotificationManager { + SysUINavigationMode.NavigationModeChangeListener { private final Context mContext; private final DisplayController mDisplayController; @@ -62,8 +59,6 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen private boolean mUserUnlocked = false; - private List mSystemTaskbarNotifiers = new ArrayList<>(); - public TaskbarManager(TouchInteractionService service) { mDisplayController = DisplayController.INSTANCE.get(service); mSysUINavigationMode = SysUINavigationMode.INSTANCE.get(service); @@ -129,7 +124,7 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen return; } mTaskbarActivityContext = new TaskbarActivityContext( - mContext, dp.copy(mContext), mNavButtonController, this); + mContext, dp.copy(mContext), mNavButtonController); mTaskbarActivityContext.init(); if (mLauncher != null) { mTaskbarActivityContext.setUIController( @@ -137,9 +132,6 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen } } - // TODO - I don't think this is the best place for these pass through methods, - // maybe directly in TaskbarIconController? - /** * See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags} * @param systemUiStateFlags The latest SystemUiStateFlags @@ -151,16 +143,6 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen } } - public void registerSystemTaskbarNotifications(SystemTaskbarNotifier notifier) { - if (!mSystemTaskbarNotifiers.contains(notifier)) { - mSystemTaskbarNotifiers.add(notifier); - } - } - - public void removeSystemTaskbarNotifications(SystemTaskbarNotifier notifier) { - mSystemTaskbarNotifiers.remove(notifier); - } - /** * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from * instantiating at all, which is what's responsible for sending sysui state flags over. @@ -171,26 +153,26 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen */ public void updateImeStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher) { - for (SystemTaskbarNotifier notifier : mSystemTaskbarNotifiers) { - notifier.updateImeStatus(displayId, vis, backDisposition, showImeSwitcher); + if (mTaskbarActivityContext != null) { + mTaskbarActivityContext.updateImeStatus(displayId, vis, showImeSwitcher); } } public void onRotationProposal(int rotation, boolean isValid) { - for (SystemTaskbarNotifier notifier : mSystemTaskbarNotifiers) { - notifier.onRotationProposal(rotation, isValid); + if (mTaskbarActivityContext != null) { + mTaskbarActivityContext.onRotationProposal(rotation, isValid); } } public void disable(int displayId, int state1, int state2, boolean animate) { - for (SystemTaskbarNotifier notifier : mSystemTaskbarNotifiers) { - notifier.disable(displayId, state1, state2, animate); + if (mTaskbarActivityContext != null) { + mTaskbarActivityContext.disable(displayId, state1, state2, animate); } } public void onSystemBarAttributesChanged(int displayId, int behavior) { - for (SystemTaskbarNotifier notifier : mSystemTaskbarNotifiers) { - notifier.onSystemBarAttributesChanged(displayId, behavior); + if (mTaskbarActivityContext != null) { + mTaskbarActivityContext.onSystemBarAttributesChanged(displayId, behavior); } } @@ -202,18 +184,4 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen mDisplayController.removeChangeListener(this); mSysUINavigationMode.removeModeChangeListener(this); } - - public interface SystemTaskbarNotifier { - void updateImeStatus(int displayId, int vis, int backDisposition, - boolean showImeSwitcher); - void onRotationProposal(int rotation, boolean isValid); - void disable(int displayId, int state1, int state2, boolean animate); - void onSystemBarAttributesChanged(int displayId, int behavior); - - } } - -interface SystemTaskbarNotificationManager { - void registerSystemTaskbarNotifications(TaskbarManager.SystemTaskbarNotifier notifier); - void removeSystemTaskbarNotifications(TaskbarManager.SystemTaskbarNotifier notifier); -} \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java index a701aae088..e56ee8710b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java @@ -16,7 +16,6 @@ package com.android.launcher3.taskbar; import static com.android.launcher3.LauncherState.TASKBAR; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.LINEAR; import androidx.annotation.Nullable; @@ -27,7 +26,7 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.states.StateAnimationConfig; -import com.android.quickstep.AnimatedFloat; +import com.android.launcher3.util.MultiValueAlpha; /** * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar @@ -37,15 +36,15 @@ public class TaskbarStateHandler implements StateManager.StateHandler mActivityContext.setTaskbarWindowHeight( + mActivityContext.getDeviceProfile().taskbarSize); } /** @@ -168,9 +158,8 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa */ protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) { for (int i = 0; i < hotseatItemInfos.length; i++) { - ItemInfo hotseatItemInfo = hotseatItemInfos[ - !mIsRtl ? i : hotseatItemInfos.length - i - 1]; - View hotseatView = mHotseatIconsContainer.getChildAt(i); + ItemInfo hotseatItemInfo = hotseatItemInfos[i]; + View hotseatView = getChildAt(i); // Replace any Hotseat views with the appropriate type if it's not already that type. final int expectedLayoutResId; @@ -191,7 +180,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId || needsReinflate) { - mHotseatIconsContainer.removeView(hotseatView); + removeView(hotseatView); if (isFolder) { FolderInfo folderInfo = (FolderInfo) hotseatItemInfo; FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId, @@ -201,10 +190,9 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa } else { hotseatView = inflate(expectedLayoutResId); } - int iconSize = mActivityContext.getDeviceProfile().iconSizePx; - LayoutParams lp = new LayoutParams(iconSize, iconSize); - lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0); - mHotseatIconsContainer.addView(hotseatView, i, lp); + LayoutParams lp = new LayoutParams(mIconTouchSize, mIconTouchSize); + hotseatView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding); + addView(hotseatView, i, lp); } // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index. @@ -222,22 +210,42 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa hotseatView.setOnLongClickListener(null); hotseatView.setTag(null); } - updateHotseatItemVisibility(hotseatView); + hotseatView.setVisibility(hotseatView.getTag() != null ? VISIBLE : INVISIBLE); } } - protected void updateHotseatItemsVisibility() { - for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) { - updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i)); + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + int count = getChildCount(); + // Find total visible children + int visibleChildren = 0; + for (int i = 0; i < count; i++) { + if (getChildAt(i).getVisibility() == VISIBLE) { + visibleChildren++; + } } - } - private void updateHotseatItemVisibility(View hotseatView) { - if (!mControllerCallbacks.canUpdateViewVisibility(hotseatView)) { - return; + int spaceNeeded = visibleChildren * (mItemMarginLeftRight * 2 + mIconTouchSize); + int iconStart = (right - left - spaceNeeded) / 2; + int startOffset = ApiWrapper.getHotseatStartOffset(getContext()); + if (startOffset > iconStart) { + int diff = startOffset - iconStart; + iconStart = isLayoutRtl() ? (iconStart - diff) : iconStart + diff; } - hotseatView.setVisibility( - hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE)); + // Layout the children + mIconLayoutBounds.left = iconStart; + mIconLayoutBounds.top = (bottom - top - mIconTouchSize) / 2; + mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize; + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() == VISIBLE) { + iconStart += mItemMarginLeftRight; + int iconEnd = iconStart + mIconTouchSize; + child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom); + iconStart = iconEnd + mItemMarginLeftRight; + } + } + mIconLayoutBounds.right = iconStart; } @Override @@ -248,154 +256,21 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa return super.dispatchTouchEvent(ev); } - @Override - public boolean onTouchEvent(MotionEvent event) { - boolean handled = delegateTouchIfNecessary(event); - return super.onTouchEvent(event) || handled; - } - public void setTouchesEnabled(boolean touchEnabled) { this.mTouchEnabled = touchEnabled; } - /** - * User touched the Taskbar background. Determine whether the touch is close enough to a view - * that we should forward the touches to it. - * @return Whether a delegate view was chosen and it handled the touch event. - */ - private boolean delegateTouchIfNecessary(MotionEvent event) { - final float x = event.getX(); - final float y = event.getY(); - if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) { - View delegateView = findDelegateView(x, y); - if (delegateView != null) { - mDelegateTargeted = true; - mDelegateView = delegateView; - mDelegateSlopBounds.set(mTempDelegateBounds); - mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop); - } - } - - boolean sendToDelegate = mDelegateTargeted; - boolean inBounds = true; - switch (event.getAction()) { - case MotionEvent.ACTION_MOVE: - inBounds = mDelegateSlopBounds.contains(x, y); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mDelegateTargeted = false; - break; - } - - boolean handled = false; - if (sendToDelegate) { - if (inBounds) { - // Offset event coordinates to be inside the target view - event.setLocation(mDelegateView.getWidth() / 2f, mDelegateView.getHeight() / 2f); - } else { - // Offset event coordinates to be outside the target view (in case it does - // something like tracking pressed state) - event.setLocation(-mTouchSlop * 2, -mTouchSlop * 2); - } - handled = mDelegateView.dispatchTouchEvent(event); - // Cleanup if this was the last event to send to the delegate. - if (!mDelegateTargeted) { - mDelegateView = null; - } - } - return handled; - } - - /** - * Return an item whose touch bounds contain the given coordinates, - * or null if no such item exists. - * - * Also sets {@link #mTempDelegateBounds} to be the touch bounds of the chosen delegate view. - */ - private @Nullable View findDelegateView(float x, float y) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (!child.isShown() || !child.isClickable()) { - continue; - } - int childCenterX = child.getLeft() + child.getWidth() / 2; - int childCenterY = child.getTop() + child.getHeight() / 2; - mTempDelegateBounds.set( - childCenterX - mIconTouchSize / 2f, - childCenterY - mIconTouchSize / 2f, - childCenterX + mIconTouchSize / 2f, - childCenterY + mIconTouchSize / 2f); - if (mTempDelegateBounds.contains(x, y)) { - return child; - } - } - return null; - } - /** * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's * touch bounds. */ public boolean isEventOverAnyItem(MotionEvent ev) { getLocationOnScreen(mTempOutLocation); - float xInOurCoordinates = ev.getX() - mTempOutLocation[0]; - float yInOurCoorindates = ev.getY() - mTempOutLocation[1]; - return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null; + int xInOurCoordinates = (int) ev.getX() - mTempOutLocation[0]; + int yInOurCoorindates = (int) ev.getY() - mTempOutLocation[1]; + return isShown() && mIconLayoutBounds.contains(xInOurCoordinates, yInOurCoorindates); } - /** - * Add back/home/recents buttons into a single ViewGroup that will be inserted at - * {@param navButtonStartIndex} - */ - private void createNavButtons() { - LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams( - mActivityContext.getDeviceProfile().iconSizePx, - mActivityContext.getDeviceProfile().iconSizePx - ); - buttonParams.gravity = Gravity.CENTER; - - mSystemButtonContainer.addView(mButtonProvider.getBack(), buttonParams); - mSystemButtonContainer.addView(mButtonProvider.getHome(), buttonParams); - mSystemButtonContainer.addView(mButtonProvider.getRecents(), buttonParams); - } - - /** - * @return The bounding box of where the hotseat elements are relative to this TaskbarView. - */ - protected RectF getHotseatBounds() { - RectF result; - mDisableRelayout = true; - boolean wereHolesAllowed = mAreHolesAllowed; - setHolesAllowedInLayoutNoAnimation(true); - result = new RectF( - mHotseatIconsContainer.getLeft(), - mHotseatIconsContainer.getTop(), - mHotseatIconsContainer.getRight(), - mHotseatIconsContainer.getBottom()); - setHolesAllowedInLayoutNoAnimation(wereHolesAllowed); - mDisableRelayout = false; - - return result; - } - - @Override - public void requestLayout() { - if (!mDisableRelayout) { - super.requestLayout(); - } - } - - private void createContextualRegion() { - mContextualRotationButton = mButtonProvider.getContextualRotation(); - mContextualRotationButton.setVisibility(GONE); - mContextualButtonContainer.addView(mContextualRotationButton); - } - - @Nullable - public RotationContextButton getContextualRotationButton() { - return mContextualRotationButton; - } // FolderIconParent implemented methods. @Override @@ -432,11 +307,8 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa // Ignore, we just implement Insettable to draw behind system insets. } - public void setIconsVisibility(boolean isVisible) { - mHotseatIconsContainer.setVisibility(isVisible ? VISIBLE : INVISIBLE); - } - public boolean areIconsVisible() { - return mHotseatIconsContainer.getVisibility() == VISIBLE; + // Consider the overall visibility + return getVisibility() == VISIBLE; } } diff --git a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java index d42107742e..40930972d1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java +++ b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java @@ -30,16 +30,24 @@ import android.view.View; * * Directly use AnimatedVectorDrawable instead of KeyButtonDrawable */ public interface RotationButton { - void setRotationButtonController(RotationButtonController rotationButtonController); - View getCurrentView(); - boolean show(); - boolean hide(); - boolean isVisible(); - void updateIcon(int lightIconColor, int darkIconColor); - void setOnClickListener(View.OnClickListener onClickListener); - void setOnHoverListener(View.OnHoverListener onHoverListener); - AnimatedVectorDrawable getImageDrawable(); - void setDarkIntensity(float darkIntensity); + default void setRotationButtonController(RotationButtonController rotationButtonController) { } + + default View getCurrentView() { + return null; + } + default void show() { } + default void hide() { } + default boolean isVisible() { + return false; + } + + default void updateIcon(int lightIconColor, int darkIconColor) { } + default void setOnClickListener(View.OnClickListener onClickListener) { } + default void setOnHoverListener(View.OnHoverListener onHoverListener) { } + default AnimatedVectorDrawable getImageDrawable() { + return null; + } + default void setDarkIntensity(float darkIntensity) { } default boolean acceptRotationProposal() { return getCurrentView() != null; } diff --git a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java index 6f6abc2533..99dc2828ea 100644 --- a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java +++ b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java @@ -104,7 +104,7 @@ public class RotationButtonController { private final IRotationWatcher.Stub mRotationWatcher = new IRotationWatcher.Stub() { @Override - public void onRotationChanged(final int rotation) throws RemoteException { + public void onRotationChanged(final int rotation) { // We need this to be scheduled as early as possible to beat the redrawing of // window in response to the orientation change. mMainThreadHandler.postAtFrontOfQueue(() -> { @@ -137,7 +137,7 @@ public class RotationButtonController { mAccessibilityManager = AccessibilityManager.getInstance(context); mTaskStackListener = new TaskStackListenerImpl(); - mDisplayController = DisplayController.INSTANCE.getNoCreate(); + mDisplayController = DisplayController.INSTANCE.get(context); } public void setRotationButton(RotationButton rotationButton) { @@ -156,7 +156,7 @@ public class RotationButtonController { } } - public void cleanup() { + public void onDestroy() { unregisterListeners(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationContextButton.java b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationContextButton.java deleted file mode 100644 index 7ad3191c49..0000000000 --- a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationContextButton.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 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.contextual; - -import android.content.Context; -import android.graphics.drawable.AnimatedVectorDrawable; -import android.view.View; -import android.widget.ImageView; - -import com.android.launcher3.R; - -/** Containing logic for the rotation button in nav bar. */ -public class RotationContextButton extends ImageView implements RotationButton { - - private AnimatedVectorDrawable mImageDrawable; - - public RotationContextButton(Context context) { - super(context); - setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect); - } - - @Override - public void setRotationButtonController(RotationButtonController rotationButtonController) { - // TODO(b/187754252) UI polish, different icons based on light/dark context, etc - mImageDrawable = (AnimatedVectorDrawable) getContext() - .getDrawable(rotationButtonController.getIconResId()); - setImageDrawable(mImageDrawable); - mImageDrawable.setCallback(this); - } - - @Override - public View getCurrentView() { - return this; - } - - @Override - public boolean show() { - setVisibility(VISIBLE); - return true; - } - - @Override - public boolean hide() { - setVisibility(GONE); - return true; - } - - @Override - public boolean isVisible() { - return getVisibility() == VISIBLE; - } - - @Override - public void updateIcon(int lightIconColor, int darkIconColor) { - // TODO(b/187754252): UI Polish - } - - @Override - public void setOnClickListener(View.OnClickListener onClickListener) { - super.setOnClickListener(onClickListener); - } - - @Override - public void setOnHoverListener(View.OnHoverListener onHoverListener) { - super.setOnHoverListener(onHoverListener); - } - - @Override - public AnimatedVectorDrawable getImageDrawable() { - return mImageDrawable; - } - - @Override - public void setDarkIntensity(float darkIntensity) { - // TODO(b/187754252) UI polish - } - - @Override - public void setVisibility(int visibility) { - super.setVisibility(visibility); - - if (visibility != View.VISIBLE && mImageDrawable != null) { - mImageDrawable.clearAnimationCallbacks(); - mImageDrawable.reset(); - } - - // Start the rotation animation once it becomes visible - if (visibility == View.VISIBLE && mImageDrawable != null) { - mImageDrawable.reset(); - mImageDrawable.start(); - } - } - - @Override - public boolean acceptRotationProposal() { - return isAttachedToWindow(); - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java index 76a5782d11..a595f54322 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java @@ -17,10 +17,15 @@ package com.android.launcher3.uioverrides; import android.app.Person; +import android.content.Context; import android.content.pm.ShortcutInfo; +import android.content.res.Resources; import android.view.Display; +import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.quickstep.SysUINavigationMode; +import com.android.quickstep.SysUINavigationMode.Mode; public class ApiWrapper { @@ -37,4 +42,18 @@ public class ApiWrapper { public static boolean isInternalDisplay(Display display) { return display.getType() == Display.TYPE_INTERNAL; } + + /** + * Returns the minimum space that should be left empty at the start of hotseat + */ + public static int getHotseatStartOffset(Context context) { + if (SysUINavigationMode.INSTANCE.get(context).getMode() == Mode.THREE_BUTTONS) { + Resources res = context.getResources(); + return 2 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_spacing) + + 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size); + } else { + return 0; + } + + } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index 8c128c8e08..90e17c0276 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -82,16 +82,6 @@ public class OverviewState extends LauncherState { return new float[] {NO_SCALE, NO_OFFSET}; } - @Override - public float getTaskbarScale(Launcher launcher) { - return 1f; - } - - @Override - public float getTaskbarTranslationY(Launcher launcher) { - return 0f; - } - @Override public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) { return new PageAlphaProvider(DEACCEL_2) { diff --git a/res/values/dimens.xml b/res/values/dimens.xml index d6a6f43803..584ecb7c79 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -295,6 +295,10 @@ 0dp + 0dp + 44dp + + 8dp 16dp diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index e9245b0819..2da7ac39e0 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -44,6 +44,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.GraphicsUtils; import com.android.launcher3.icons.IconNormalizer; +import com.android.launcher3.uioverrides.ApiWrapper; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.WindowBounds; @@ -54,6 +55,8 @@ import java.io.PrintWriter; public class DeviceProfile { private static final int DEFAULT_DOT_SIZE = 100; + // Ratio of empty space, qsb should take up to appear visually centered. + private static final float QSB_CENTER_FACTOR = .325f; public final InvariantDeviceProfile inv; private final Info mInfo; @@ -156,6 +159,7 @@ public class DeviceProfile { // Start is the side next to the nav bar, end is the side next to the workspace public final int hotseatBarSidePaddingStartPx; public final int hotseatBarSidePaddingEndPx; + public final int hotseatQsbHeight; // All apps public int allAppsOpenVerticalTranslate; @@ -240,6 +244,7 @@ public class DeviceProfile { mMetrics = context.getResources().getDisplayMetrics(); final Resources res = context.getResources(); + hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height); isTaskbarPresent = isTablet && FeatureFlags.ENABLE_TASKBAR.get(); if (isTaskbarPresent) { // Taskbar will be added later, but provides bottom insets that we should subtract @@ -740,7 +745,10 @@ public class DeviceProfile { } } - public Rect getHotseatLayoutPadding() { + /** + * Returns the padding for hotseat view + */ + public Rect getHotseatLayoutPadding(Context context) { if (isVerticalBarLayout()) { if (isSeascape()) { mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, @@ -749,6 +757,30 @@ public class DeviceProfile { mHotseatPadding.set(hotseatBarSidePaddingEndPx, mInsets.top, mInsets.right + hotseatBarSidePaddingStartPx, mInsets.bottom); } + } else if (isTaskbarPresent) { + int hotseatHeight = workspacePadding.bottom + taskbarSize; + int taskbarOffset = getTaskbarOffsetY(); + int hotseatTopDiff = hotseatHeight - taskbarSize - taskbarOffset; + + int startOffset = ApiWrapper.getHotseatStartOffset(context); + int requiredWidth = iconSizePx * numShownHotseatIcons; + + Resources res = context.getResources(); + float taskbarIconSize = res.getDimension(R.dimen.taskbar_icon_size); + float taskbarIconSpacing = 2 * res.getDimension(R.dimen.taskbar_icon_spacing); + int maxSize = (int) (requiredWidth + * (taskbarIconSize + taskbarIconSpacing) / taskbarIconSize); + int hotseatSize = Math.min(maxSize, availableWidthPx - startOffset); + int sideSpacing = (availableWidthPx - hotseatSize) / 2; + mHotseatPadding.set(sideSpacing, hotseatTopDiff, sideSpacing, taskbarOffset); + + if (startOffset > sideSpacing) { + int diff = Utilities.isRtl(context.getResources()) + ? sideSpacing - startOffset + : startOffset - sideSpacing; + mHotseatPadding.left += diff; + mHotseatPadding.right -= diff; + } } else { // We want the edges of the hotseat to line up with the edges of the workspace, but the // icons in the hotseat are a different size, and so don't line up perfectly. To account @@ -768,6 +800,24 @@ public class DeviceProfile { return mHotseatPadding; } + /** + * Returns the number of pixels the QSB is translated from the bottom of the screen. + */ + public int getQsbOffsetY() { + int freeSpace = isTaskbarPresent + ? workspacePadding.bottom + : hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight; + return (int) (freeSpace * QSB_CENTER_FACTOR) + + (isTaskbarPresent ? taskbarSize : getInsets().bottom); + } + + /** + * Returns the number of pixels the taskbar is translated from the bottom of the screen. + */ + public int getTaskbarOffsetY() { + return (getQsbOffsetY() - taskbarSize) / 2; + } + /** * @return the bounds for which the open folders should be contained within */ diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index ff380ce1ae..b3ae15ed4d 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -49,8 +49,6 @@ public class Hotseat extends CellLayout implements Insettable { private final View mQsb; private final int mQsbHeight; - private final int mTaskbarViewHeight; - public Hotseat(Context context) { this(context, null); } @@ -63,10 +61,9 @@ public class Hotseat extends CellLayout implements Insettable { super(context, attrs, defStyle); mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false); - mQsbHeight = mQsb.getLayoutParams().height; addView(mQsb); - mTaskbarViewHeight = context.getResources().getDimensionPixelSize(R.dimen.taskbar_size); + mQsbHeight = getResources().getDimensionPixelSize(R.dimen.qsb_widget_height); } /** @@ -114,18 +111,13 @@ public class Hotseat extends CellLayout implements Insettable { lp.gravity = Gravity.BOTTOM; lp.width = ViewGroup.LayoutParams.MATCH_PARENT; lp.height = (grid.isTaskbarPresent - ? grid.workspacePadding.bottom + ? grid.workspacePadding.bottom : grid.hotseatBarSizePx) + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom); } - if (!grid.isTaskbarPresent) { - // When taskbar is present, we set the padding separately to ensure a seamless visual - // handoff between taskbar and hotseat during drag and drop. - Rect padding = grid.getHotseatLayoutPadding(); - setPadding(padding.left, padding.top, padding.right, padding.bottom); - } - + Rect padding = grid.getHotseatLayoutPadding(getContext()); + setPadding(padding.left, padding.top, padding.right, padding.bottom); setLayoutParams(lp); InsettableFrameLayout.dispatchInsets(this, insets); } @@ -193,30 +185,11 @@ public class Hotseat extends CellLayout implements Insettable { int left = (r - l - qsbWidth) / 2; int right = left + qsbWidth; - int bottom = b - t - getQsbOffsetY(); + int bottom = b - t - mActivity.getDeviceProfile().getQsbOffsetY(); int top = bottom - mQsbHeight; mQsb.layout(left, top, right, bottom); } - /** - * Returns the number of pixels the QSB is translated from the bottom of the screen. - */ - private int getQsbOffsetY() { - DeviceProfile dp = mActivity.getDeviceProfile(); - int freeSpace = dp.isTaskbarPresent - ? dp.workspacePadding.bottom - : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight; - return (int) (freeSpace * QSB_CENTER_FACTOR) - + (dp.isTaskbarPresent ? dp.taskbarSize : dp.getInsets().bottom); - } - - /** - * Returns the number of pixels the taskbar is translated from the bottom of the screen. - */ - public int getTaskbarOffsetY() { - return (getQsbOffsetY() - mTaskbarViewHeight) / 2; - } - /** * Sets the alpha value of just our ShortcutAndWidgetContainer. */ diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 892fb6d6f0..fc8d65c85c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2801,13 +2801,6 @@ public class Launcher extends StatefulActivity implements Launche return new float[] {NO_SCALE, NO_OFFSET}; } - /** - * @see LauncherState#getTaskbarScale(Launcher) - */ - public float getNormalTaskbarScale() { - return 1f; - } - public static Launcher getLauncher(Context context) { return fromContext(context); } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 3399ce9cd6..7985ab5ad3 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -181,14 +181,6 @@ public abstract class LauncherState implements BaseState { return launcher.getNormalOverviewScaleAndOffset(); } - public float getTaskbarScale(Launcher launcher) { - return launcher.getNormalTaskbarScale(); - } - - public float getTaskbarTranslationY(Launcher launcher) { - return -launcher.getHotseat().getTaskbarOffsetY(); - } - public float getOverviewFullscreenProgress() { return 0; } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java index 4407fe1149..c606861cd8 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java @@ -17,6 +17,7 @@ package com.android.launcher3.uioverrides; import android.app.Person; +import android.content.Context; import android.content.pm.ShortcutInfo; import android.view.Display; @@ -36,4 +37,11 @@ public class ApiWrapper { public static boolean isInternalDisplay(Display display) { return display.getDisplayId() == Display.DEFAULT_DISPLAY; } + + /** + * Returns the minimum space that should be left empty at the start of hotseat + */ + public static int getHotseatStartOffset(Context context) { + return 0; + } }