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:
committed by
Android (Google) Code Review
commit
a9d2db38f5
+2
-2
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user