Snap for 9024801 from afff936235 to tm-qpr1-release

Change-Id: I64b24d1722728fa372b3774106eb1fc658fd6fae
This commit is contained in:
Android Build Coastguard Worker
2022-09-03 01:25:31 +00:00
34 changed files with 554 additions and 182 deletions
@@ -117,14 +117,15 @@ public class QuickstepModelDelegate extends ModelDelegate {
// TODO: Implement caching and preloading
super.loadItems(ums, pinnedShortcuts);
WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(
mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns);
FixedContainerItems allAppsItems = new FixedContainerItems(mAllAppsState.containerId,
mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
mDataModel.extraItems.put(mAllAppsState.containerId, allAppsItems);
WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts,
mIDP.numDatabaseAllAppsColumns, mAllAppsState.containerId);
FixedContainerItems allAppsPredictionItems = new FixedContainerItems(
mAllAppsState.containerId, mAllAppsState.storage.read(mApp.getContext(),
allAppsFactory, ums.allUsers::get));
mDataModel.extraItems.put(mAllAppsState.containerId, allAppsPredictionItems);
WorkspaceItemFactory hotseatFactory =
new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseHotseatIcons);
WorkspaceItemFactory hotseatFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts,
mIDP.numDatabaseHotseatIcons, mHotseatState.containerId);
FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId,
mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems);
@@ -432,15 +433,17 @@ public class QuickstepModelDelegate extends ModelDelegate {
private final UserManagerState mUMS;
private final Map<ShortcutKey, ShortcutInfo> mPinnedShortcuts;
private final int mMaxCount;
private final int mContainer;
private int mReadCount = 0;
protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums,
Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount) {
Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount, int container) {
mAppState = appState;
mUMS = ums;
mPinnedShortcuts = pinnedShortcuts;
mMaxCount = maxCount;
mContainer = container;
}
@Nullable
@@ -458,6 +461,7 @@ public class QuickstepModelDelegate extends ModelDelegate {
return null;
}
AppInfo info = new AppInfo(lai, user, mUMS.isUserQuiet(user));
info.container = mContainer;
mAppState.getIconCache().getTitleAndIcon(info, lai, false);
mReadCount++;
return info.makeWorkspaceItem(mAppState.getContext());
@@ -472,6 +476,7 @@ public class QuickstepModelDelegate extends ModelDelegate {
return null;
}
WorkspaceItemInfo wii = new WorkspaceItemInfo(si, mAppState.getContext());
wii.container = mContainer;
mAppState.getIconCache().getShortcutIcon(wii, si);
mReadCount++;
return wii;
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
import android.animation.Animator;
@@ -169,6 +170,13 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
}
}
if (ENABLE_SHELL_TRANSITIONS
&& !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) {
// Launcher is resumed, but in a state where taskbar is still independent, so
// ignore the state change.
return null;
}
mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
}
@@ -16,8 +16,7 @@
package com.android.launcher3.taskbar.allapps;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import android.animation.PropertyValuesHolder;
import android.content.Context;
@@ -60,7 +59,7 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllApp
if (animate) {
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
mOpenCloseAnimator.setInterpolator(EMPHASIZED);
mOpenCloseAnimator.setDuration(
ALL_APPS.getTransitionDuration(mActivityContext, true /* isToState */)).start();
} else {
@@ -87,7 +86,7 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllApp
@Override
protected Interpolator getIdleInterpolator() {
return EMPHASIZED_ACCELERATE;
return EMPHASIZED;
}
@Override
@@ -28,11 +28,10 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SP
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.quickstep.views.FloatingTaskView.PRIMARY_TRANSLATE_OFFSCREEN;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN;
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.SPLIT_INSTRUCTIONS_FADE;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA;
@@ -112,6 +111,7 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
// TODO (b/238651489): Refactor state management to avoid need for double check
FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView();
if (floatingTask != null) {
// We are in split selection state currently, transitioning to another state
DragLayer dragLayer = mLauncher.getDragLayer();
RectF onScreenRectF = new RectF();
Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), floatingTask,
@@ -127,8 +127,8 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
);
setter.setFloat(
mRecentsView,
FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN,
mRecentsView.getFirstFloatingTaskView(),
PRIMARY_TRANSLATE_OFFSCREEN,
mRecentsView.getPagedOrientationHandler()
.getFloatingTaskOffscreenTranslationTarget(
floatingTask,
@@ -140,14 +140,14 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
LINEAR
));
setter.setFloat(
mRecentsView,
SPLIT_INSTRUCTIONS_FADE,
1,
setter.setViewAlpha(
mRecentsView.getSplitInstructionsView(),
0,
config.getInterpolator(
ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
LINEAR
));
)
);
}
}
@@ -64,6 +64,7 @@ import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.SystemProperties;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.View;
@@ -159,6 +160,9 @@ import java.util.stream.Stream;
public class QuickstepLauncher extends Launcher {
public static final boolean ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", false);
public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
/**
* Reusable command for applying the shelf height on the background thread.
@@ -349,16 +353,18 @@ public class QuickstepLauncher extends Launcher {
*/
private void onStateOrResumeChanging(boolean inTransition) {
LauncherState state = getStateManager().getState();
boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0;
if (started) {
DeviceProfile profile = getDeviceProfile();
boolean willUserBeActive =
(getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
boolean visible = (state == NORMAL || state == OVERVIEW)
&& (willUserBeActive || isUserActive())
&& !profile.isVerticalBarLayout();
UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
profile.hotseatBarSizePx);
if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) {
boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0;
if (started) {
DeviceProfile profile = getDeviceProfile();
boolean willUserBeActive =
(getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
boolean visible = (state == NORMAL || state == OVERVIEW)
&& (willUserBeActive || isUserActive())
&& !profile.isVerticalBarLayout();
UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
profile.hotseatBarSizePx);
}
}
if (state == NORMAL && !inTransition) {
((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
@@ -16,14 +16,17 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import android.content.Context;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
/**
@@ -41,9 +44,9 @@ public class AllAppsState extends LauncherState {
@Override
public <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfileListenable>
int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) {
return !context.getDeviceProfile().isTablet && isToState
? 600
: isToState ? 500 : 300;
return context.getDeviceProfile().isTablet
? 500
: isToState ? 600 : 300;
}
@Override
@@ -77,10 +80,23 @@ public class AllAppsState extends LauncherState {
}
@Override
protected float getDepthUnchecked(Context context) {
// The scrim fades in at approximately 50% of the swipe gesture.
// This means that the depth should be greater than 1, in order to fully zoom out.
return 2f;
protected <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
if (context.getDeviceProfile().isTablet) {
// The goal is to set wallpaper to zoom at workspaceContentScale when in AllApps.
// When depth is 0, wallpaper zoom is set to maxWallpaperScale.
// When depth is 1, wallpaper zoom is set to 1.
// For depth to achieve zoom set to maxWallpaperScale * workspaceContentScale:
float maxWallpaperScale = context.getResources().getFloat(
com.android.internal.R.dimen.config_wallpaperMaxScale);
return Utilities.mapToRange(
maxWallpaperScale * context.getDeviceProfile().workspaceContentScale,
maxWallpaperScale, 1f, 0f, 1f, LINEAR);
} else {
// The scrim fades in at approximately 50% of the swipe gesture.
// This means that the depth should be greater than 1, in order to fully zoom out.
return 2f;
}
}
@Override
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import android.content.Context;
import android.graphics.Color;
@@ -101,6 +102,12 @@ public class BackgroundAppState extends OverviewState {
return Color.TRANSPARENT;
}
@Override
public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
if (ENABLE_SHELL_TRANSITIONS) return false;
return super.isTaskbarAlignedWithHotseat(launcher);
}
public static float[] getOverviewScaleAndOffsetForBackgroundState(
BaseDraggingActivity activity) {
return new float[] {
@@ -33,6 +33,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
import static com.android.launcher3.uioverrides.QuickstepLauncher.ENABLE_PIP_KEEP_CLEAR_ALGORITHM;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
@@ -1461,12 +1462,13 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
homeToWindowPositionMap.invert(windowToHomePositionMap);
windowToHomePositionMap.mapRect(startRect);
final Rect hotseatKeepClearArea = getKeepClearAreaForHotseat();
final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
.startSwipePipToHome(taskInfo.topActivity,
taskInfo.topActivityInfo,
runningTaskTarget.taskInfo.pictureInPictureParams,
homeRotation,
mDp.hotseatBarSizePx);
hotseatKeepClearArea);
final Rect appBounds = new Rect();
final WindowConfiguration winConfig = taskInfo.configuration.windowConfiguration;
// Adjust the appBounds for TaskBar by using the calculated window crop Rect
@@ -1529,6 +1531,35 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
return swipePipToHomeAnimator;
}
private Rect getKeepClearAreaForHotseat() {
Rect keepClearArea;
if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) {
// make the height equal to hotseatBarSizePx only
keepClearArea = new Rect(0, 0, mDp.hotseatBarSizePx, 0);
return keepClearArea;
}
// the keep clear area in global screen coordinates, in pixels
if (mDp.isPhone) {
if (mDp.isSeascape()) {
// in seascape the Hotseat is on the left edge of the screen
keepClearArea = new Rect(0, 0, mDp.hotseatBarSizePx, mDp.heightPx);
} else if (mDp.isLandscape) {
// in landscape the Hotseat is on the right edge of the screen
keepClearArea = new Rect(mDp.widthPx - mDp.hotseatBarSizePx, 0,
mDp.widthPx, mDp.heightPx);
} else {
// in portrait mode the Hotseat is at the bottom of the screen
keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx,
mDp.widthPx, mDp.heightPx);
}
} else {
// large screens have Hotseat always at the bottom of the screen
keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx,
mDp.widthPx, mDp.heightPx);
}
return keepClearArea;
}
private void startInterceptingTouchesForGesture() {
if (mRecentsAnimationController == null) {
return;
@@ -28,7 +28,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.content.pm.ShortcutInfo;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Bundle;
@@ -509,11 +509,12 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
}
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) {
PictureInPictureParams pictureInPictureParams, int launcherRotation,
Rect hotseatKeepClearArea) {
if (mPip != null) {
try {
return mPip.startSwipePipToHome(componentName, activityInfo,
pictureInPictureParams, launcherRotation, shelfHeight);
pictureInPictureParams, launcherRotation, hotseatKeepClearArea);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startSwipePipToHome", e);
}
@@ -602,7 +603,21 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startTasksWithLegacyTransition");
Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition");
}
}
}
public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, int taskId,
Bundle mainOptions, Bundle sideOptions,
@SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
RemoteAnimationAdapter adapter) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId,
mainOptions, sideOptions, sidePosition, splitRatio, adapter);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startShortcutAndTaskWithLegacyTransition");
}
}
}
@@ -195,7 +195,8 @@ public final class TaskViewUtils {
int taskIndex = recentsView.indexOfChild(v);
Context context = v.getContext();
DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
BaseActivity baseActivity = BaseActivity.fromContext(context);
DeviceProfile dp = baseActivity.getDeviceProfile();
boolean showAsGrid = dp.isTablet;
boolean parallaxCenterAndAdjacentTask =
taskIndex != recentsView.getCurrentPage() && !showAsGrid;
@@ -368,7 +369,7 @@ public final class TaskViewUtils {
});
if (depthController != null) {
out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(context),
out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(baseActivity),
TOUCH_RESPONSE_INTERPOLATOR);
}
}
@@ -226,6 +226,11 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
setFreezeViewVisibility(true);
}
@Override
public void onStateTransitionFailed(RecentsState toState) {
reset();
}
@Override
public void onStateTransitionComplete(RecentsState finalState) {
if (finalState == HOME) {
@@ -31,16 +31,20 @@ import android.app.ActivityThread;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.RemoteAnimationAdapter;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import androidx.annotation.Nullable;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.testing.TestLogging;
@@ -66,6 +70,7 @@ import java.util.function.Consumer;
* and is in the process of either a) selecting a second app or b) exiting intention to invoke split
*/
public class SplitSelectStateController {
private static final String TAG = "SplitSelectStateCtor";
private final Context mContext;
private final Handler mHandler;
@@ -196,7 +201,7 @@ public class SplitSelectStateController {
null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
ActivityThread.currentActivityThread().getApplicationThread()));
// TODO: handle intent + task with shell transition
// TODO(b/237635859): handle intent/shortcut + task with shell transition
} else {
RemoteSplitLaunchAnimationRunner animationRunner =
new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2,
@@ -215,9 +220,17 @@ public class SplitSelectStateController {
taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
splitRatio, adapter);
} else {
mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
stagePosition, splitRatio, adapter);
final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent,
taskPendingIntent.getCreatorUserHandle());
if (shortcutInfo != null) {
mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId2,
mainOpts.toBundle(), null /* sideOptions */, stagePosition, splitRatio,
adapter);
} else {
mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
stagePosition, splitRatio, adapter);
}
}
}
}
@@ -230,6 +243,28 @@ public class SplitSelectStateController {
this.mRecentsAnimationRunning = running;
}
@Nullable
private ShortcutInfo getShortcutInfo(Intent intent, UserHandle userHandle) {
if (intent == null || intent.getPackage() == null) {
return null;
}
final String shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID);
if (shortcutId == null) {
return null;
}
try {
final Context context = mContext.createPackageContextAsUser(
intent.getPackage(), 0 /* flags */, userHandle);
return new ShortcutInfo.Builder(context, shortcutId).build();
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Failed to create a ShortcutInfo for " + intent.getPackage());
}
return null;
}
/**
* Requires Shell Transitions
*/
@@ -102,7 +102,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
private boolean mLayoutValid = false;
private int mOrientationStateId;
private SplitBounds mSplitBounds;
private boolean mDrawsBelowRecents;
private Boolean mDrawsBelowRecents = null;
private boolean mIsGridTask;
private int mTaskRectTranslationX;
private int mTaskRectTranslationY;
@@ -391,7 +391,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
.withWindowCrop(mTmpCropRect)
.withCornerRadius(getCurrentCornerRadius());
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// If mDrawsBelowRecents is unset, no reordering will be enforced.
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mDrawsBelowRecents != null) {
// In legacy transitions, the animation leashes remain in same hierarchy in the
// TaskDisplayArea, so we don't want to bump the layer too high otherwise it will
// conflict with layers that WM core positions (ie. the input consumers). For shell
@@ -14,6 +14,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -49,6 +50,29 @@ import com.android.systemui.shared.system.QuickStepContract;
*/
public class FloatingTaskView extends FrameLayout {
public static final FloatProperty<FloatingTaskView> PRIMARY_TRANSLATE_OFFSCREEN =
new FloatProperty<FloatingTaskView>("floatingTaskPrimaryTranslateOffscreen") {
@Override
public void setValue(FloatingTaskView view, float translation) {
((RecentsView) view.mActivity.getOverviewPanel()).getPagedOrientationHandler()
.setFloatingTaskPrimaryTranslation(
view,
translation,
view.mActivity.getDeviceProfile()
);
}
@Override
public Float get(FloatingTaskView view) {
return ((RecentsView) view.mActivity.getOverviewPanel())
.getPagedOrientationHandler()
.getFloatingTaskPrimaryTranslation(
view,
view.mActivity.getDeviceProfile()
);
}
};
private FloatingTaskThumbnailView mThumbnailView;
private SplitPlaceholderView mSplitPlaceholderView;
private RectF mStartingPosition;
@@ -319,4 +319,10 @@ public class GroupedTaskView extends TaskView {
super.applyThumbnailSplashAlpha();
mSnapshotView2.setSplashAlpha(mTaskThumbnailSplashAlpha);
}
@Override
void setThumbnailVisibility(int visibility) {
super.setThumbnailVisibility(visibility);
mSnapshotView2.setVisibility(visibility);
}
}
@@ -119,6 +119,11 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
setFreezeViewVisibility(true);
}
@Override
public void onStateTransitionFailed(LauncherState toState) {
reset();
}
@Override
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == NORMAL || finalState == SPRING_LOADED) {
@@ -397,39 +397,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
};
public static final FloatProperty<RecentsView> FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN =
new FloatProperty<RecentsView>("firstFloatingTaskTranslateOffscreen") {
@Override
public void setValue(RecentsView view, float translation) {
view.getPagedOrientationHandler().setFloatingTaskPrimaryTranslation(
view.mFirstFloatingTaskView,
translation,
view.mActivity.getDeviceProfile()
);
}
@Override
public Float get(RecentsView view) {
return view.getPagedOrientationHandler().getFloatingTaskPrimaryTranslation(
view.mFirstFloatingTaskView,
view.mActivity.getDeviceProfile()
);
}
};
public static final FloatProperty<RecentsView> SPLIT_INSTRUCTIONS_FADE =
new FloatProperty<RecentsView>("splitInstructionsFade") {
@Override
public void setValue(RecentsView view, float fade) {
view.mSplitInstructionsView.setAlpha(1 - fade);
}
@Override
public Float get(RecentsView view) {
return 1 - view.mSplitInstructionsView.getAlpha();
}
};
// OverScroll constants
private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
@@ -2827,7 +2794,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
RectF startingTaskRect = new RectF();
if (mSplitHiddenTaskView != null) {
mSplitHiddenTaskView.setVisibility(INVISIBLE);
// Split staging is initiated, hide the original TaskView thumbnail.
// Toggled back on in resetFromSplitSelectionState().
mSplitHiddenTaskView.setThumbnailVisibility(INVISIBLE);
anim.addFloat(mSplitHiddenTaskView, TaskView.ICON_ALPHA, 1, 0,
clampToProgress(LINEAR, 0, 0.167f));
mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
mSplitHiddenTaskView.getThumbnail(),
mSplitHiddenTaskView.getThumbnail().getThumbnail(),
@@ -2855,6 +2826,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
InteractionJankMonitorWrapper.end(
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
} else {
// If transition to split select was interrupted, clean up to prevent glitches
resetFromSplitSelectionState();
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
}
@@ -4222,7 +4195,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
resetTaskVisuals();
mSplitHiddenTaskViewIndex = -1;
if (mSplitHiddenTaskView != null) {
mSplitHiddenTaskView.setVisibility(VISIBLE);
// Toggle thumbnail visibility back on (turned off in
// createInitialSplitSelectAnimation()).
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE);
mSplitHiddenTaskView = null;
}
}
@@ -5334,6 +5309,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
return mFirstFloatingTaskView;
}
@Nullable
public SplitInstructionsView getSplitInstructionsView() {
return mSplitInstructionsView;
}
/** Update the current activity locus id to show the enabled state of Overview */
public void updateLocusId() {
String locusId = "Overview";
@@ -323,6 +323,19 @@ public class TaskView extends FrameLayout implements Reusable {
}
};
public static final FloatProperty<TaskView> ICON_ALPHA =
new FloatProperty<TaskView>("iconAlpha") {
@Override
public void setValue(TaskView taskView, float v) {
taskView.mIconView.setAlpha(v);
}
@Override
public Float get(TaskView taskView) {
return taskView.mIconView.getAlpha();
}
};
@Nullable
protected Task mTask;
protected TaskThumbnailView mSnapshotView;
@@ -1488,6 +1501,10 @@ public class TaskView extends FrameLayout implements Reusable {
return display != null ? display.getDisplayId() : DEFAULT_DISPLAY;
}
void setThumbnailVisibility(int visibility) {
mSnapshotView.setVisibility(visibility);
}
/**
* We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
*/
+1
View File
@@ -21,4 +21,5 @@
android:layout_height="match_parent"
android:theme="@style/HomeScreenElementTheme"
android:importantForAccessibility="no"
android:preferKeepClear="true"
launcher:containerType="hotseat" />
+3 -3
View File
@@ -106,13 +106,13 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Zezwalaj na obrót ekranu głównego"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Po obróceniu telefonu"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Plakietki z powiadomieniami"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Kropki powiadomień"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Włączono"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Wyłączono"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Wymagany jest dostęp do powiadomień"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Aby pokazać plakietki z powiadomieniami, włącz powiadomienia aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Aby pokazywkropki powiadomień, włącz powiadomienia aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Zmień ustawienia"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaż plakietki z powiadomieniami"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaż kropki powiadomień"</string>
<string name="developer_options_title" msgid="700788437593726194">"Opcje programisty"</string>
<string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Dodawaj ikony aplikacji do ekranu głównego"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"W przypadku nowych aplikacji"</string>
+6 -3
View File
@@ -265,7 +265,8 @@ public abstract class LauncherState implements BaseState<LauncherState> {
*
* 0 means completely zoomed in, without blurs. 1 is zoomed out, with blurs.
*/
public final float getDepth(Context context) {
public final <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
float getDepth(DEVICE_PROFILE_CONTEXT context) {
return getDepth(context,
BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode);
}
@@ -275,14 +276,16 @@ public abstract class LauncherState implements BaseState<LauncherState> {
*
* @see #getDepth(Context).
*/
public final float getDepth(Context context, boolean isMultiWindowMode) {
public final <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
float getDepth(DEVICE_PROFILE_CONTEXT context, boolean isMultiWindowMode) {
if (isMultiWindowMode) {
return 0;
}
return getDepthUnchecked(context);
}
protected float getDepthUnchecked(Context context) {
protected <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
return 0f;
}
@@ -57,6 +57,10 @@ public class Interpolators {
public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
/**
* The default emphasized interpolator. Used for hero / emphasized movement of content.
*/
public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
0.3f, 0f, 0.8f, 0.15f);
public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
@@ -87,7 +91,6 @@ public class Interpolators {
public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL =
v -> ACCEL_DEACCEL.getInterpolation(TOUCH_RESPONSE_INTERPOLATOR.getInterpolation(v));
/**
* Inversion of ZOOM_OUT, compounded with an ease-out.
*/
@@ -218,4 +221,14 @@ public class Interpolators {
public static Interpolator reverse(Interpolator interpolator) {
return t -> 1 - interpolator.getInterpolation(1 - t);
}
// Create the default emphasized interpolator
private static PathInterpolator createEmphasizedInterpolator() {
Path path = new Path();
// Doing the same as fast_out_extra_slow_in
path.moveTo(0f, 0f);
path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
return new PathInterpolator(path);
}
}
@@ -342,6 +342,11 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
public void onAnimationSuccess(Animator animator) {
onStateTransitionEnd(state);
}
@Override
public void onAnimationCancel(Animator animation) {
onStateTransitionFailed(state);
}
};
}
@@ -354,6 +359,12 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
}
}
private void onStateTransitionFailed(STATE_TYPE state) {
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onStateTransitionFailed(state);
}
}
private void onStateTransitionEnd(STATE_TYPE state) {
// Only change the stable states after the transitions have finished
if (state != mCurrentStableState) {
@@ -588,6 +599,11 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
default void onStateTransitionStart(STATE_TYPE toState) { }
/**
* If the state transition animation fails (e.g. is canceled by the user), this fires.
*/
default void onStateTransitionFailed(STATE_TYPE toState) { }
default void onStateTransitionComplete(STATE_TYPE finalState) { }
}
@@ -18,6 +18,7 @@ package com.android.launcher3.touch;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
@@ -199,8 +200,10 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER));
config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME);
if (!config.userControlled) {
config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE);
config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED);
}
config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED);
config.setInterpolator(ANIM_DEPTH, EMPHASIZED);
} else {
if (config.userControlled) {
config.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR_MANUAL));
@@ -238,8 +241,10 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER);
if (!config.userControlled) {
config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_DECELERATE);
config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED);
}
config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED);
config.setInterpolator(ANIM_DEPTH, EMPHASIZED);
} else {
config.setInterpolator(ANIM_DEPTH, config.userControlled ? BLUR_MANUAL : BLUR_ATOMIC);
config.setInterpolator(ANIM_WORKSPACE_FADE,
@@ -43,13 +43,14 @@ public class OnboardingPrefs<T extends ActivityContext> {
public static final String SEARCH_ONBOARDING_COUNT = "launcher.search_onboarding_count";
public static final String TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen";
public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
public static final String QSB_SEARCH_ONBOARDING_CARD_DISMISSED = "launcher.qsb_edu_dismiss";
// When adding a new key, add it here as well, to be able to reset it from Developer Options.
public static final Map<String, String[]> ALL_PREF_KEYS = Map.of(
"All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT },
"Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT,
HOTSEAT_LONGPRESS_TIP_SEEN },
"Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT,
SEARCH_ONBOARDING_COUNT},
SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED},
"Taskbar Education", new String[] { TASKBAR_EDU_SEEN },
"All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT}
);
@@ -61,7 +62,8 @@ public class OnboardingPrefs<T extends ActivityContext> {
HOME_BOUNCE_SEEN,
HOTSEAT_LONGPRESS_TIP_SEEN,
SEARCH_KEYBOARD_EDU_SEEN,
TASKBAR_EDU_SEEN
TASKBAR_EDU_SEEN,
QSB_SEARCH_ONBOARDING_CARD_DISMISSED
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventBoolKey {}
@@ -32,7 +32,6 @@ import com.android.launcher3.util.Themes;
public class AllAppsState extends LauncherState {
private static final float PARALLAX_COEFFICIENT = .125f;
private static final float WORKSPACE_SCALE_FACTOR = 0.97f;
private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE;
@@ -60,7 +59,8 @@ public class AllAppsState extends LauncherState {
@Override
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
return new ScaleAndTranslation(WORKSPACE_SCALE_FACTOR, NO_OFFSET, NO_OFFSET);
return new ScaleAndTranslation(launcher.getDeviceProfile().workspaceContentScale, NO_OFFSET,
NO_OFFSET);
}
@Override
@@ -71,7 +71,7 @@ public class AllAppsState extends LauncherState {
ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
.getWorkspaceScaleAndTranslation(launcher);
return new ScaleAndTranslation(
WORKSPACE_SCALE_FACTOR,
launcher.getDeviceProfile().workspaceContentScale,
overviewScaleAndTranslation.translationX,
overviewScaleAndTranslation.translationY);
}
@@ -30,39 +30,117 @@ import java.util.Set;
public class CellLayoutBoard {
public static class CellType {
// The cells marked by this will be filled by 1x1 widgets and will be ignored when
// validating
public static final char IGNORE = 'x';
// The cells marked by this will be filled by app icons
public static final char ICON = 'i';
// Empty space
public static final char EMPTY = '-';
// Widget that will be saved as "main widget" for easier retrieval
public static final char MAIN_WIDGET = 'm';
// Everything else will be consider a widget
}
public static class WidgetRect {
public char mType;
public Rect mBounds;
WidgetRect(char type, Rect bounds) {
this.mType = type;
this.mBounds = bounds;
}
int getSpanX() {
return mBounds.right - mBounds.left + 1;
}
int getSpanY() {
return mBounds.top - mBounds.bottom + 1;
}
int getCellX() {
return mBounds.left;
}
int getCellY() {
return mBounds.bottom;
}
boolean shouldIgnore() {
return this.mType == CellType.IGNORE;
}
@Override
public String toString() {
return "WidgetRect type = " + mType + " bounds = " + mBounds.toString();
}
}
public static class IconPoint {
public Point coord;
public char mType;
public IconPoint(Point coord, char type) {
this.coord = coord;
mType = type;
}
public char getType() {
return mType;
}
public void setType(char type) {
mType = type;
}
public Point getCoord() {
return coord;
}
public void setCoord(Point coord) {
this.coord = coord;
}
}
static final int INFINITE = 99999;
char[][] mBoard = new char[30][30];
char[][] mWidget = new char[30][30];
List<TestBoardWidget> mWidgetsRects = new ArrayList<>();
Map<Character, TestBoardWidget> mWidgetsMap = new HashMap<>();
List<WidgetRect> mWidgetsRects = new ArrayList<>();
Map<Character, WidgetRect> mWidgetsMap = new HashMap<>();
List<TestBoardAppIcon> mIconPoints = new ArrayList<>();
Map<Character, TestBoardAppIcon> mIconsMap = new HashMap<>();
List<IconPoint> mIconPoints = new ArrayList<>();
Map<Character, IconPoint> mIconsMap = new HashMap<>();
Point mMain = new Point();
CellLayoutBoard() {
for (int x = 0; x < mBoard.length; x++) {
for (int y = 0; y < mBoard[0].length; y++) {
mBoard[x][y] = '-';
for (int x = 0; x < mWidget.length; x++) {
for (int y = 0; y < mWidget[0].length; y++) {
mWidget[x][y] = CellType.EMPTY;
}
}
}
public List<TestBoardWidget> getWidgets() {
public List<WidgetRect> getWidgets() {
return mWidgetsRects;
}
public List<IconPoint> getIcons() {
return mIconPoints;
}
public Point getMain() {
return mMain;
}
public TestBoardWidget getWidgetRect(char c) {
public WidgetRect getWidgetRect(char c) {
return mWidgetsMap.get(c);
}
public static TestBoardWidget getWidgetRect(int x, int y, Set<Point> used, char[][] board) {
public static WidgetRect getWidgetRect(int x, int y, Set<Point> used, char[][] board) {
char type = board[x][y];
Queue<Point> search = new ArrayDeque<Point>();
Point current = new Point(x, y);
@@ -91,20 +169,20 @@ public class CellLayoutBoard {
}
}
}
return new TestBoardWidget(type, widgetRect);
return new WidgetRect(type, widgetRect);
}
public static boolean isWidget(char type) {
return type != 'i' && type != '-';
return type != CellType.ICON && type != CellType.EMPTY;
}
public static boolean isIcon(char type) {
return type == 'i';
return type == CellType.ICON;
}
private static List<TestBoardWidget> getRects(char[][] board) {
private static List<WidgetRect> getRects(char[][] board) {
Set<Point> used = new HashSet<>();
List<TestBoardWidget> widgetsRects = new ArrayList<>();
List<WidgetRect> widgetsRects = new ArrayList<>();
for (int x = 0; x < board.length; x++) {
for (int y = 0; y < board[0].length; y++) {
if (!used.contains(new Point(x, y)) && isWidget(board[x][y])) {
@@ -115,12 +193,12 @@ public class CellLayoutBoard {
return widgetsRects;
}
private static List<TestBoardAppIcon> getIconPoints(char[][] board) {
List<TestBoardAppIcon> iconPoints = new ArrayList<>();
private static List<IconPoint> getIconPoints(char[][] board) {
List<IconPoint> iconPoints = new ArrayList<>();
for (int x = 0; x < board.length; x++) {
for (int y = 0; y < board[0].length; y++) {
if (isIcon(board[x][y])) {
iconPoints.add(new TestBoardAppIcon(new Point(x, y), board[x][y]));
iconPoints.add(new IconPoint(new Point(x, y), board[x][y]));
}
}
}
@@ -135,18 +213,18 @@ public class CellLayoutBoard {
String line = lines[y];
for (int x = 0; x < line.length(); x++) {
char c = line.charAt(x);
if (c == 'm') {
if (c == CellType.MAIN_WIDGET) {
board.mMain = new Point(x, y);
}
if (c != '-') {
board.mBoard[x][y] = line.charAt(x);
if (c != CellType.EMPTY) {
board.mWidget[x][y] = line.charAt(x);
}
}
}
board.mWidgetsRects = getRects(board.mBoard);
board.mWidgetsRects = getRects(board.mWidget);
board.mWidgetsRects.forEach(
widgetRect -> board.mWidgetsMap.put(widgetRect.mType, widgetRect));
board.mIconPoints = getIconPoints(board.mBoard);
board.mIconPoints = getIconPoints(board.mWidget);
return board;
}
}
@@ -15,16 +15,13 @@
*/
package com.android.launcher3.celllayout;
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -34,13 +31,14 @@ import com.android.launcher3.celllayout.testcases.MoveOutReorderCase;
import com.android.launcher3.celllayout.testcases.PushReorderCase;
import com.android.launcher3.celllayout.testcases.ReorderTestCase;
import com.android.launcher3.celllayout.testcases.SimpleReorderCase;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.tapl.Widget;
import com.android.launcher3.tapl.WidgetResizeFrame;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import org.junit.Assume;
import org.junit.Before;
@@ -60,6 +58,8 @@ public class ReorderWidgets extends AbstractLauncherUiTest {
private static final String TAG = ReorderWidgets.class.getSimpleName();
TestWorkspaceBuilder mBoardBuilder;
private View getViewAt(int cellX, int cellY) {
return getFromLauncher(l -> l.getWorkspace().getScreenWithId(
l.getWorkspace().getScreenIdForPageIndex(0)).getChildAt(cellX, cellY));
@@ -76,6 +76,7 @@ public class ReorderWidgets extends AbstractLauncherUiTest {
@Before
public void setup() throws Throwable {
mBoardBuilder = new TestWorkspaceBuilder(this);
TaplTestsLauncher3.initialize(this);
clearHomescreen();
}
@@ -86,78 +87,44 @@ public class ReorderWidgets extends AbstractLauncherUiTest {
private boolean validateBoard(CellLayoutBoard board) {
boolean match = true;
Point cellDimensions = getCellDimensions();
for (TestBoardWidget widgetRect: board.getWidgets()) {
for (CellLayoutBoard.WidgetRect widgetRect: board.getWidgets()) {
if (widgetRect.shouldIgnore()) {
continue;
}
View widget = getViewAt(widgetRect.getCellX(), widgetRect.getCellY());
assertTrue("The view selected at " + board + " is not a widget",
widget instanceof LauncherAppWidgetHostView);
match &= widgetRect.getSpanX()
== Math.round(widget.getWidth() / (float) cellDimensions.x);
match &= widgetRect.getSpanY()
== Math.round(widget.getHeight() / (float) cellDimensions.y);
if (!match) return match;
}
for (CellLayoutBoard.IconPoint iconPoint : board.getIcons()) {
View icon = getViewAt(iconPoint.getCoord().x, iconPoint.getCoord().y);
assertTrue("The view selected at " + iconPoint.coord + " is not an Icon",
icon instanceof DoubleShadowBubbleTextView);
}
return match;
}
/**
* Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases.
*/
private void fillWithWidgets(TestBoardWidget widgetRect) {
int initX = widgetRect.getCellX();
int initY = widgetRect.getCellY();
for (int x = 0; x < widgetRect.getSpanX(); x++) {
for (int y = 0; y < widgetRect.getSpanY(); y++) {
int auxX = initX + x;
int auxY = initY + y;
try {
// this widgets are filling, we don't care if we can't place them
addWidgetInCell(
new TestBoardWidget('x',
new Rect(auxX, auxY, auxX, auxY))
);
} catch (Exception e) {
Log.d(TAG, "Unable to place filling widget at " + auxX + "," + auxY);
}
}
}
}
private void addWidgetInCell(TestBoardWidget widgetRect) {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
LauncherAppWidgetInfo item = createWidgetInfo(info,
ApplicationProvider.getApplicationContext(), true);
item.cellX = widgetRect.getCellX();
item.cellY = widgetRect.getCellY();
item.spanX = widgetRect.getSpanX();
item.spanY = widgetRect.getSpanY();
addItemToScreen(item);
}
private void addCorrespondingWidgetRect(TestBoardWidget widgetRect) {
if (widgetRect.mType == 'x') {
fillWithWidgets(widgetRect);
} else {
addWidgetInCell(widgetRect);
}
}
private void runTestCase(ReorderTestCase testCase) {
Point mainWidgetCellPos = testCase.mStart.getMain();
testCase.mStart.getWidgets().forEach(this::addCorrespondingWidgetRect);
mBoardBuilder.buildBoard(testCase.mStart);
mLauncher.getWorkspace()
.getWidgetAtCell(mainWidgetCellPos.x, mainWidgetCellPos.y)
.dragWidgetToWorkspace(testCase.moveMainTo.x, testCase.moveMainTo.y)
.dismiss(); // dismiss resize frame
Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.x,
mainWidgetCellPos.y);
assertNotNull(widget);
WidgetResizeFrame resizeFrame = widget.dragWidgetToWorkspace(testCase.moveMainTo.x,
testCase.moveMainTo.y);
resizeFrame.dismiss();
boolean isValid = false;
for (CellLayoutBoard board : testCase.mEnd) {
isValid |= validateBoard(board);
}
assertTrue("None of the valid boards match with the current state", isValid);
assertTrue("Non of the valid boards match with the current state", isValid);
}
/**
@@ -0,0 +1,110 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.celllayout;
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
public class TestWorkspaceBuilder {
private static final ComponentName APP_COMPONENT_NAME = new ComponentName(
"com.google.android.calculator", "com.android.calculator2.Calculator");
public AbstractLauncherUiTest mTest;
private UserHandle mMyUser;
public TestWorkspaceBuilder(AbstractLauncherUiTest test) {
mTest = test;
mMyUser = Process.myUserHandle();
}
private static final String TAG = "CellLayoutBoardBuilder";
/**
* Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases.
*/
private void fillWithWidgets(CellLayoutBoard.WidgetRect widgetRect) {
int initX = widgetRect.getCellX();
int initY = widgetRect.getCellY();
for (int x = initX; x < initX + widgetRect.getSpanX(); x++) {
for (int y = initY; y < initY + widgetRect.getSpanY(); y++) {
try {
// this widgets are filling, we don't care if we can't place them
addWidgetInCell(
new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE,
new Rect(x, y, x, y))
);
} catch (Exception e) {
Log.d(TAG, "Unable to place filling widget at " + x + "," + y);
}
}
}
}
private void addWidgetInCell(CellLayoutBoard.WidgetRect widgetRect) {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(mTest, false);
LauncherAppWidgetInfo item = createWidgetInfo(info,
ApplicationProvider.getApplicationContext(), true);
item.cellX = widgetRect.getCellX();
item.cellY = widgetRect.getCellY();
item.spanX = widgetRect.getSpanX();
item.spanY = widgetRect.getSpanY();
mTest.addItemToScreen(item);
}
private void addIconInCell(CellLayoutBoard.IconPoint iconPoint) {
AppInfo appInfo = new AppInfo(APP_COMPONENT_NAME, "test icon", mMyUser,
AppInfo.makeLaunchIntent(APP_COMPONENT_NAME));
appInfo.cellX = iconPoint.getCoord().x;
appInfo.cellY = iconPoint.getCoord().y;
appInfo.minSpanY = appInfo.minSpanX = appInfo.spanX = appInfo.spanY = 1;
appInfo.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
appInfo.componentName = APP_COMPONENT_NAME;
mTest.addItemToScreen(new WorkspaceItemInfo(appInfo));
}
private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect) {
if (widgetRect.mType == 'x') {
fillWithWidgets(widgetRect);
} else {
addWidgetInCell(widgetRect);
}
}
public void buildBoard(CellLayoutBoard board) {
board.getWidgets().forEach(this::addCorrespondingWidgetRect);
board.getIcons().forEach(this::addIconInCell);
}
}
@@ -19,6 +19,10 @@ import android.graphics.Point;
import java.util.Map;
/**
* The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
* in the board mean refer to {@code CellType}
*/
public class FullReorderCase {
/** 5x5 Test
@@ -19,6 +19,10 @@ import android.graphics.Point;
import java.util.Map;
/**
* The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
* in the board mean refer to {@code CellType}
*/
public class MoveOutReorderCase {
/** 5x5 Test
@@ -19,6 +19,10 @@ import android.graphics.Point;
import java.util.Map;
/**
* The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
* in the board mean refer to {@code CellType}
*/
public class PushReorderCase {
/** 5x5 Test
@@ -19,6 +19,10 @@ import android.graphics.Point;
import java.util.Map;
/**
* The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
* in the board mean refer to {@code CellType}
*/
public class SimpleReorderCase {
/** 5x5 Test
@@ -319,7 +319,7 @@ public abstract class AbstractLauncherUiTest {
/**
* Adds {@param item} on the homescreen on the 0th screen
*/
protected void addItemToScreen(ItemInfo item) {
public void addItemToScreen(ItemInfo item) {
WidgetUtils.addItemToScreen(item, mTargetContext);
resetLoaderState();