Snap for 7223915 from 5951856992 to sc-release

Change-Id: I42a53060b51caba8e23b76bc1ffee2b14fc0330c
This commit is contained in:
android-build-team Robot
2021-03-20 23:07:48 +00:00
20 changed files with 531 additions and 107 deletions
+1 -1
View File
@@ -20,5 +20,5 @@
<string name="niu_actions_package" translatable="false"/>
<!-- Feature Flags -->
<bool name="enable_niu_actions">false</bool>
<bool name="enable_niu_actions">true</bool>
</resources>
+9
View File
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Launcher3_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Launcher3_license"],
}
filegroup {
name: "launcher3-quickstep-robolectric-src",
path: "robolectric_tests",
+6 -5
View File
@@ -18,10 +18,11 @@
android:layout_height="wrap_content">
<!-- The image of the widget. This view does not support padding. Any placement adjustment
should be done using margins. -->
should be done using margins.
width & height are set at runtime after scaling the preview image. -->
<com.android.launcher3.widget.WidgetImageView
android:id="@+id/widget_preview"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:importantForAccessibility="no"
@@ -38,7 +39,7 @@
android:singleLine="true"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp" />
android:textSize="@dimen/widget_cell_font_size" />
<!-- The original dimensions of the widget (can't be the same text as above due to different
style. -->
@@ -48,7 +49,7 @@
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textColor="?android:attr/textColorTertiary"
android:textSize="14sp"
android:textSize="@dimen/widget_cell_font_size"
android:alpha="0.8" />
<TextView
@@ -56,7 +57,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textSize="14sp"
android:textSize="@dimen/widget_cell_font_size"
android:textColor="?android:attr/textColorTertiary"
android:maxLines="2"
android:ellipsize="end"
@@ -18,12 +18,15 @@
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:paddingHorizontal="16dp"
android:layout_marginBottom="16dp"
android:orientation="vertical">
<View
android:id="@+id/collapse_handle"
android:layout_width="48dp"
android:layout_height="2dp"
android:layout_marginTop="16dp"
android:elevation="2dp"
android:layout_gravity="center_horizontal"
android:background="?android:attr/textColorSecondary"/>
<TextView
@@ -36,4 +39,11 @@
android:textColor="?android:attr/textColorSecondary"
android:text="@string/widget_button_text"/>
<include layout="@layout/widgets_search_bar"/>
<com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
android:id="@+id/recommended_widget_table"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone" />
</LinearLayout>
+2 -1
View File
@@ -6,7 +6,8 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="16dp"
android:background="@drawable/bg_widgets_searchbox">
android:background="@drawable/bg_widgets_searchbox"
android:elevation="2dp">
<com.android.launcher3.ExtendedEditText
android:id="@+id/widgets_search_bar_edit_text"
+1 -1
View File
@@ -109,6 +109,7 @@
<!-- Widget tray -->
<dimen name="widget_cell_vertical_padding">8dp</dimen>
<dimen name="widget_cell_horizontal_padding">16dp</dimen>
<dimen name="widget_cell_font_size">14sp</dimen>
<dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
@@ -186,7 +187,6 @@
<dimen name="deep_shortcuts_elevation">0dp</dimen>
<dimen name="bg_popup_item_width">234dp</dimen>
<dimen name="bg_popup_item_height">56dp</dimen>
<dimen name="bg_popup_item_condensed_height">48dp</dimen>
<dimen name="pre_drag_view_scale">6dp</dimen>
<!-- an icon with shortcuts must be dragged this far before the container is removed. -->
<dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
+9 -1
View File
@@ -16,6 +16,15 @@
// Launcher Robolectric test target.
//
// "robolectric_android-all-stub", not needed, we write our own stubs
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Launcher3_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Launcher3_license"],
}
filegroup {
name: "launcher3-robolectric-resources",
path: "resources",
@@ -47,4 +56,3 @@ android_robolectric_test {
timeout: 36000,
},
}
@@ -97,7 +97,7 @@ public class ClippedFolderIconLayoutRule {
double thetaShift = 0;
if (curNumItems == 3) {
thetaShift = Math.PI / 6;
thetaShift = Math.PI / 2;
} else if (curNumItems == 4) {
thetaShift = Math.PI / 4;
}
@@ -26,6 +26,7 @@ import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTI
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -695,6 +696,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
mPageIndicator.stopAllAnimations();
startAnimation(anim);
// Because t=0 has the folder match the folder icon, we can skip the
// first frame and have the same movement one frame earlier.
anim.setCurrentPlayTime(Math.min(getSingleFrameMs(getContext()), anim.getTotalDuration()));
// Make sure the folder picks up the last drag move even if the finger doesn't move.
if (mDragController.isDragging()) {
@@ -92,6 +92,10 @@ public class NotificationItemView {
});
}
public void updateBackgroundColor(int color) {
mMainView.updateBackgroundColor(color);
}
public void addGutter() {
if (mGutter == null) {
mGutter = mPopupContainer.inflateAndAdd(R.layout.notification_gutter, mRootView);
@@ -97,15 +97,24 @@ public class NotificationMainView extends FrameLayout implements SingleAxisSwipe
super.onFinishInflate();
mTextAndBackground = findViewById(R.id.text_and_background);
ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
mBackgroundColor = colorBackground.getColor();
RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
colorBackground, null);
mTextAndBackground.setBackground(rippleBackground);
mTitleView = mTextAndBackground.findViewById(R.id.title);
mTextView = mTextAndBackground.findViewById(R.id.text);
mIconView = findViewById(R.id.popup_item_icon);
ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
updateBackgroundColor(colorBackground.getColor());
}
public void updateBackgroundColor(int color) {
mBackgroundColor = color;
RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
new ColorDrawable(color), null);
mTextAndBackground.setBackground(rippleBackground);
if (mNotificationInfo != null) {
mIconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
mBackgroundColor));
}
}
public void setSwipeDetector(SingleAxisSwipeDetector swipeDetector) {
@@ -17,10 +17,12 @@
package com.android.launcher3.popup;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
@@ -28,6 +30,8 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.util.Pair;
@@ -63,6 +67,9 @@ import java.util.Collections;
*/
public abstract class ArrowPopup<T extends BaseDraggingActivity> extends AbstractFloatingView {
// +1 for system shortcut view
private static final int MAX_NUM_CHILDREN = MAX_SHORTCUTS + 1;
private final Rect mTempRect = new Rect();
protected final LayoutInflater mInflater;
@@ -93,6 +100,9 @@ public abstract class ArrowPopup<T extends BaseDraggingActivity> extends Abstrac
private Runnable mOnCloseCallback = () -> { };
private int mArrowColor;
private final int[] mColors;
public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mInflater = LayoutInflater.from(context);
@@ -121,15 +131,22 @@ public abstract class ArrowPopup<T extends BaseDraggingActivity> extends Abstrac
mArrowPointRadius = resources.getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
mRoundedTop = new GradientDrawable();
mRoundedTop.setColor(Themes.getAttrColor(context, R.attr.popupColorPrimary));
mRoundedTop.setCornerRadii(new float[] { mOutlineRadius, mOutlineRadius, mOutlineRadius,
mOutlineRadius, 0, 0, 0, 0});
mRoundedBottom = new GradientDrawable();
mRoundedBottom.setColor(Themes.getAttrColor(context, R.attr.popupColorPrimary));
mRoundedBottom.setCornerRadii(new float[] { 0, 0, 0, 0, mOutlineRadius, mOutlineRadius,
mOutlineRadius, mOutlineRadius});
int primaryColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
int secondaryColor = Themes.getAttrColor(context, R.attr.popupColorSecondary);
ArgbEvaluator argb = new ArgbEvaluator();
mColors = new int[MAX_NUM_CHILDREN];
// Interpolate between the two colors, exclusive.
float step = 1f / (MAX_NUM_CHILDREN + 1);
for (int i = 0; i < mColors.length; ++i) {
mColors[i] = (int) argb.evaluate((i + 1) * step, primaryColor, secondaryColor);
}
}
public ArrowPopup(Context context, AttributeSet attrs) {
@@ -187,6 +204,7 @@ public abstract class ArrowPopup<T extends BaseDraggingActivity> extends Abstrac
int numVisibleShortcut = 0;
View lastView = null;
int numVisibleChild = 0;
for (int i = 0; i < count; i++) {
View view = getChildAt(i);
boolean isShortcut = view instanceof DeepShortcutView;
@@ -207,15 +225,41 @@ public abstract class ArrowPopup<T extends BaseDraggingActivity> extends Abstrac
view.setBackground(mRoundedTop);
} else if (numVisibleShortcut == (totalVisibleShortcuts - 1)) {
view.setBackground(mRoundedBottom);
} else {
view.setBackgroundResource(R.drawable.middle_item_primary);
}
numVisibleShortcut++;
}
}
int color = mColors[numVisibleChild % mColors.length];
setChildColor(view, color);
// Arrow color matches the first child or the last child.
if (!mIsAboveIcon && numVisibleChild == 0) {
mArrowColor = color;
} else if (mIsAboveIcon) {
mArrowColor = color;
}
numVisibleChild++;
}
}
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
}
/**
* Sets the background color of the child.
*/
protected void setChildColor(View view, int color) {
Drawable bg = view.getBackground();
if (bg instanceof GradientDrawable) {
((GradientDrawable) bg.mutate()).setColor(color);
} else if (bg instanceof ColorDrawable) {
((ColorDrawable) bg.mutate()).setColor(color);
}
}
/**
* Shows the popup at the desired location, optionally reversing the children.
* @param viewsToFlip number of views from the top to to flip in case of reverse order
@@ -293,7 +337,7 @@ public abstract class ArrowPopup<T extends BaseDraggingActivity> extends Abstrac
mOutlineRadius, getMeasuredWidth(), getMeasuredHeight(),
mArrowOffsetHorizontal, -mArrowOffsetVertical,
!mIsAboveIcon, mIsLeftAligned,
Themes.getAttrColor(getContext(), R.attr.popupColorPrimary)));
mArrowColor));
mArrow.setElevation(getElevation());
}
@@ -60,6 +60,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationItemView;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationMainView;
import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
@@ -170,6 +171,14 @@ public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends Arr
return false;
}
@Override
protected void setChildColor(View v, int color) {
super.setChildColor(v, color);
if (v.getId() == R.id.notification_container && mNotificationItemView != null) {
mNotificationItemView.updateBackgroundColor(color);
}
}
/**
* Returns true if we can show the container.
*/
@@ -333,19 +342,11 @@ public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends Arr
private void updateHiddenShortcuts() {
int allowedCount = mNotificationItemView != null
? MAX_SHORTCUTS_IF_NOTIFICATIONS : MAX_SHORTCUTS;
int originalHeight = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height);
int itemHeight = mNotificationItemView != null ?
getResources().getDimensionPixelSize(R.dimen.bg_popup_item_condensed_height)
: originalHeight;
float iconScale = ((float) itemHeight) / originalHeight;
int total = mShortcuts.size();
for (int i = 0; i < total; i++) {
DeepShortcutView view = mShortcuts.get(i);
view.setVisibility(i >= allowedCount ? GONE : VISIBLE);
view.getLayoutParams().height = itemHeight;
view.getIconView().setScaleX(iconScale);
view.getIconView().setScaleY(iconScale);
}
}
@@ -567,6 +568,7 @@ public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends Arr
// No more notifications, remove the notification views and expand all shortcuts.
mNotificationItemView.removeAllViews();
mNotificationItemView = null;
mNotificationContainer.setVisibility(GONE);
updateHiddenShortcuts();
assignMarginsAndBackgrounds();
} else {
@@ -74,6 +74,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
protected int mPreviewHeight;
protected int mPresetPreviewSize;
private int mCellSize;
private float mPreviewScale = 1f;
private WidgetImageView mWidgetImage;
private TextView mWidgetName;
@@ -254,8 +255,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
}
if (drawable != null) {
LayoutParams layoutParams = (LayoutParams) mWidgetImage.getLayoutParams();
layoutParams.width = drawable.getIntrinsicWidth();
layoutParams.height = drawable.getIntrinsicHeight();
layoutParams.width = (int) (drawable.getIntrinsicWidth() * mPreviewScale);
layoutParams.height = (int) (drawable.getIntrinsicHeight() * mPreviewScale);
mWidgetImage.setLayoutParams(layoutParams);
mWidgetImage.setDrawable(drawable, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
@@ -305,10 +306,16 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
/** Sets the widget preview image size in number of cells. */
public void setPreviewSize(int spanX, int spanY) {
setPreviewSize(spanX, spanY, 1f);
}
/** Sets the widget preview image size, in number of cells, and preview scale. */
public void setPreviewSize(int spanX, int spanY, float previewScale) {
int padding = 2 * getResources()
.getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
mPreviewWidth = mDeviceProfile.cellWidthPx * spanX + padding;
mPreviewHeight = mDeviceProfile.cellHeightPx * spanY + padding;
mPreviewScale = previewScale;
}
@Override
@@ -35,6 +35,7 @@ final class SearchAndRecommendationsScrollController implements
private final SearchAndRecommendationViewHolder mViewHolder;
private final WidgetsRecyclerView mPrimaryRecyclerView;
private final WidgetsRecyclerView mSearchRecyclerView;
private final int mTabsHeight;
// The following are only non null if mHasWorkProfile is true.
@Nullable private final WidgetsRecyclerView mWorkRecyclerView;
@@ -42,10 +43,28 @@ final class SearchAndRecommendationsScrollController implements
@Nullable private final PersonalWorkPagedView mPrimaryWorkViewPager;
private WidgetsRecyclerView mCurrentRecyclerView;
private int mMaxCollapsibleHeight = 0;
/**
* The vertical distance, in pixels, until the search is pinned at the top of the screen when
* the user scrolls down the recycler view.
*/
private int mCollapsibleHeightForSearch = 0;
/**
* The vertical distance, in pixels, until the recommendation table disappears from the top of
* the screen when the user scrolls down the recycler view.
*/
private int mCollapsibleHeightForRecommendation = 0;
/**
* The vertical distance, in pixels, until the tabs is pinned at the top of the screen when the
* user scrolls down the recycler view.
*
* <p>Always 0 if there is no work profile.
*/
private int mCollapsibleHeightForTabs = 0;
SearchAndRecommendationsScrollController(
boolean hasWorkProfile,
int tabsHeight,
SearchAndRecommendationViewHolder viewHolder,
WidgetsRecyclerView primaryRecyclerView,
@Nullable WidgetsRecyclerView workRecyclerView,
@@ -55,46 +74,63 @@ final class SearchAndRecommendationsScrollController implements
mHasWorkProfile = hasWorkProfile;
mViewHolder = viewHolder;
mPrimaryRecyclerView = primaryRecyclerView;
mCurrentRecyclerView = mPrimaryRecyclerView;
mWorkRecyclerView = workRecyclerView;
mSearchRecyclerView = searchRecyclerView;
mPrimaryWorkTabsView = personalWorkTabsView;
mPrimaryWorkViewPager = primaryWorkViewPager;
mCurrentRecyclerView = mPrimaryRecyclerView;
mTabsHeight = tabsHeight;
}
/** Sets the current active {@link WidgetsRecyclerView}. */
public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
mCurrentRecyclerView = currentRecyclerView;
mCurrentRecyclerView = currentRecyclerView;
mViewHolder.mHeaderTitle.setTranslationY(0);
mViewHolder.mRecommendedWidgetsTable.setTranslationY(0);
mViewHolder.mSearchBar.setTranslationY(0);
if (mHasWorkProfile) {
mPrimaryWorkTabsView.setTranslationY(0);
}
}
/**
* Updates the margin and padding of {@link WidgetsFullSheet} to accumulate collapsible views.
*
* @return {@code true} if margins or/and padding of views in the search and recommendations
* container have been updated.
*/
public void updateMarginAndPadding() {
// The maximum vertical distance, in pixels, until the last collapsible element is not
// visible from the screen when the user scrolls down the recycler view.
mMaxCollapsibleHeight = mViewHolder.mContainer.getPaddingTop()
+ measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
+ measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
public boolean updateMarginAndPadding() {
boolean hasMarginOrPaddingUpdated = false;
mCollapsibleHeightForSearch = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
mCollapsibleHeightForRecommendation =
measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
+ measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
+ measureHeightWithVerticalMargins((View) mViewHolder.mSearchBar)
+ measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
int topContainerHeight = measureHeightWithVerticalMargins(mViewHolder.mContainer);
if (mHasWorkProfile) {
mCollapsibleHeightForTabs = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
+ measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
// In a work profile setup, the full widget sheet contains the following views:
// ------- -|
// Widgets -|---> LinearLayout for search & recommendations
// Search bar -|
// Personal | Work
// ------- (pinned) -|
// Widgets (collapsible) -|---> LinearLayout for search & recommendations
// Search bar (pinned) -|
// Widgets recommendation (collapsible)-|
// Personal | Work (pinned)
// View Pager
//
// Views after the search & recommendations are not bound by RelativelyLayout param.
// To position them on the expected location, padding & margin are added to these views
// Tabs should have a padding of the height of the search & recommendations container.
mPrimaryWorkTabsView.setPadding(
mPrimaryWorkTabsView.getPaddingLeft(),
topContainerHeight,
mPrimaryWorkTabsView.getPaddingRight(),
mPrimaryWorkTabsView.getPaddingBottom());
RelativeLayout.LayoutParams tabsLayoutParams =
(RelativeLayout.LayoutParams) mPrimaryWorkTabsView.getLayoutParams();
tabsLayoutParams.topMargin = topContainerHeight;
mPrimaryWorkTabsView.setLayoutParams(tabsLayoutParams);
// Instead of setting the top offset directly, we split the top offset into two values:
// 1. topOffsetAfterAllViewsCollapsed: this is the top offset after all collapsible
@@ -124,39 +160,52 @@ final class SearchAndRecommendationsScrollController implements
//
// When the views are first inflated, the sum of topOffsetAfterAllViewsCollapsed and
// mMaxCollapsibleDistance should equal to the top container height.
int tabsViewActualHeight = measureHeightWithVerticalMargins(mPrimaryWorkTabsView)
- mPrimaryWorkTabsView.getPaddingTop();
int topOffsetAfterAllViewsCollapsed =
topContainerHeight + tabsViewActualHeight - mMaxCollapsibleHeight;
topContainerHeight + mTabsHeight - mCollapsibleHeightForTabs;
RelativeLayout.LayoutParams layoutParams =
RelativeLayout.LayoutParams viewPagerLayoutParams =
(RelativeLayout.LayoutParams) mPrimaryWorkViewPager.getLayoutParams();
layoutParams.setMargins(0, topOffsetAfterAllViewsCollapsed, 0, 0);
mPrimaryWorkViewPager.setLayoutParams(layoutParams);
mPrimaryWorkViewPager.requestLayout();
if (viewPagerLayoutParams.topMargin != topOffsetAfterAllViewsCollapsed) {
viewPagerLayoutParams.topMargin = topOffsetAfterAllViewsCollapsed;
mPrimaryWorkViewPager.setLayoutParams(viewPagerLayoutParams);
hasMarginOrPaddingUpdated = true;
}
mPrimaryRecyclerView.setPadding(
mPrimaryRecyclerView.getPaddingLeft(),
mMaxCollapsibleHeight,
mPrimaryRecyclerView.getPaddingRight(),
mPrimaryRecyclerView.getPaddingBottom());
mWorkRecyclerView.setPadding(
mWorkRecyclerView.getPaddingLeft(),
mMaxCollapsibleHeight,
mWorkRecyclerView.getPaddingRight(),
mWorkRecyclerView.getPaddingBottom());
if (mPrimaryRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
mPrimaryRecyclerView.setPadding(
mPrimaryRecyclerView.getPaddingLeft(),
mCollapsibleHeightForTabs,
mPrimaryRecyclerView.getPaddingRight(),
mPrimaryRecyclerView.getPaddingBottom());
hasMarginOrPaddingUpdated = true;
}
if (mWorkRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
mWorkRecyclerView.setPadding(
mWorkRecyclerView.getPaddingLeft(),
mCollapsibleHeightForTabs,
mWorkRecyclerView.getPaddingRight(),
mWorkRecyclerView.getPaddingBottom());
hasMarginOrPaddingUpdated = true;
}
} else {
mPrimaryRecyclerView.setPadding(
mPrimaryRecyclerView.getPaddingLeft(),
topContainerHeight,
mPrimaryRecyclerView.getPaddingRight(),
mPrimaryRecyclerView.getPaddingBottom());
if (mPrimaryRecyclerView.getPaddingTop() != topContainerHeight) {
mPrimaryRecyclerView.setPadding(
mPrimaryRecyclerView.getPaddingLeft(),
topContainerHeight,
mPrimaryRecyclerView.getPaddingRight(),
mPrimaryRecyclerView.getPaddingBottom());
hasMarginOrPaddingUpdated = true;
}
}
mSearchRecyclerView.setPadding(
mSearchRecyclerView.getPaddingLeft(),
topContainerHeight,
mSearchRecyclerView.getPaddingRight(),
mSearchRecyclerView.getPaddingBottom());
if (mSearchRecyclerView.getPaddingTop() != topContainerHeight) {
mSearchRecyclerView.setPadding(
mSearchRecyclerView.getPaddingLeft(),
topContainerHeight,
mSearchRecyclerView.getPaddingRight(),
mSearchRecyclerView.getPaddingBottom());
hasMarginOrPaddingUpdated = true;
}
return hasMarginOrPaddingUpdated;
}
/**
@@ -168,13 +217,22 @@ final class SearchAndRecommendationsScrollController implements
// Always use the recycler view offset because fast scroller offset has a different scale.
int recyclerViewYOffset = mCurrentRecyclerView.getCurrentScrollY();
if (recyclerViewYOffset < 0) return;
if (mMaxCollapsibleHeight > 0) {
int yDisplacement = Math.max(-recyclerViewYOffset, -mMaxCollapsibleHeight);
if (mCollapsibleHeightForRecommendation > 0) {
int yDisplacement = Math.max(-recyclerViewYOffset,
-mCollapsibleHeightForRecommendation);
mViewHolder.mHeaderTitle.setTranslationY(yDisplacement);
mViewHolder.mSearchBar.setTranslationY(yDisplacement);
if (mHasWorkProfile) {
mPrimaryWorkTabsView.setTranslationY(yDisplacement);
}
mViewHolder.mRecommendedWidgetsTable.setTranslationY(yDisplacement);
}
if (mCollapsibleHeightForSearch > 0) {
int searchYDisplacement = Math.max(-recyclerViewYOffset, -mCollapsibleHeightForSearch);
mViewHolder.mSearchBar.setTranslationY(searchYDisplacement);
}
if (mHasWorkProfile && mCollapsibleHeightForTabs > 0) {
int yDisplacementForTabs = Math.max(-recyclerViewYOffset, -mCollapsibleHeightForTabs);
mPrimaryWorkTabsView.setTranslationY(yDisplacementForTabs);
}
}
@@ -189,6 +247,9 @@ final class SearchAndRecommendationsScrollController implements
/** private the height, in pixel, + the vertical margins of a given view. */
private static int measureHeightWithVerticalMargins(View view) {
if (view.getVisibility() != View.VISIBLE) {
return 0;
}
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
+ marginLayoutParams.topMargin;
@@ -33,6 +33,7 @@ import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.TextView;
@@ -48,6 +49,7 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.TopRoundedCornerView;
import com.android.launcher3.widget.BaseWidgetSheet;
@@ -55,6 +57,7 @@ import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListene
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.search.SearchModeListener;
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
import com.android.launcher3.widget.util.WidgetsTableUtils;
import com.android.launcher3.workprofile.PersonalWorkPagedView;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
@@ -68,10 +71,15 @@ import java.util.function.Predicate;
public class WidgetsFullSheet extends BaseWidgetSheet
implements Insettable, ProviderChangedListener, OnActivePageChangedListener,
WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
private static final String TAG = WidgetsFullSheet.class.getSimpleName();
private static final long DEFAULT_OPEN_DURATION = 267;
private static final long FADE_IN_DURATION = 150;
private static final float VERTICAL_START_POSITION = 0.3f;
// The widget recommendation table can easily take over the entire screen on devices with small
// resolution or landscape on phone. This ratio defines the max percentage of content area that
// the table can display.
private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f;
private final Rect mInsets = new Rect();
private final boolean mHasWorkProfile;
@@ -81,10 +89,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mCurrentUser.equals(entry.mPkgItem.user);
private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
mPrimaryWidgetsFilter.negate();
private final int mTabsHeight;
private final int mWidgetCellHorizontalPadding;
@Nullable private PersonalWorkPagedView mViewPager;
private int mInitialTabsHeight = 0;
private boolean mIsInSearchMode;
private int mMaxSpansPerRow = 4;
private View mTabsView;
private TextView mNoWidgetsView;
private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder;
@@ -96,6 +106,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
mTabsHeight = mHasWorkProfile
? getContext().getResources()
.getDimensionPixelSize(R.dimen.all_apps_header_tab_height)
: 0;
mWidgetCellHorizontalPadding = 2 * getResources().getDimensionPixelOffset(
R.dimen.widget_cell_horizontal_padding);
}
public WidgetsFullSheet(Context context, AttributeSet attrs) {
@@ -140,6 +156,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
findViewById(R.id.search_and_recommendations_container));
mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
mHasWorkProfile,
mTabsHeight,
mSearchAndRecommendationViewHolder,
findViewById(R.id.primary_widgets_list_view),
mHasWorkProfile ? findViewById(R.id.work_widgets_list_view) : null,
@@ -150,6 +167,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mNoWidgetsView = findViewById(R.id.no_widgets_text);
onRecommendedWidgetsBound();
onWidgetsBound();
mSearchAndRecommendationViewHolder.mSearchBar.initialize(
@@ -257,6 +275,22 @@ public class WidgetsFullSheet extends BaseWidgetSheet
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
doMeasure(widthMeasureSpec, heightMeasureSpec);
if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
doMeasure(widthMeasureSpec, heightMeasureSpec);
}
if (updateMaxSpansPerRow()) {
doMeasure(widthMeasureSpec, heightMeasureSpec);
if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
doMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
private void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
int widthUsed;
if (mInsets.bottom > 0) {
@@ -272,24 +306,29 @@ public class WidgetsFullSheet extends BaseWidgetSheet
widthUsed, heightMeasureSpec, heightUsed);
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}
int paddingPx = 2 * getResources().getDimensionPixelOffset(
R.dimen.widget_cell_horizontal_padding);
int maxSpansPerRow = getMeasuredWidth() / (deviceProfile.cellWidthPx + paddingPx);
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
maxSpansPerRow);
mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
maxSpansPerRow);
if (mHasWorkProfile) {
mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
maxSpansPerRow);
/** Returns {@code true} if the max spans have been updated. */
private boolean updateMaxSpansPerRow() {
if (getMeasuredWidth() == 0) return false;
int previousMaxSpansPerRow = mMaxSpansPerRow;
mMaxSpansPerRow = getMeasuredWidth()
/ (mLauncher.getDeviceProfile().cellWidthPx + mWidgetCellHorizontalPadding);
if (previousMaxSpansPerRow != mMaxSpansPerRow) {
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
mMaxSpansPerRow);
mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
mMaxSpansPerRow);
if (mHasWorkProfile) {
mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
mMaxSpansPerRow);
}
onRecommendedWidgetsBound();
return true;
}
if (mInitialTabsHeight == 0 && mTabsView != null) {
mInitialTabsHeight = measureHeightWithVerticalMargins(mTabsView);
}
mSearchAndRecommendationsScrollController.updateMarginAndPadding();
return false;
}
@Override
@@ -346,6 +385,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mViewPager.snapToPage(AdapterHolder.PRIMARY);
}
attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView);
mSearchAndRecommendationsScrollController.updateMarginAndPadding();
}
@Override
@@ -357,6 +398,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
mIsInSearchMode = isInSearchMode;
mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable
.setVisibility(isInSearchMode ? GONE : VISIBLE);
if (mHasWorkProfile) {
mViewPager.setVisibility(isInSearchMode ? GONE : VISIBLE);
mTabsView.setVisibility(isInSearchMode ? GONE : VISIBLE);
@@ -374,6 +417,25 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.resetExpandedHeader();
}
@Override
public void onRecommendedWidgetsBound() {
List<WidgetItem> recommendedWidgets =
mLauncher.getPopupDataProvider().getRecommendedWidgets();
WidgetsRecommendationTableLayout table =
mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable;
if (recommendedWidgets.size() > 0) {
float maxTableHeight =
(mLauncher.getDeviceProfile().heightPx - mTabsHeight - getHeaderViewHeight())
* RECOMMENDATION_TABLE_HEIGHT_RATIO;
List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
WidgetsTableUtils.groupWidgetItemsIntoTable(recommendedWidgets,
mMaxSpansPerRow);
table.setRecommendedWidgets(recommendedWidgetsInTable, maxTableHeight);
} else {
table.setVisibility(GONE);
}
}
private void open(boolean animate) {
if (animate) {
if (getPopupContainer().getInsets().bottom > 0) {
@@ -534,20 +596,29 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mWidgetsRecyclerView.setEdgeEffectFactory(
((TopRoundedCornerView) mContent).createEdgeEffectFactory());
mWidgetsListAdapter.setApplyBitmapDeferred(false, mWidgetsRecyclerView);
mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
}
}
final class SearchAndRecommendationViewHolder {
final View mContainer;
final ViewGroup mContainer;
final View mCollapseHandle;
final WidgetsSearchBar mSearchBar;
final TextView mHeaderTitle;
final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
SearchAndRecommendationViewHolder(View searchAndRecommendationContainer) {
SearchAndRecommendationViewHolder(ViewGroup searchAndRecommendationContainer) {
mContainer = searchAndRecommendationContainer;
mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
mHeaderTitle = mContainer.findViewById(R.id.title);
mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table);
mRecommendedWidgetsTable.setWidgetCellOnTouchListener((view, event) -> {
getRecyclerView().onTouchEvent(event);
return false;
});
mRecommendedWidgetsTable.setWidgetCellLongClickListener(WidgetsFullSheet.this);
mRecommendedWidgetsTable.setWidgetCellOnClickListener(WidgetsFullSheet.this);
}
}
}
@@ -0,0 +1,175 @@
/*
* Copyright (C) 2021 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.widget.picker;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TableLayout;
import android.widget.TableRow;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetImageView;
import java.util.ArrayList;
import java.util.List;
/** A {@link TableLayout} for showing recommended widgets. */
public final class WidgetsRecommendationTableLayout extends TableLayout {
private static final float SCALE_DOWN_RATIO = 0.9f;
private final DeviceProfile mDeviceProfile;
private final float mWidgetCellTextViewsHeight;
private float mRecommendationTableMaxHeight = Float.MAX_VALUE;
@Nullable private OnLongClickListener mWidgetCellOnLongClickListener;
@Nullable private OnClickListener mWidgetCellOnClickListener;
@Nullable private OnTouchListener mWidgetCellOnTouchListener;
public WidgetsRecommendationTableLayout(Context context) {
this(context, /* attrs= */ null);
}
public WidgetsRecommendationTableLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mDeviceProfile = Launcher.getLauncher(context).getDeviceProfile();
// There are 1 row for title, 1 row for dimension and 2 rows for description.
mWidgetCellTextViewsHeight = 4 * getResources().getDimension(R.dimen.widget_cell_font_size);
}
/** Sets a {@link android.view.View.OnLongClickListener} for all widget cells in this table. */
public void setWidgetCellLongClickListener(OnLongClickListener onLongClickListener) {
mWidgetCellOnLongClickListener = onLongClickListener;
}
/** Sets a {@link android.view.View.OnClickListener} for all widget cells in this table. */
public void setWidgetCellOnClickListener(OnClickListener widgetCellOnClickListener) {
mWidgetCellOnClickListener = widgetCellOnClickListener;
}
/** Sets a {@link android.view.View.OnTouchListener} for all widget cells in this table. */
public void setWidgetCellOnTouchListener(OnTouchListener widgetCellOnTouchListener) {
mWidgetCellOnTouchListener = widgetCellOnTouchListener;
}
/**
* Sets a list of recommended widgets that would like to be displayed in this table within the
* desired {@code recommendationTableMaxHeight}.
*
* <p>If the content can't fit {@code recommendationTableMaxHeight}, this view will remove a
* last row from the {@code recommendedWidgets} until it fits or only one row left. If the only
* row still doesn't fit, we scale down the preview image.
*/
public void setRecommendedWidgets(List<ArrayList<WidgetItem>> recommendedWidgets,
float recommendationTableMaxHeight) {
mRecommendationTableMaxHeight = recommendationTableMaxHeight;
RecommendationTableData data = fitRecommendedWidgetsToTableSpace(/* previewScale= */ 1f,
recommendedWidgets);
bindData(data);
}
private void bindData(RecommendationTableData data) {
if (data.mRecommendationTable.size() == 0) {
setVisibility(GONE);
return;
}
removeAllViews();
for (int i = 0; i < data.mRecommendationTable.size(); i++) {
List<WidgetItem> widgetItems = data.mRecommendationTable.get(i);
TableRow tableRow = new TableRow(getContext());
tableRow.setGravity(Gravity.TOP);
for (WidgetItem widgetItem : widgetItems) {
WidgetCell widgetCell = addItemCell(tableRow);
widgetCell.setPreviewSize(widgetItem.spanX, widgetItem.spanY, data.mPreviewScale);
widgetCell.applyFromCellItem(widgetItem,
LauncherAppState.getInstance(getContext()).getWidgetCache());
widgetCell.ensurePreview();
}
addView(tableRow);
}
setVisibility(VISIBLE);
}
private WidgetCell addItemCell(ViewGroup parent) {
WidgetCell widget = (WidgetCell) LayoutInflater.from(
getContext()).inflate(R.layout.widget_cell, parent, false);
widget.setOnTouchListener(mWidgetCellOnTouchListener);
WidgetImageView preview = widget.findViewById(R.id.widget_preview);
preview.setOnClickListener(mWidgetCellOnClickListener);
preview.setOnLongClickListener(mWidgetCellOnLongClickListener);
widget.setAnimatePreview(false);
parent.addView(widget);
return widget;
}
private RecommendationTableData fitRecommendedWidgetsToTableSpace(
float previewScale,
List<ArrayList<WidgetItem>> recommendedWidgetsInTable) {
// A naive estimation of the widgets recommendation table height without inflation.
float totalHeight = 0;
for (int i = 0; i < recommendedWidgetsInTable.size(); i++) {
List<WidgetItem> widgetItems = recommendedWidgetsInTable.get(i);
float rowHeight = 0;
for (int j = 0; j < widgetItems.size(); j++) {
float previewHeight = widgetItems.get(j).spanY * mDeviceProfile.allAppsCellHeightPx
* previewScale;
rowHeight = Math.max(rowHeight, previewHeight + mWidgetCellTextViewsHeight);
}
totalHeight += rowHeight;
}
if (totalHeight < mRecommendationTableMaxHeight) {
return new RecommendationTableData(recommendedWidgetsInTable, previewScale);
}
if (recommendedWidgetsInTable.size() > 1) {
// We don't want to scale down widgets preview unless we really need to. Reduce the
// num of row by 1 to see if it fits.
return fitRecommendedWidgetsToTableSpace(
previewScale,
recommendedWidgetsInTable.subList(/* fromIndex= */0,
/* toIndex= */recommendedWidgetsInTable.size() - 1));
}
float nextPreviewScale = previewScale * SCALE_DOWN_RATIO;
return fitRecommendedWidgetsToTableSpace(nextPreviewScale, recommendedWidgetsInTable);
}
/** Data class for the widgets recommendation table and widgets preview scaling. */
private class RecommendationTableData {
private final List<ArrayList<WidgetItem>> mRecommendationTable;
private final float mPreviewScale;
RecommendationTableData(List<ArrayList<WidgetItem>> recommendationTable,
float previewScale) {
mRecommendationTable = recommendationTable;
mPreviewScale = previewScale;
}
}
}
+9
View File
@@ -11,6 +11,15 @@
// 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Launcher3_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Launcher3_license"],
}
filegroup {
name: "launcher3-test-src-common",
srcs: ["src_common/**/*.java"],
@@ -1214,7 +1214,7 @@ public final class LauncherInstrumentation {
final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
assertTrue("injectInputEvent failed",
mInstrumentation.getUiAutomation().injectInputEvent(event, true));
mInstrumentation.getUiAutomation().injectInputEvent(event, true, false));
event.recycle();
}
@@ -30,7 +30,6 @@ import androidx.test.uiautomator.Until;
import com.android.launcher3.testing.TestProtocol;
import java.util.Collection;
import java.util.List;
/**
* All widgets container.
@@ -106,7 +105,8 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
fullWidgetsPicker.wait(Until.scrollable(true), WAIT_TIME_MS));
final Point displaySize = mLauncher.getRealDisplaySize();
final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer();
Rect headerRect = new Rect();
final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer(headerRect);
mLauncher.assertTrue("Can't locate widgets list for the test app: "
+ mLauncher.getLauncherPackageName(),
widgetsContainer != null);
@@ -132,7 +132,12 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
mLauncher.assertTrue("Too many attempts", ++i <= 40);
final int scroll = getWidgetsScroll();
mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, tableRows, 0);
mLauncher.scroll(
fullWidgetsPicker,
Direction.DOWN,
headerRect,
10,
true);
final int newScroll = getWidgetsScroll();
mLauncher.assertTrue(
"Scrolled in a wrong direction in Widgets: from " + scroll + " to "
@@ -143,7 +148,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
}
/** Finds the widgets list of this test app from the collapsed full widgets picker. */
private UiObject2 findTestAppWidgetsTableContainer() {
private UiObject2 findTestAppWidgetsTableContainer(Rect outHeaderRect) {
final BySelector headerSelector = By.res(mLauncher.getLauncherPackageName(),
"widgets_list_header");
final BySelector targetAppSelector = By.clazz("android.widget.TextView").text(
@@ -156,6 +161,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
UiObject2 fullWidgetsPicker = verifyActiveContainer();
UiObject2 header = fullWidgetsPicker.findObject(headerSelector);
outHeaderRect.set(0, 0, 0, header.getVisibleBounds().height());
mLauncher.assertTrue("Can't find a widget header", header != null);
// Look for a header that has the test app name.
@@ -179,11 +185,14 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
if (widgetsContainer != null) {
return widgetsContainer;
}
mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, List.of(headerTitle), 0);
} else {
mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, fullWidgetsPicker.getChildren(),
0);
}
mLauncher.scroll(
fullWidgetsPicker,
Direction.DOWN,
outHeaderRect,
/* steps= */ 10,
/* slowDown= */ true);
}
return null;