diff --git a/quickstep/res/layout/taskbar_edu.xml b/quickstep/res/layout/taskbar_edu.xml index 10444f1d96..d7daea31cd 100644 --- a/quickstep/res/layout/taskbar_edu.xml +++ b/quickstep/res/layout/taskbar_edu.xml @@ -46,7 +46,7 @@ android:id="@+id/edu_start_button" android:layout_width="wrap_content" android:layout_height="36dp" - android:layout_marginBottom="92dp" + android:layout_marginBottom="18dp" android:layout_marginTop="32dp" app:layout_constraintTop_toBottomOf="@id/content" app:layout_constraintBottom_toBottomOf="parent" diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 315665c945..7571b851b7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -878,6 +878,16 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false); } + /** Called when we want to hide the overlay window when user performs swipe up gesture. */ + public void onSwipeToHideOverlay() { + mControllers.taskbarOverlayController.hideWindow(); + } + + /** Returns {@code true} if taskbar is stashed. */ + public boolean isTaskbarStashed() { + return mControllers.taskbarStashController.isStashed(); + } + /** * Called to start the taskbar translation spring to its settled translation (0). */ diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java index 2cb05ff684..d3f1b2f51e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java @@ -15,11 +15,11 @@ */ package com.android.launcher3.taskbar; -import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_APP_EDU; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; - import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext; import com.android.launcher3.taskbar.overlay.TaskbarOverlayController; @@ -62,20 +62,21 @@ public class TaskbarEduController implements TaskbarControllers.LoggableTaskbarC : R.layout.taskbar_edu_pages_persistent, mPagedView, true); + + // Provide enough room for taskbar. + View startButton = mTaskbarEduView.findViewById(R.id.edu_start_button); + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) startButton.getLayoutParams(); + DeviceProfile dp = overlayContext.getDeviceProfile(); + layoutParams.bottomMargin += DisplayController.isTransientTaskbar(overlayContext) + ? dp.taskbarSize + dp.transientTaskbarMargin + : dp.taskbarSize; + mTaskbarEduView.init(new TaskbarEduCallbacks()); mControllers.navbarButtonsViewController.setSlideInViewVisible(true); - TaskbarStashController stashController = mControllers.taskbarStashController; - stashController.updateStateForFlag(FLAG_STASHED_IN_APP_EDU, true); - stashController.applyState(overlayController.getOpenDuration()); - - mTaskbarEduView.setOnCloseBeginListener(() -> { - mControllers.navbarButtonsViewController.setSlideInViewVisible(false); - // Post in case view is closing due to gesture navigation. If a gesture is in progress, - // wait to unstash until after the gesture is finished. - MAIN_EXECUTOR.post(() -> stashController.resetFlagIfNoGestureInProgress( - FLAG_STASHED_IN_APP_EDU)); - }); + mTaskbarEduView.setOnCloseBeginListener( + () -> mControllers.navbarButtonsViewController.setSlideInViewVisible(false)); mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null); mTaskbarEduView.show(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 6d7b2ff548..95337ce77d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -82,7 +82,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba public static final int FLAG_IN_SETUP = 1 << 8; // In the Setup Wizard public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 9; // phone screen gesture nav, stashed public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 10; // Autohide (transient taskbar). - public static final int FLAG_STASHED_IN_APP_EDU = 1 << 11; // EDU is visible. // If any of these flags are enabled, isInApp should return true. private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP; @@ -91,7 +90,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL | FLAG_STASHED_IN_SYSUI_STATE | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS - | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO | FLAG_STASHED_IN_APP_EDU; + | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO; private static final int FLAGS_STASHED_IN_APP_IGNORING_IME = FLAGS_STASHED_IN_APP & ~FLAG_STASHED_IN_APP_IME; @@ -101,8 +100,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba // Currently any flag that causes us to stash in an app is included, except for IME or All Apps // since those cover the underlying app anyway and thus the app shouldn't change insets. private static final int FLAGS_REPORT_STASHED_INSETS_TO_APP = FLAGS_STASHED_IN_APP - & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_TASKBAR_ALL_APPS - & ~FLAG_STASHED_IN_APP_EDU; + & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_TASKBAR_ALL_APPS; /** * How long to stash/unstash when manually invoked via long press. @@ -728,7 +726,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba // Only update the following flags when system gesture is not in progress. boolean shouldStashForIme = shouldStashForIme(); updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false); - updateStateForFlag(FLAG_STASHED_IN_APP_EDU, false); if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != shouldStashForIme) { updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme); applyState(TASKBAR_STASH_DURATION_FOR_IME, getTaskbarStashStartDelayForIme()); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index a059295bfc..b3b53c2732 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -25,6 +25,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; +import com.android.launcher3.util.DisplayController; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -84,7 +85,9 @@ public class TaskbarUIController { * Manually closes the overlay window. */ public void hideOverlayWindow() { - mControllers.taskbarOverlayController.hideWindow(); + if (!DisplayController.isTransientTaskbar(mControllers.taskbarActivityContext)) { + mControllers.taskbarOverlayController.hideWindow(); + } } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java index f8d9d11d39..70405d94cb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java @@ -19,6 +19,8 @@ import android.content.Context; import android.util.AttributeSet; import android.view.WindowInsets; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.R; import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext; @@ -44,4 +46,11 @@ public class TaskbarAllAppsContainerView extends protected boolean isSearchSupported() { return false; } + + @Override + protected void updateBackground(DeviceProfile deviceProfile) { + super.updateBackground(deviceProfile); + // TODO(b/240670050): Remove this and add header protection for the taskbar entrypoint. + mBottomSheetBackground.setBackgroundResource(R.drawable.bg_rounded_corner_bottom_sheet); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java index 41654866bd..721f81656a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java @@ -16,6 +16,7 @@ package com.android.launcher3.taskbar.allapps; import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_APP_AUTO; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_TASKBAR_ALL_APPS; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT; @@ -28,6 +29,7 @@ import com.android.launcher3.taskbar.TaskbarControllers; import com.android.launcher3.taskbar.TaskbarStashController; import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext; import com.android.launcher3.taskbar.overlay.TaskbarOverlayController; +import com.android.launcher3.util.DisplayController; /** * Handles the {@link TaskbarAllAppsContainerView} behavior and synchronizes its transitions with @@ -88,18 +90,25 @@ final class TaskbarAllAppsViewController { } private void setUpTaskbarStashing() { - mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true); + mTaskbarStashController.updateStateForFlag( + DisplayController.isTransientTaskbar(mContext) + ? FLAG_STASHED_IN_APP_AUTO + : FLAG_STASHED_IN_TASKBAR_ALL_APPS, + true); mTaskbarStashController.applyState( ALL_APPS.getTransitionDuration(mContext, true /* isToState */)); + mNavbarButtonsViewController.setSlideInViewVisible(true); mSlideInView.setOnCloseBeginListener(() -> { mNavbarButtonsViewController.setSlideInViewVisible(false); AbstractFloatingView.closeOpenContainer( mContext, AbstractFloatingView.TYPE_ACTION_POPUP); - // Post in case view is closing due to gesture navigation. If a gesture is in progress, - // wait to unstash until after the gesture is finished. - MAIN_EXECUTOR.post(() -> mTaskbarStashController.resetFlagIfNoGestureInProgress( - FLAG_STASHED_IN_TASKBAR_ALL_APPS)); + if (!DisplayController.isTransientTaskbar(mContext)) { + // Post in case view is closing due to gesture navigation. If a gesture is in + // progress, wait to unstash until after the gesture is finished. + MAIN_EXECUTOR.post(() -> mTaskbarStashController.resetFlagIfNoGestureInProgress( + FLAG_STASHED_IN_TASKBAR_ALL_APPS)); + } }); } diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java index c6b5117862..cbb1ade7aa 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -34,6 +34,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; +import android.util.Log; import android.util.SparseIntArray; import androidx.annotation.NonNull; @@ -54,6 +55,8 @@ import java.util.function.Consumer; * and provide callers the relevant classes. */ public final class OverviewComponentObserver { + private static final String TAG = "OverviewComponentObserver"; + private final BroadcastReceiver mUserPreferenceChangeReceiver = new SimpleBroadcastReceiver(this::updateOverviewTargets); private final BroadcastReceiver mOtherHomeAppUpdateReceiver = @@ -147,7 +150,12 @@ public final class OverviewComponentObserver { } } - if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) { + // TODO(b/258022658): Remove temporary logging. + Log.i(TAG, "updateOverviewTargets: mIsHomeDisabled=" + mIsHomeDisabled + + ", isDefaultHomeNull=" + (defaultHome == null) + + ", mIsDefaultHome=" + mIsDefaultHome); + + if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) { // User default home is same as out home app. Use Overview integrated in Launcher. mActivityInterface = LauncherActivityInterface.INSTANCE; mIsHomeAndOverviewSame = true; diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java index 3a094905bf..c8326c4189 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java @@ -53,8 +53,11 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { private final float mUnstashArea; private final float mScreenWidth; - private final int mTaskbarThresholdY; - private boolean mHasPassedTaskbarThreshold; + private final int mTaskbarNavThresholdY; + private final int mTaskbarAppWindowThresholdY; + private final boolean mTaskbarAlreadyOpen; + private boolean mHasPassedTaskbarNavThreshold; + private boolean mHasPassedTaskbarAppWindowThreshold; private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); @@ -73,11 +76,18 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { Resources res = context.getResources(); mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area); - int taskbarThreshold = res.getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get() + int taskbarNavThreshold = res.getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get() ? R.dimen.taskbar_nav_threshold_v2 : R.dimen.taskbar_nav_threshold); + int taskbarAppWindowThreshold = res.getDimensionPixelSize( + ENABLE_TASKBAR_REVISED_THRESHOLDS.get() + ? R.dimen.taskbar_app_window_threshold_v2 + : R.dimen.taskbar_app_window_threshold); int screenHeight = taskbarActivityContext.getDeviceProfile().heightPx; - mTaskbarThresholdY = screenHeight - taskbarThreshold; + mTaskbarNavThresholdY = screenHeight - taskbarNavThreshold; + mTaskbarAppWindowThresholdY = screenHeight - taskbarAppWindowThreshold; + mTaskbarAlreadyOpen = mTaskbarActivityContext != null + && !mTaskbarActivityContext.isTaskbarStashed(); mIsTransientTaskbar = DisplayController.isTransientTaskbar(context); @@ -113,7 +123,8 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { mDownPos.set(ev.getX(), ev.getY()); mLastPos.set(mDownPos); - mHasPassedTaskbarThreshold = false; + mHasPassedTaskbarNavThreshold = false; + mHasPassedTaskbarAppWindowThreshold = false; mTaskbarActivityContext.setAutohideSuspendFlag( FLAG_AUTOHIDE_SUSPEND_TOUCHING, true); if (isInArea(x)) { @@ -156,18 +167,23 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { if (mIsTransientTaskbar) { float dY = mLastPos.y - mDownPos.y; - boolean passedTaskbarThreshold = dY < 0 - && mLastPos.y < mTaskbarThresholdY; - - if (!mHasPassedTaskbarThreshold - && passedTaskbarThreshold) { - mHasPassedTaskbarThreshold = true; + boolean passedTaskbarNavThreshold = dY < 0 + && mLastPos.y < mTaskbarNavThresholdY; + boolean passedTaskbarAppWindowThreshold = dY < 0 + && mLastPos.y < mTaskbarAppWindowThresholdY; + if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) { + mHasPassedTaskbarNavThreshold = true; mTaskbarActivityContext.onSwipeToUnstashTaskbar(); } + if (mTaskbarAlreadyOpen || (!mHasPassedTaskbarAppWindowThreshold + && passedTaskbarAppWindowThreshold)) { + mHasPassedTaskbarAppWindowThreshold = true; + mTaskbarActivityContext.onSwipeToHideOverlay(); + } if (dY < 0) { - dY = -OverScroll.dampedScroll(-dY, mTaskbarThresholdY); + dY = -OverScroll.dampedScroll(-dY, mTaskbarNavThresholdY); if (mTransitionCallback != null) { mTransitionCallback.onActionMove(dY); } @@ -185,7 +201,8 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { if (mTransitionCallback != null) { mTransitionCallback.onActionEnd(); } - mHasPassedTaskbarThreshold = false; + mHasPassedTaskbarNavThreshold = false; + mHasPassedTaskbarAppWindowThreshold = false; break; } } diff --git a/res/layout/all_apps_bottom_sheet_background.xml b/res/layout/all_apps_bottom_sheet_background.xml index 3e47690197..b0157c9c3e 100644 --- a/res/layout/all_apps_bottom_sheet_background.xml +++ b/res/layout/all_apps_bottom_sheet_background.xml @@ -16,8 +16,7 @@ + android:layout_height="match_parent"> @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { - if ((SHOW_DOT_PAGINATION.get() || SHOW_DELIGHTFUL_PAGINATION.get()) - && WorkspacePageIndicator.class.getName().equals(name)) { + if ((SHOW_DOT_PAGINATION.get()) && WorkspacePageIndicator.class.getName().equals(name)) { return LayoutInflater.from(context).inflate(R.layout.page_indicator_dots, (ViewGroup) parent, false); } diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index 5296df1d46..e21b4db391 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -281,6 +281,10 @@ public class ActivityAllAppsContainerView @Override public int getHeaderBottom() { if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) { + if (mActivityContext.getDeviceProfile().isTablet) { + return super.getHeaderBottom() + mHeader.getClipTop() + + mBottomSheetBackground.getTop(); + } return super.getHeaderBottom() + mHeader.getClipTop(); } return super.getHeaderBottom() + mSearchContainer.getBottom(); diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index 65b4661199..aea98ae712 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -26,8 +26,11 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Path.Direction; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Bundle; import android.os.Parcelable; import android.os.Process; @@ -35,6 +38,7 @@ import android.os.UserManager; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; +import android.util.TypedValue; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -121,7 +125,7 @@ public abstract class BaseAllAppsContainerView 0 && tabsHeight != 0) { - if (DEBUG_HEADER_PROTECTION) { - mHeaderPaint.setColor(Color.BLUE); - mHeaderPaint.setAlpha(255); - } else { - mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha)); - } - canvas.drawRect(0, bottom, canvas.getWidth(), bottom + tabsHeight, mHeaderPaint); + } + int tabsHeight = headerView.getPeripheralProtectionHeight(); + if (mTabsProtectionAlpha > 0 && tabsHeight != 0) { + if (DEBUG_HEADER_PROTECTION) { + mHeaderPaint.setColor(Color.BLUE); + mHeaderPaint.setAlpha(255); + } else { + mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha)); } + int left = 0; + int right = canvas.getWidth(); + if (isTablet) { + left = mBottomSheetBackground.getLeft(); + right = mBottomSheetBackground.getRight(); + } + canvas.drawRect(left, bottom, right, bottom + tabsHeight, mHeaderPaint); } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 9064217d71..710e7ab2bf 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -311,10 +311,6 @@ public final class FeatureFlags { "SCROLL_TOP_TO_RESET", false, "Bring up IME and focus on " + "input when scroll to top if 'Always show keyboard' is enabled or in prefix state"); - public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION = getDebugFlag( - "SHOW_DELIGHTFUL_PAGINATION", false, - "Enable showing the new 'delightful pagination' which is a brand" - + " new animation for folder pagination and workspace pagination"); public static final BooleanFlag POPUP_MATERIAL_U = new DeviceFlag( "POPUP_MATERIAL_U", false, "Switch popup UX to use material U"); @@ -339,7 +335,7 @@ public final class FeatureFlags { + " nav mode and when transient taskbar is enabled."); public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag( - "ENABLE_TRANSIENT_TASKBAR", false, "Enables transient taskbar."); + "ENABLE_TRANSIENT_TASKBAR", true, "Enables transient taskbar."); public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag( "ENABLE_TRACKPAD_GESTURE", false, "Enables trackpad gesture."); diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java index e9b660626b..c324ce3cf5 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java +++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java @@ -16,7 +16,6 @@ package com.android.launcher3.pageindicators; -import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION; import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION; import android.animation.Animator; @@ -32,7 +31,6 @@ import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; @@ -48,7 +46,6 @@ import androidx.annotation.Nullable; import com.android.launcher3.Insettable; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.util.Themes; /** @@ -70,7 +67,6 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator private static final int PAGE_INDICATOR_ALPHA = 255; private static final int DOT_ALPHA = 128; private static final int DOT_GAP_FACTOR = 3; - private static final float DOT_GAP_FACTOR_FLOAT = 3.8f; private static final int VISIBLE_ALPHA = 1; private static final int INVISIBLE_ALPHA = 0; private Paint mPaginationPaint; @@ -78,8 +74,6 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator // This value approximately overshoots to 1.5 times the original size. private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f; - private static final float INDICATOR_ROTATION = 180f; - private static final RectF sTempRect = new RectF(); private static final FloatProperty CURRENT_POSITION = @@ -112,11 +106,8 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator }; private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper()); - private final Drawable mPageIndicatorDrawable; private final float mDotRadius; private final float mCircleGap; - private final float mPageIndicatorSize; - private final float mPageIndicatorRadius; private final boolean mIsRtl; private int mNumPages; @@ -159,31 +150,14 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator mPaginationPaint.setStyle(Style.FILL); mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor)); mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2; - - if (SHOW_DELIGHTFUL_PAGINATION.get()) { - mPageIndicatorSize = getResources().getDimension( - R.dimen.page_indicator_size); - mPageIndicatorRadius = mPageIndicatorSize / 2; - mPageIndicatorDrawable = context.getDrawable(R.drawable.page_indicator); - mPageIndicatorDrawable.setBounds(0, 0, (int) mPageIndicatorSize, - (int) mPageIndicatorSize); - mCircleGap = DOT_GAP_FACTOR_FLOAT * mDotRadius; - - } else { - mPageIndicatorSize = 0; - mPageIndicatorRadius = 0; - mPageIndicatorDrawable = null; - mCircleGap = DOT_GAP_FACTOR * mDotRadius; - } - if (!SHOW_DELIGHTFUL_PAGINATION.get()) { - setOutlineProvider(new MyOutlineProver()); - } + mCircleGap = DOT_GAP_FACTOR * mDotRadius; + setOutlineProvider(new MyOutlineProver()); mIsRtl = Utilities.isRtl(getResources()); } @Override public void setScroll(int currentScroll, int totalScroll) { - if (SHOW_DELIGHTFUL_PAGINATION.get() || SHOW_DOT_PAGINATION.get()) { + if (SHOW_DOT_PAGINATION.get()) { animatePaginationToAlpha(VISIBLE_ALPHA); } @@ -197,16 +171,6 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator } mTotalScroll = totalScroll; - if (SHOW_DELIGHTFUL_PAGINATION.get()) { - mCurrentScroll = currentScroll; - invalidate(); - - if (mShouldAutoHide - && (getScrollPerPage() == 0 || mCurrentScroll % getScrollPerPage() == 0)) { - hideAfterDelay(); - } - return; - } int scrollPerPage = totalScroll / (mNumPages - 1); int pageToLeft = currentScroll / scrollPerPage; @@ -404,122 +368,45 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator } for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) { mPaginationPaint.setAlpha(i == mActivePage ? PAGE_INDICATOR_ALPHA : DOT_ALPHA); - if (SHOW_DELIGHTFUL_PAGINATION.get()) { - if (i != mActivePage) { - canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i], - mPaginationPaint); - } else { - drawPageIndicator(canvas, mEntryAnimationRadiusFactors[i]); - } - } else { - canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i], - mPaginationPaint); - } + canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i], + mPaginationPaint); x += circleGap; } } else { // Here we draw the dots mPaginationPaint.setAlpha(DOT_ALPHA); for (int i = 0; i < mNumPages; i++) { - if (SHOW_DELIGHTFUL_PAGINATION.get()) { - canvas.drawCircle(x, y, getRadius(x), mPaginationPaint); - } else { - canvas.drawCircle(x, y, mDotRadius, mPaginationPaint); - } + canvas.drawCircle(x, y, mDotRadius, mPaginationPaint); x += circleGap; } // Here we draw the current page indicator mPaginationPaint.setAlpha(PAGE_INDICATOR_ALPHA); - if (SHOW_DELIGHTFUL_PAGINATION.get()) { - drawPageIndicator(canvas, 1); - } else { - canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint); - } + canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint); } } - /** - * Draws the page indicator, denoting the currently selected page - * - * @param canvas is used to draw the page indicator and to rotate it as we scroll - * @param scale is used to set the scale of our canvas - */ - private void drawPageIndicator(Canvas canvas, float scale) { - RectF currRect = getActiveRect(); - - // saves the canvas so we can later restore it to its original scale - canvas.save(); - - // Moves the canvas to start at the top left corner of the page indicator - canvas.translate(currRect.left, currRect.top); - - // Scales the canvas in place to animate the indicator on entry - canvas.scale(scale, scale, mPageIndicatorRadius, mPageIndicatorRadius); - - int scrollPerPage = getScrollPerPage(); - // This IF is to avoid division by 0 - if (scrollPerPage != 0) { - int delta = mCurrentScroll % scrollPerPage; - canvas.rotate((INDICATOR_ROTATION * delta) / scrollPerPage, - mPageIndicatorRadius, mPageIndicatorRadius); - } - - mPageIndicatorDrawable.draw(canvas); - canvas.restore(); - } - - /** - * Returns the radius of the circle based on how close the page indicator is to it - * - * @param dotPositionX is the position the dot is located at in the x-axis - */ - private float getRadius(float dotPositionX) { - - float startXIndicator = - ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2) - getOffset(); - float indicatorPosition = startXIndicator + getIndicatorScrollDistance() - + mPageIndicatorRadius; - - // If the indicator gets close enough to a dot then we change the radius - // of the dot based on how close the indicator is to it. - float dotDistance = Math.abs(indicatorPosition - dotPositionX); - if (dotDistance <= mCircleGap) { - return Utilities.mapToRange(dotDistance, 0, mCircleGap, 0f, mDotRadius, - Interpolators.LINEAR); - } - return mDotRadius; - } - private RectF getActiveRect() { float startCircle = (int) mCurrentPosition; float delta = mCurrentPosition - startCircle; float diameter = 2 * mDotRadius; float startX; - if (SHOW_DELIGHTFUL_PAGINATION.get()) { - startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2) - getOffset(); - sTempRect.top = (getHeight() - mPageIndicatorSize) * 0.5f; - sTempRect.bottom = (getHeight() + mPageIndicatorSize) * 0.5f; - sTempRect.left = startX + getIndicatorScrollDistance(); - sTempRect.right = sTempRect.left + mPageIndicatorSize; + startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2); + sTempRect.top = (getHeight() * 0.5f) - mDotRadius; + sTempRect.bottom = (getHeight() * 0.5f) + mDotRadius; + sTempRect.left = startX + (startCircle * mCircleGap); + sTempRect.right = sTempRect.left + diameter; + + if (delta < SHIFT_PER_ANIMATION) { + // dot is capturing the right circle. + sTempRect.right += delta * mCircleGap * 2; } else { - startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2); - sTempRect.top = (getHeight() * 0.5f) - mDotRadius; - sTempRect.bottom = (getHeight() * 0.5f) + mDotRadius; - sTempRect.left = startX + (startCircle * mCircleGap); - sTempRect.right = sTempRect.left + diameter; + // Dot is leaving the left circle. + sTempRect.right += mCircleGap; - if (delta < SHIFT_PER_ANIMATION) { - // dot is capturing the right circle. - sTempRect.right += delta * mCircleGap * 2; - } else { - // Dot is leaving the left circle. - sTempRect.right += mCircleGap; - - delta -= SHIFT_PER_ANIMATION; - sTempRect.left += delta * mCircleGap * 2; - } + delta -= SHIFT_PER_ANIMATION; + sTempRect.left += delta * mCircleGap * 2; } if (mIsRtl) { @@ -531,29 +418,6 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator return sTempRect; } - /** - * The offset between the radius of the dot and the midpoint of the indicator so that - * the indicator is centered in with the indicator circles - */ - private float getOffset() { - return mPageIndicatorRadius - mDotRadius; - } - - /** - * Returns an int that is the amount we need to scroll per page - */ - private int getScrollPerPage() { - return mNumPages > 1 ? mTotalScroll / (mNumPages - 1) : 0; - } - - /** - * The current scroll adjusted for the distance the indicator needs to travel on the screen - */ - private float getIndicatorScrollDistance() { - int scrollPerPage = getScrollPerPage(); - return scrollPerPage != 0 ? ((float) mCurrentScroll / scrollPerPage) * mCircleGap : 0; - } - private class MyOutlineProver extends ViewOutlineProvider { @Override diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java index 6cb021bc42..96ae4a32fd 100644 --- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java +++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java @@ -44,6 +44,7 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; +import com.android.launcher3.util.TouchUtil; /** * Helper class to handle touch on empty space in workspace and show options popup on long press @@ -105,6 +106,11 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe if (handleLongPress) { mLongPressState = STATE_REQUESTED; mTouchDownPoint.set(ev.getX(), ev.getY()); + // Mouse right button's ACTION_DOWN should immediately show menu + if (TouchUtil.isMouseRightClickDownOrMove(ev)) { + maybeShowMenu(); + return true; + } } mWorkspace.onTouchEvent(ev); @@ -185,6 +191,10 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe @Override public void onLongPress(MotionEvent event) { + maybeShowMenu(); + } + + private void maybeShowMenu() { if (mLongPressState == STATE_REQUESTED) { TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Workspace.longPress"); if (canHandleLongPress()) { diff --git a/src/com/android/launcher3/util/TouchUtil.java b/src/com/android/launcher3/util/TouchUtil.java new file mode 100644 index 0000000000..b18a2ef485 --- /dev/null +++ b/src/com/android/launcher3/util/TouchUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 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.util; + +import android.view.InputDevice; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; + +/** Util class for touch event. */ +public final class TouchUtil { + + private TouchUtil() {} + + /** + * Detect ACTION_DOWN or ACTION_MOVE from mouse right button. Note that we cannot detect + * ACTION_UP from mouse's right button because, in that case, + * {@link MotionEvent#getButtonState()} returns 0 for any mouse button (right, middle, right). + */ + public static boolean isMouseRightClickDownOrMove(@NonNull MotionEvent event) { + return event.isFromSource(InputDevice.SOURCE_MOUSE) + && ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0); + } +} diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 1f5590e843..0fccf7960b 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -33,6 +33,7 @@ import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -85,6 +86,7 @@ public class AddWidgetTest extends AbstractLauncherUiTest { * A custom shortcut is a 1x1 widget that launches a specific intent when user tap on it. * Custom shortcuts are replaced by deep shortcuts after api 25. */ + @Ignore @Test @PortraitLandscape public void testDragCustomShortcut() throws Throwable { diff --git a/tests/src/com/android/launcher3/util/TouchUtilTest.kt b/tests/src/com/android/launcher3/util/TouchUtilTest.kt new file mode 100644 index 0000000000..d6c6e91e49 --- /dev/null +++ b/tests/src/com/android/launcher3/util/TouchUtilTest.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 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.util + +import android.view.InputDevice +import android.view.MotionEvent +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Test +import org.junit.runner.RunWith + +/** Unit tests for [TouchUtil] */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class TouchUtilTest { + + @Test + fun isMouseRightClickDownOrMove_onMouseRightButton_returnsTrue() { + val ev = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 1.0f, 0.0f, 0) + ev.source = InputDevice.SOURCE_MOUSE + ev.buttonState = MotionEvent.BUTTON_SECONDARY + + assertThat(TouchUtil.isMouseRightClickDownOrMove(ev)).isTrue() + } + + @Test + fun isMouseRightClickDownOrMove_onMouseLeftButton_returnsFalse() { + val ev = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 1.0f, 0.0f, 0) + ev.source = InputDevice.SOURCE_MOUSE + ev.buttonState = MotionEvent.BUTTON_PRIMARY + + assertThat(TouchUtil.isMouseRightClickDownOrMove(ev)).isFalse() + } + + @Test + fun isMouseRightClickDownOrMove_onMouseTertiaryButton_returnsFalse() { + val ev = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 1.0f, 0.0f, 0) + ev.source = InputDevice.SOURCE_MOUSE + ev.buttonState = MotionEvent.BUTTON_TERTIARY + + assertThat(TouchUtil.isMouseRightClickDownOrMove(ev)).isFalse() + } + + @Test + fun isMouseRightClickDownOrMove_onDpadRightButton_returnsFalse() { + val ev = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 1.0f, 0.0f, 0) + ev.source = InputDevice.SOURCE_DPAD + ev.buttonState = MotionEvent.BUTTON_SECONDARY + + assertThat(TouchUtil.isMouseRightClickDownOrMove(ev)).isFalse() + } +}