diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index 339f3a9174..af422cb4c0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -23,7 +23,6 @@ import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; import static com.android.launcher3.taskbar.LauncherTaskbarUIController.SYSUI_SURFACE_PROGRESS_INDEX; import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode; -import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode; import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y; import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK; import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME; @@ -54,6 +53,7 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.graphics.Region.Op; @@ -81,6 +81,9 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AlphaUpdateListener; import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton; +import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory; +import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter; +import com.android.launcher3.util.DimensionUtils; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.BaseDragLayer; @@ -181,6 +184,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT private final ViewTreeObserver.OnComputeInternalInsetsListener mSeparateWindowInsetsComputer = this::onComputeInsetsForSeparateWindow; private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender(); + private View mRecentsButton; public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) { mContext = context; @@ -203,11 +207,11 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT boolean isThreeButtonNav = mContext.isThreeButtonNav(); DeviceProfile deviceProfile = mContext.getDeviceProfile(); Resources resources = mContext.getResources(); - mNavButtonsView.getLayoutParams().height = !isPhoneMode(deviceProfile) ? - mContext.isUserSetupComplete() - ? deviceProfile.taskbarSize - : resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame) - : resources.getDimensionPixelSize(R.dimen.taskbar_size); + Point p = !mContext.isUserSetupComplete() + ? new Point(0, resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame)) + : DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources, + TaskbarManager.isPhoneMode(deviceProfile)); + mNavButtonsView.getLayoutParams().height = p.y; mIsImeRenderingNavButtons = InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar(); @@ -268,81 +272,6 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT mControllers.navButtonController); updateButtonLayoutSpacing(); updateStateForFlag(FLAG_SMALL_SCREEN, isPhoneButtonNavMode(mContext)); - if (isInSetup) { - handleSetupUi(); - - // Hide back button in SUW if keyboard is showing (IME draws its own back). - mPropertyHolders.add(new StatePropertyHolder( - mBackButtonAlpha.getProperty(ALPHA_INDEX_SUW), - flags -> (flags & FLAG_IME_VISIBLE) == 0)); - - // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set - // it based on dark theme for now. - int mode = resources.getConfiguration().uiMode - & Configuration.UI_MODE_NIGHT_MASK; - boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES; - mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1); - } else if (isInKidsMode) { - int iconSize = resources.getDimensionPixelSize( - R.dimen.taskbar_icon_size_kids); - int buttonWidth = resources.getDimensionPixelSize( - R.dimen.taskbar_nav_buttons_width_kids); - int buttonHeight = resources.getDimensionPixelSize( - R.dimen.taskbar_nav_buttons_height_kids); - int buttonRadius = resources.getDimensionPixelSize( - R.dimen.taskbar_nav_buttons_corner_radius_kids); - int paddingleft = (buttonWidth - iconSize) / 2; - int paddingRight = paddingleft; - int paddingTop = (buttonHeight - iconSize) / 2; - int paddingBottom = paddingTop; - - // Update icons - ((ImageView) mBackButton).setImageDrawable( - mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids)); - ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER); - mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom); - ((ImageView) mHomeButton).setImageDrawable( - mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids)); - ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER); - mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom); - - // Home button layout - LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams( - buttonWidth, - buttonHeight - ); - int homeButtonLeftMargin = resources.getDimensionPixelSize( - R.dimen.taskbar_home_button_left_margin_kids); - homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0); - mHomeButton.setLayoutParams(homeLayoutparams); - - // Back button layout - LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams( - buttonWidth, - buttonHeight - ); - int backButtonLeftMargin = resources.getDimensionPixelSize( - R.dimen.taskbar_back_button_left_margin_kids); - backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0); - mBackButton.setLayoutParams(backLayoutParams); - - // Button backgrounds - int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1); - PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha); - buttonBackground.setCornerRadius(buttonRadius); - mHomeButton.setBackground(buttonBackground); - mBackButton.setBackground(buttonBackground); - - // Update alignment within taskbar - FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams) - mNavButtonContainer.getLayoutParams(); - navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd() / 2); - navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart()); - navButtonsLayoutParams.gravity = Gravity.CENTER; - mNavButtonContainer.requestLayout(); - - mHomeButton.setOnLongClickListener(null); - } // Animate taskbar background when either.. // notification shade expanded AND not on keyguard @@ -445,20 +374,20 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT (flags & FLAG_DISABLE_HOME) == 0)); // Recents button - View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS, + mRecentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS, navContainer, navButtonController, R.id.recent_apps); - mHitboxExtender.init(recentsButton, mNavButtonsView, mContext.getDeviceProfile(), + mHitboxExtender.init(mRecentsButton, mNavButtonsView, mContext.getDeviceProfile(), () -> { float[] recentsCoords = new float[2]; - getDescendantCoordRelativeToAncestor(recentsButton, mNavButtonsView, + getDescendantCoordRelativeToAncestor(mRecentsButton, mNavButtonsView, recentsCoords, false); return recentsCoords; }, new Handler()); - recentsButton.setOnClickListener(v -> { + mRecentsButton.setOnClickListener(v -> { navButtonController.onButtonClick(BUTTON_RECENTS, v); mHitboxExtender.onRecentsButtonClicked(); }); - mPropertyHolders.add(new StatePropertyHolder(recentsButton, + mPropertyHolders.add(new StatePropertyHolder(mRecentsButton, flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && (flags & FLAG_DISABLE_RECENTS) == 0 && !mContext.isNavBarKidsModeActive())); @@ -773,14 +702,22 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT || !mContext.isUserSetupComplete()) { return; } - - if (isPhoneButtonNavMode(mContext)) { - updatePhoneButtonSpacing(); - return; - } - DeviceProfile dp = mContext.getDeviceProfile(); Resources res = mContext.getResources(); + boolean isInSetup = !mContext.isUserSetupComplete(); + // TODO(b/244231596) we're getting the incorrect kidsMode value in small-screen + boolean isInKidsMode = mContext.isNavBarKidsModeActive(); + + if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) { + boolean isThreeButtonNav = mContext.isThreeButtonNav(); + + NavButtonLayoutter navButtonLayoutter = + NavButtonLayoutFactory.Companion.getUiLayoutter( + dp, mNavButtonsView, res, isInKidsMode, isInSetup, isThreeButtonNav, + TaskbarManager.isPhoneMode(dp)); + navButtonLayoutter.layoutButtons(dp, isContextualButtonShowing()); + return; + } // Add spacing after the end of the last nav button FrameLayout.LayoutParams navButtonParams = @@ -816,38 +753,84 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT buttonLayoutParams.setMarginEnd(spaceInBetween / 2); } } - } - /** Uniformly spaces out the 3 button nav for smaller phone screens */ - private void updatePhoneButtonSpacing() { - DeviceProfile dp = mContext.getDeviceProfile(); - Resources res = mContext.getResources(); + if (isInSetup) { + handleSetupUi(); - // TODO: Polish pending, this is just to make it usable - FrameLayout.LayoutParams navContainerParams = - (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams(); - int endStartMargins = res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size); - navContainerParams.gravity = Gravity.CENTER; - navContainerParams.setMarginEnd(endStartMargins); - navContainerParams.setMarginStart(endStartMargins); - mNavButtonContainer.setLayoutParams(navContainerParams); + // Hide back button in SUW if keyboard is showing (IME draws its own back). + mPropertyHolders.add(new StatePropertyHolder( + mBackButtonAlpha.getProperty(ALPHA_INDEX_SUW), + flags -> (flags & FLAG_IME_VISIBLE) == 0)); - // Add the spaces in between the nav buttons - int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone); - for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) { - View navButton = mNavButtonContainer.getChildAt(i); - LinearLayout.LayoutParams buttonLayoutParams = - (LinearLayout.LayoutParams) navButton.getLayoutParams(); - buttonLayoutParams.weight = 1; - if (i == 0) { - buttonLayoutParams.setMarginEnd(spaceInBetween / 2); - } else if (i == mNavButtonContainer.getChildCount() - 1) { - buttonLayoutParams.setMarginStart(spaceInBetween / 2); - } else { - buttonLayoutParams.setMarginStart(spaceInBetween / 2); - buttonLayoutParams.setMarginEnd(spaceInBetween / 2); - } + // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set + // it based on dark theme for now. + int mode = res.getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_MASK; + boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES; + mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1); + } else if (isInKidsMode) { + int iconSize = res.getDimensionPixelSize( + R.dimen.taskbar_icon_size_kids); + int buttonWidth = res.getDimensionPixelSize( + R.dimen.taskbar_nav_buttons_width_kids); + int buttonHeight = res.getDimensionPixelSize( + R.dimen.taskbar_nav_buttons_height_kids); + int buttonRadius = res.getDimensionPixelSize( + R.dimen.taskbar_nav_buttons_corner_radius_kids); + int paddingleft = (buttonWidth - iconSize) / 2; + int paddingRight = paddingleft; + int paddingTop = (buttonHeight - iconSize) / 2; + int paddingBottom = paddingTop; + + // Update icons + ((ImageView) mBackButton).setImageDrawable( + mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids)); + ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER); + mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom); + ((ImageView) mHomeButton).setImageDrawable( + mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids)); + ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER); + mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom); + + // Home button layout + LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams( + buttonWidth, + buttonHeight + ); + int homeButtonLeftMargin = res.getDimensionPixelSize( + R.dimen.taskbar_home_button_left_margin_kids); + homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0); + mHomeButton.setLayoutParams(homeLayoutparams); + + // Back button layout + LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams( + buttonWidth, + buttonHeight + ); + int backButtonLeftMargin = res.getDimensionPixelSize( + R.dimen.taskbar_back_button_left_margin_kids); + backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0); + mBackButton.setLayoutParams(backLayoutParams); + + // Button backgrounds + int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1); + PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha); + buttonBackground.setCornerRadius(buttonRadius); + mHomeButton.setBackground(buttonBackground); + mBackButton.setBackground(buttonBackground); + + // Update alignment within taskbar + FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams) + mNavButtonContainer.getLayoutParams(); + navButtonsLayoutParams.setMarginStart( + navButtonsLayoutParams.getMarginEnd() / 2); + navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart()); + navButtonsLayoutParams.gravity = Gravity.CENTER; + mNavButtonContainer.requestLayout(); + + mHomeButton.setOnLongClickListener(null); } + } public void onDestroy() { @@ -859,6 +842,8 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT moveNavButtonsBackToTaskbarWindow(); mNavButtonContainer.removeAllViews(); + mEndContextualContainer.removeAllViews(); + mStartContextualContainer.removeAllViews(); mAllButtons.clear(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java index 6b67b50fd8..e23e27ed0f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java @@ -101,8 +101,8 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT resources.getDimensionPixelSize(R.dimen.taskbar_stashed_small_screen); } else { mStashedHandleView.getLayoutParams().height = deviceProfile.taskbarSize; - mStashedHandleWidth = - resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width); + mStashedHandleWidth = resources + .getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width); } mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_STASHED).setValue( diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 72c163ebc3..0c488cb4b1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -154,6 +154,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0); mIsNavBarForceVisible = settingsCache.getValue( Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0); + // TODO(b/244231596) For shared Taskbar window, update this value in init() instead so + // to get correct value when recreating the taskbar mIsNavBarKidsMode = settingsCache.getValue( Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0); @@ -276,9 +278,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext { * @param type The window type to pass to the created WindowManager.LayoutParams. */ public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type) { + DeviceProfile deviceProfile = getDeviceProfile(); + // Taskbar is on the logical bottom of the screen + boolean isVerticalBarLayout = TaskbarManager.isPhoneMode(deviceProfile) && + deviceProfile.isLandscape; WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams( - MATCH_PARENT, - mLastRequestedNonFullscreenHeight, + isVerticalBarLayout ? mLastRequestedNonFullscreenHeight : MATCH_PARENT, + isVerticalBarLayout ? MATCH_PARENT : mLastRequestedNonFullscreenHeight, type, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SLIPPERY @@ -286,7 +292,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext { PixelFormat.TRANSLUCENT); windowLayoutParams.setTitle(WINDOW_TITLE); windowLayoutParams.packageName = getPackageName(); - windowLayoutParams.gravity = Gravity.BOTTOM; + windowLayoutParams.gravity = !isVerticalBarLayout ? + Gravity.BOTTOM : + Gravity.END; // TODO(b/230394142): seascape + windowLayoutParams.setFitInsetsTypes(0); windowLayoutParams.receiveInsetsIgnoringZOrder = true; windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; @@ -803,7 +812,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mIsUserSetupComplete; } - protected boolean isNavBarKidsModeActive() { + public boolean isNavBarKidsModeActive() { return mIsNavBarKidsMode && isThreeButtonNav(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java index 025fe7a017..353f1e03cb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java @@ -16,11 +16,13 @@ package com.android.launcher3.taskbar; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.Rect; import android.view.ViewTreeObserver; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.util.DimensionUtils; import com.android.launcher3.util.TouchController; import com.android.quickstep.AnimatedFloat; @@ -177,9 +179,12 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa DeviceProfile deviceProfile = mActivity.getDeviceProfile(); if (TaskbarManager.isPhoneMode(deviceProfile)) { Resources resources = mActivity.getResources(); - return mActivity.isThreeButtonNav() ? - resources.getDimensionPixelSize(R.dimen.taskbar_size) : - resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size); + Point taskbarDimensions = + DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources, + TaskbarManager.isPhoneMode(deviceProfile)); + return taskbarDimensions.y == -1 ? + deviceProfile.getDisplayInfo().currentSize.y : + taskbarDimensions.y; } else { return deviceProfile.taskbarSize; } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt index 079e8a1f2d..bbbc1e6b80 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt @@ -84,16 +84,16 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps for (provider in windowLayoutParams.providedInsets) { if (provider.type == ITYPE_EXTRA_NAVIGATION_BAR) { - provider.insetsSize = Insets.of(0, 0, 0, contentHeight) + provider.insetsSize = getInsetsByNavMode(contentHeight) } else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT || provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES) { - provider.insetsSize = Insets.of(0, 0, 0, tappableHeight) + provider.insetsSize = getInsetsByNavMode(tappableHeight) } } - val imeInsetsSize = Insets.of(0, 0, 0, taskbarHeightForIme) + val imeInsetsSize = getInsetsByNavMode(taskbarHeightForIme) // Use 0 insets for the VoiceInteractionWindow (assistant) when gesture nav is enabled. - val visInsetsSize = Insets.of(0, 0, 0, if (context.isGestureNav) 0 else tappableHeight) + val visInsetsSize = getInsetsByNavMode(if (context.isGestureNav) 0 else tappableHeight) val insetsSizeOverride = arrayOf( InsetsFrameProvider.InsetsSizeOverride( TYPE_INPUT_METHOD, @@ -109,6 +109,21 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask } } + /** + * @return [Insets] where the [bottomInset] is either used as a bottom inset or + * right/left inset if using 3 button nav + */ + private fun getInsetsByNavMode(bottomInset: Int) : Insets { + val devicePortrait = !context.deviceProfile.isLandscape + if (!TaskbarManager.isPhoneButtonNavMode(context) || devicePortrait) { + // Taskbar or portrait phone mode + return Insets.of(0, 0, 0, bottomInset) + } + + // TODO(b/230394142): seascape + return Insets.of(0, 0, bottomInset, 0) + } + /** * Sets {@param providesInsetsTypes} as the inset types provided by {@param params}. * @param params The window layout params. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 9fd2bf90b3..c5e1b8fdcd 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -715,8 +715,16 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay); } + /** + * We stash when IME or IME switcher is showing AND NOT + * * in small screen AND + * * 3 button nav AND + * * landscape (or seascape) + */ private boolean shouldStashForIme() { - return mIsImeShowing || mIsImeSwitcherShowing; + return (mIsImeShowing || mIsImeSwitcherShowing) && + !(isPhoneMode() && mActivity.isThreeButtonNav() + && mActivity.getDeviceProfile().isLandscape); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt new file mode 100644 index 0000000000..68ea27a8ec --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt @@ -0,0 +1,49 @@ +/* + * 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.navbutton + +import android.content.res.Resources +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import com.android.launcher3.R +import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter + +/** + * Meant to be a simple container for data subclasses will need + * + * Assumes that the 3 navigation buttons (back/home/recents) have already been added to + * [navButtonContainer] + * + * @property navButtonContainer ViewGroup that holds the 3 navigation buttons. + * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME dismiss). + * @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y). + */ +abstract class AbstractNavButtonLayoutter( + val resources: Resources, + val navButtonContainer: LinearLayout, + protected val endContextualContainer: ViewGroup, + protected val startContextualContainer: ViewGroup +) : NavButtonLayoutter { + protected val homeButton: ImageView = navButtonContainer + .findViewById(R.id.home) + protected val recentsButton: ImageView = navButtonContainer + .findViewById(R.id.recent_apps) + protected val backButton: ImageView = navButtonContainer + .findViewById(R.id.back) +} + diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt new file mode 100644 index 0000000000..c67ab7964e --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt @@ -0,0 +1,109 @@ +/* + * 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.navbutton + +import android.content.res.Resources +import android.graphics.Color +import android.graphics.drawable.PaintDrawable +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_ICON_SIZE_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_BACK_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_HOME_KIDS + +class KidsNavLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : AbstractNavButtonLayoutter( + resources, + navBarContainer, + endContextualContainer, + startContextualContainer +) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + val iconSize: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_ICON_SIZE_KIDS) + val buttonWidth: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS) + val buttonHeight: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS) + val buttonRadius: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS) + val paddingLeft = (buttonWidth - iconSize) / 2 + val paddingTop = (buttonHeight - iconSize) / 2 + + // Update icons + backButton.setImageDrawable( + backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS)) + backButton.scaleType = ImageView.ScaleType.FIT_CENTER + backButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop) + homeButton.setImageDrawable( + homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS)) + homeButton.scaleType = ImageView.ScaleType.FIT_CENTER + homeButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop) + + // Home button layout + val homeLayoutparams = LinearLayout.LayoutParams( + buttonWidth, + buttonHeight + ) + val homeButtonLeftMargin: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS) + homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0) + homeButton.layoutParams = homeLayoutparams + + // Back button layout + val backLayoutParams = LinearLayout.LayoutParams( + buttonWidth, + buttonHeight + ) + val backButtonLeftMargin: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS) + backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0) + backButton.layoutParams = backLayoutParams + + // Button backgrounds + val whiteWith10PctAlpha = Color.argb(0.1f, 1f, 1f, 1f) + val buttonBackground = PaintDrawable(whiteWith10PctAlpha) + buttonBackground.setCornerRadius(buttonRadius.toFloat()) + homeButton.background = buttonBackground + backButton.background = buttonBackground + + // Update alignment within taskbar + val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + navButtonsLayoutParams.apply { + marginStart = navButtonsLayoutParams.marginEnd / 2 + marginEnd = navButtonsLayoutParams.marginStart + gravity = Gravity.CENTER + } + navButtonContainer.requestLayout() + + homeButton.onLongClickListener = null + } +} \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java new file mode 100644 index 0000000000..0d9b855c91 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java @@ -0,0 +1,68 @@ +/* + * 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.navbutton; + +import android.annotation.DimenRes; +import android.annotation.DrawableRes; +import android.annotation.IdRes; + +import com.android.launcher3.R; + +/** + * A class for retrieving resources in Kotlin. + * + * This class should be removed once the build system supports resources loading in Kotlin. + */ +public final class LayoutResourceHelper { + + // -------------------------- + // Kids Nav Layout + @DimenRes + public static final int DIMEN_TASKBAR_ICON_SIZE_KIDS = R.dimen.taskbar_icon_size_kids; + @DrawableRes + public static final int DRAWABLE_SYSBAR_BACK_KIDS = R.drawable.ic_sysbar_back_kids; + @DrawableRes + public static final int DRAWABLE_SYSBAR_HOME_KIDS = R.drawable.ic_sysbar_home_kids; + @DimenRes + public static final int DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS = + R.dimen.taskbar_home_button_left_margin_kids; + @DimenRes + public static final int DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS = + R.dimen.taskbar_back_button_left_margin_kids; + @DimenRes + public static final int DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS = + R.dimen.taskbar_nav_buttons_width_kids; + @DimenRes + public static final int DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS = + R.dimen.taskbar_nav_buttons_height_kids; + @DimenRes + public static final int DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS = + R.dimen.taskbar_nav_buttons_corner_radius_kids; + + // -------------------------- + // Nav Layout Factory + @IdRes + public static final int ID_START_CONTEXTUAL_BUTTONS = R.id.start_contextual_buttons; + @IdRes + public static final int ID_END_CONTEXTUAL_BUTTONS = R.id.end_contextual_buttons; + @IdRes + public static final int ID_END_NAV_BUTTONS = R.id.end_nav_buttons; + + private LayoutResourceHelper() { + + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt new file mode 100644 index 0000000000..db0a2d8d44 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt @@ -0,0 +1,105 @@ +/* + * 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.navbutton + +import android.content.res.Resources +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_CONTEXTUAL_BUTTONS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_NAV_BUTTONS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_START_CONTEXTUAL_BUTTONS +import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.Companion +import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter + +/** + * Select the correct layout for nav buttons + * + * Since layouts are done dynamically for the nav buttons on Taskbar, this + * class returns a corresponding [NavButtonLayoutter] via + * [Companion.getUiLayoutter] + * that can help position the buttons based on the current [DeviceProfile] + */ +class NavButtonLayoutFactory { + companion object { + /** + * Get the correct instance of [NavButtonLayoutter] + * + * No layouts supported for configurations where: + * * taskbar isn't showing AND + * * the device is not in [phoneMode] + * OR + * * phone is showing + * * device is using gesture navigation + * + * @param navButtonsView ViewGroup that contains start, end, nav button ViewGroups + * @param isKidsMode no-op when taskbar is hidden/not showing + * @param isInSetup no-op when taskbar is hidden/not showing + * @param phoneMode refers to the device using the taskbar window on phones + * @param isThreeButtonNav are no-ops when taskbar is present/showing + */ + fun getUiLayoutter(deviceProfile: DeviceProfile, + navButtonsView: FrameLayout, + resources: Resources, + isKidsMode: Boolean, + isInSetup: Boolean, + isThreeButtonNav: Boolean, + phoneMode: Boolean): + NavButtonLayoutter { + val navButtonContainer = + navButtonsView.findViewById(ID_END_NAV_BUTTONS) + val endContextualContainer = + navButtonsView.findViewById(ID_END_CONTEXTUAL_BUTTONS) + val startContextualContainer = + navButtonsView.findViewById(ID_START_CONTEXTUAL_BUTTONS) + val isPhoneNavMode = phoneMode && isThreeButtonNav + return when { + isPhoneNavMode -> { + if (!deviceProfile.isLandscape) { + PhonePortraitNavLayoutter(resources, navButtonContainer, + endContextualContainer, startContextualContainer) + } else { + PhoneLandscapeNavLayoutter(resources, navButtonContainer, + endContextualContainer, startContextualContainer) + } + } + deviceProfile.isTaskbarPresent -> { + return when { + isInSetup -> { + SetupNavLayoutter(resources, navButtonContainer, endContextualContainer, + startContextualContainer) + } + isKidsMode -> { + KidsNavLayoutter(resources, navButtonContainer, endContextualContainer, + startContextualContainer) + } + else -> + TaskbarNavLayoutter(resources, navButtonContainer, endContextualContainer, + startContextualContainer) + } + } + else -> error("No layoutter found") + } + } + } + + /** Lays out and provides access to the home, recents, and back buttons for various mischief */ + interface NavButtonLayoutter { + fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) + } +} \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt new file mode 100644 index 0000000000..a89476e15a --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt @@ -0,0 +1,89 @@ +/* + * 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.navbutton + +import android.content.res.Resources +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import androidx.core.view.children +import com.android.launcher3.DeviceProfile +import com.android.launcher3.R +import com.android.launcher3.taskbar.TaskbarManager +import com.android.launcher3.util.DimensionUtils + +class PhoneLandscapeNavLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : AbstractNavButtonLayoutter( + resources, + navBarContainer, + endContextualContainer, + startContextualContainer +) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // TODO(b/230395757): Polish pending, this is just to make it usable + val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + val endStartMargins = + resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) + val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources, + TaskbarManager.isPhoneMode(dp)) + navButtonContainer.removeAllViews() + navButtonContainer.orientation = LinearLayout.VERTICAL + + navContainerParams.apply { + width = taskbarDimensions.x + height = ViewGroup.LayoutParams.MATCH_PARENT + gravity = Gravity.CENTER + topMargin = endStartMargins + bottomMargin = endStartMargins + marginEnd = 0 + marginStart = 0 + } + + // Swap recents and back button + navButtonContainer.addView(recentsButton) + navButtonContainer.addView(homeButton) + navButtonContainer.addView(backButton) + + navButtonContainer.layoutParams = navContainerParams + + // Add the spaces in between the nav buttons + val spaceInBetween: Int = + resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone) + navButtonContainer.children.forEachIndexed { i, navButton -> + val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams + buttonLayoutParams.weight = 1f + when (i) { + 0 -> { + buttonLayoutParams.bottomMargin = spaceInBetween / 2 + } + navButtonContainer.childCount - 1 -> { + buttonLayoutParams.topMargin = spaceInBetween / 2 + } + else -> { + buttonLayoutParams.bottomMargin = spaceInBetween / 2 + buttonLayoutParams.topMargin = spaceInBetween / 2 + } + } + } + } +} \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt new file mode 100644 index 0000000000..275f59faef --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt @@ -0,0 +1,83 @@ +/* + * 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.navbutton + +import android.content.res.Resources +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile +import com.android.launcher3.R +import com.android.launcher3.taskbar.TaskbarManager +import com.android.launcher3.util.DimensionUtils + +class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup) : + AbstractNavButtonLayoutter(resources, navBarContainer, endContextualContainer, + startContextualContainer) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // TODO(b/230395757): Polish pending, this is just to make it usable + val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources, + TaskbarManager.isPhoneMode(dp)) + val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) + navContainerParams.width = taskbarDimensions.x + navContainerParams.height = ViewGroup.LayoutParams.MATCH_PARENT + navContainerParams.gravity = Gravity.CENTER_VERTICAL + + // Ensure order of buttons is correct + navButtonContainer.removeAllViews() + navButtonContainer.orientation = LinearLayout.HORIZONTAL + navContainerParams.topMargin = 0 + navContainerParams.bottomMargin = 0 + navContainerParams.marginEnd = endStartMargins + navContainerParams.marginStart = endStartMargins + // Swap recents and back button in case we were landscape prior to this + navButtonContainer.addView(backButton) + navButtonContainer.addView(homeButton) + navButtonContainer.addView(recentsButton) + + navButtonContainer.layoutParams = navContainerParams + + // Add the spaces in between the nav buttons + val spaceInBetween = + resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone) + for (i in 0 until navButtonContainer.childCount) { + val navButton = navButtonContainer.getChildAt(i) + val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams + buttonLayoutParams.weight = 1f + when (i) { + 0 -> { + // First button + buttonLayoutParams.marginEnd = spaceInBetween / 2 + } + navButtonContainer.childCount - 1 -> { + // Last button + buttonLayoutParams.marginStart = spaceInBetween / 2 + } + else -> { + // other buttons + buttonLayoutParams.marginStart = spaceInBetween / 2 + buttonLayoutParams.marginEnd = spaceInBetween / 2 + } + } + } + } +} \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt new file mode 100644 index 0000000000..afe70d6c21 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt @@ -0,0 +1,49 @@ +/* + * 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.navbutton + +import android.content.res.Resources +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile + +class SetupNavLayoutter( + resources: Resources, + navButtonContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : AbstractNavButtonLayoutter( + resources, + navButtonContainer, + endContextualContainer, + startContextualContainer +) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // Since setup wizard only has back button enabled, it looks strange to be + // end-aligned, so start-align instead. + val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + navButtonsLayoutParams.apply { + marginStart = navButtonsLayoutParams.marginEnd + marginEnd = 0 + gravity = Gravity.START + } + navButtonContainer.requestLayout() + } +} \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt new file mode 100644 index 0000000000..b2ca2afa04 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt @@ -0,0 +1,83 @@ +/* + * 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.navbutton + +import android.content.res.Resources +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile +import com.android.launcher3.R + +/** + * Layoutter for showing 3 button navigation on large screen + */ +class TaskbarNavLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : AbstractNavButtonLayoutter( + resources, + navBarContainer, + endContextualContainer, + startContextualContainer +) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // Add spacing after the end of the last nav button + val navButtonParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + var navMarginEnd = resources.getDimension(dp.inv.inlineNavButtonsEndSpacing).toInt() + val contextualWidth = endContextualContainer.width + // If contextual buttons are showing, we check if the end margin is enough for the + // contextual button to be showing - if not, move the nav buttons over a smidge + if (isContextualButtonShowing && navMarginEnd < contextualWidth) { + // Additional spacing, eat up half of space between last icon and nav button + navMarginEnd += resources.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2 + } + + navButtonParams.apply { + gravity = Gravity.END + width = FrameLayout.LayoutParams.WRAP_CONTENT + height = ViewGroup.LayoutParams.MATCH_PARENT + marginEnd = navMarginEnd + } + navButtonContainer.orientation = LinearLayout.HORIZONTAL + navButtonContainer.layoutParams = navButtonParams + + // Add the spaces in between the nav buttons + val spaceInBetween = resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween) + for (i in 0 until navButtonContainer.childCount) { + val navButton = navButtonContainer.getChildAt(i) + val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams + buttonLayoutParams.weight = 0f + when (i) { + 0 -> { + buttonLayoutParams.marginEnd = spaceInBetween / 2 + } + navButtonContainer.childCount - 1 -> { + buttonLayoutParams.marginStart = spaceInBetween / 2 + } + else -> { + buttonLayoutParams.marginStart = spaceInBetween / 2 + buttonLayoutParams.marginEnd = spaceInBetween / 2 + } + } + } + } +} \ No newline at end of file diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt new file mode 100644 index 0000000000..58f0949213 --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt @@ -0,0 +1,148 @@ +package com.android.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.LinearLayout +import androidx.test.runner.AndroidJUnit4 +import com.android.launcher3.DeviceProfile +import com.android.launcher3.taskbar.TaskbarManager +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import com.android.launcher3.R +import org.junit.Assume.assumeTrue +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations +import java.lang.IllegalStateException + +@RunWith(AndroidJUnit4::class) +class NavButtonLayoutFactoryTest { + + @Mock + lateinit var mockDeviceProfile: DeviceProfile + @Mock + lateinit var mockParentButtonContainer: FrameLayout + @Mock + lateinit var mockNavLayout: LinearLayout + @Mock + lateinit var mockStartContextualLayout: ViewGroup + @Mock + lateinit var mockEndContextualLayout: ViewGroup + @Mock + lateinit var mockResources: Resources + @Mock + lateinit var mockBackButton: ImageView + @Mock + lateinit var mockRecentsButton: ImageView + @Mock + lateinit var mockHomeButton: ImageView + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + // Init end nav buttons + whenever(mockNavLayout.childCount).thenReturn(3) + whenever(mockNavLayout.findViewById(R.id.back)).thenReturn(mockBackButton) + whenever(mockNavLayout.findViewById(R.id.home)).thenReturn(mockHomeButton) + whenever(mockNavLayout.findViewById(R.id.recent_apps)).thenReturn(mockRecentsButton) + + // Init top level layout + whenever(mockParentButtonContainer.findViewById(R.id.end_nav_buttons)) + .thenReturn(mockNavLayout) + whenever(mockParentButtonContainer.findViewById(R.id.end_contextual_buttons)) + .thenReturn(mockEndContextualLayout) + whenever(mockParentButtonContainer.findViewById(R.id.start_contextual_buttons)) + .thenReturn(mockStartContextualLayout) + } + + @Test + fun getKidsLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = true + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = true, isInSetup = false, isThreeButtonNav = false, + phoneMode = false) + assert(layoutter is KidsNavLayoutter) + } + + @Test + fun getSetupLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = true + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = false, isInSetup = true, isThreeButtonNav = false, + phoneMode = false) + assert(layoutter is SetupNavLayoutter) + } + + @Test + fun getTaskbarNavLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = true + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false, + phoneMode = false) + assert(layoutter is TaskbarNavLayoutter) + } + + @Test(expected = IllegalStateException::class) + fun noValidLayoutForLargeScreenTaskbarNotPresent() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = false + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false, + phoneMode = false) + } + + @Test + fun getTaskbarPortraitLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = false + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true, + phoneMode = true) + assert(layoutter is PhonePortraitNavLayoutter) + } + + @Test + fun getTaskbarLandscapeLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = false + setDeviceProfileLandscape() + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true, + phoneMode = true) + assert(layoutter is PhoneLandscapeNavLayoutter) + } + + @Test(expected = IllegalStateException::class) + fun noValidLayoutForPhoneGestureNav() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = false + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false, + phoneMode = true) + } + + private fun setDeviceProfileLandscape() { + // Use reflection to modify landscape field + val landscapeField = mockDeviceProfile.javaClass.getDeclaredField("isLandscape") + landscapeField.isAccessible = true + landscapeField.set(mockDeviceProfile, true) + } + + private fun getLayoutter(isKidsMode: Boolean, isInSetup: Boolean, + isThreeButtonNav: Boolean, phoneMode: Boolean): + NavButtonLayoutFactory.NavButtonLayoutter { + return NavButtonLayoutFactory.getUiLayoutter( + deviceProfile = mockDeviceProfile, + navButtonsView = mockParentButtonContainer, + resources = mockResources, + isKidsMode = isKidsMode, isInSetup = isInSetup, + isThreeButtonNav = isThreeButtonNav, phoneMode = phoneMode + ) + } +} \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 47584e2baf..a9ba07d2b1 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -368,6 +368,7 @@ 0dp 0dp 0dp + 0dp 0dp 0dp 0dp diff --git a/src/com/android/launcher3/util/DimensionUtils.kt b/src/com/android/launcher3/util/DimensionUtils.kt new file mode 100644 index 0000000000..758b3a962f --- /dev/null +++ b/src/com/android/launcher3/util/DimensionUtils.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.util + +import android.content.res.Resources +import android.graphics.Point +import android.view.ViewGroup +import com.android.launcher3.DeviceProfile +import com.android.launcher3.R + +object DimensionUtils { + /** + * Point where x is width, and y is height of taskbar based on provided [deviceProfile] + * x or y could also be -1 to indicate there is no dimension specified + */ + @JvmStatic + fun getTaskbarPhoneDimensions(deviceProfile: DeviceProfile, res: Resources, + isPhoneMode: Boolean): Point { + val p = Point() + // Taskbar for large screen + if (!isPhoneMode) { + p.x = ViewGroup.LayoutParams.MATCH_PARENT + p.y = deviceProfile.taskbarSize + return p + } + + // Taskbar on phone using gesture nav, it will always be stashed + if (deviceProfile.isGestureMode) { + p.x = ViewGroup.LayoutParams.MATCH_PARENT + p.y = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size) + return p + } + + // Taskbar on phone, portrait + if (!deviceProfile.isLandscape) { + p.x = ViewGroup.LayoutParams.MATCH_PARENT + p.y = res.getDimensionPixelSize(R.dimen.taskbar_size) + return p + } + + // Taskbar on phone, landscape + p.x = res.getDimensionPixelSize(R.dimen.taskbar_size) + p.y = ViewGroup.LayoutParams.MATCH_PARENT + return p + } +} \ No newline at end of file