diff --git a/quickstep/res/layout/taskbar_all_apps.xml b/quickstep/res/layout/taskbar_all_apps.xml index 7dc0cbe1ab..d402469833 100644 --- a/quickstep/res/layout/taskbar_all_apps.xml +++ b/quickstep/res/layout/taskbar_all_apps.xml @@ -51,6 +51,12 @@ + + diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java index 27e89baa90..4e1f54c9a2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java @@ -23,14 +23,14 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.DeviceProfileListenable; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.util.Themes; -import com.android.launcher3.views.ActivityContext; +import com.android.launcher3.views.AppLauncher; import java.util.ArrayList; import java.util.List; // TODO(b/218912746): Share more behavior to avoid all apps context depending directly on taskbar. /** Base for common behavior between taskbar window contexts. */ -public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext, +public abstract class BaseTaskbarContext extends ContextThemeWrapper implements AppLauncher, DeviceProfileListenable { protected final LayoutInflater mLayoutInflater; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 6c74ae379b..0f3a6eee47 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -442,7 +442,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mControllers.stashedHandleViewController.setIsHomeButtonDisabled( mControllers.navbarButtonsViewController.isHomeDisabled()); mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags); - mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags, fromInit); + mControllers.taskbarStashController.updateStateForSysuiFlags( + systemUiStateFlags, fromInit || !isUserSetupComplete()); mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags, fromInit); mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index f9a282b561..54dd0b2379 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -54,6 +54,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba public static final int FLAG_STASHED_IN_APP_IME = 1 << 5; // IME is visible public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 6; public static final int FLAG_STASHED_IN_APP_ALL_APPS = 1 << 7; // All apps is visible. + public static final int FLAG_IN_SETUP = 1 << 8; // In the Setup Wizard + + // If any of these flags are enabled, isInApp should return true. + private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP; // If we're in an app and any of these flags are enabled, taskbar should be stashed. private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL @@ -141,7 +145,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba // Evaluate whether the handle should be stashed private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder( flags -> { - boolean inApp = hasAnyFlag(flags, FLAG_IN_APP); + boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP); boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP); boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE); return (inApp && stashedInApp) || (!inApp && stashedLauncherState); @@ -179,10 +183,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba boolean isInSetup = !mActivity.isUserSetupComplete() || sharedState.setupUIVisible; updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp); updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup); - if (isInSetup) { - // Update the in-app state to ensure isStashed() reflects right state during SUW - updateStateForFlag(FLAG_IN_APP, true); - } + updateStateForFlag(FLAG_IN_SETUP, isInSetup); applyState(); notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp()); @@ -216,9 +217,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * Sets the flag indicating setup UI is visible */ protected void setSetupUIVisible(boolean isVisible) { - updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, - isVisible || !mActivity.isUserSetupComplete()); - applyState(); + boolean hideTaskbar = isVisible || !mActivity.isUserSetupComplete(); + updateStateForFlag(FLAG_IN_SETUP, hideTaskbar); + updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, hideTaskbar); + applyState(hideTaskbar ? 0 : TASKBAR_STASH_DURATION); } /** @@ -259,7 +261,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } private boolean isInApp() { - return hasAnyFlag(FLAG_IN_APP); + return hasAnyFlag(FLAGS_IN_APP); } /** @@ -478,7 +480,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } public void applyState() { - applyState(TASKBAR_STASH_DURATION); + applyState(hasAnyFlag(FLAG_IN_SETUP) ? 0 : TASKBAR_STASH_DURATION); } public void applyState(long duration) { @@ -568,8 +570,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP)) { mControllers.uiController.onStashedInAppChanged(); } - if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP | FLAG_IN_APP)) { - notifyStashChange(/* visible */ hasAnyFlag(FLAG_IN_APP), + if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP | FLAGS_IN_APP)) { + notifyStashChange(/* visible */ hasAnyFlag(FLAGS_IN_APP), /* stashed */ isStashedInApp()); } if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_MANUAL)) { @@ -613,6 +615,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba str.add((flags & FLAG_IN_STASHED_LAUNCHER_STATE) != 0 ? "FLAG_IN_STASHED_LAUNCHER_STATE" : ""); str.add((flags & FLAG_STASHED_IN_APP_ALL_APPS) != 0 ? "FLAG_STASHED_IN_APP_ALL_APPS" : ""); + str.add((flags & FLAG_IN_SETUP) != 0 ? "FLAG_IN_SETUP" : ""); return str.toString(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java index 37cd753ba4..0ea2aa0647 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java @@ -17,22 +17,17 @@ package com.android.launcher3.taskbar.allapps; import android.content.Context; import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; import android.view.WindowInsets; -import androidx.recyclerview.widget.RecyclerView; - +import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.AlphabeticalAppsList; import com.android.launcher3.allapps.BaseAdapterProvider; import com.android.launcher3.allapps.BaseAllAppsAdapter; -import com.android.launcher3.allapps.BaseAllAppsContainerView; -import com.android.launcher3.allapps.search.SearchAdapterProvider; /** All apps container accessible from taskbar. */ -public class TaskbarAllAppsContainerView extends BaseAllAppsContainerView { +public class TaskbarAllAppsContainerView extends + ActivityAllAppsContainerView { public TaskbarAllAppsContainerView(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -42,41 +37,6 @@ public class TaskbarAllAppsContainerView extends BaseAllAppsContainerView createMainAdapterProvider() { - // Taskbar all apps does not yet support search, so this implementation is minimal. - return new SearchAdapterProvider(mActivityContext) { - @Override - public boolean launchHighlightedItem() { - return false; - } - - @Override - public View getHighlightedItem() { - return null; - } - - @Override - public RecyclerView.ItemDecoration getDecorator() { - return null; - } - - @Override - public boolean isViewSupported(int viewType) { - return false; - } - - @Override - public void onBindView(AllAppsGridAdapter.ViewHolder holder, int position) { } - - @Override - public AllAppsGridAdapter.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, - ViewGroup parent, int viewType) { - return null; - } - }; - } - @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { setInsets(insets.getInsets(WindowInsets.Type.systemBars()).toRect()); diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java index 22fffdf317..50dfff0854 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java @@ -32,6 +32,9 @@ import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider; +import com.android.launcher3.allapps.search.SearchAdapterProvider; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.PopupDataProvider; @@ -154,6 +157,12 @@ class TaskbarAllAppsContext extends BaseTaskbarContext { @Override public void onPopupVisibilityChanged(boolean isVisible) {} + @Override + public SearchAdapterProvider createSearchAdapterProvider( + ActivityAllAppsContainerView appsView) { + return new DefaultSearchAdapterProvider(this); + } + /** Root drag layer for this context. */ private static class TaskbarAllAppsDragLayer extends BaseDragLayer implements OnComputeInsetsListener { diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsFallbackSearchContainer.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsFallbackSearchContainer.java new file mode 100644 index 0000000000..53fe06d32c --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsFallbackSearchContainer.java @@ -0,0 +1,54 @@ +/* + * 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.taskbar.allapps; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.Nullable; + +import com.android.launcher3.ExtendedEditText; +import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.allapps.SearchUiManager; + +/** Empty search container for Taskbar All Apps used as a fallback if search is not supported. */ +public class TaskbarAllAppsFallbackSearchContainer extends View implements SearchUiManager { + public TaskbarAllAppsFallbackSearchContainer(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public TaskbarAllAppsFallbackSearchContainer( + Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void initializeSearch(ActivityAllAppsContainerView containerView) { + // Do nothing. + } + + @Override + public void resetSearch() { + // Do nothing. + } + + @Nullable + @Override + public ExtendedEditText getEditText() { + return null; + } +} diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 253903c798..43be051dd3 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1273,8 +1273,7 @@ public abstract class AbsSwipeUpHandler, HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip, runningTaskTarget); - mIsSwipingPipToHome = !mIsSwipeForStagedSplit - && homeAnimFactory.supportSwipePipToHome() && appCanEnterPip; + mIsSwipingPipToHome = !mIsSwipeForStagedSplit && appCanEnterPip; final RectFSpringAnim[] windowAnim; if (mIsSwipingPipToHome) { mSwipePipToHomeAnimator = createWindowAnimationToPip( diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java index 4eb190a2f5..d76484c135 100644 --- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java @@ -50,12 +50,14 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.SpringAnimationBuilder; +import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.util.DisplayController; import com.android.quickstep.fallback.FallbackRecentsView; import com.android.quickstep.fallback.RecentsState; @@ -93,6 +95,8 @@ public class FallbackSwipeHandler extends private final Matrix mTmpMatrix = new Matrix(); private float mMaxLauncherScale = 1; + private boolean mAppCanEnterPip; + public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState, TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs, boolean continuingLastGesture, InputConsumerController inputConsumer) { @@ -135,16 +139,27 @@ public class FallbackSwipeHandler extends protected HomeAnimationFactory createHomeAnimationFactory(ArrayList launchCookies, long duration, boolean isTargetTranslucent, boolean appCanEnterPip, RemoteAnimationTargetCompat runningTaskTarget) { + mAppCanEnterPip = appCanEnterPip; + if (appCanEnterPip) { + return new FallbackPipToHomeAnimationFactory(); + } mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration); + startHomeIntent(mActiveAnimationFactory); + return mActiveAnimationFactory; + } + + private void startHomeIntent( + @Nullable FallbackHomeAnimationFactory gestureContractAnimationFactory) { ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); Intent intent = new Intent(mGestureState.getHomeIntent()); - mActiveAnimationFactory.addGestureContract(intent); + if (gestureContractAnimationFactory != null) { + gestureContractAnimationFactory.addGestureContract(intent); + } try { mContext.startActivity(intent, options.toBundle()); } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { mContext.startActivity(createHomeIntent()); } - return mActiveAnimationFactory; } @Override @@ -160,8 +175,19 @@ public class FallbackSwipeHandler extends @Override protected void finishRecentsControllerToHome(Runnable callback) { + final Runnable recentsCallback; + if (mAppCanEnterPip) { + // Make sure Launcher is resumed after auto-enter-pip transition to actually trigger + // the PiP task appearing. + recentsCallback = () -> { + callback.run(); + startHomeIntent(null /* gestureContractAnimationFactory */); + }; + } else { + recentsCallback = callback; + } mRecentsAnimationController.finish( - false /* toRecents */, callback, true /* sendUserLeaveHint */); + mAppCanEnterPip /* toRecents */, recentsCallback, true /* sendUserLeaveHint */); } @Override @@ -186,6 +212,17 @@ public class FallbackSwipeHandler extends } } + private class FallbackPipToHomeAnimationFactory extends HomeAnimationFactory { + @NonNull + @Override + public AnimatorPlaybackController createActivityAnimationToHome() { + // copied from {@link LauncherSwipeHandlerV2.LauncherHomeAnimationFactory} + long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx); + return mActivity.getStateManager().createAnimationToNewWorkspace( + RecentsState.HOME, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS); + } + } + private class FallbackHomeAnimationFactory extends HomeAnimationFactory { private final Rect mTempRect = new Rect(); private final TransformParams mHomeAlphaParams = new TransformParams(); diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java index bc7a6ae449..e9571e0ca1 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -284,10 +284,5 @@ public class LauncherSwipeHandlerV2 extends getViewIgnoredInWorkspaceRevealAnimation()) .start(); } - - @Override - public boolean supportSwipePipToHome() { - return true; - } } } diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index 8d9a6855af..8862073725 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -183,14 +183,6 @@ public abstract class SwipeUpAnimationLogic implements public void onCancel() { } - /** - * @return {@code true} if this factory supports animating an Activity to PiP window on - * swiping up to home. - */ - public boolean supportSwipePipToHome() { - return false; - } - /** * @param progress The progress of the animation to the home screen. * @return The current alpha to set on the animating app window. diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java index 27a748d3f2..79b15c713a 100644 --- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java +++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java @@ -306,7 +306,7 @@ public final class DigitalWellBeingToast { private void setBanner(@Nullable View view) { mBanner = view; - if (view != null) { + if (view != null && mTaskView.getRecentsView() != null) { setupAndAddBanner(); setBannerOutline(); } diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index d9f668dd1b..b9615abf1d 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -271,7 +271,8 @@ public class GroupedTaskView extends TaskView { getPagedOrientationHandler().setSplitIconParams(mIconView, mIconView2, taskIconHeight, mSnapshotView.getMeasuredWidth(), mSnapshotView.getMeasuredHeight(), - isRtl, deviceProfile, mSplitBoundsConfig); + getMeasuredHeight(), getMeasuredWidth(), isRtl, deviceProfile, + mSplitBoundsConfig); } private void updateSecondaryDwbPlacement() { diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 1d621dc740..ce033e5a03 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -890,15 +890,7 @@ public class TaskView extends FrameLayout implements Reusable { if (confirmSecondSplitSelectApp()) { return; } - if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) { - RecentsView recentsView = getRecentsView(); - recentsView.switchToScreenshot( - () -> recentsView.finishRecentsAnimation(true /* toRecents */, - false /* shouldPip */, - () -> showTaskMenu(iconView))); - } else { - showTaskMenu(iconView); - } + showTaskMenu(iconView); }); iconView.setOnLongClickListener(v -> { requestDisallowInterceptTouchEvent(true); @@ -1008,8 +1000,11 @@ public class TaskView extends FrameLayout implements Reusable { // resetViewTransforms is called during Quickswitch scrolling. mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX = mSplitSelectTranslationX = mGridEndTranslationX = 0f; - mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = - mSplitSelectTranslationY = 0f; + mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f; + if (getRecentsView() == null || !getRecentsView().isSplitSelectionActive()) { + mSplitSelectTranslationY = 0f; + } + setSnapshotScale(1f); applyTranslationX(); applyTranslationY(); diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml index 6c5be914dd..daca048878 100644 --- a/res/values-sw600dp-land/dimens.xml +++ b/res/values-sw600dp-land/dimens.xml @@ -16,14 +16,17 @@ --> - + 44dp - + 11.33dp 11.33dp - + 0dp 16dp + + + 52dp diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index 501ead685f..73edc6f8bd 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -23,7 +23,7 @@ 0dp - 46dp + 48dp 75dp diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml index 33da4a1bd8..eb5c751588 100644 --- a/res/values-sw720dp-land/dimens.xml +++ b/res/values-sw720dp-land/dimens.xml @@ -18,13 +18,20 @@ 0dp 32dp + 21.93dp 29.33dp + 64dp + + + 32dp + 49dp + 0dp diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index b4c8837224..3ec211a142 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -16,10 +16,12 @@ - 65dp + 28dp + 27.59dp 36dp + 20sp 72dp @@ -28,6 +30,7 @@ 32dp 32dp 32dp + 164dp diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 4ed31f8968..c96a2284e7 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -241,9 +241,28 @@ if not specified --> + + + + + + + + + + + + + + + + + + + diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index 3af43c021b..6de3884b93 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -216,6 +216,7 @@ public abstract class BaseDraggingActivity extends BaseActivity * Creates and returns {@link SearchAdapterProvider} for build variant specific search result * views */ + @Override public SearchAdapterProvider createSearchAdapterProvider( ActivityAllAppsContainerView allApps) { return new DefaultSearchAdapterProvider(this); diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index e184a91e3c..f7133c49dd 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -93,7 +93,7 @@ public class CellLayout extends ViewGroup { private int mFixedCellWidth; private int mFixedCellHeight; @ViewDebug.ExportedProperty(category = "launcher") - private final Point mBorderSpace; + private Point mBorderSpace; @ViewDebug.ExportedProperty(category = "launcher") private int mCountX; @@ -239,22 +239,7 @@ public class CellLayout extends ViewGroup { mActivity = ActivityContext.lookupContext(context); DeviceProfile deviceProfile = mActivity.getDeviceProfile(); - switch (mContainerType) { - case FOLDER: - mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx); - break; - case HOTSEAT: - mBorderSpace = new Point(deviceProfile.hotseatBorderSpace, - deviceProfile.hotseatBorderSpace); - break; - case WORKSPACE: - default: - mBorderSpace = new Point(deviceProfile.cellLayoutBorderSpacePx); - break; - } - - mCellWidth = mCellHeight = -1; - mFixedCellWidth = mFixedCellHeight = -1; + resetCellSizeInternal(deviceProfile); mCountX = deviceProfile.inv.numColumns; mCountY = deviceProfile.inv.numRows; @@ -379,6 +364,12 @@ public class CellLayout extends ViewGroup { return mShortcutsAndWidgets.getLayerType() == LAYER_TYPE_HARDWARE; } + /** + * Change sizes of cells + * + * @param width the new width of the cells + * @param height the new height of the cells + */ public void setCellDimensions(int width, int height) { mFixedCellWidth = mCellWidth = width; mFixedCellHeight = mCellHeight = height; @@ -386,6 +377,33 @@ public class CellLayout extends ViewGroup { mBorderSpace); } + private void resetCellSizeInternal(DeviceProfile deviceProfile) { + switch (mContainerType) { + case FOLDER: + mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx); + break; + case HOTSEAT: + mBorderSpace = new Point(deviceProfile.hotseatBorderSpace, + deviceProfile.hotseatBorderSpace); + break; + case WORKSPACE: + default: + mBorderSpace = new Point(deviceProfile.cellLayoutBorderSpacePx); + break; + } + + mCellWidth = mCellHeight = -1; + mFixedCellWidth = mFixedCellHeight = -1; + } + + /** + * Reset the cell sizes and border space + */ + public void resetCellSize(DeviceProfile deviceProfile) { + resetCellSizeInternal(deviceProfile); + requestLayout(); + } + public void setGridSize(int x, int y) { mCountX = x; mCountY = y; diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 91fa4ca497..009ee2789f 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -624,14 +624,15 @@ public class DeviceProfile { + textHeight + (topBottomPadding * 2); } - private void updateAllAppsWidth(Resources res) { + private void updateAllAppsContainerWidth(Resources res) { int cellLayoutHorizontalPadding = (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right) / 2; if (isTablet) { - allAppsLeftRightPadding = res.getDimensionPixelSize( - R.dimen.all_apps_bottom_sheet_horizontal_padding) + cellLayoutHorizontalPadding; + allAppsLeftRightPadding = + res.getDimensionPixelSize(R.dimen.all_apps_bottom_sheet_horizontal_padding); + int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns) - + (allAppsBorderSpacePx.x * (numShownAllAppsColumns + 1)) + + (allAppsBorderSpacePx.x * (numShownAllAppsColumns - 1)) + allAppsLeftRightPadding * 2; allAppsLeftRightMargin = Math.max(1, (availableWidthPx - usedWidth) / 2); } else { @@ -755,24 +756,26 @@ public class DeviceProfile { * Updates the iconSize for allApps* variants. */ public void updateAllAppsIconSize(float scale, Resources res) { - if (numShownAllAppsColumns != inv.numColumns) { + //TODO(b/218638090): remove the tablet condition once we have phone specs + if (isScalableGrid && isTablet) { allAppsIconSizePx = pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics); allAppsIconTextSizePx = pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics); allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx; - autoResizeAllAppsCells(); + allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale); + allAppsCellHeightPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].y, mMetrics, scale); } else { float invIconSizeDp = inv.iconSize[mTypeIndex]; float invIconTextSizeSp = inv.iconTextSize[mTypeIndex]; allAppsIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale)); allAppsIconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * scale); allAppsIconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale); + allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx); allAppsCellHeightPx = getCellSize().y; } - allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx); - updateAllAppsWidth(res); + updateAllAppsContainerWidth(res); if (isVerticalBarLayout()) { hideWorkspaceLabelsIfNotEnoughSpace(); } @@ -998,7 +1001,7 @@ public class DeviceProfile { additionalLeftSpace = qsbWidth + hotseatBorderSpace; } - int hotseatTopDiff = hotseatHeight - taskbarOffset; + int hotseatTopPadding = hotseatHeight - taskbarOffset - hotseatCellHeightPx; int endOffset = ApiWrapper.getHotseatEndOffset(context); int requiredWidth = iconSizePx * numShownHotseatIcons @@ -1007,7 +1010,7 @@ public class DeviceProfile { int hotseatSize = Math.min(requiredWidth, availableWidthPx - endOffset); int sideSpacing = (availableWidthPx - hotseatSize) / 2; - mHotseatPadding.set(sideSpacing + additionalLeftSpace, hotseatTopDiff, sideSpacing, + mHotseatPadding.set(sideSpacing + additionalLeftSpace, hotseatTopPadding, sideSpacing, taskbarOffset); if (endOffset > sideSpacing) { @@ -1223,6 +1226,7 @@ public class DeviceProfile { allAppsIconDrawablePaddingPx)); writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx)); writer.println(prefix + pxToDpStr("allAppsCellWidthPx", allAppsCellWidthPx)); + writer.println(prefix + pxToDpStr("allAppsBorderSpacePx", allAppsBorderSpacePx.x)); writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns); writer.println(prefix + pxToDpStr("allAppsLeftRightPadding", allAppsLeftRightPadding)); writer.println(prefix + pxToDpStr("allAppsLeftRightMargin", allAppsLeftRightMargin)); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index e1680fc7e7..9c749aa790 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -84,6 +84,7 @@ public class Hotseat extends CellLayout implements Insettable { removeAllViewsInLayout(); mHasVerticalHotseat = hasVerticalHotseat; DeviceProfile dp = mActivity.getDeviceProfile(); + resetCellSize(dp); if (hasVerticalHotseat) { setGridSize(1, dp.numShownHotseatIcons); } else { @@ -110,10 +111,9 @@ public class Hotseat extends CellLayout implements Insettable { mQsb.setVisibility(View.VISIBLE); lp.gravity = Gravity.BOTTOM; lp.width = ViewGroup.LayoutParams.MATCH_PARENT; - lp.height = (grid.isTaskbarPresent + lp.height = grid.isTaskbarPresent ? grid.workspacePadding.bottom - : grid.hotseatBarSizePx) - + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom); + : grid.hotseatBarSizePx + insets.bottom; } Rect padding = grid.getHotseatLayoutPadding(getContext()); diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 9dd1493d16..219ed9e371 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -128,6 +128,7 @@ public class InvariantDeviceProfile { public float[] horizontalMargin; + public PointF[] allAppsCellSize; public float[] allAppsIconSize; public float[] allAppsIconTextSize; public PointF[] allAppsBorderSpaces; @@ -357,6 +358,7 @@ public class InvariantDeviceProfile { numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns; + allAppsCellSize = displayOption.allAppsCellSize; allAppsBorderSpaces = displayOption.allAppsBorderSpaces; allAppsIconSize = displayOption.allAppsIconSizes; allAppsIconTextSize = displayOption.allAppsIconTextSizes; @@ -798,6 +800,7 @@ public class InvariantDeviceProfile { private final float[] iconSizes = new float[COUNT_SIZES]; private final float[] textSizes = new float[COUNT_SIZES]; + private final PointF[] allAppsCellSize = new PointF[COUNT_SIZES]; private final float[] allAppsIconSizes = new float[COUNT_SIZES]; private final float[] allAppsIconTextSizes = new float[COUNT_SIZES]; private final PointF[] allAppsBorderSpaces = new PointF[COUNT_SIZES]; @@ -873,9 +876,35 @@ public class InvariantDeviceProfile { folderBorderSpace = borderSpace; + x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidth, + minCellSize[INDEX_DEFAULT].x); + y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeight, + minCellSize[INDEX_DEFAULT].y); + allAppsCellSize[INDEX_DEFAULT] = new PointF(x, y); + + x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidthLandscape, + allAppsCellSize[INDEX_DEFAULT].x); + y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeightLandscape, + allAppsCellSize[INDEX_DEFAULT].y); + allAppsCellSize[INDEX_LANDSCAPE] = new PointF(x, y); + + x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidthTwoPanelPortrait, + allAppsCellSize[INDEX_DEFAULT].x); + y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeightTwoPanelPortrait, + allAppsCellSize[INDEX_DEFAULT].y); + allAppsCellSize[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y); + + x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidthTwoPanelLandscape, + allAppsCellSize[INDEX_DEFAULT].x); + y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeightTwoPanelLandscape, + allAppsCellSize[INDEX_DEFAULT].y); + allAppsCellSize[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y); + x = y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpace, borderSpace); allAppsBorderSpaces[INDEX_DEFAULT] = new PointF(x, y); + x = y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpaceLandscape, + allAppsBorderSpaces[INDEX_DEFAULT].x); allAppsBorderSpaces[INDEX_LANDSCAPE] = new PointF(x, y); x = y = a.getFloat( R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelPortrait, @@ -971,6 +1000,7 @@ public class InvariantDeviceProfile { textSizes[i] = 0; borderSpaces[i] = new PointF(); minCellSize[i] = new PointF(); + allAppsCellSize[i] = new PointF(); allAppsIconSizes[i] = 0; allAppsIconTextSizes[i] = 0; allAppsBorderSpaces[i] = new PointF(); @@ -987,6 +1017,8 @@ public class InvariantDeviceProfile { minCellSize[i].y *= w; horizontalMargin[i] *= w; hotseatBorderSpaces[i] *= w; + allAppsCellSize[i].x *= w; + allAppsCellSize[i].y *= w; allAppsIconSizes[i] *= w; allAppsIconTextSizes[i] *= w; allAppsBorderSpaces[i].x *= w; @@ -1008,6 +1040,8 @@ public class InvariantDeviceProfile { minCellSize[i].y += p.minCellSize[i].y; horizontalMargin[i] += p.horizontalMargin[i]; hotseatBorderSpaces[i] += p.hotseatBorderSpaces[i]; + allAppsCellSize[i].x += p.allAppsCellSize[i].x; + allAppsCellSize[i].y += p.allAppsCellSize[i].y; allAppsIconSizes[i] += p.allAppsIconSizes[i]; allAppsIconTextSizes[i] += p.allAppsIconTextSizes[i]; allAppsBorderSpaces[i].x += p.allAppsBorderSpaces[i].x; diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index b94a61251b..11e0a1faae 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -26,12 +26,13 @@ import android.widget.RelativeLayout; import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.RecyclerView; -import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.DeviceProfile.DeviceProfileListenable; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.search.SearchAdapterProvider; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.views.AppLauncher; import java.util.Objects; @@ -40,8 +41,8 @@ import java.util.Objects; * * @param Type of context inflating all apps. */ -public class ActivityAllAppsContainerView extends - BaseAllAppsContainerView { +public class ActivityAllAppsContainerView extends BaseAllAppsContainerView { protected SearchUiManager mSearchUiManager; /** @@ -103,13 +104,8 @@ public class ActivityAllAppsContainerView extend } } - /** Handles selection on focused view and returns {@code true} on success. */ - public boolean launchHighlightedItem() { - return getMainAdapterProvider().launchHighlightedItem(); - } - @Override - protected SearchAdapterProvider createMainAdapterProvider() { + protected final SearchAdapterProvider createMainAdapterProvider() { return mActivityContext.createSearchAdapterProvider(this); } diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java index fd8945a0f6..a6a47a71db 100644 --- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java @@ -29,14 +29,13 @@ import android.view.inputmethod.EditorInfo; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ExtendedEditText; -import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.search.SearchAlgorithm; import com.android.launcher3.search.SearchCallback; +import com.android.launcher3.views.ActivityContext; /** * An interface to a search box that AllApps can command. @@ -45,7 +44,7 @@ public class AllAppsSearchBarController implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener, OnFocusChangeListener { - protected BaseDraggingActivity mLauncher; + protected ActivityContext mLauncher; protected SearchCallback mCallback; protected ExtendedEditText mInput; protected String mQuery; @@ -62,7 +61,7 @@ public class AllAppsSearchBarController */ public final void initialize( SearchAlgorithm searchAlgorithm, ExtendedEditText input, - BaseDraggingActivity launcher, SearchCallback callback) { + ActivityContext launcher, SearchCallback callback) { mCallback = callback; mLauncher = launcher; @@ -125,7 +124,7 @@ public class AllAppsSearchBarController mLauncher.getStatsLogManager().logger() .log(LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME); // selectFocusedView should return SearchTargetEvent that is passed onto onClick - return Launcher.getLauncher(mLauncher).getAppsView().launchHighlightedItem(); + return mLauncher.getAppsView().getMainAdapterProvider().launchHighlightedItem(); } return false; } diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index cb459eacb3..893e547340 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -32,7 +32,6 @@ import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup.MarginLayoutParams; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Insettable; @@ -43,6 +42,7 @@ import com.android.launcher3.allapps.AlphabeticalAppsList; import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; import com.android.launcher3.allapps.SearchUiManager; import com.android.launcher3.search.SearchCallback; +import com.android.launcher3.views.ActivityContext; import java.util.ArrayList; @@ -53,7 +53,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText implements SearchUiManager, SearchCallback, AllAppsStore.OnUpdateListener, Insettable { - private final BaseDraggingActivity mLauncher; + private final ActivityContext mLauncher; private final AllAppsSearchBarController mSearchBarController; private final SpannableStringBuilder mSearchQueryBuilder; @@ -74,7 +74,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mLauncher = BaseDraggingActivity.fromContext(context); + mLauncher = ActivityContext.lookupContext(context); mSearchBarController = new AllAppsSearchBarController(); mSearchQueryBuilder = new SpannableStringBuilder(); @@ -134,7 +134,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText mApps = appsView.getApps(); mAppsView = appsView; mSearchBarController.initialize( - new DefaultAppSearchAlgorithm(mLauncher), + new DefaultAppSearchAlgorithm(getContext()), this, mLauncher, this); } diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java index 2fe4915e2f..a95bd514da 100644 --- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java +++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java @@ -23,20 +23,20 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BubbleTextView; import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.views.AppLauncher; /** - * Provides views for local search results + * Provides views for local search results. */ -public class DefaultSearchAdapterProvider extends SearchAdapterProvider { +public class DefaultSearchAdapterProvider extends SearchAdapterProvider { private final RecyclerView.ItemDecoration mDecoration; private View mHighlightedView; - public DefaultSearchAdapterProvider(BaseDraggingActivity launcher) { + public DefaultSearchAdapterProvider(AppLauncher launcher) { super(launcher); mDecoration = new RecyclerView.ItemDecoration() { @Override diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index de33ae5f87..8ba2070ce6 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -258,6 +258,9 @@ public final class FeatureFlags { "ENABLE_NEW_MIGRATION_LOGIC", true, "Enable the new grid migration logic, keeping pages when src < dest"); + public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = new DeviceFlag( + "ENABLE_ONE_SEARCH_MOTION", false, "Enables animations in OneSearch."); + public static void initialize(Context context) { synchronized (sDebugFlags) { for (DebugFlag flag : sDebugFlags) { diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index 5ece4a62dd..7ba2317868 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -16,6 +16,7 @@ package com.android.launcher3.icons; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY; @@ -340,14 +341,15 @@ public class IconCache extends BaseIconCache { Map, List>> iconLoadSubsectionsMap = iconRequestInfos.stream() .filter(iconRequest -> { - if (iconRequest.itemInfo.getTargetComponent() != null) { - return true; + if (iconRequest.itemInfo.getTargetComponent() == null) { + Log.i(TAG, + "Skipping Item info with null component name: " + + iconRequest.itemInfo); + iconRequest.itemInfo.bitmap = getDefaultIcon( + iconRequest.itemInfo.user); + return false; } - Log.i(TAG, - "Skipping Item info with null component name: " - + iconRequest.itemInfo); - iconRequest.itemInfo.bitmap = getDefaultIcon(iconRequest.itemInfo.user); - return false; + return true; }) .collect(groupingBy(iconRequest -> Pair.create(iconRequest.itemInfo.user, iconRequest.useLowResIcon))); @@ -356,6 +358,17 @@ public class IconCache extends BaseIconCache { iconLoadSubsectionsMap.forEach((sectionKey, filteredList) -> { Map>> duplicateIconRequestsMap = filteredList.stream() + .filter(iconRequest -> { + // Filter out icons that should not share the same bitmap and title + if (iconRequest.itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT) { + Log.e(TAG, + "Skipping Item info for deep shortcut: " + + iconRequest.itemInfo, + new IllegalStateException()); + return false; + } + return true; + }) .collect(groupingBy(iconRequest -> iconRequest.itemInfo.getTargetComponent())); diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index b9fa21d10d..f1c5d59fc8 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -615,7 +615,13 @@ public class LoaderTask implements Runnable { } if (info != null) { - iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon)); + if (info.itemType + != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + // Skip deep shortcuts; their title and icons have already been + // loaded above. + iconRequestInfos.add( + c.createIconRequestInfo(info, useLowResIcon)); + } c.applyCommonProperties(info); diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index 2609e549a5..217a5583dd 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -16,6 +16,7 @@ package com.android.launcher3.touch; +import static android.view.Gravity.BOTTOM; import static android.view.Gravity.CENTER_VERTICAL; import static android.view.Gravity.END; import static android.view.Gravity.START; @@ -414,14 +415,17 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { @Override public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, StagedSplitBounds splitInfo, int desiredStagePosition) { - float diff; - float horizontalDividerDiff = splitInfo.visualDividerBounds.width() / 2f; + float topLeftTaskPercent = splitInfo.appsStackedVertically + ? splitInfo.topTaskPercent + : splitInfo.leftTaskPercent; + float dividerBarPercent = splitInfo.appsStackedVertically + ? splitInfo.dividerHeightPercent + : splitInfo.dividerWidthPercent; + if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) { - diff = outRect.height() * (1f - splitInfo.leftTaskPercent) + horizontalDividerDiff; - outRect.bottom -= diff; + outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent); } else { - diff = outRect.height() * splitInfo.leftTaskPercent + horizontalDividerDiff; - outRect.top += diff; + outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent)); } } @@ -468,18 +472,42 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { @Override public void setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, - boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { + int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl, + DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { FrameLayout.LayoutParams primaryIconParams = (FrameLayout.LayoutParams) primaryIconView.getLayoutParams(); FrameLayout.LayoutParams secondaryIconParams = new FrameLayout.LayoutParams(primaryIconParams); - primaryIconParams.gravity = CENTER_VERTICAL | (isRtl ? START : END); + // We calculate the "midpoint" of the thumbnail area, and place the icons there. + // This is the place where the thumbnail area splits by default, in a near-50/50 split. + // It is usually not exactly 50/50, due to insets/screen cutouts. + int fullscreenInsetThickness = deviceProfile.getInsets().top; + int fullscreenMidpointFromBottom = ((deviceProfile.heightPx - fullscreenInsetThickness) + / 2); + float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx; + float insetPct = (float) fullscreenInsetThickness / deviceProfile.heightPx; + int spaceAboveSnapshots = deviceProfile.overviewTaskThumbnailTopMarginPx; + int overviewThumbnailAreaThickness = groupedTaskViewHeight - spaceAboveSnapshots; + int bottomToMidpointOffset = (int) (overviewThumbnailAreaThickness * midpointFromBottomPct); + int insetOffset = (int) (overviewThumbnailAreaThickness * insetPct); + + primaryIconParams.gravity = BOTTOM | (isRtl ? START : END); + secondaryIconParams.gravity = BOTTOM | (isRtl ? START : END); primaryIconView.setTranslationX(0); - primaryIconView.setTranslationY(-(taskIconHeight / 2f)); - secondaryIconParams.gravity = CENTER_VERTICAL | (isRtl ? START : END); secondaryIconView.setTranslationX(0); - secondaryIconView.setTranslationY(taskIconHeight / 2f); + if (splitConfig.initiatedFromSeascape) { + // if the split was initiated from seascape, + // the task on the right (secondary) is slightly larger + primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset); + secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset + + taskIconHeight); + } else { + // if not, + // the task on the left (primary) is slightly larger + primaryIconView.setTranslationY(-bottomToMidpointOffset); + secondaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight); + } primaryIconView.setLayoutParams(primaryIconParams); secondaryIconView.setLayoutParams(secondaryIconParams); diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index 6e594e9cc6..1b171266fa 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -146,7 +146,8 @@ public interface PagedOrientationHandler { int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl); void setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, - boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig); + int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl, + DeviceProfile deviceProfile, StagedSplitBounds splitConfig); /* * The following two methods try to center the TaskMenuView in landscape by finding the center diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index 2c9afd6506..7e1cb256dc 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -18,6 +18,7 @@ package com.android.launcher3.touch; import static android.view.Gravity.BOTTOM; import static android.view.Gravity.CENTER_HORIZONTAL; +import static android.view.Gravity.END; import static android.view.Gravity.START; import static android.view.Gravity.TOP; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; @@ -334,8 +335,14 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { // Set translations if (deviceProfile.isLandscape) { if (desiredTaskId == splitBounds.rightBottomTaskId) { - translationX = ((taskViewWidth * splitBounds.leftTaskPercent) - + (taskViewWidth * splitBounds.dividerWidthPercent)); + float leftTopTaskPercent = splitBounds.appsStackedVertically + ? splitBounds.topTaskPercent + : splitBounds.leftTaskPercent; + float dividerThicknessPercent = splitBounds.appsStackedVertically + ? splitBounds.dividerHeightPercent + : splitBounds.dividerWidthPercent; + translationX = ((taskViewWidth * leftTopTaskPercent) + + (taskViewWidth * dividerThicknessPercent)); } } else { if (desiredTaskId == splitBounds.leftTopTaskId) { @@ -488,19 +495,24 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, StagedSplitBounds splitInfo, int desiredStagePosition) { boolean isLandscape = dp.isLandscape; + float topLeftTaskPercent = splitInfo.appsStackedVertically + ? splitInfo.topTaskPercent + : splitInfo.leftTaskPercent; + float dividerBarPercent = splitInfo.appsStackedVertically + ? splitInfo.dividerHeightPercent + : splitInfo.dividerWidthPercent; + if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) { if (isLandscape) { - outRect.right = outRect.left + (int) (outRect.width() * splitInfo.leftTaskPercent); + outRect.right = outRect.left + (int) (outRect.width() * topLeftTaskPercent); } else { - outRect.bottom = outRect.top + (int) (outRect.height() * splitInfo.topTaskPercent); + outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent); } } else { if (isLandscape) { - outRect.left += (int) (outRect.width() * - (splitInfo.leftTaskPercent + splitInfo.dividerWidthPercent)); + outRect.left += (int) (outRect.width() * (topLeftTaskPercent + dividerBarPercent)); } else { - outRect.top += (int) (outRect.height() * - (splitInfo.topTaskPercent + splitInfo.dividerHeightPercent)); + outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent)); } } } @@ -559,18 +571,70 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { @Override public void setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, - boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { + int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl, + DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { FrameLayout.LayoutParams primaryIconParams = (FrameLayout.LayoutParams) primaryIconView.getLayoutParams(); FrameLayout.LayoutParams secondaryIconParams = new FrameLayout.LayoutParams(primaryIconParams); - primaryIconParams.gravity = TOP | CENTER_HORIZONTAL; - // shifts icon half a width left (height is used conveniently here since icons are square) - primaryIconView.setTranslationX(-(taskIconHeight / 2f)); + if (deviceProfile.isLandscape) { + // We calculate the "midpoint" of the thumbnail area, and place the icons there. + // This is the place where the thumbnail area splits by default, in a near-50/50 split. + // It is usually not exactly 50/50, due to insets/screen cutouts. + int fullscreenInsetThickness = deviceProfile.isSeascape() + ? deviceProfile.getInsets().right + : deviceProfile.getInsets().left; + int fullscreenMidpointFromBottom = ((deviceProfile.widthPx + - fullscreenInsetThickness) / 2); + float midpointFromBottomPct = (float) fullscreenMidpointFromBottom + / deviceProfile.widthPx; + float insetPct = (float) fullscreenInsetThickness / deviceProfile.widthPx; + int spaceAboveSnapshots = 0; + int overviewThumbnailAreaThickness = groupedTaskViewWidth - spaceAboveSnapshots; + int bottomToMidpointOffset = (int) (overviewThumbnailAreaThickness + * midpointFromBottomPct); + int insetOffset = (int) (overviewThumbnailAreaThickness * insetPct); + + if (deviceProfile.isSeascape()) { + primaryIconParams.gravity = TOP | (isRtl ? END : START); + secondaryIconParams.gravity = TOP | (isRtl ? END : START); + if (splitConfig.initiatedFromSeascape) { + // if the split was initiated from seascape, + // the task on the right (secondary) is slightly larger + primaryIconView.setTranslationX(bottomToMidpointOffset - taskIconHeight); + secondaryIconView.setTranslationX(bottomToMidpointOffset); + } else { + // if not, + // the task on the left (primary) is slightly larger + primaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset + - taskIconHeight); + secondaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset); + } + } else { + primaryIconParams.gravity = TOP | (isRtl ? START : END); + secondaryIconParams.gravity = TOP | (isRtl ? START : END); + if (!splitConfig.initiatedFromSeascape) { + // if the split was initiated from landscape, + // the task on the left (primary) is slightly larger + primaryIconView.setTranslationX(-bottomToMidpointOffset); + secondaryIconView.setTranslationX(-bottomToMidpointOffset + taskIconHeight); + } else { + // if not, + // the task on the right (secondary) is slightly larger + primaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset); + secondaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset + + taskIconHeight); + } + } + } else { + primaryIconParams.gravity = TOP | CENTER_HORIZONTAL; + // shifts icon half a width left (height is used here since icons are square) + primaryIconView.setTranslationX(-(taskIconHeight / 2f)); + secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL; + secondaryIconView.setTranslationX(taskIconHeight / 2f); + } primaryIconView.setTranslationY(0); - secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL; - secondaryIconView.setTranslationX(taskIconHeight / 2f); secondaryIconView.setTranslationY(0); primaryIconView.setLayoutParams(primaryIconParams); diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java index 9151796fc5..74b6a5b28e 100644 --- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java @@ -178,16 +178,46 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { @Override public void setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, - boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { + int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl, + DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { super.setSplitIconParams(primaryIconView, secondaryIconView, taskIconHeight, - primarySnapshotWidth, primarySnapshotHeight, isRtl, deviceProfile, splitConfig); + primarySnapshotWidth, primarySnapshotHeight, groupedTaskViewHeight, + groupedTaskViewWidth, isRtl, deviceProfile, splitConfig); FrameLayout.LayoutParams primaryIconParams = (FrameLayout.LayoutParams) primaryIconView.getLayoutParams(); FrameLayout.LayoutParams secondaryIconParams = (FrameLayout.LayoutParams) secondaryIconView.getLayoutParams(); - primaryIconParams.gravity = CENTER_VERTICAL | (isRtl ? END : START); - secondaryIconParams.gravity = CENTER_VERTICAL | (isRtl ? END : START); + // We calculate the "midpoint" of the thumbnail area, and place the icons there. + // This is the place where the thumbnail area splits by default, in a near-50/50 split. + // It is usually not exactly 50/50, due to insets/screen cutouts. + int fullscreenInsetThickness = deviceProfile.getInsets().top; + int fullscreenMidpointFromBottom = ((deviceProfile.heightPx + - fullscreenInsetThickness) / 2); + float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx; + float insetPct = (float) fullscreenInsetThickness / deviceProfile.heightPx; + int spaceAboveSnapshots = deviceProfile.overviewTaskThumbnailTopMarginPx; + int overviewThumbnailAreaThickness = groupedTaskViewHeight - spaceAboveSnapshots; + int bottomToMidpointOffset = (int) (overviewThumbnailAreaThickness * midpointFromBottomPct); + int insetOffset = (int) (overviewThumbnailAreaThickness * insetPct); + + primaryIconParams.gravity = BOTTOM | (isRtl ? END : START); + secondaryIconParams.gravity = BOTTOM | (isRtl ? END : START); + primaryIconView.setTranslationX(0); + secondaryIconView.setTranslationX(0); + if (splitConfig.initiatedFromSeascape) { + // if the split was initiated from seascape, + // the task on the right (secondary) is slightly larger + primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset); + secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset + + taskIconHeight); + } else { + // if not, + // the task on the left (primary) is slightly larger + primaryIconView.setTranslationY(-bottomToMidpointOffset); + secondaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight); + } + primaryIconView.setLayoutParams(primaryIconParams); secondaryIconView.setLayoutParams(secondaryIconParams); } diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java index cb714b2a8f..b40493ac25 100644 --- a/src/com/android/launcher3/util/SplitConfigurationOptions.java +++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java @@ -113,6 +113,14 @@ public final class SplitConfigurationOptions { * the bounds were originally in */ public final boolean appsStackedVertically; + /** + * If {@code true}, that means at the time of creation of this object, the phone was in + * seascape orientation. This is important on devices with insets, because they do not split + * evenly -- one of the insets must be slightly larger to account for the inset. + * From landscape, it is the leftTop task that expands slightly. + * From seascape, it is the rightBottom task that expands slightly. + */ + public final boolean initiatedFromSeascape; public final int leftTopTaskId; public final int rightBottomTaskId; @@ -128,11 +136,22 @@ public final class SplitConfigurationOptions { this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom, leftTopBounds.right, rightBottomBounds.top); appsStackedVertically = true; + initiatedFromSeascape = false; } else { // horizontal apps, vertical divider this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top, rightBottomBounds.left, leftTopBounds.bottom); appsStackedVertically = false; + // The following check is unreliable on devices without insets + // (initiatedFromSeascape will always be set to false.) This happens to be OK for + // all our current uses, but should be refactored. + // TODO: Create a more reliable check, or refactor how splitting works on devices + // with insets. + if (rightBottomBounds.width() > leftTopBounds.width()) { + initiatedFromSeascape = true; + } else { + initiatedFromSeascape = false; + } } leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right; diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 3c90eea5d1..93078e4cf6 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -25,7 +25,8 @@ import android.view.View.AccessibilityDelegate; import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.allapps.BaseAllAppsContainerView; +import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.allapps.search.SearchAdapterProvider; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.folder.FolderIcon; @@ -99,7 +100,7 @@ public interface ActivityContext { /** * The all apps container, if it exists in this context. */ - default BaseAllAppsContainerView getAppsView() { + default ActivityAllAppsContainerView getAppsView() { return null; } @@ -190,4 +191,14 @@ public interface ActivityContext { default StringCache getStringCache() { return null; } + + /** + * Creates and returns {@link SearchAdapterProvider} for build variant specific search result + * views. + */ + @Nullable + default SearchAdapterProvider createSearchAdapterProvider( + ActivityAllAppsContainerView appsView) { + return null; + } } diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 867e488155..2a9a8a5d36 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -229,7 +229,7 @@ public class OptionsPopupView extends ArrowPopup Launcher launcher = Launcher.getLauncher(view.getContext()); launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES) .setPackage(launcher.getPackageName()) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)); return true; } diff --git a/tests/src/com/android/launcher3/DeviceProfileTest.kt b/tests/src/com/android/launcher3/DeviceProfileTest.kt index 75ad21d8aa..d1e91ed072 100644 --- a/tests/src/com/android/launcher3/DeviceProfileTest.kt +++ b/tests/src/com/android/launcher3/DeviceProfileTest.kt @@ -208,6 +208,12 @@ class DeviceProfileTest { PointF(64f, 83f), PointF(64f, 83f) ).toTypedArray() + allAppsCellSize = listOf( + PointF(64f, 83f), + PointF(64f, 83f), + PointF(64f, 83f), + PointF(64f, 83f) + ).toTypedArray() } } } \ No newline at end of file