Snap for 7233012 from a4cb0f02de to sc-release

Change-Id: I39bb246225bd95b114002cf5368d7de5e9fde2e7
This commit is contained in:
android-build-team Robot
2021-03-25 01:09:04 +00:00
34 changed files with 553 additions and 192 deletions
+1 -2
View File
@@ -116,8 +116,7 @@
android:theme="@style/AppItemActivityTheme"
android:excludeFromRecents="true"
android:autoRemoveFromRecents="true"
android:exported="true"
android:label="@string/action_add_to_workspace" >
android:exported="true">
<intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
<action android:name="android.content.pm.action.CONFIRM_PIN_APPWIDGET" />
@@ -25,6 +25,7 @@ import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
import static com.android.quickstep.views.SplitPlaceholderView.ALPHA_FLOAT;
import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
import android.annotation.TargetApi;
import android.os.Build;
@@ -73,7 +74,7 @@ public final class RecentsViewStateController extends
if (toState.overviewUi) {
// While animating into recents, update the visible task data as needed
builder.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
} else {
builder.addListener(
@@ -81,20 +81,6 @@ public class BackgroundAppState extends OverviewState {
return false;
}
@Override
public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
// Translate hotseat offscreen if we show it in overview.
RecentsView recentsView = launcher.getOverviewPanel();
ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher);
scaleAndTranslation.translationY += LayoutUtils.getShelfTrackingDistance(launcher,
launcher.getDeviceProfile(),
recentsView.getPagedOrientationHandler());
return scaleAndTranslation;
}
return super.getHotseatScaleAndTranslation(launcher);
}
@Override
protected float getDepthUnchecked(Context context) {
return 1f;
@@ -77,40 +77,11 @@ public class OverviewState extends LauncherState {
return new ScaleAndTranslation(scale, 0, -getDefaultSwipeHeight(launcher) * parallaxFactor);
}
@Override
public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
DeviceProfile dp = launcher.getDeviceProfile();
if (dp.allAppsIconSizePx >= dp.iconSizePx) {
return new ScaleAndTranslation(1, 0, 0);
} else {
float scale = ((float) dp.allAppsIconSizePx) / dp.iconSizePx;
// Distance between the screen center (which is the pivotY for hotseat) and the
// bottom of the hotseat (which we want to preserve)
float distanceFromBottom = dp.heightPx / 2 - dp.hotseatBarBottomPaddingPx;
// On scaling, the bottom edge is moved closer to the pivotY. We move the
// hotseat back down so that the bottom edge's position is preserved.
float translationY = distanceFromBottom * (1 - scale);
return new ScaleAndTranslation(scale, 0, translationY);
}
}
return getWorkspaceScaleAndTranslation(launcher);
}
@Override
public float[] getOverviewScaleAndOffset(Launcher launcher) {
return new float[] {NO_SCALE, NO_OFFSET};
}
@Override
public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
if (this == OVERVIEW) {
// Treat the QSB as part of the hotseat so they move together.
return getHotseatScaleAndTranslation(launcher);
}
return super.getQsbScaleAndTranslation(launcher);
}
@Override
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
return new PageAlphaProvider(DEACCEL_2) {
@@ -29,6 +29,7 @@ import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
@@ -70,7 +71,7 @@ public class FallbackRecentsStateController implements StateHandler<RecentsState
return;
}
// While animating into recents, update the visible task data as needed
setter.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
setter.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
setProperties(toState, config, setter);
@@ -83,34 +83,25 @@ public class StaggeredWorkspaceAnim {
DeviceProfile grid = launcher.getDeviceProfile();
Workspace workspace = launcher.getWorkspace();
CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets();
Hotseat hotseat = launcher.getHotseat();
// Hotseat and QSB takes up two additional rows.
int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
// Add animation for all the visible workspace pages
workspace.getVisiblePages()
.forEach(page -> addAnimationForPage((CellLayout) page, totalRows));
boolean workspaceClipChildren = workspace.getClipChildren();
boolean workspaceClipToPadding = workspace.getClipToPadding();
boolean cellLayoutClipChildren = cellLayout.getClipChildren();
boolean cellLayoutClipToPadding = cellLayout.getClipToPadding();
boolean hotseatClipChildren = hotseat.getClipChildren();
boolean hotseatClipToPadding = hotseat.getClipToPadding();
workspace.setClipChildren(false);
workspace.setClipToPadding(false);
cellLayout.setClipChildren(false);
cellLayout.setClipToPadding(false);
hotseat.setClipChildren(false);
hotseat.setClipToPadding(false);
// Hotseat and QSB takes up two additional rows.
int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
// Set up springs on workspace items.
for (int i = currentPage.getChildCount() - 1; i >= 0; i--) {
View child = currentPage.getChildAt(i);
CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows);
}
// Set up springs for the hotseat and qsb.
ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
if (grid.isVerticalBarLayout()) {
@@ -155,14 +146,37 @@ public class StaggeredWorkspaceAnim {
public void onAnimationEnd(Animator animation) {
workspace.setClipChildren(workspaceClipChildren);
workspace.setClipToPadding(workspaceClipToPadding);
cellLayout.setClipChildren(cellLayoutClipChildren);
cellLayout.setClipToPadding(cellLayoutClipToPadding);
hotseat.setClipChildren(hotseatClipChildren);
hotseat.setClipToPadding(hotseatClipToPadding);
}
});
}
private void addAnimationForPage(CellLayout page, int totalRows) {
ShortcutAndWidgetContainer itemsContainer = page.getShortcutsAndWidgets();
boolean pageClipChildren = page.getClipChildren();
boolean pageClipToPadding = page.getClipToPadding();
page.setClipChildren(false);
page.setClipToPadding(false);
// Set up springs on workspace items.
for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) {
View child = itemsContainer.getChildAt(i);
CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows);
}
mAnimators.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
page.setClipChildren(pageClipChildren);
page.setClipToPadding(pageClipToPadding);
}
});
}
/**
* Setup workspace with 0 duration to prepare for our staggered animation.
*/
@@ -315,6 +315,10 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
private final ClearAllButton mClearAllButton;
private final Rect mClearAllButtonDeadZoneRect = new Rect();
private final Rect mTaskViewDeadZoneRect = new Rect();
/**
* Reflects if Recents is currently in the middle of a gesture
*/
private boolean mGestureActive;
private final ScrollState mScrollState = new ScrollState();
// Keeps track of the previously known visible tasks for purposes of loading/unloading task data
@@ -624,8 +628,8 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
return;
}
mModel.getIconCache().clear();
unloadVisibleTaskData();
loadVisibleTaskData();
unloadVisibleTaskData(TaskView.FLAG_UPDATE_ICON);
loadVisibleTaskData(TaskView.FLAG_UPDATE_ICON);
}
public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
@@ -908,7 +912,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
}
// Unload existing visible task data
unloadVisibleTaskData();
unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
TaskView ignoreResetTaskView =
mIgnoreResetTaskId == -1 ? null : getTaskView(mIgnoreResetTaskId);
@@ -1031,7 +1035,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
updateCurveProperties();
// Update the set of visible task's data
loadVisibleTaskData();
loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
setTaskModalness(0);
}
@@ -1147,7 +1151,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
}
// After scrolling, update the visible task's data
loadVisibleTaskData();
loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
}
// Update the high res thumbnail loader state
@@ -1210,7 +1214,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
* Iterates through all the tasks, and loads the associated task data for newly visible tasks,
* and unloads the associated task data for tasks that are no longer visible.
*/
public void loadVisibleTaskData() {
public void loadVisibleTaskData(@TaskView.TaskDataChanges int dataChanges) {
if (!mOverviewStateEnabled || mTaskListChangeId == -1) {
// Skip loading visible task data if we've already left the overview state, or if the
// task list hasn't been loaded yet (the task views will not reflect the task list)
@@ -1252,12 +1256,18 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
continue;
}
if (!mHasVisibleTaskData.get(task.key.id)) {
taskView.onTaskListVisibilityChanged(true /* visible */);
// Ignore thumbnail update if it's current running task during the gesture
// We snapshot at end of gesture, it will update then
int changes = dataChanges;
if (taskView == getRunningTaskView() && mGestureActive) {
changes &= ~TaskView.FLAG_UPDATE_THUMBNAIL;
}
taskView.onTaskListVisibilityChanged(true /* visible */, changes);
}
mHasVisibleTaskData.put(task.key.id, visible);
} else {
if (mHasVisibleTaskData.get(task.key.id)) {
taskView.onTaskListVisibilityChanged(false /* visible */);
taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
}
mHasVisibleTaskData.delete(task.key.id);
}
@@ -1267,12 +1277,12 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
/**
* Unloads any associated data from the currently visible tasks
*/
private void unloadVisibleTaskData() {
private void unloadVisibleTaskData(@TaskView.TaskDataChanges int dataChanges) {
for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
if (mHasVisibleTaskData.valueAt(i)) {
TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i));
if (taskView != null) {
taskView.onTaskListVisibilityChanged(false /* visible */);
taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
}
}
}
@@ -1310,7 +1320,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
mRecentsAnimationController = null;
mLiveTileParams.setTargetSet(null);
unloadVisibleTaskData();
unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
setCurrentPage(0);
mDwbToastShown = false;
mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
@@ -1358,6 +1368,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
* Called when a gesture from an app is starting.
*/
public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
mGestureActive = true;
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
updateOrientationHandler();
@@ -1428,6 +1439,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
* Called when a gesture from an app has finished, and the animation to the target has ended.
*/
public void onGestureAnimationEnd() {
mGestureActive = false;
if (mOrientationState.setGestureActive(false)) {
updateOrientationHandler();
}
@@ -2733,7 +2745,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
loadVisibleTaskData();
loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
updateEnabledOverlays();
}
@@ -42,6 +42,8 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -67,6 +69,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
@@ -106,6 +109,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
import java.lang.annotation.Retention;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -117,6 +121,19 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private static final String TAG = TaskView.class.getSimpleName();
public static final int FLAG_UPDATE_ICON = 1;
public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1;
public static final int FLAG_UPDATE_ALL = FLAG_UPDATE_ICON | FLAG_UPDATE_THUMBNAIL;
/**
* Used in conjunction with {@link #onTaskListVisibilityChanged(boolean, int)}, providing more
* granularity on which components of this task require an update
*/
@Retention(SOURCE)
@IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL})
public @interface TaskDataChanges {}
/**
* The alpha of a black scrim on a page in the carousel as it leaves the screen.
* In the resting position of the carousel, the adjacent pages have about half this scrim.
@@ -557,7 +574,19 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
}
/**
* See {@link TaskDataChanges}
* @param visible If this task view will be visible to the user in overview or hidden
*/
public void onTaskListVisibilityChanged(boolean visible) {
onTaskListVisibilityChanged(visible, FLAG_UPDATE_ALL);
}
/**
* See {@link TaskDataChanges}
* @param visible If this task view will be visible to the user in overview or hidden
*/
public void onTaskListVisibilityChanged(boolean visible, @TaskDataChanges int changes) {
if (mTask == null) {
return;
}
@@ -568,22 +597,37 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
RecentsModel model = RecentsModel.INSTANCE.get(getContext());
TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
TaskIconCache iconCache = model.getIconCache();
mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
mTask, thumbnail -> mSnapshotView.setThumbnail(mTask, thumbnail));
mIconLoadRequest = iconCache.updateIconInBackground(mTask,
(task) -> {
setIcon(task.icon);
mDigitalWellBeingToast.initialize(mTask);
});
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
mTask, thumbnail -> {
mSnapshotView.setThumbnail(mTask, thumbnail);
});
}
if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
mIconLoadRequest = iconCache.updateIconInBackground(mTask,
(task) -> {
setIcon(task.icon);
mDigitalWellBeingToast.initialize(mTask);
});
}
} else {
mSnapshotView.setThumbnail(null, null);
setIcon(null);
// Reset the task thumbnail reference as well (it will be fetched from the cache or
// reloaded next time we need it)
mTask.thumbnail = null;
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
mSnapshotView.setThumbnail(null, null);
// Reset the task thumbnail reference as well (it will be fetched from the cache or
// reloaded next time we need it)
mTask.thumbnail = null;
}
if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
setIcon(null);
}
}
}
private boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
return (dataChange & flag) == flag;
}
private void cancelPendingLoadTasks() {
if (mThumbnailLoadRequest != null) {
mThumbnailLoadRequest.cancel();
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="?android:attr/colorBackground" />
<corners
android:topLeftRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="?android:attr/dialogCornerRadius" />
</shape>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<inset
android:insetLeft="@dimen/pin_widget_button_inset_horizontal"
android:insetRight="@dimen/pin_widget_button_inset_horizontal"
android:insetTop="@dimen/pin_widget_button_inset_vertical"
android:insetBottom="@dimen/pin_widget_button_inset_vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<ripple
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:tint="?android:attr/colorAccent" android:shape="rectangle">
<corners android:radius="18dp" />
<solid android:color="#FFFFFF" />
<padding
android:left="@dimen/pin_widget_button_padding_horizontal"
android:top="@dimen/pin_widget_button_padding_vertical"
android:right="@dimen/pin_widget_button_padding_horizontal"
android:bottom="@dimen/pin_widget_button_padding_vertical" />
</shape>
</item>
</ripple>
</inset>
+28 -47
View File
@@ -17,70 +17,51 @@
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/add_item_confirmation"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
android:orientation="vertical">
<ScrollView
<TextView
style="@style/TextHeadline"
android:id="@+id/widget_appName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textColor="?android:attr/textColorPrimary"
android:textSize="24sp"
android:ellipsize="end"
android:fadingEdge="horizontal"
android:singleLine="true"
android:maxLines="1" />
<include layout="@layout/widget_cell"
android:id="@+id/widget_cell"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clipToPadding="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingTop="4dp"
android:text="@string/add_item_request_drag_hint" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/colorPrimaryDark"
android:theme="?attr/widgetsTheme">
<com.android.launcher3.widget.WidgetCell
android:id="@+id/widget_cell"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:background="?android:attr/colorPrimaryDark"
android:focusable="true"
android:gravity="center_horizontal"
android:orientation="vertical" >
<include layout="@layout/widget_cell_content" />
</com.android.launcher3.widget.WidgetCell>
</FrameLayout>
</LinearLayout>
</ScrollView>
android:layout_marginVertical="16dp" />
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingBottom="4dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:paddingTop="4dp" >
android:padding="8dp"
android:orientation="horizontal">
<Button
style="?android:attr/buttonBarButtonStyle"
style="@style/Widget.DeviceDefault.Button.Rounded.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onCancelClick"
android:text="@android:string/cancel" />
<Space
android:layout_width="4dp"
android:layout_height="wrap_content" />
<Button
style="?android:attr/buttonBarButtonStyle"
style="@style/Widget.DeviceDefault.Button.Rounded.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onPlaceAutomaticallyClick"
+1
View File
@@ -26,6 +26,7 @@
<!-- Frame -->
<ImageView
android:id="@+id/widget_resize_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
+8 -8
View File
@@ -18,8 +18,8 @@
android:layout_height="wrap_content">
<!-- The image of the widget. This view does not support padding. Any placement adjustment
should be done using margins.
width & height are set at runtime after scaling the preview image. -->
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="0dp"
@@ -41,16 +41,15 @@
android:textColor="?android:attr/textColorPrimary"
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. -->
<!-- The original dimensions of the widget -->
<TextView
android:id="@+id/widget_dims"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textColor="?android:attr/textColorTertiary"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/widget_cell_font_size"
android:alpha="0.8" />
android:alpha="0.7" />
<TextView
android:id="@+id/widget_description"
@@ -58,9 +57,10 @@
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textSize="@dimen/widget_cell_font_size"
android:textColor="?android:attr/textColorTertiary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="2"
android:ellipsize="end"
android:fadingEdge="horizontal" />
android:fadingEdge="horizontal"
android:alpha="0.7" />
</merge>
+2 -1
View File
@@ -56,7 +56,8 @@
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorTertiary"
android:textColor="?android:attr/textColorSecondary"
android:alpha="0.7"
tools:text="m widgets, n shortcuts" />
</LinearLayout>
+1
View File
@@ -32,5 +32,6 @@
android:src="@drawable/ic_gm_close_24"
android:background="?android:selectableItemBackground"
android:layout_gravity="center"
android:contentDescription="@string/widgets_full_sheet_cancel_button_description"
android:visibility="gone"/>
</com.android.launcher3.widget.picker.search.LauncherWidgetsSearchBar>
+2
View File
@@ -21,6 +21,8 @@
<style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
<item name="android:windowBackground">@drawable/add_item_dialog_background</item>
<item name="android:windowNoTitle">true</item>
</style>
</resources>
+9
View File
@@ -138,6 +138,12 @@
<dimen name="shortcut_preview_padding_right">0dp</dimen>
<dimen name="shortcut_preview_padding_top">0dp</dimen>
<!-- Pin widget dialog -->
<dimen name="pin_widget_button_padding_horizontal">8dp</dimen>
<dimen name="pin_widget_button_padding_vertical">4dp</dimen>
<dimen name="pin_widget_button_inset_horizontal">4dp</dimen>
<dimen name="pin_widget_button_inset_vertical">6dp</dimen>
<!-- Dragging -->
<!-- Drag padding to add to the bottom of drop targets -->
<dimen name="drop_target_drag_padding">14dp</dimen>
@@ -277,4 +283,7 @@
<!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
<dimen name="taskbar_size">0dp</dimen>
<!-- Size of the maximum radius for the enforced rounded rectangles. -->
<dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
</resources>
+3
View File
@@ -81,6 +81,9 @@
<!-- Search bar text shown in the popup view showing all available widgets installed on the
device. [CHAR_LIMIT=50] -->
<string name="widgets_full_sheet_search_bar_hint">Search</string>
<!-- Spoken text for screen readers. This text lets a user know that the button is used to clear
the text that the user entered in the search box. [CHAR_LIMIT=none] -->
<string name="widgets_full_sheet_cancel_button_description">Clear text from search box</string>
<!-- Text shown when there is no widgets shown in the popup view showing all available widgets
installed on the device. [CHAR_LIMIT=none] -->
<string name="no_widgets_available">No widgets available</string>
+6
View File
@@ -145,6 +145,8 @@
<style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
<item name="android:windowBackground">@drawable/add_item_dialog_background</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
@@ -281,4 +283,8 @@
<item name="android:colorControlHighlight">#DFE1E5</item>
<item name="android:colorForeground">@color/all_apps_bg_hand_fill_dark</item>
</style>
<style name="Widget.DeviceDefault.Button.Rounded.Colored" parent="@android:style/Widget.DeviceDefault.Button.Colored">
<item name="android:background">@drawable/add_item_dialog_button_background</item>
</style>
</resources>
@@ -14,12 +14,15 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.SizeF;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.Nullable;
@@ -166,6 +169,15 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
DragLayer dl = launcher.getDragLayer();
AppWidgetResizeFrame frame = (AppWidgetResizeFrame) launcher.getLayoutInflater()
.inflate(R.layout.app_widget_resize_frame, dl, false);
if (widget.hasEnforcedCornerRadius()) {
float enforcedCornerRadius = widget.getEnforcedCornerRadius();
ImageView imageView = frame.findViewById(R.id.widget_resize_frame);
Drawable d = imageView.getDrawable();
if (d instanceof GradientDrawable) {
GradientDrawable gd = (GradientDrawable) d.mutate();
gd.setCornerRadius(enforcedCornerRadius);
}
}
frame.setupForWidget(widget, cellLayout, dl);
((DragLayer.LayoutParams) frame.getLayoutParams()).customPosition = true;
@@ -176,10 +176,6 @@ public abstract class LauncherState implements BaseState<LauncherState> {
return launcher.getNormalOverviewScaleAndOffset();
}
public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
return new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
}
public float getOverviewFullscreenProgress() {
return 0;
}
+1 -1
View File
@@ -307,7 +307,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
/**
* Returns the currently visible pages.
*/
protected Iterable<View> getVisiblePages() {
public Iterable<View> getVisiblePages() {
int panelCount = getPanelCount();
List<View> visiblePages = new ArrayList<>(panelCount);
for (int i = mCurrentPage; i < mCurrentPage + panelCount; i++) {
@@ -95,7 +95,6 @@ public class WorkspaceStateTransitionAnimation {
ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(
mLauncher);
ScaleAndTranslation qsbScaleAndTranslation = state.getQsbScaleAndTranslation(mLauncher);
mNewScale = scaleAndTranslation.scale;
PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
final int childCount = mWorkspace.getChildCount();
@@ -215,6 +215,9 @@ public final class FeatureFlags {
public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag(
"ENABLE_SPLIT_SELECT", false, "Uses new split screen selection overview UI");
public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag(
"ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
@@ -37,11 +37,14 @@ import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.TextView;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InvariantDeviceProfile;
@@ -127,6 +130,9 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
if (savedInstanceState == null) {
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_START);
}
TextView widgetAppName = findViewById(R.id.widget_appName);
widgetAppName.setText(getApplicationInfo().labelRes);
}
@Override
@@ -326,4 +332,15 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
.log(command);
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
View view = getWindow().getDecorView();
WindowManager.LayoutParams layoutParams =
(WindowManager.LayoutParams) view.getLayoutParams();
layoutParams.gravity = Gravity.BOTTOM;
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
getWindowManager().updateViewLayout(view, layoutParams);
}
}
@@ -17,8 +17,12 @@ package com.android.launcher3.dragndrop;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -28,14 +32,30 @@ public final class AppWidgetHostViewDrawable extends Drawable {
private final LauncherAppWidgetHostView mAppWidgetHostView;
private Paint mPaint = new Paint();
private final Path mClipPath;
public AppWidgetHostViewDrawable(LauncherAppWidgetHostView appWidgetHostView) {
mAppWidgetHostView = appWidgetHostView;
Path clipPath = null;
if (appWidgetHostView.getClipToOutline()) {
Outline outline = new Outline();
mAppWidgetHostView.getOutlineProvider().getOutline(mAppWidgetHostView, outline);
Rect rect = new Rect();
if (outline.getRect(rect)) {
float radius = outline.getRadius();
clipPath = new Path();
clipPath.addRoundRect(new RectF(rect), radius, radius, Path.Direction.CCW);
}
}
mClipPath = clipPath;
}
@Override
public void draw(Canvas canvas) {
int saveCount = canvas.saveLayer(0, 0, getIntrinsicWidth(), getIntrinsicHeight(), mPaint);
if (mClipPath != null) {
canvas.clipPath(mClipPath);
}
mAppWidgetHostView.draw(canvas);
canvas.restoreToCount(saveCount);
}
@@ -110,6 +110,8 @@ public class DragLayer extends BaseDragLayer<Launcher> {
mDragController = dragController;
recreateControllers();
mOverviewScrim.setup();
mWorkspaceDragScrim = new WorkspaceDragScrim((this));
mWorkspaceDragScrim.setWorkspace(workspace);
@@ -118,8 +120,6 @@ public class DragLayer extends BaseDragLayer<Launcher> {
mRootView = (LauncherRootView) getParent();
mSysUiScrim = new SysUiScrim(mRootView);
mRootView.setSysUiScrim(mSysUiScrim);
}
@Override
@@ -555,7 +555,6 @@ public class DragLayer extends BaseDragLayer<Launcher> {
public void setInsets(Rect insets) {
super.setInsets(insets);
mSysUiScrim.onInsetsChanged(insets, mAllowSysuiScrims);
mOverviewScrim.onInsetsChanged(insets);
}
public WorkspaceDragScrim getWorkspaceDragScrim() {
@@ -18,10 +18,6 @@ package com.android.launcher3.graphics;
import static android.view.View.VISIBLE;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.OVERVIEW;
import android.graphics.Rect;
import android.util.FloatProperty;
import android.view.View;
import android.view.ViewGroup;
@@ -55,15 +51,15 @@ public class OverviewScrim extends Scrim {
public OverviewScrim(View view) {
super(view);
mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel();
onExtractedColorsChanged(mWallpaperColorInfo);
}
public void onInsetsChanged(Rect insets) {
mStableScrimmedView = (OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0
? mLauncher.getHotseat()
: mLauncher.getOverviewPanel();
/**
* Initializes once view hierarchy is established.
*/
public void setup() {
mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel();
}
public void updateCurrentScrimmedView(ViewGroup root) {
@@ -53,10 +53,4 @@ public class HintState extends LauncherState {
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
return new ScaleAndTranslation(0.92f, 0, 0);
}
@Override
public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
// Treat the QSB as part of the hotseat so they move together.
return getHotseatScaleAndTranslation(launcher);
}
}
@@ -59,10 +59,11 @@ public class SpringLoadedState extends LauncherState {
float scale = grid.workspaceSpringLoadShrinkFactor;
Rect insets = launcher.getDragLayer().getInsets();
int insetsBottom = grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom;
float scaledHeight = scale * ws.getNormalChildHeight();
float shrunkTop = insets.top + grid.dropTargetBarSizePx;
float shrunkBottom = ws.getMeasuredHeight() - insets.bottom
float shrunkBottom = ws.getMeasuredHeight() - insetsBottom
- grid.workspacePadding.bottom
- grid.workspaceSpringLoadedBottomSpace;
float totalShrunkSpace = shrunkBottom - shrunkTop;
@@ -20,6 +20,7 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
@@ -32,12 +33,14 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.AdapterView;
import android.widget.Advanceable;
import android.widget.RemoteViews;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.Launcher;
@@ -95,6 +98,18 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
private final Rect mWidgetSizeAtDrag = new Rect();
private final RectF mTempRectF = new RectF();
private final boolean mIsRtl;
private final Rect mEnforcedRectangle = new Rect();
private final float mEnforcedCornerRadius;
private final ViewOutlineProvider mCornerRadiusEnforcementOutline = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (mEnforcedRectangle.isEmpty() || mEnforcedCornerRadius <= 0) {
outline.setEmpty();
} else {
outline.setRoundRect(mEnforcedRectangle, mEnforcedCornerRadius);
}
}
};
public LauncherAppWidgetHostView(Context context) {
super(context);
@@ -112,6 +127,8 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
mIsRtl = Utilities.isRtl(context.getResources());
mColorExtractor = LocalColorExtractor.newInstance(getContext());
mColorExtractor.setListener(this);
mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(getContext());
}
@Override
@@ -169,7 +186,7 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
if (viewGroup instanceof AdapterView) {
return true;
} else {
for (int i=0; i < viewGroup.getChildCount(); i++) {
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
if (child instanceof ViewGroup) {
if (checkScrollableRecursively((ViewGroup) child)) {
@@ -272,6 +289,8 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
int pageId = mWorkspace.getPageIndexForScreenId(info.screenId);
updateColorExtraction(mCurrentWidgetSize, pageId);
}
enforceRoundedCorners();
}
/** Starts the drag mode. */
@@ -469,4 +488,39 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
}
return false;
}
@UiThread
private void resetRoundedCorners() {
setOutlineProvider(ViewOutlineProvider.BACKGROUND);
setClipToOutline(false);
}
@UiThread
private void enforceRoundedCorners() {
if (mEnforcedCornerRadius <= 0 || !RoundedCornerEnforcement.isRoundedCornerEnabled(this)) {
resetRoundedCorners();
return;
}
View background = RoundedCornerEnforcement.findBackground(this);
if (RoundedCornerEnforcement.hasAppWidgetOptedOut(this, background)) {
resetRoundedCorners();
return;
}
RoundedCornerEnforcement.computeRoundedRectangle(this,
background,
mEnforcedRectangle);
setOutlineProvider(mCornerRadiusEnforcementOutline);
setClipToOutline(true);
}
/** Returns the corner radius currently enforced, in pixels. */
public float getEnforcedCornerRadius() {
return mEnforcedCornerRadius;
}
/** Returns true if the corner radius are enforced for this App Widget. */
public boolean hasEnforcedCornerRadius() {
return getClipToOutline();
}
}
@@ -0,0 +1,170 @@
/*
* 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;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import java.util.ArrayList;
import java.util.List;
/**
* Utilities to compute the enforced the use of rounded corners on App Widgets.
*/
public class RoundedCornerEnforcement {
// This class is only a namespace and not meant to be instantiated.
private RoundedCornerEnforcement() {
}
/**
* Find the background view for a widget.
*
* @param appWidget the view containing the App Widget (typically the instance of
* {@link AppWidgetHostView}).
*/
@Nullable
public static View findBackground(@NonNull View appWidget) {
List<View> backgrounds = findViewsWithId(appWidget, android.R.id.background);
if (backgrounds.size() == 1) {
return backgrounds.get(0);
}
// Really, the argument should contain the widget, so it cannot be the background.
if (appWidget instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) appWidget;
if (vg.getChildCount() > 0) {
return findUndefinedBackground(vg.getChildAt(0));
}
}
return appWidget;
}
/**
* Check whether the app widget has opted out of the enforcement.
*/
public static boolean hasAppWidgetOptedOut(@NonNull View appWidget, @NonNull View background) {
return background.getId() == android.R.id.background && background.getClipToOutline();
}
/** Check if the app widget is in the deny list. */
public static boolean isRoundedCornerEnabled(@NonNull View view) {
if (!Utilities.ATLEAST_S || !FeatureFlags.ENABLE_ENFORCED_ROUNDED_CORNERS.get()) {
return false;
}
// Here we need to test if the view's component is in the (to be created) deny list.
return true;
}
/**
* Computes the rounded rectangle needed for this app widget.
*
* @param appWidget View onto which the rounded rectangle will be applied.
* @param background Background view. This must be either {@code appWidget} or a descendant
* of {@code appWidget}.
* @param outRect Rectangle set to the rounded rectangle coordinates, in the reference frame
* of {@code appWidget}.
*/
public static void computeRoundedRectangle(@NonNull View appWidget, @NonNull View background,
@NonNull Rect outRect) {
outRect.left = 0;
outRect.right = background.getWidth();
outRect.top = 0;
outRect.bottom = background.getHeight();
while (background != appWidget) {
outRect.offset(background.getLeft(), background.getTop());
background = (View) background.getParent();
}
}
/**
* Computes the radius of the rounded rectangle that should be applied to a widget expanded
* in the given context.
*/
public static float computeEnforcedRadius(@NonNull Context context) {
if (!Utilities.ATLEAST_S) {
return 0;
}
Resources res = context.getResources();
float systemRadius = res.getDimension(android.R.dimen.system_app_widget_background_radius);
float defaultRadius = res.getDimension(R.dimen.enforced_rounded_corner_max_radius);
return Math.min(defaultRadius, systemRadius);
}
private static List<View> findViewsWithId(View view, @IdRes int viewId) {
List<View> output = new ArrayList<>();
accumulateViewsWithId(view, viewId, output);
return output;
}
// Traverse views. If the predicate returns true, continue on the children, otherwise, don't.
private static void accumulateViewsWithId(View view, @IdRes int viewId, List<View> output) {
if (view.getId() == viewId) {
output.add(view);
return;
}
if (view instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) view;
for (int i = 0; i < vg.getChildCount(); i++) {
accumulateViewsWithId(vg.getChildAt(i), viewId, output);
}
}
}
private static boolean isViewVisible(View view) {
if (view.getVisibility() != View.VISIBLE) {
return false;
}
return !view.willNotDraw() || view.getForeground() != null || view.getBackground() != null;
}
@Nullable
private static View findUndefinedBackground(View current) {
if (current.getVisibility() != View.VISIBLE) {
return null;
}
if (isViewVisible(current)) {
return current;
}
View lastVisibleView = null;
// Find the first view that is either not a ViewGroup, or a ViewGroup which will draw
// something, or a ViewGroup that contains more than one view.
if (current instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) current;
for (int i = 0; i < vg.getChildCount(); i++) {
View visibleView = findUndefinedBackground(vg.getChildAt(i));
if (visibleView != null) {
if (lastVisibleView != null) {
return current; // At least two visible children
}
lastVisibleView = visibleView;
}
}
}
return lastVisibleView;
}
}
@@ -34,6 +34,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.TextView;
@@ -554,6 +555,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
return super.onBackPressed();
}
@Override
public void onDragStart(boolean start, float startDisplacement) {
super.onDragStart(start, startDisplacement);
getWindowInsetsController().hide(WindowInsets.Type.ime());
}
/** A holder class for holding adapters & their corresponding recycler view. */
private final class AdapterHolder {
static final int PRIMARY = 0;
@@ -18,7 +18,6 @@ package com.android.launcher3.tapl;
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
import android.graphics.Point;
import android.graphics.Rect;
import androidx.test.uiautomator.By;
@@ -30,6 +29,7 @@ import androidx.test.uiautomator.Until;
import com.android.launcher3.testing.TestProtocol;
import java.util.Collection;
import java.util.List;
/**
* All widgets container.
@@ -100,17 +100,18 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"getting widget " + labelText + " in widgets list")) {
final UiObject2 searchBar = findSearchBar();
final UiObject2 fullWidgetsPicker = verifyActiveContainer();
mLauncher.assertTrue("Widgets container didn't become scrollable",
fullWidgetsPicker.wait(Until.scrollable(true), WAIT_TIME_MS));
final Point displaySize = mLauncher.getRealDisplaySize();
Rect headerRect = new Rect();
final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer(headerRect);
final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer(searchBar);
mLauncher.assertTrue("Can't locate widgets list for the test app: "
+ mLauncher.getLauncherPackageName(),
widgetsContainer != null);
final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
final BySelector previewSelector = By.res(mLauncher.getLauncherPackageName(),
"widget_preview");
int i = 0;
for (; ; ) {
final Collection<UiObject2> tableRows = widgetsContainer.getChildren();
@@ -125,19 +126,28 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
"View is not WidgetCell",
"com.android.launcher3.widget.WidgetCell",
widget.getClassName());
return new Widget(mLauncher, widget);
UiObject2 preview = widget.findObject(previewSelector);
mLauncher.assertTrue("Can't find widget preview", preview != null);
Rect previewRect = new Rect(preview.getVisibleBounds());
boolean intersected = searchBar.getVisibleBounds().intersect(previewRect);
if (intersected) {
Rect scrollUp = new Rect(/* left= */ 0, /* top= */0, /* right*/ 0,
/* bottom= */ searchBar.getVisibleBounds().height());
mLauncher.scroll(
fullWidgetsPicker,
Direction.UP,
scrollUp,
/* steps= */ 2,
/* slowDown= */ true);
}
preview = widget.findObject(previewSelector);
return new Widget(mLauncher, preview);
}
}
mLauncher.assertTrue("Too many attempts", ++i <= 40);
final int scroll = getWidgetsScroll();
mLauncher.scroll(
fullWidgetsPicker,
Direction.DOWN,
headerRect,
10,
true);
mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, tableRows, 0);
final int newScroll = getWidgetsScroll();
mLauncher.assertTrue(
"Scrolled in a wrong direction in Widgets: from " + scroll + " to "
@@ -147,8 +157,21 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
}
}
private UiObject2 findSearchBar() {
final BySelector searchBarContainerSelector = By.res(mLauncher.getLauncherPackageName(),
"search_and_recommendations_container");
final BySelector searchBarSelector = By.res(mLauncher.getLauncherPackageName(),
"widgets_search_bar");
final UiObject2 searchBarContainer = mLauncher.waitForLauncherObject(
searchBarContainerSelector);
mLauncher.assertTrue("Can't find a search bar container", searchBarContainer != null);
UiObject2 searchBar = searchBarContainer.findObject(searchBarSelector);
mLauncher.assertTrue("Can't find a search bar", searchBar != null);
return searchBar;
}
/** Finds the widgets list of this test app from the collapsed full widgets picker. */
private UiObject2 findTestAppWidgetsTableContainer(Rect outHeaderRect) {
private UiObject2 findTestAppWidgetsTableContainer(final UiObject2 searchBar) {
final BySelector headerSelector = By.res(mLauncher.getLauncherPackageName(),
"widgets_list_header");
final BySelector targetAppSelector = By.clazz("android.widget.TextView").text(
@@ -161,12 +184,23 @@ 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.
UiObject2 headerTitle = fullWidgetsPicker.findObject(targetAppSelector);
if (headerTitle != null) {
Rect headerTitleRect = new Rect(headerTitle.getVisibleBounds());
boolean intersected = searchBar.getVisibleBounds().intersect(headerTitleRect);
if (intersected) {
Rect scrollUp = new Rect(/* left= */ 0, /* top= */0, /* right*/ 0,
/* bottom= */ searchBar.getVisibleBounds().height());
mLauncher.scroll(
fullWidgetsPicker,
Direction.UP,
scrollUp,
/* steps= */ 2,
/* slowDown= */ true);
}
// If we find the header and it has not been expanded, let's click it to see the
// widgets list.
if (!hasHeaderExpanded) {
@@ -185,14 +219,11 @@ 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;