Merge "Landscape 3 button nav on taskbar phone supported" into tm-qpr-dev

This commit is contained in:
Vinit Nayak
2022-10-17 22:30:05 +00:00
committed by Android (Google) Code Review
17 changed files with 1000 additions and 134 deletions
@@ -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();
}
@@ -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(
@@ -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();
}
@@ -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;
}
@@ -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.
@@ -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);
}
/**
@@ -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)
}
@@ -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
}
}
@@ -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() {
}
}
@@ -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<LinearLayout>(ID_END_NAV_BUTTONS)
val endContextualContainer =
navButtonsView.findViewById<ViewGroup>(ID_END_CONTEXTUAL_BUTTONS)
val startContextualContainer =
navButtonsView.findViewById<ViewGroup>(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)
}
}
@@ -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
}
}
}
}
}
@@ -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
}
}
}
}
}
@@ -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()
}
}
@@ -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
}
}
}
}
}
@@ -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<View>(R.id.back)).thenReturn(mockBackButton)
whenever(mockNavLayout.findViewById<View>(R.id.home)).thenReturn(mockHomeButton)
whenever(mockNavLayout.findViewById<View>(R.id.recent_apps)).thenReturn(mockRecentsButton)
// Init top level layout
whenever(mockParentButtonContainer.findViewById<LinearLayout>(R.id.end_nav_buttons))
.thenReturn(mockNavLayout)
whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.end_contextual_buttons))
.thenReturn(mockEndContextualLayout)
whenever(mockParentButtonContainer.findViewById<ViewGroup>(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
)
}
}
+1
View File
@@ -368,6 +368,7 @@
<dimen name="taskbar_hotseat_nav_spacing">0dp</dimen>
<dimen name="taskbar_button_margin_default">0dp</dimen>
<dimen name="taskbar_button_space_inbetween">0dp</dimen>
<dimen name="taskbar_button_space_inbetween_phone">0dp</dimen>
<dimen name="taskbar_button_margin_5_5">0dp</dimen>
<dimen name="taskbar_button_margin_6_5">0dp</dimen>
<dimen name="taskbar_button_margin_4_5">0dp</dimen>
@@ -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
}
}