Snap for 7233012 from a4cb0f02de to sc-release
Change-Id: I39bb246225bd95b114002cf5368d7de5e9fde2e7
This commit is contained in:
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user