Merge changes from topics "draglayer-inline-qsb", "draglayer-search" into udc-qpr-dev

* changes:
  Allow LauncherState to define floating search side margins.
  Put the "floating" in ENABLE_FLOATING_SEARCH_BAR.
This commit is contained in:
TreeHugger Robot
2023-06-13 03:19:49 +00:00
committed by Android (Google) Code Review
19 changed files with 393 additions and 75 deletions
@@ -47,9 +47,9 @@ public class TaskbarAllAppsContainerView extends
}
@Override
protected View inflateSearchBox() {
protected View inflateSearchBar() {
if (isSearchSupported()) {
return super.inflateSearchBox();
return super.inflateSearchBar();
}
// Remove top padding of header, since we do not have any search
@@ -169,6 +169,13 @@ public final class TaskbarAllAppsController {
}
private void cleanUpOverlay() {
// Floating search bar is added to the drag layer in ActivityAllAppsContainerView onAttach;
// removed here as this is a special case that we remove the all apps panel.
if (mAppsView != null && mOverlayContext != null
&& mAppsView.getSearchUiDelegate().isSearchBarFloating()) {
mOverlayContext.getDragLayer().removeView(mAppsView.getSearchView());
mAppsView.getSearchUiDelegate().onDestroySearchBar();
}
if (mSearchSessionController != null) {
mSearchSessionController.onDestroy();
mSearchSessionController = null;
@@ -20,6 +20,7 @@ import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAP
import android.content.Context;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -104,9 +105,35 @@ public class AllAppsState extends LauncherState {
@Override
public int getVisibleElements(Launcher launcher) {
// Don't add HOTSEAT_ICONS for non-tablets in ALL_APPS state.
return launcher.getDeviceProfile().isTablet ? ALL_APPS_CONTENT | HOTSEAT_ICONS
: ALL_APPS_CONTENT;
int elements = ALL_APPS_CONTENT | FLOATING_SEARCH_BAR;
// Only add HOTSEAT_ICONS for tablets in ALL_APPS state.
if (launcher.getDeviceProfile().isTablet) {
elements |= HOTSEAT_ICONS;
}
return elements;
}
@Override
public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
return 0;
}
@Override
public int getFloatingSearchBarRestingMarginStart(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
}
@Override
public int getFloatingSearchBarRestingMarginEnd(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
}
@Override
public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
return dp.isPhone && !dp.isLandscape;
}
@Override
@@ -107,7 +107,32 @@ public class OverviewState extends LauncherState {
@Override
public int getVisibleElements(Launcher launcher) {
return CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
int elements = CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
DeviceProfile dp = launcher.getDeviceProfile();
boolean showFloatingSearch;
if (dp.isPhone) {
// Only show search in phone overview in portrait mode.
showFloatingSearch = !dp.isLandscape;
} else {
// Only show search in tablet overview if taskbar is not visible.
showFloatingSearch = !dp.isTaskbarPresent || isTaskbarStashed(launcher);
}
if (showFloatingSearch) {
elements |= FLOATING_SEARCH_BAR;
}
return elements;
}
@Override
public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
return areElementsVisible(launcher, FLOATING_SEARCH_BAR) ? 0
: super.getFloatingSearchBarRestingMarginBottom(launcher);
}
@Override
public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
return dp.isPhone && !dp.isLandscape;
}
@Override
@@ -811,6 +811,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
maybeUpdateRecentsAttachedState(true);
if (mActivity != null) {
mActivity.getAppsView().getSearchUiManager().prepareToFocusEditText(mIsInAllAppsRegion);
}
// Draw active task below Launcher so that All Apps can appear over it.
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(isInAllAppsRegion));
@@ -56,7 +56,7 @@ public class AnimatorControllerWithResistance {
private enum RecentsResistanceParams {
FROM_APP(0.75f, 0.5f, 1f, false),
FROM_APP_TO_ALL_APPS(0.75f, 0.5f, 0.8f, false),
FROM_APP_TO_ALL_APPS(1f, 0.6f, 0.8f, false),
FROM_APP_TABLET(1f, 0.7f, 1f, true),
FROM_APP_TO_ALL_APPS_TABLET(1f, 0.5f, 0.5f, false),
FROM_OVERVIEW(1f, 0.75f, 0.5f, false);
+22 -1
View File
@@ -546,7 +546,9 @@ public class DeviceProfile {
overviewTaskIconDrawableSizeGridPx =
res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size_grid);
overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx;
overviewActionsTopMarginPx = res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
// Don't add margin with floating search bar to minimize risk of overlapping.
overviewActionsTopMarginPx = FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() ? 0
: res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
overviewActionsButtonSpacing = res.getDimensionPixelSize(
R.dimen.overview_actions_button_spacing);
@@ -1434,6 +1436,25 @@ public class DeviceProfile {
return hotseatBarPadding;
}
/** The margin between the edge of all apps and the edge of the first icon. */
public int getAllAppsIconStartMargin() {
int allAppsSpacing;
if (isVerticalBarLayout()) {
// On phones, the landscape layout uses a different setup.
allAppsSpacing = workspacePadding.left + workspacePadding.right;
} else {
allAppsSpacing = allAppsLeftRightPadding * 2 + allAppsLeftRightMargin * 2;
}
int cellWidth = DeviceProfile.calculateCellWidth(
availableWidthPx - allAppsSpacing,
0 /* borderSpace */,
numShownAllAppsColumns);
int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * allAppsIconSizePx);
int iconAlignmentMargin = (cellWidth - iconVisibleSize) / 2;
return allAppsLeftRightPadding + iconAlignmentMargin;
}
private int getAdditionalQsbSpace() {
return isQsbInline ? hotseatQsbWidth + hotseatBorderSpace : 0;
}
+56 -1
View File
@@ -33,6 +33,7 @@ import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_ST
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
@@ -66,6 +67,7 @@ public abstract class LauncherState implements BaseState<LauncherState> {
public static final int CLEAR_ALL_BUTTON = 1 << 4;
public static final int WORKSPACE_PAGE_INDICATOR = 1 << 5;
public static final int SPLIT_PLACHOLDER_VIEW = 1 << 6;
public static final int FLOATING_SEARCH_BAR = 1 << 7;
// Flag indicating workspace has multiple pages visible.
public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
@@ -202,8 +204,61 @@ public abstract class LauncherState implements BaseState<LauncherState> {
return 0;
}
/**
* How far from the bottom of the screen the <em>floating</em> search bar should rest in this
* state when the IME is not present.
* <p>
* To hide offscreen, use a negative value.
* <p>
* Note: if the provided value is non-negative but less than the current bottom insets, the
* insets will be applied. As such, you can use 0 to default to this.
*/
public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
return areElementsVisible(launcher, FLOATING_SEARCH_BAR) ? dp.getQsbOffsetY()
: -dp.hotseatQsbHeight;
}
/**
* How far from the start of the screen the <em>floating</em> search bar should rest.
* <p>
* To use original margin, return a negative value.
*/
public int getFloatingSearchBarRestingMarginStart(Launcher launcher) {
boolean isRtl = Utilities.isRtl(launcher.getResources());
View qsb = launcher.getHotseat().getQsb();
return isRtl ? launcher.getHotseat().getRight() - qsb.getRight() : qsb.getLeft();
}
/**
* How far from the end of the screen the <em>floating</em> search bar should rest.
* <p>
* To use original margin, return a negative value.
*/
public int getFloatingSearchBarRestingMarginEnd(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
if (dp.isQsbInline) {
int marginStart = getFloatingSearchBarRestingMarginStart(launcher);
return dp.widthPx - marginStart - dp.hotseatQsbWidth;
}
boolean isRtl = Utilities.isRtl(launcher.getResources());
View qsb = launcher.getHotseat().getQsb();
return isRtl ? qsb.getLeft() : launcher.getHotseat().getRight() - qsb.getRight();
}
/** Whether the <em>floating</em> search bar should use the pill UI when not focused. */
public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
return false;
}
public int getVisibleElements(Launcher launcher) {
return HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR;
int elements = HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR;
// Floating search bar is visible in normal state except in landscape on phones.
if (!(launcher.getDeviceProfile().isPhone && launcher.getDeviceProfile().isLandscape)) {
elements |= FLOATING_SEARCH_BAR;
}
return elements;
}
/**
@@ -229,6 +229,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
return new AllAppsSearchUiDelegate(this);
}
public AllAppsSearchUiDelegate getSearchUiDelegate() {
return mSearchUiDelegate;
}
/**
* Initializes the view hierarchy and internal variables. Any initialization which actually uses
* these members should be done in {@link #onFinishInflate()}.
@@ -255,11 +259,13 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
mFastScroller = findViewById(R.id.fast_scroller);
mFastScroller.setPopupView(findViewById(R.id.fast_scroller_popup));
// Add the search box above everything else.
mSearchContainer = inflateSearchBox();
addView(mSearchContainer);
mSearchContainer = inflateSearchBar();
if (!isSearchBarFloating()) {
// Add the search box above everything else in this container (if the flag is enabled,
// it's added to drag layer in onAttach instead).
addView(mSearchContainer);
}
mSearchUiManager = (SearchUiManager) mSearchContainer;
mSearchUiDelegate.onInitializeSearchBox();
}
@Override
@@ -290,6 +296,13 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (isSearchBarFloating()) {
// Note: for Taskbar this is removed in TaskbarAllAppsController#cleanUpOverlay when the
// panel is closed. Can't do so in onDetach because we are also a child of drag layer
// so can't remove its views during that dispatch.
mActivityContext.getDragLayer().addView(mSearchContainer);
mSearchUiDelegate.onInitializeSearchBar();
}
mActivityContext.addOnDeviceProfileChangeListener(this);
}
@@ -311,7 +324,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
* Temporarily force the bottom sheet to be visible on non-tablets.
*
* @param force {@code true} means bottom sheet will be visible on phones until {@code reset()}.
**/
*/
public void forceBottomSheetVisible(boolean force) {
mForceBottomSheetVisible = force;
updateBackgroundVisibility(mActivityContext.getDeviceProfile());
@@ -421,7 +434,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
* A-Z apps list.
*
* @param animate Whether to animate the header during the reset (e.g. switching profile tabs).
**/
*/
public void reset(boolean animate) {
reset(animate, true);
}
@@ -431,7 +444,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
*
* @param animate Whether to animate the header during the reset (e.g. switching profile tabs).
* @param exitSearch Whether to force exit the search state and return to A-Z apps list.
**/
*/
public void reset(boolean animate, boolean exitSearch) {
for (int i = 0; i < mAH.size(); i++) {
if (mAH.get(i).mRecyclerView != null) {
@@ -494,6 +507,9 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
// Will be called at the end of the animation.
return;
}
if (currentActivePage != SEARCH) {
mActivityContext.hideKeyboard();
}
if (mAH.get(currentActivePage).mRecyclerView != null) {
mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar(mFastScroller);
}
@@ -551,7 +567,6 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
mActivityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
}
mActivityContext.hideKeyboard();
});
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> {
@@ -559,24 +574,24 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
mActivityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
}
mActivityContext.hideKeyboard();
});
setDeviceManagementResources();
onActivePageChanged(mViewPager.getNextPage());
if (mHeader.isSetUp()) {
onActivePageChanged(mViewPager.getNextPage());
}
} else {
mAH.get(AdapterHolder.MAIN).setup(findViewById(R.id.apps_list_view), null);
mAH.get(AdapterHolder.WORK).mRecyclerView = null;
}
setupHeader();
if (isSearchBarOnBottom()) {
if (isSearchBarFloating()) {
// Keep the scroller above the search bar.
RelativeLayout.LayoutParams scrollerLayoutParams =
(LayoutParams) mFastScroller.getLayoutParams();
scrollerLayoutParams.addRule(RelativeLayout.ABOVE, R.id.search_container_all_apps);
scrollerLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
scrollerLayoutParams.bottomMargin = getResources().getDimensionPixelSize(
R.dimen.fastscroll_bottom_margin_floating_search);
scrollerLayoutParams.bottomMargin = mSearchContainer.getHeight()
+ getResources().getDimensionPixelSize(
R.dimen.fastscroll_bottom_margin_floating_search);
}
mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
@@ -628,11 +643,9 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
removeCustomRules(getSearchRecyclerView());
if (!isSearchSupported()) {
layoutWithoutSearchContainer(rvContainer, showTabs);
} else if (isSearchBarOnBottom()) {
} else if (isSearchBarFloating()) {
alignParentTop(rvContainer, showTabs);
alignParentTop(getSearchRecyclerView(), /* tabs= */ false);
layoutAboveSearchContainer(rvContainer);
layoutAboveSearchContainer(getSearchRecyclerView());
} else {
layoutBelowSearchContainer(rvContainer, showTabs);
layoutBelowSearchContainer(getSearchRecyclerView(), /* tabs= */ false);
@@ -663,7 +676,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
removeCustomRules(mHeader);
if (!isSearchSupported()) {
layoutWithoutSearchContainer(mHeader, false /* includeTabsMargin */);
} else if (isSearchBarOnBottom()) {
} else if (isSearchBarFloating()) {
alignParentTop(mHeader, false /* includeTabsMargin */);
} else {
layoutBelowSearchContainer(mHeader, false /* includeTabsMargin */);
@@ -703,16 +716,62 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
}
/**
* It is up to the search container view created by {@link #inflateSearchBox()} to use the
* floating search bar flag to move itself to the bottom of this container. This method checks
* if that had been done; otherwise the flag will be ignored.
*
* @return true if the search bar is at the bottom of the container (as opposed to the top).
**/
private boolean isSearchBarOnBottom() {
return FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()
&& ((RelativeLayout.LayoutParams) mSearchContainer.getLayoutParams()).getRule(
ALIGN_PARENT_BOTTOM) == RelativeLayout.TRUE;
* @return true if the search bar is floating above this container (at the bottom of the screen)
*/
protected boolean isSearchBarFloating() {
return mSearchUiDelegate.isSearchBarFloating();
}
/**
* Whether the <em>floating</em> search bar should appear as a small pill when not focused.
* <p>
* Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely
* makes sense to use that method to derive an appropriate value for the current/target state.
*/
public boolean shouldFloatingSearchBarBePillWhenUnfocused() {
return false;
}
/**
* How far from the bottom of the screen the <em>floating</em> search bar should rest when the
* IME is not present.
* <p>
* To hide offscreen, use a negative value.
* <p>
* Note: if the provided value is non-negative but less than the current bottom insets, the
* insets will be applied. As such, you can use 0 to default to this.
* <p>
* Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely
* makes sense to use that method to derive an appropriate value for the current/target state.
*/
public int getFloatingSearchBarRestingMarginBottom() {
return 0;
}
/**
* How far from the start of the screen the <em>floating</em> search bar should rest.
* <p>
* To use original margin, return a negative value.
* <p>
* Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely
* makes sense to use that method to derive an appropriate value for the current/target state.
*/
public int getFloatingSearchBarRestingMarginStart() {
DeviceProfile dp = mActivityContext.getDeviceProfile();
return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
}
/**
* How far from the end of the screen the <em>floating</em> search bar should rest.
* <p>
* To use original margin, return a negative value.
* <p>
* Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely
* makes sense to use that method to derive an appropriate value for the current/target state.
*/
public int getFloatingSearchBarRestingMarginEnd() {
DeviceProfile dp = mActivityContext.getDeviceProfile();
return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
}
private void layoutBelowSearchContainer(View v, boolean includeTabsMargin) {
@@ -732,15 +791,6 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
layoutParams.topMargin = topMargin;
}
private void layoutAboveSearchContainer(View v) {
if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
return;
}
RelativeLayout.LayoutParams layoutParams = (LayoutParams) v.getLayoutParams();
layoutParams.addRule(RelativeLayout.ABOVE, R.id.search_container_all_apps);
}
private void alignParentTop(View v, boolean includeTabsMargin) {
if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
return;
@@ -794,10 +844,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
}
/**
* Inflates the search box
* Inflates the search bar
*/
protected View inflateSearchBox() {
return mSearchUiDelegate.inflateSearchBox();
protected View inflateSearchBar() {
return mSearchUiDelegate.inflateSearchBar();
}
/** The adapter provider for the main section. */
@@ -977,7 +1027,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
/**
* The container for A-Z apps (the ViewPager for main+work tabs, or main RV). This is currently
* hidden while searching.
**/
*/
public ViewGroup getAppsRecyclerViewContainer() {
return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
}
@@ -1024,7 +1074,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
} else {
int topPadding = grid.allAppsTopPadding;
if (isSearchBarOnBottom() && !grid.isTablet) {
if (isSearchBarFloating() && !grid.isTablet) {
topPadding += getResources().getDimensionPixelSize(
R.dimen.all_apps_additional_top_padding_floating_search);
}
@@ -1236,7 +1286,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
final FloatingHeaderView headerView = getFloatingHeaderView();
if (hasBottomSheet) {
// Start adding header protection if search bar or tabs will attach to the top.
if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) {
if (!isSearchBarFloating() || mUsingTabs) {
mTmpRectF.set(
leftWithScale,
topWithScale,
@@ -1292,7 +1342,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
/** Returns the position of the bottom edge of the header */
public int getHeaderBottom() {
int bottom = (int) getTranslationY() + mHeader.getClipTop();
if (isSearchBarOnBottom()) {
if (isSearchBarFloating()) {
if (mActivityContext.getDeviceProfile().isTablet) {
return bottom + mBottomSheetBackground.getTop();
}
@@ -1363,6 +1413,9 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
if (isWork() && mWorkManager.getWorkModeSwitch() != null) {
bottomOffset = mInsets.bottom + mWorkManager.getWorkModeSwitch().getHeight();
}
if (isSearchBarFloating()) {
bottomOffset += mSearchContainer.getHeight();
}
mRecyclerView.setPadding(mPadding.left, mPadding.top, mPadding.right,
mPadding.bottom + bottomOffset);
}
@@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
@@ -234,7 +235,11 @@ public class AllAppsTransitionController
*/
public void setProgress(float progress) {
mProgress = progress;
getAppsViewProgressTranslationY().setValue(mProgress * mShiftRange);
boolean fromBackground =
mLauncher.getStateManager().getCurrentStableState() == BACKGROUND_APP;
// Allow apps panel to shift the full screen if coming from another app.
float shiftRange = fromBackground ? mLauncher.getDeviceProfile().heightPx : mShiftRange;
getAppsViewProgressTranslationY().setValue(mProgress * shiftRange);
mLauncher.onAllAppsTransition(1 - progress);
boolean hasScrim = progress < NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD
@@ -22,6 +22,7 @@ import android.view.WindowInsets;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.StateManager;
/**
* AllAppsContainerView with launcher specific callbacks
@@ -53,4 +54,75 @@ public class LauncherAllAppsContainerView extends ActivityAllAppsContainerView<L
public boolean isInAllApps() {
return mActivityContext.getStateManager().isInStableState(LauncherState.ALL_APPS);
}
@Override
public boolean shouldFloatingSearchBarBePillWhenUnfocused() {
if (!isSearchBarFloating()) {
return false;
}
Launcher launcher = mActivityContext;
StateManager<LauncherState> manager = launcher.getStateManager();
if (manager.isInTransition() && manager.getTargetState() != null) {
return manager.getTargetState().shouldFloatingSearchBarUsePillWhenUnfocused(launcher);
}
return manager.getCurrentStableState()
.shouldFloatingSearchBarUsePillWhenUnfocused(launcher);
}
@Override
public int getFloatingSearchBarRestingMarginBottom() {
if (!isSearchBarFloating()) {
return super.getFloatingSearchBarRestingMarginBottom();
}
Launcher launcher = mActivityContext;
StateManager<LauncherState> stateManager = launcher.getStateManager();
// We want to rest at the current state's resting position, unless we are in transition and
// the target state's resting position is higher (that way if we are closing the keyboard,
// we can stop translating at that point).
int currentStateMarginBottom = stateManager.getCurrentStableState()
.getFloatingSearchBarRestingMarginBottom(launcher);
int targetStateMarginBottom = -1;
if (stateManager.isInTransition() && stateManager.getTargetState() != null) {
targetStateMarginBottom = stateManager.getTargetState()
.getFloatingSearchBarRestingMarginBottom(launcher);
if (targetStateMarginBottom < 0) {
// Go ahead and move offscreen.
return targetStateMarginBottom;
}
}
return Math.max(targetStateMarginBottom, currentStateMarginBottom);
}
@Override
public int getFloatingSearchBarRestingMarginStart() {
if (!isSearchBarFloating()) {
return super.getFloatingSearchBarRestingMarginStart();
}
StateManager<LauncherState> stateManager = mActivityContext.getStateManager();
if (stateManager.isInTransition() && stateManager.getTargetState() != null) {
return stateManager.getTargetState()
.getFloatingSearchBarRestingMarginStart(mActivityContext);
}
return stateManager.getCurrentStableState()
.getFloatingSearchBarRestingMarginStart(mActivityContext);
}
@Override
public int getFloatingSearchBarRestingMarginEnd() {
if (!isSearchBarFloating()) {
return super.getFloatingSearchBarRestingMarginEnd();
}
StateManager<LauncherState> stateManager = mActivityContext.getStateManager();
if (stateManager.isInTransition() && stateManager.getTargetState() != null) {
return stateManager.getTargetState()
.getFloatingSearchBarRestingMarginEnd(mActivityContext);
}
return stateManager.getCurrentStableState()
.getFloatingSearchBarRestingMarginEnd(mActivityContext);
}
}
@@ -48,6 +48,14 @@ public interface SearchUiManager {
@Nullable
ExtendedEditText getEditText();
/**
* Hint to the edit text that it is about to be focused or unfocused. This can be used to start
* animating the edit box accordingly, e.g. after a gesture completes.
*
* @param focused true if the edit text is about to be focused, false if it will be unfocused
*/
default void prepareToFocusEditText(boolean focused) {}
/**
* Sets whether EditText background should be visible
* @param maxAlpha defines the maximum alpha the background should animates to
@@ -36,7 +36,6 @@ import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
@@ -108,7 +107,7 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
if (lp != null) {
int bottomMargin = getResources().getDimensionPixelSize(R.dimen.work_fab_margin_bottom);
DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
if (mActivityContext.getAppsView().isSearchBarFloating()) {
bottomMargin += dp.hotseatQsbHeight;
}
@@ -49,8 +49,13 @@ public class AllAppsSearchUiDelegate {
// Do nothing.
}
/** Invoked when the search box has been added to All Apps. */
public void onInitializeSearchBox() {
/** Invoked when the search bar has been added to All Apps. */
public void onInitializeSearchBar() {
// Do nothing.
}
/** Invoked when the search bar has been removed from All Apps. */
public void onDestroySearchBar() {
// Do nothing.
}
@@ -59,11 +64,16 @@ public class AllAppsSearchUiDelegate {
return LayoutInflater.from(mAppsView.getContext());
}
/** Inflate the search box for All Apps. */
public View inflateSearchBox() {
/** Inflate the search bar for All Apps. */
public View inflateSearchBar() {
return getLayoutInflater().inflate(R.layout.search_container_all_apps, mAppsView, false);
}
/** Whether the search box is floating above the apps surface (inset by the IME). */
public boolean isSearchBarFloating() {
return false;
}
/** Creates the adapter provider for the main section. */
public SearchAdapterProvider<?> createMainAdapterProvider() {
return new DefaultSearchAdapterProvider(mActivityContext);
@@ -44,14 +44,30 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba
private float mInitialTranslation;
private float mTerminalTranslation;
private KeyboardTranslationState mKeyboardTranslationState = KeyboardTranslationState.SYSTEM;
/** Current state of the keyboard. */
public enum KeyboardTranslationState {
// We are not controlling the keyboard, and it may or may not be translating.
SYSTEM,
// We are about to gain control of the keyboard, but the current state may be transient.
MANUAL_PREPARED,
// We are manually translating the keyboard.
MANUAL_ONGOING
}
public KeyboardInsetAnimationCallback(View view) {
super(DISPATCH_MODE_STOP);
mView = view;
}
public KeyboardTranslationState getKeyboardTranslationState() {
return mKeyboardTranslationState;
}
@Override
public void onPrepare(WindowInsetsAnimation animation) {
mKeyboardTranslationState = KeyboardTranslationState.MANUAL_PREPARED;
mInitialTranslation = mView.getTranslationY();
}
@@ -62,6 +78,7 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba
mTerminalTranslation = mView.getTranslationY();
// Reset the translation in case the view is drawn before onProgress gets called.
mView.setTranslationY(mInitialTranslation);
mKeyboardTranslationState = KeyboardTranslationState.MANUAL_ONGOING;
if (mView instanceof KeyboardInsetListener) {
((KeyboardInsetListener) mView).onTranslationStart();
}
@@ -90,6 +107,10 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba
mView.setTranslationY(translationY);
}
if (mView instanceof KeyboardInsetListener) {
((KeyboardInsetListener) mView).onKeyboardAlphaChanged(animation.getAlpha());
}
return windowInsets;
}
@@ -98,7 +119,7 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba
if (mView instanceof KeyboardInsetListener) {
((KeyboardInsetListener) mView).onTranslationEnd();
}
super.onEnd(animation);
mKeyboardTranslationState = KeyboardTranslationState.SYSTEM;
}
/**
@@ -110,6 +131,13 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba
*/
void onTranslationStart();
/**
* Called from {@link KeyboardInsetAnimationCallback#onProgress}
*
* @param alpha the current IME alpha
*/
default void onKeyboardAlphaChanged(float alpha) {}
/**
* Called from {@link KeyboardInsetAnimationCallback#onEnd}
*/
@@ -116,7 +116,8 @@ public final class FeatureFlags {
// TODO(Block 4): Cleanup flags
public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR =
getReleaseFlag(268388460, "ENABLE_FLOATING_SEARCH_BAR", DISABLED,
"Keep All Apps search bar at the bottom (but above keyboard if open)");
"Allow search bar to persist and animate across states, and attach to"
+ " the keyboard from the bottom of the screen");
public static final BooleanFlag ENABLE_ALL_APPS_FROM_OVERVIEW =
getDebugFlag(275132633, "ENABLE_ALL_APPS_FROM_OVERVIEW", DISABLED,
@@ -76,6 +76,10 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
return mState;
}
public STATE_TYPE getTargetState() {
return (STATE_TYPE) mConfig.targetState;
}
public STATE_TYPE getCurrentStableState() {
return mCurrentStableState;
}
@@ -66,7 +66,8 @@ public class StateAnimationConfig {
ANIM_WORKSPACE_PAGE_TRANSLATE_X,
ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
ANIM_ALL_APPS_BOTTOM_SHEET_FADE
ANIM_ALL_APPS_BOTTOM_SHEET_FADE,
ANIM_ALL_APPS_KEYBOARD_FADE
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimType {}
@@ -90,8 +91,9 @@ public class StateAnimationConfig {
public static final int ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN = 17;
public static final int ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE = 18;
public static final int ANIM_ALL_APPS_BOTTOM_SHEET_FADE = 19;
public static final int ANIM_ALL_APPS_KEYBOARD_FADE = 20;
private static final int ANIM_TYPES_COUNT = 20;
private static final int ANIM_TYPES_COUNT = 21;
protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
@@ -22,10 +22,12 @@ import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.app.animation.Interpolators.mapToProgress;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_KEYBOARD_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
@@ -293,20 +295,15 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
config.setInterpolator(ANIM_WORKSPACE_SCALE, INSTANT);
config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, INSTANT);
} else {
// Remove scrim for this transition.
config.setInterpolator(ANIM_SCRIM_FADE, progress -> 0);
// For now, pop the background panel in at full opacity at the threshold.
// Pop the background panel, keyboard, and content in at full opacity at the threshold.
config.setInterpolator(ANIM_ALL_APPS_BOTTOM_SHEET_FADE,
thresholdInterpolator(threshold, INSTANT));
// Fade the apps in when the scrim normally does, so it's apparent sooner what is
// happening (in this case we are fading them on top of the background panel).
config.setInterpolator(ANIM_ALL_APPS_FADE,
thresholdInterpolator(threshold, SCRIM_FADE_MANUAL));
config.setInterpolator(ANIM_ALL_APPS_KEYBOARD_FADE,
thresholdInterpolator(threshold, INSTANT));
config.setInterpolator(ANIM_ALL_APPS_FADE, thresholdInterpolator(threshold, INSTANT));
config.setInterpolator(ANIM_VERTICAL_PROGRESS,
thresholdInterpolator(threshold, ALL_APPS_VERTICAL_PROGRESS_MANUAL));
thresholdInterpolator(threshold, mapToProgress(LINEAR, threshold, 1f)));
}
}