Snap for 7479185 from a29220f6b6 to sc-v2-release

Change-Id: I3ec2119bbf229f5dd9a798801f14054f13abd76c
This commit is contained in:
android-build-team Robot
2021-06-22 01:11:26 +00:00
36 changed files with 738 additions and 90 deletions
+3 -3
View File
@@ -15,10 +15,10 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="42dp"
android:height="40dp"
android:height="42dp"
android:viewportWidth="42"
android:viewportHeight="40">
android:viewportHeight="42">
<path
android:pathData="M38,14H25.38L27.28,4.86L27.34,4.22C27.34,3.4 27,2.64 26.46,2.1L24.34,0C24.34,0 10.16,13.7 10,14H0V40H32C33.66,40 35.08,39 35.68,37.56L41.72,23.46C41.9,23 42,22.52 42,22V18C42,15.8 40.2,14 38,14ZM10,36H4V18H10V36ZM38,22L32,36H14V16L22.68,7.32L20,18H38V22Z"
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"
android:fillColor="#FFFFFF"/>
</vector>
+12
View File
@@ -15,6 +15,7 @@
-->
<com.android.launcher3.taskbar.TaskbarDragLayer
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/taskbar_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -39,6 +40,7 @@
android:id="@+id/start_nav_buttons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
android:gravity="center_vertical"
@@ -54,4 +56,14 @@
android:layout_gravity="end"/>
</FrameLayout>
<View
android:id="@+id/stashed_handle"
tools:comment1="The actual size and shape will be set as a ViewOutlineProvider at runtime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:comment2="TODO: Tint dynamically"
android:background="?android:attr/textColorPrimary"
android:clipToOutline="true"
android:layout_gravity="bottom"/>
</com.android.launcher3.taskbar.TaskbarDragLayer>
+3
View File
@@ -153,4 +153,7 @@
<dimen name="taskbar_folder_margin">16dp</dimen>
<dimen name="taskbar_nav_buttons_spacing">16dp</dimen>
<dimen name="taskbar_nav_buttons_size">48dp</dimen>
<dimen name="taskbar_stashed_size">24dp</dimen>
<dimen name="taskbar_stashed_handle_width">220dp</dimen>
<dimen name="taskbar_stashed_handle_height">6dp</dimen>
</resources>
@@ -497,8 +497,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets()));
viewsToAnimate.add(mLauncher.getHotseat());
// Add QSB
viewsToAnimate.add(mLauncher.findViewById(R.id.search_container_all_apps));
viewsToAnimate.forEach(view -> {
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
@@ -50,21 +50,23 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
private final TaskbarHotseatController mHotseatController;
private final TaskbarActivityContext mContext;
final TaskbarDragLayer mTaskbarDragLayer;
final TaskbarView mTaskbarView;
private final TaskbarDragLayer mTaskbarDragLayer;
private final TaskbarView mTaskbarView;
private final AnimatedFloat mIconAlignmentForResumedState =
new AnimatedFloat(this::onIconAlignmentRatioChanged);
private final AnimatedFloat mIconAlignmentForGestureState =
new AnimatedFloat(this::onIconAlignmentRatioChanged);
// Initialized in init.
private TaskbarControllers mControllers;
private AnimatedFloat mTaskbarBackgroundAlpha;
private AlphaProperty mIconAlphaForHome;
private boolean mIsAnimatingToLauncher;
private boolean mIsAnimatingToLauncherViaResume;
private boolean mIsAnimatingToLauncherViaGesture;
private TaskbarKeyguardController mKeyguardController;
private LauncherState mTargetStateOverride = null;
private TaskbarControllers mControllers;
public LauncherTaskbarUIController(
BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
@@ -80,13 +82,14 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
@Override
protected void init(TaskbarControllers taskbarControllers) {
mTaskbarBackgroundAlpha = taskbarControllers.taskbarDragLayerController
.getTaskbarBackgroundAlpha();
MultiValueAlpha taskbarIconAlpha = taskbarControllers.taskbarViewController
.getTaskbarIconAlpha();
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
mControllers = taskbarControllers;
mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
.getTaskbarBackgroundAlpha();
MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
mHotseatController.init();
mLauncher.setTaskbarUIController(this);
mKeyguardController = taskbarControllers.taskbarKeyguardController;
@@ -109,19 +112,17 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
@Override
protected boolean isTaskbarTouchable() {
return !mIsAnimatingToLauncher && mTargetStateOverride == null;
return !isAnimatingToLauncher() && !mControllers.taskbarStashController.isStashed();
}
private boolean isAnimatingToLauncher() {
return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
}
@Override
protected void updateContentInsets(Rect outContentInsets) {
// TaskbarDragLayer provides insets to other apps based on contentInsets. These
// insets should stay consistent even if we expand TaskbarDragLayer's bounds, e.g.
// to show a floating view like Folder. Thus, we set the contentInsets to be where
// mTaskbarView is, since its position never changes and insets rather than overlays.
outContentInsets.left = mTaskbarView.getLeft();
outContentInsets.top = mTaskbarView.getTop();
outContentInsets.right = mTaskbarDragLayer.getWidth() - mTaskbarView.getRight();
outContentInsets.bottom = mTaskbarDragLayer.getHeight() - mTaskbarView.getBottom();
int contentHeight = mControllers.taskbarStashController.getContentHeight();
outContentInsets.top = mTaskbarDragLayer.getHeight() - contentHeight;
}
/**
@@ -137,13 +138,20 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
}
}
long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue(
getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
.setDuration(QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
.setDuration(duration);
anim.addListener(AnimatorListeners.forEndCallback(() -> mIsAnimatingToLauncher = false));
anim.addListener(AnimatorListeners.forEndCallback(
() -> mIsAnimatingToLauncherViaResume = false));
anim.start();
mIsAnimatingToLauncher = isResumed;
mIsAnimatingToLauncherViaResume = isResumed;
if (!isResumed) {
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.animateToIsStashed(stashController.isStashedInApp(), duration);
}
}
/**
@@ -155,36 +163,48 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
public Animator createAnimToLauncher(@NonNull LauncherState toState,
@NonNull RecentsAnimationCallbacks callbacks,
long duration) {
TaskbarStashController stashController = mControllers.taskbarStashController;
ObjectAnimator animator = mIconAlignmentForGestureState
.animateToValue(mIconAlignmentForGestureState.value, 1)
.animateToValue(1)
.setDuration(duration);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mTargetStateOverride = null;
animator.removeListener(this);
}
@Override
public void onAnimationStart(Animator animation) {
mTargetStateOverride = toState;
mIsAnimatingToLauncherViaGesture = true;
// TODO: base this on launcher state
stashController.animateToIsStashed(false, duration);
}
});
callbacks.addListener(new RecentsAnimationListener() {
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
endGestureStateOverride();
endGestureStateOverride(true);
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
endGestureStateOverride();
endGestureStateOverride(!controller.getFinishTargetIsLauncher());
}
private void endGestureStateOverride() {
private void endGestureStateOverride(boolean finishedToApp) {
callbacks.removeListener(this);
mIsAnimatingToLauncherViaGesture = false;
mIconAlignmentForGestureState
.animateToValue(mIconAlignmentForGestureState.value, 0)
.animateToValue(0)
.start();
if (finishedToApp) {
// We only need this for the exiting live tile case.
stashController.animateToIsStashed(stashController.isStashedInApp());
}
}
});
return animator;
@@ -215,6 +235,11 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
}
}
@Override
public boolean onLongPressToUnstashTaskbar() {
return mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
}
/**
* Should be called when one or more items in the Hotseat have changed.
*/
@@ -0,0 +1,108 @@
/*
* 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 android.animation.Animator;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewOutlineProvider;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.quickstep.AnimatedFloat;
/**
* Handles properties/data collection, then passes the results to our stashed handle View to render.
*/
public class StashedHandleViewController {
private final TaskbarActivityContext mActivity;
private final View mStashedHandleView;
private final int mStashedHandleWidth;
private final int mStashedHandleHeight;
private final AnimatedFloat mTaskbarStashedHandleAlpha = new AnimatedFloat(
this::updateStashedHandleAlpha);
// Initialized in init.
private TaskbarControllers mControllers;
// The bounds we want to clip to in the settled state when showing the stashed handle.
private final Rect mStashedHandleBounds = new Rect();
private float mStashedHandleRadius;
private boolean mIsAtStashedRevealBounds = true;
public StashedHandleViewController(TaskbarActivityContext activity, View stashedHandleView) {
mActivity = activity;
mStashedHandleView = stashedHandleView;
final Resources resources = mActivity.getResources();
mStashedHandleWidth = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
mStashedHandleHeight = resources.getDimensionPixelSize(
R.dimen.taskbar_stashed_handle_height);
}
public void init(TaskbarControllers controllers) {
mControllers = controllers;
mStashedHandleView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
updateStashedHandleAlpha();
final int stashedTaskbarHeight = mControllers.taskbarStashController.getStashedHeight();
mStashedHandleView.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
final int stashedCenterX = view.getWidth() / 2;
final int stashedCenterY = view.getHeight() - stashedTaskbarHeight / 2;
mStashedHandleBounds.set(
stashedCenterX - mStashedHandleWidth / 2,
stashedCenterY - mStashedHandleHeight / 2,
stashedCenterX + mStashedHandleWidth / 2,
stashedCenterY + mStashedHandleHeight / 2);
mStashedHandleRadius = view.getHeight() / 2f;
outline.setRoundRect(mStashedHandleBounds, mStashedHandleRadius);
}
});
}
public AnimatedFloat getStashedHandleAlpha() {
return mTaskbarStashedHandleAlpha;
}
/**
* Creates and returns a {@link RevealOutlineAnimation} Animator that updates the stashed handle
* shape and size. When stashed, the shape is a thin rounded pill. When unstashed, the shape
* morphs into the size of where the taskbar icons will be.
*/
public @Nullable Animator createRevealAnimToIsStashed(boolean isStashed) {
if (mIsAtStashedRevealBounds == isStashed) {
return null;
}
mIsAtStashedRevealBounds = isStashed;
final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
mStashedHandleRadius, mStashedHandleRadius,
mControllers.taskbarViewController.getIconLayoutBounds(), mStashedHandleBounds);
return handleRevealProvider.createRevealAnimator(mStashedHandleView, !isStashed);
}
protected void updateStashedHandleAlpha() {
mStashedHandleView.setAlpha(mTaskbarStashedHandleAlpha.value);
}
}
@@ -115,6 +115,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
R.layout.taskbar, null, false);
TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
View stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
// Construct controllers.
mControllers = new TaskbarControllers(this,
@@ -125,7 +126,9 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
R.color.popup_color_primary_light),
new TaskbarDragLayerController(this, mDragLayer),
new TaskbarViewController(this, taskbarView),
new TaskbarKeyguardController(this));
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
new TaskbarStashController(this));
Display display = windowContext.getDisplay();
Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
@@ -32,6 +32,8 @@ public class TaskbarControllers {
public final TaskbarDragLayerController taskbarDragLayerController;
public final TaskbarViewController taskbarViewController;
public final TaskbarKeyguardController taskbarKeyguardController;
public final StashedHandleViewController stashedHandleViewController;
public final TaskbarStashController taskbarStashController;
/** Do not store this controller, as it may change at runtime. */
@NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT;
@@ -43,7 +45,9 @@ public class TaskbarControllers {
RotationButtonController rotationButtonController,
TaskbarDragLayerController taskbarDragLayerController,
TaskbarViewController taskbarViewController,
TaskbarKeyguardController taskbarKeyguardController) {
TaskbarKeyguardController taskbarKeyguardController,
StashedHandleViewController stashedHandleViewController,
TaskbarStashController taskbarStashController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -52,6 +56,8 @@ public class TaskbarControllers {
this.taskbarDragLayerController = taskbarDragLayerController;
this.taskbarViewController = taskbarViewController;
this.taskbarKeyguardController = taskbarKeyguardController;
this.stashedHandleViewController = stashedHandleViewController;
this.taskbarStashController = taskbarStashController;
}
/**
@@ -67,6 +73,8 @@ public class TaskbarControllers {
taskbarDragLayerController.init(this);
taskbarViewController.init(this);
taskbarKeyguardController.init(navbarButtonsViewController);
stashedHandleViewController.init(this);
taskbarStashController.init(this);
}
/**
@@ -40,10 +40,11 @@ import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInset
public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
private final Paint mTaskbarBackgroundPaint;
private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks;
private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
private float mTaskbarBackgroundOffset;
public TaskbarDragLayer(@NonNull Context context) {
this(context, null);
@@ -118,8 +119,10 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
@Override
protected void dispatchDraw(Canvas canvas) {
canvas.drawRect(0, canvas.getHeight() - mControllerCallbacks.getTaskbarBackgroundHeight(),
canvas.getWidth(), canvas.getHeight(), mTaskbarBackgroundPaint);
float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight()
* (1f - mTaskbarBackgroundOffset);
canvas.drawRect(0, canvas.getHeight() - backgroundHeight, canvas.getWidth(),
canvas.getHeight(), mTaskbarBackgroundPaint);
super.dispatchDraw(canvas);
}
@@ -132,6 +135,15 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
invalidate();
}
/**
* Sets the translation of the background color behind all the Taskbar contents.
* @param offset 0 is fully onscreen, 1 is fully offscreen.
*/
protected void setTaskbarBackgroundOffset(float offset) {
mTaskbarBackgroundOffset = offset;
invalidate();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
@@ -39,6 +39,8 @@ public class TaskbarDragLayerController {
// Alpha properties for taskbar background.
private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
private final AnimatedFloat mBgNavbar = new AnimatedFloat(this::updateBackgroundAlpha);
// Translation property for taskbar background.
private final AnimatedFloat mBgOffset = new AnimatedFloat(this::updateBackgroundOffset);
// Initialized in init.
private TaskbarControllers mControllers;
@@ -78,10 +80,18 @@ public class TaskbarDragLayerController {
return mBgNavbar;
}
public AnimatedFloat getTaskbarBackgroundOffset() {
return mBgOffset;
}
private void updateBackgroundAlpha() {
mTaskbarDragLayer.setTaskbarBackgroundAlpha(Math.max(mBgNavbar.value, mBgTaskbar.value));
}
private void updateBackgroundOffset() {
mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
}
/**
* Callbacks for {@link TaskbarDragLayer} to interact with its controller.
*/
@@ -0,0 +1,262 @@
/*
* 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 android.view.HapticFeedbackConstants.LONG_PRESS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.annotation.Nullable;
import android.content.SharedPreferences;
import android.content.res.Resources;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.quickstep.AnimatedFloat;
/**
* Coordinates between controllers such as TaskbarViewController and StashedHandleViewController to
* create a cohesive animation between stashed/unstashed states.
*/
public class TaskbarStashController {
/**
* How long to stash/unstash when manually invoked via long press.
*/
private static final long TASKBAR_STASH_DURATION = 300;
/**
* The scale TaskbarView animates to when being stashed.
*/
private static final float STASHED_TASKBAR_SCALE = 0.5f;
/**
* The SharedPreferences key for whether user has manually stashed the taskbar.
*/
private static final String SHARED_PREFS_STASHED_KEY = "taskbar_is_stashed";
/**
* Whether taskbar should be stashed out of the box.
*/
private static final boolean DEFAULT_STASHED_PREF = false;
private final TaskbarActivityContext mActivity;
private final SharedPreferences mPrefs;
private final int mStashedHeight;
private final int mUnstashedHeight;
// Initialized in init.
private TaskbarControllers mControllers;
// Taskbar background properties.
private AnimatedFloat mTaskbarBackgroundOffset;
// TaskbarView icon properties.
private AlphaProperty mIconAlphaForStash;
private AnimatedFloat mIconScaleForStash;
private AnimatedFloat mIconTranslationYForStash;
// Stashed handle properties.
private AnimatedFloat mTaskbarStashedHandleAlpha;
/** Whether the user has manually invoked taskbar stashing, which we persist. */
private boolean mIsStashedInApp;
/** Whether we are currently visually stashed (might change based on launcher state). */
private boolean mIsStashed = false;
private @Nullable AnimatorSet mAnimator;
public TaskbarStashController(TaskbarActivityContext activity) {
mActivity = activity;
mPrefs = Utilities.getPrefs(mActivity);
final Resources resources = mActivity.getResources();
mStashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
}
public void init(TaskbarControllers controllers) {
mControllers = controllers;
TaskbarDragLayerController dragLayerController = controllers.taskbarDragLayerController;
mTaskbarBackgroundOffset = dragLayerController.getTaskbarBackgroundOffset();
TaskbarViewController taskbarViewController = controllers.taskbarViewController;
mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().getProperty(
TaskbarViewController.ALPHA_INDEX_STASH);
mIconScaleForStash = taskbarViewController.getTaskbarIconScaleForStash();
mIconTranslationYForStash = taskbarViewController.getTaskbarIconTranslationYForStash();
StashedHandleViewController stashedHandleController =
controllers.stashedHandleViewController;
mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha();
mIsStashedInApp = supportsStashing()
&& mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
}
/**
* Returns whether the user can manually stash the taskbar based on the current device state.
*/
private boolean supportsStashing() {
return !mActivity.isThreeButtonNav();
}
/**
* Returns whether the taskbar is currently visually stashed.
*/
public boolean isStashed() {
return mIsStashed;
}
/**
* Returns whether the user has manually stashed the taskbar in apps.
*/
public boolean isStashedInApp() {
return mIsStashedInApp;
}
public int getContentHeight() {
return isStashed() ? mStashedHeight : mUnstashedHeight;
}
public int getStashedHeight() {
return mStashedHeight;
}
/**
* Should be called when long pressing the nav region when taskbar is present.
* @return Whether taskbar was stashed and now is unstashed.
*/
public boolean onLongPressToUnstashTaskbar() {
if (!isStashed()) {
// We only listen for long press on the nav region to unstash the taskbar. To stash the
// taskbar, we use an OnLongClickListener on TaskbarView instead.
return false;
}
if (updateAndAnimateIsStashedInApp(false)) {
mControllers.taskbarActivityContext.getDragLayer().performHapticFeedback(LONG_PRESS);
return true;
}
return false;
}
/**
* Updates whether we should stash the taskbar when in apps, and animates to the changed state.
* @return Whether we started an animation to either be newly stashed or unstashed.
*/
public boolean updateAndAnimateIsStashedInApp(boolean isStashedInApp) {
if (!supportsStashing()) {
return false;
}
if (mIsStashedInApp != isStashedInApp) {
boolean wasStashed = mIsStashedInApp;
mIsStashedInApp = isStashedInApp;
mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_KEY, mIsStashedInApp).apply();
boolean isStashed = mIsStashedInApp;
if (wasStashed != isStashed) {
createAnimToIsStashed(isStashed, TASKBAR_STASH_DURATION).start();
return true;
}
}
return false;
}
/**
* Starts an animation to the new stashed state with a default duration.
*/
public void animateToIsStashed(boolean isStashed) {
animateToIsStashed(isStashed, TASKBAR_STASH_DURATION);
}
/**
* Starts an animation to the new stashed state with the specified duration.
*/
public void animateToIsStashed(boolean isStashed, long duration) {
createAnimToIsStashed(isStashed, duration).start();
}
private Animator createAnimToIsStashed(boolean isStashed, long duration) {
AnimatorSet fullLengthAnimatorSet = new AnimatorSet();
// Not exactly half and may overlap. See [first|second]HalfDurationScale below.
AnimatorSet firstHalfAnimatorSet = new AnimatorSet();
AnimatorSet secondHalfAnimatorSet = new AnimatorSet();
final float firstHalfDurationScale;
final float secondHalfDurationScale;
if (isStashed) {
firstHalfDurationScale = 0.75f;
secondHalfDurationScale = 0.5f;
final float stashTranslation = (mUnstashedHeight - mStashedHeight) / 2f;
fullLengthAnimatorSet.playTogether(
mTaskbarBackgroundOffset.animateToValue(1),
mIconTranslationYForStash.animateToValue(stashTranslation)
);
firstHalfAnimatorSet.playTogether(
mIconAlphaForStash.animateToValue(0),
mIconScaleForStash.animateToValue(STASHED_TASKBAR_SCALE)
);
secondHalfAnimatorSet.playTogether(
mTaskbarStashedHandleAlpha.animateToValue(1)
);
} else {
firstHalfDurationScale = 0.5f;
secondHalfDurationScale = 0.75f;
fullLengthAnimatorSet.playTogether(
mTaskbarBackgroundOffset.animateToValue(0),
mIconScaleForStash.animateToValue(1),
mIconTranslationYForStash.animateToValue(0)
);
firstHalfAnimatorSet.playTogether(
mTaskbarStashedHandleAlpha.animateToValue(0)
);
secondHalfAnimatorSet.playTogether(
mIconAlphaForStash.animateToValue(1)
);
}
Animator stashedHandleRevealAnim = mControllers.stashedHandleViewController
.createRevealAnimToIsStashed(isStashed);
if (stashedHandleRevealAnim != null) {
fullLengthAnimatorSet.play(stashedHandleRevealAnim);
}
fullLengthAnimatorSet.setDuration(duration);
firstHalfAnimatorSet.setDuration((long) (duration * firstHalfDurationScale));
secondHalfAnimatorSet.setDuration((long) (duration * secondHalfDurationScale));
secondHalfAnimatorSet.setStartDelay((long) (duration * (1 - secondHalfDurationScale)));
if (mAnimator != null) {
mAnimator.cancel();
}
mAnimator = new AnimatorSet();
mAnimator.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
secondHalfAnimatorSet);
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mIsStashed = isStashed;
}
@Override
public void onAnimationEnd(Animator animation) {
mAnimator = null;
}
});
return mAnimator;
}
}
@@ -33,4 +33,8 @@ public class TaskbarUIController {
}
protected void updateContentInsets(Rect outContentInsets) { }
protected boolean onLongPressToUnstashTaskbar() {
return false;
}
}
@@ -94,8 +94,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
protected void init(TaskbarViewController.TaskbarViewCallbacks callbacks) {
mControllerCallbacks = callbacks;
mIconClickListener = mControllerCallbacks.getOnClickListener();
mIconLongClickListener = mControllerCallbacks.getOnLongClickListener();
mIconClickListener = mControllerCallbacks.getIconOnClickListener();
mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener();
setOnLongClickListener(mControllerCallbacks.getBackgroundOnLongClickListener());
}
private void removeAndRecycle(View view) {
@@ -235,6 +237,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
return isShown() && mIconLayoutBounds.contains(xInOurCoordinates, yInOurCoorindates);
}
public Rect getIconLayoutBounds() {
return mIconLayoutBounds;
}
// FolderIconParent implemented methods.
@Override
@@ -17,8 +17,8 @@ package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.AnimatedFloat.VALUE;
import android.graphics.Rect;
import android.view.View;
@@ -28,6 +28,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
/**
* Handles properties/data collection, then passes the results to TaskbarView to render.
@@ -38,10 +39,16 @@ public class TaskbarViewController {
public static final int ALPHA_INDEX_HOME = 0;
public static final int ALPHA_INDEX_IME = 1;
public static final int ALPHA_INDEX_KEYGUARD = 2;
public static final int ALPHA_INDEX_STASH = 3;
private final TaskbarActivityContext mActivity;
private final TaskbarView mTaskbarView;
private final MultiValueAlpha mTaskbarIconAlpha;
private final AnimatedFloat mTaskbarIconScaleForStash = new AnimatedFloat(this::updateScale);
private final AnimatedFloat mTaskbarIconTranslationYForHome = new AnimatedFloat(
this::updateTranslationY);
private final AnimatedFloat mTaskbarIconTranslationYForStash = new AnimatedFloat(
this::updateTranslationY);
// Initialized in init.
private TaskbarControllers mControllers;
@@ -54,7 +61,7 @@ public class TaskbarViewController {
public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
mActivity = activity;
mTaskbarView = taskbarView;
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 3);
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 4);
mTaskbarIconAlpha.setUpdateVisibility(true);
}
@@ -62,6 +69,8 @@ public class TaskbarViewController {
mControllers = controllers;
mTaskbarView.init(new TaskbarViewCallbacks());
mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
mTaskbarIconScaleForStash.updateValue(1f);
}
public boolean areIconsVisible() {
@@ -86,6 +95,32 @@ public class TaskbarViewController {
mTaskbarView.setClickAndLongClickListenersForIcon(icon);
}
public Rect getIconLayoutBounds() {
return mTaskbarView.getIconLayoutBounds();
}
public AnimatedFloat getTaskbarIconScaleForStash() {
return mTaskbarIconScaleForStash;
}
public AnimatedFloat getTaskbarIconTranslationYForStash() {
return mTaskbarIconTranslationYForStash;
}
/**
* Applies scale properties for the entire TaskbarView (rather than individual icons).
*/
private void updateScale() {
float scale = mTaskbarIconScaleForStash.value;
mTaskbarView.setScaleX(scale);
mTaskbarView.setScaleY(scale);
}
private void updateTranslationY() {
mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value
+ mTaskbarIconTranslationYForStash.value);
}
/**
* Sets the taskbar icon alignment relative to Launcher hotseat icons
* @param alignmentRatio [0, 1]
@@ -116,7 +151,7 @@ public class TaskbarViewController {
/ launcherDp.numShownHotseatIcons;
int offsetY = launcherDp.getTaskbarOffsetY();
setter.setFloat(mTaskbarView, VIEW_TRANSLATE_Y, -offsetY, LINEAR);
setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
int collapsedHeight = mActivity.getDeviceProfile().taskbarSize;
int expandedHeight = collapsedHeight + offsetY;
@@ -144,12 +179,16 @@ public class TaskbarViewController {
* Callbacks for {@link TaskbarView} to interact with its controller.
*/
public class TaskbarViewCallbacks {
public View.OnClickListener getOnClickListener() {
public View.OnClickListener getIconOnClickListener() {
return mActivity::onTaskbarIconClicked;
}
public View.OnLongClickListener getOnLongClickListener() {
public View.OnLongClickListener getIconOnLongClickListener() {
return mControllers.taskbarDragController::startDragOnLongClick;
}
public View.OnLongClickListener getBackgroundOnLongClickListener() {
return view -> mControllers.taskbarStashController.updateAndAnimateIsStashedInApp(true);
}
}
}
@@ -53,6 +53,16 @@ public class AnimatedFloat {
mUpdateCallback = updateCallback;
}
/**
* Returns an animation from the current value to the given value.
*/
public ObjectAnimator animateToValue(float end) {
return animateToValue(value, end);
}
/**
* Returns an animation from the given start value to the given end value.
*/
public ObjectAnimator animateToValue(float start, float end) {
cancelAnimation();
mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, start, end);
@@ -363,6 +363,14 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
return null;
}
/**
* Called when we detect a long press in the nav region before passing the gesture slop.
* @return Whether taskbar handled the long press, and thus should cancel the gesture.
*/
public boolean onLongPressToUnstashTaskbar() {
return false;
}
/**
* Returns the color of the scrim behind overview when at rest in this state.
* Return {@link Color#TRANSPARENT} for no scrim.
@@ -39,6 +39,7 @@ public interface InputConsumer {
int TYPE_OVERSCROLL = 1 << 9;
int TYPE_SYSUI_OVERLAY = 1 << 10;
int TYPE_ONE_HANDED = 1 << 11;
int TYPE_TASKBAR_STASH = 1 << 12;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -53,6 +54,7 @@ public interface InputConsumer {
"TYPE_OVERSCROLL", // 9
"TYPE_SYSUI_OVERLAY", // 10
"TYPE_ONE_HANDED", // 11
"TYPE_TASKBAR_STASH", // 12
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
@@ -302,6 +302,15 @@ public final class LauncherActivityInterface extends
}
}
@Override
public boolean onLongPressToUnstashTaskbar() {
LauncherTaskbarUIController taskbarController = getTaskbarController();
if (taskbarController == null) {
return super.onLongPressToUnstashTaskbar();
}
return taskbarController.onLongPressToUnstashTaskbar();
}
@Override
protected int getOverviewScrimColorForState(BaseQuickstepLauncher launcher,
LauncherState state) {
@@ -46,6 +46,8 @@ public class RecentsAnimationController {
private boolean mUseLauncherSysBarFlags = false;
private boolean mSplitScreenMinimized = false;
private boolean mFinishRequested = false;
// Only valid when mFinishRequested == true.
private boolean mFinishTargetIsLauncher;
private RunnableList mPendingFinishCallbacks = new RunnableList();
public RecentsAnimationController(RecentsAnimationControllerCompat controller,
@@ -145,6 +147,7 @@ public class RecentsAnimationController {
// Finish not yet requested
mFinishRequested = true;
mFinishTargetIsLauncher = toRecents;
mOnFinishedListener.accept(this);
mPendingFinishCallbacks.add(callback);
UI_HELPER_EXECUTOR.execute(() -> {
@@ -201,4 +204,12 @@ public class RecentsAnimationController {
public RecentsAnimationControllerCompat getController() {
return mController;
}
/**
* RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether
* the animation was finished to launcher vs an app.
*/
public boolean getFinishTargetIsLauncher() {
return mFinishTargetIsLauncher;
}
}
@@ -95,6 +95,7 @@ import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.util.ProtoTracer;
@@ -673,6 +674,14 @@ public class TouchInteractionService extends Service implements PluginListener<O
mDeviceState, event);
}
// If Taskbar is present, we listen for long press to unstash it.
BaseActivityInterface activityInterface = newGestureState.getActivityInterface();
StatefulActivity activity = activityInterface.getCreatedActivity();
if (activity != null && activity.getDeviceProfile().isTaskbarPresent) {
base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat,
activityInterface);
}
if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
OverscrollPlugin plugin = null;
if (FeatureFlags.FORCE_LOCAL_OVERSCROLL_PLUGIN.get()) {
@@ -832,7 +841,13 @@ public class TouchInteractionService extends Service implements PluginListener<O
}
private void reset() {
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
if (mResetGestureInputConsumer != null) {
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
} else {
// mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
// NO_OP until then (we never want these to be null).
mConsumer = mUncheckedConsumer = InputConsumer.NO_OP;
}
mGestureState = DEFAULT_STATE;
// By default, use batching of the input events, but check receiver before using in the rare
// case that the monitor was disposed before the swipe settled
@@ -0,0 +1,66 @@
/*
* 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.quickstep.inputconsumers;
import android.content.Context;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
* Listens for a long press, and cancels the current gesture if that causes Taskbar to be unstashed.
*/
public class TaskbarStashInputConsumer extends DelegateInputConsumer {
private final BaseActivityInterface mActivityInterface;
private final GestureDetector mLongPressDetector;
public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor, BaseActivityInterface activityInterface) {
super(delegate, inputMonitor);
mActivityInterface = activityInterface;
mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent motionEvent) {
onLongPressDetected(motionEvent);
}
});
}
@Override
public int getType() {
return TYPE_TASKBAR_STASH | mDelegate.getType();
}
@Override
public void onMotionEvent(MotionEvent ev) {
mLongPressDetector.onTouchEvent(ev);
if (mState != STATE_ACTIVE) {
mDelegate.onMotionEvent(ev);
}
}
private void onLongPressDetected(MotionEvent motionEvent) {
if (mActivityInterface.onLongPressToUnstashTaskbar()) {
setActive(motionEvent);
}
}
}
@@ -24,6 +24,7 @@ import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.view.View;
@@ -98,6 +99,14 @@ public class WorkspaceRevealAnim {
alpha.setDuration(DURATION_MS);
alpha.setInterpolator(Interpolators.DECELERATED_EASE);
mAnimators.play(alpha);
mAnimators.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
SCALE_PROPERTY.set(v, 1f);
v.setAlpha(1f);
}
});
}
/**
@@ -2236,7 +2236,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
public PendingAnimation createTaskDismissAnimation(TaskView dismissedTaskView,
boolean animateTaskView, boolean shouldRemoveTask, long duration) {
if (mPendingAnimation != null) {
mPendingAnimation.createPlaybackController().dispatchOnCancel();
mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd();
}
PendingAnimation anim = new PendingAnimation(duration);
-1
View File
@@ -20,7 +20,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="@dimen/widget_list_entry_bottom_margin"
android:paddingVertical="@dimen/widget_list_header_view_vertical_padding"
android:orientation="horizontal"
launcher:appIconSize="48dp">
+1 -2
View File
@@ -18,5 +18,4 @@
android:id="@+id/widgets_table"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="@dimen/widget_list_entry_bottom_margin"/>
android:layout_marginHorizontal="16dp"/>
+1 -1
View File
@@ -147,7 +147,7 @@
<dimen name="widget_list_content_corner_radius">4dp</dimen>
<dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
<dimen name="widget_list_entry_bottom_margin">2dp</dimen>
<dimen name="widget_list_entry_spacing">2dp</dimen>
<dimen name="widget_preview_shadow_blur">0.5dp</dimen>
<dimen name="widget_preview_key_shadow_distance">1dp</dimen>
+1
View File
@@ -16,6 +16,7 @@
-->
<resources>
<item type="id" name="apps_list_view_work" />
<item type="id" name="tag_widget_entry" />
<item type="id" name="view_type_widgets_list" />
<item type="id" name="view_type_widgets_header" />
<item type="id" name="view_type_widgets_search_header" />
@@ -278,12 +278,19 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL
}
}
public void dispatchOnStart() {
public AnimatorPlaybackController dispatchOnStart() {
callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationStart);
return this;
}
public void dispatchOnCancel() {
public AnimatorPlaybackController dispatchOnCancel() {
callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationCancel);
return this;
}
public AnimatorPlaybackController dispatchOnEnd() {
callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationEnd);
return this;
}
public void dispatchSetInterpolator(TimeInterpolator interpolator) {
@@ -328,7 +335,7 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL
public void onAnimationSuccess(Animator animator) {
// We wait for the spring (if any) to finish running before completing the end callback.
if (!mDispatched) {
callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationEnd);
dispatchOnEnd();
if (mEndAction != null) {
mEndAction.run();
}
@@ -168,6 +168,8 @@ public class ItemInfo {
cellY = info.cellY;
spanX = info.spanX;
spanY = info.spanY;
minSpanX = info.minSpanX;
minSpanY = info.minSpanY;
rank = info.rank;
screenId = info.screenId;
itemType = info.itemType;
@@ -113,12 +113,6 @@ public class SettingsActivity extends FragmentActivity
Utilities.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onStop() {
super.onStop();
finish();
}
/**
* Obtains the preference fragment to instantiate in this activity.
*
@@ -209,7 +209,7 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
// Cancel the current animation. This will reset mState to mCurrentStableState, so store it.
STATE_TYPE fromState = mState;
mConfig.reset();
cancelAnimation();
if (!animated) {
mAtomicAnimationFactory.cancelAllStateElementAnimation();
@@ -303,7 +303,7 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
public AnimatorPlaybackController createAnimationToNewWorkspace(STATE_TYPE state,
StateAnimationConfig config) {
config.userControlled = true;
mConfig.reset();
cancelAnimation();
config.copyTo(mConfig);
mConfig.playbackController = createAnimationToNewWorkspaceInternal(state)
.createPlaybackController();
@@ -393,6 +393,11 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
*/
public void cancelAnimation() {
mConfig.reset();
// It could happen that a new animation is set as a result of an endListener on the
// existing animation.
while (mConfig.currentAnimation != null || mConfig.playbackController != null) {
mConfig.reset();
}
}
public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
@@ -508,14 +513,19 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
* Cancels the current animation and resets config variables.
*/
public void reset() {
AnimatorSet anim = currentAnimation;
AnimatorPlaybackController pc = playbackController;
DEFAULT.copyTo(this);
targetState = null;
currentAnimation = null;
playbackController = null;
changeId++;
if (playbackController != null) {
playbackController.getAnimationPlayer().cancel();
playbackController.dispatchOnCancel();
} else if (currentAnimation != null) {
AnimatorSet anim = currentAnimation;
if (pc != null) {
pc.getAnimationPlayer().cancel();
pc.dispatchOnCancel().dispatchOnEnd();
} else if (anim != null) {
anim.setDuration(0);
if (!anim.isStarted()) {
// If the animation is not started the listeners do not get notified,
@@ -525,10 +535,6 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
}
anim.cancel();
}
currentAnimation = null;
playbackController = null;
changeId++;
}
@Override
@@ -16,6 +16,8 @@
package com.android.launcher3.util;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.util.FloatProperty;
import android.view.View;
@@ -121,5 +123,12 @@ public class MultiValueAlpha {
public String toString() {
return Float.toString(mValue);
}
/**
* Creates and returns an Animator from the current value to the given value.
*/
public Animator animateToValue(float value) {
return ObjectAnimator.ofFloat(this, VALUE, value);
}
}
}
@@ -60,7 +60,7 @@ public final class WidgetsListSearchHeaderEntry extends WidgetsListBaseEntry
WidgetsListSearchHeaderEntry otherEntry = (WidgetsListSearchHeaderEntry) obj;
return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
&& mTitleSectionName.equals(otherEntry.mTitleSectionName)
&& mIsWidgetListShown;
&& mIsWidgetListShown == otherEntry.mIsWidgetListShown;
}
/** Returns a copy of this {@link WidgetsListSearchHeaderEntry} with the widget list shown. */
@@ -18,6 +18,7 @@ package com.android.launcher3.widget.picker;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_APP_EXPANDED;
import android.content.Context;
import android.graphics.Rect;
import android.os.Process;
import android.util.Log;
import android.util.Size;
@@ -34,6 +35,7 @@ import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.LayoutParams;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.launcher3.BaseActivity;
@@ -107,7 +109,8 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
@Nullable private RecyclerView mRecyclerView;
@Nullable private PackageUserKey mPendingClickHeader;
private int mShortcutPreviewPadding;
private final int mShortcutPreviewPadding;
private final int mSpacingBetweenEntries;
private final WidgetPreviewLoadedCallback mPreviewLoadedCallback =
ignored -> updateVisibleEntries();
@@ -141,11 +144,28 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
mShortcutPreviewPadding =
2 * context.getResources()
.getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
mSpacingBetweenEntries =
context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
mRecyclerView = recyclerView;
mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(
@NonNull Rect outRect,
@NonNull View view,
@NonNull RecyclerView parent,
@NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
boolean isHeader =
view.getTag(R.id.tag_widget_entry) instanceof WidgetsListBaseEntry.Header;
outRect.top += position > 0 && isHeader ? mSpacingBetweenEntries : 0;
}
});
}
@Override
@@ -329,7 +349,9 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
@Override
public void onBindViewHolder(ViewHolder holder, int pos) {
ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), pos);
holder.itemView.setTag(R.id.tag_widget_entry, entry);
}
@Override
@@ -30,7 +30,6 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
@@ -59,7 +58,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
@Nullable private HandlerRunnable mIconLoadRequest;
@Nullable private Drawable mIconDrawable;
private final int mIconSize;
private final int mBottomMarginSize;
private ImageView mAppIcon;
private TextView mTitle;
@@ -86,8 +84,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
grid.iconSizePx);
mBottomMarginSize =
getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin);
}
@Override
@@ -146,13 +142,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
public void setExpanded(boolean isExpanded) {
this.mIsExpanded = isExpanded;
mExpandToggle.setChecked(isExpanded);
if (getLayoutParams() instanceof RecyclerView.LayoutParams) {
int bottomMargin = isExpanded ? 0 : mBottomMarginSize;
RecyclerView.LayoutParams layoutParams =
((RecyclerView.LayoutParams) getLayoutParams());
layoutParams.bottomMargin = bottomMargin;
setLayoutParams(layoutParams);
}
}
/** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
@@ -52,7 +52,7 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
private int mLastVisibleWidgetContentTableHeight = 0;
private int mWidgetHeaderHeight = 0;
private final int mCollapsedHeaderBottomMarginSize;
private final int mSpacingBetweenEntries;
@Nullable private OnContentChangeListener mOnContentChangeListener;
public WidgetsRecyclerView(Context context) {
@@ -72,9 +72,9 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
ActivityContext activity = ActivityContext.lookupContext(getContext());
DeviceProfile grid = activity.getDeviceProfile();
// The bottom margin used when the header is not expanded.
mCollapsedHeaderBottomMarginSize =
getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin);
// The spacing used between entries.
mSpacingBetweenEntries =
getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
}
@Override
@@ -270,16 +270,16 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
if (untilIndex > mAdapter.getItems().size()) {
untilIndex = mAdapter.getItems().size();
}
int expandedHeaderPosition = mAdapter.getSelectedHeaderPosition().orElse(-1);
int totalItemsHeight = 0;
for (int i = 0; i < untilIndex; i++) {
WidgetsListBaseEntry entry = mAdapter.getItems().get(i);
if (entry instanceof WidgetsListHeaderEntry
|| entry instanceof WidgetsListSearchHeaderEntry) {
totalItemsHeight += mWidgetHeaderHeight;
if (expandedHeaderPosition != i) {
// If the header is collapsed, include the bottom margin it will use.
totalItemsHeight += mCollapsedHeaderBottomMarginSize;
if (i > 0) {
// Each header contains the spacing between entries as top decoration, except
// the first one.
totalItemsHeight += mSpacingBetweenEntries;
}
} else if (entry instanceof WidgetsListContentEntry) {
totalItemsHeight += mLastVisibleWidgetContentTableHeight;