Merging from ub-launcher3-master @ build 6682763

Test: manual, presubmit on the source branch
x20/teams/android-launcher/merge/ub-launcher3-master_master_6682763.html

Change-Id: Ibff46b3ef7ff89accb459db323f31179adb4ef21
This commit is contained in:
Becky Qiu
2020-07-15 12:43:12 -07:00
54 changed files with 445 additions and 351 deletions
-3
View File
@@ -45,9 +45,6 @@
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- TODO(b/150802536): Enabled only for ENABLE_FIXED_ROTATION_TRANSFORM feature flag -->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<!--
Permissions required for read/write access to the workspace data. These permission name
@@ -164,6 +164,12 @@ public class DebugTestInformationHandler extends TestInformationHandler {
}
case TestProtocol.REQUEST_GET_TEST_EVENTS: {
if (sEvents == null) {
// sEvents can be null if Launcher died and restarted after
// REQUEST_START_EVENT_LOGGING.
return response;
}
synchronized (sEvents) {
response.putStringArrayList(
TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
+2 -2
View File
@@ -53,8 +53,8 @@
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:enabled="true"
android:exported="true">
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
+9 -10
View File
@@ -22,14 +22,14 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3">
<permission
<permission
android:name="${packageName}.permission.HOTSEAT_EDU"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signatureOrSystem" />
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
<application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
@@ -94,12 +94,11 @@
android:clearTaskOnLaunch="true"
android:exported="false"/>
<activity
android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
android:exported="true">
<activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
android:exported="true">
<intent-filter>
<action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
<category android:name="android.intent.category.DEFAULT"/>
@@ -69,7 +69,6 @@ public class PredictionAppTracker extends AppLaunchTracker
// Accessed only on worker thread
private AppPredictor mHomeAppPredictor;
private AppPredictor mRecentsOverviewPredictor;
public PredictionAppTracker(Context context) {
mContext = context;
@@ -97,10 +96,6 @@ public class PredictionAppTracker extends AppLaunchTracker
mHomeAppPredictor.destroy();
mHomeAppPredictor = null;
}
if (mRecentsOverviewPredictor != null) {
mRecentsOverviewPredictor.destroy();
mRecentsOverviewPredictor = null;
}
}
@WorkerThread
@@ -142,7 +137,6 @@ public class PredictionAppTracker extends AppLaunchTracker
// Initialize the clients
int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns;
mHomeAppPredictor = createPredictor(Client.HOME, count);
mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count);
return true;
}
case MSG_DESTROY: {
@@ -157,12 +151,7 @@ public class PredictionAppTracker extends AppLaunchTracker
}
case MSG_PREDICT: {
if (mHomeAppPredictor != null) {
String client = (String) msg.obj;
if (Client.HOME.id.equals(client)) {
mHomeAppPredictor.requestPredictionUpdate();
} else {
mRecentsOverviewPredictor.requestPredictionUpdate();
}
mHomeAppPredictor.requestPredictionUpdate();
}
return true;
}
@@ -75,8 +75,7 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
// TODO (b/129421797): Update the client constants
public enum Client {
HOME("home"),
OVERVIEW("overview");
HOME("home");
public final String id;
@@ -91,10 +90,9 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
private final Context mContext;
private final DynamicItemCache mDynamicItemCache;
private final List[] mPredictionServicePredictions;
private List mPredictionServicePredictions = Collections.emptyList();
private int mMaxIconsPerRow;
private Client mActiveClient;
private AllAppsContainerView mAppsView;
@@ -108,16 +106,10 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated);
mActiveClient = Client.HOME;
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
mMaxIconsPerRow = idp.numColumns;
idp.addOnChangeListener(this);
mPredictionServicePredictions = new List[Client.values().length];
for (int i = 0; i < mPredictionServicePredictions.length; i++) {
mPredictionServicePredictions[i] = Collections.emptyList();
}
mGettingValidPredictionResults = Utilities.getDevicePrefs(context)
.getBoolean(LAST_PREDICTION_ENABLED_STATE, true);
@@ -130,18 +122,6 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
mMaxIconsPerRow = profile.numColumns;
}
public Client getClient() {
return mActiveClient;
}
public void switchClient(Client client) {
if (client == mActiveClient) {
return;
}
mActiveClient = client;
dispatchOnChange(true);
}
public void setTargetAppsView(AllAppsContainerView appsView) {
if (mAppsView != null) {
mAppsView.getAppsStore().removeUpdateListener(this);
@@ -195,10 +175,8 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
}
private void updatePredictionStateAfterCallback() {
boolean validResults = false;
for (List l : mPredictionServicePredictions) {
validResults |= l != null && !l.isEmpty();
}
boolean validResults = mPredictionServicePredictions != null
&& !mPredictionServicePredictions.isEmpty();
if (validResults != mGettingValidPredictionResults) {
mGettingValidPredictionResults = validResults;
Utilities.getDevicePrefs(mContext).edit()
@@ -210,7 +188,7 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
public AppPredictor.Callback appPredictorCallback(Client client) {
return targets -> {
mPredictionServicePredictions[client.ordinal()] = targets;
mPredictionServicePredictions = targets;
updatePredictionStateAfterCallback();
};
}
@@ -238,7 +216,7 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
state.apps = new ArrayList<>();
List<AppTarget> appTargets = mPredictionServicePredictions[mActiveClient.ordinal()];
List<AppTarget> appTargets = mPredictionServicePredictions;
if (!appTargets.isEmpty()) {
for (AppTarget appTarget : appTargets) {
ComponentKey key;
@@ -24,6 +24,7 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.app.ActivityManager.RunningTaskInfo;
import android.util.Log;
import android.view.animation.Interpolator;
@@ -52,17 +53,17 @@ final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extend
private final BaseActivityInterface<?, T> mActivityInterface;
// The id of the currently running task that is transitioning to overview.
private final int mTargetTaskId;
private final RunningTaskInfo mTargetTask;
private final RecentsAnimationDeviceState mDeviceState;
private T mActivity;
private RecentsView mRecentsView;
AppToOverviewAnimationProvider(
BaseActivityInterface<?, T> activityInterface, int targetTaskId,
BaseActivityInterface<?, T> activityInterface, RunningTaskInfo targetTask,
RecentsAnimationDeviceState deviceState) {
mActivityInterface = activityInterface;
mTargetTaskId = targetTaskId;
mTargetTask = targetTask;
mDeviceState = deviceState;
}
@@ -73,7 +74,7 @@ final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extend
* @param wasVisible true if it was visible before
*/
boolean onActivityReady(T activity, Boolean wasVisible) {
activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTask);
AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
mDeviceState,
@@ -122,7 +123,8 @@ final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extend
wallpaperTargets, MODE_CLOSING);
// Use the top closing app to determine the insets for the animation
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
RemoteAnimationTargetCompat runningTaskTarget = mTargetTask == null ? null
: targets.findTask(mTargetTask.taskId);
if (runningTaskTarget == null) {
Log.e(TAG, "No closing app");
return pa.buildAnim();
@@ -38,7 +38,6 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINI
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -313,12 +312,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
}
setupRecentsViewUi();
if (mDeviceState.getNavMode() == TWO_BUTTONS) {
// If the device is in two button mode, swiping up will show overview with predictions
// so we need to kick off switching to the overview predictions as soon as possible
mActivityInterface.updateOverviewPredictionState();
}
linkRecentsViewScroll();
return true;
@@ -423,7 +416,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
}
protected void notifyGestureAnimationStartToRecents() {
mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId());
mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
}
private void launcherFrameDrawn() {
@@ -451,12 +444,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
@Override
public void onMotionPauseChanged(boolean isPaused) {
setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
if (mDeviceState.isFullyGesturalNavMode() && isPaused) {
// In fully gestural nav mode, switch to overview predictions once the user has paused
// (this is a no-op if the predictions are already in that state)
mActivityInterface.updateOverviewPredictionState();
}
}
public void maybeUpdateRecentsAttachedState() {
@@ -555,14 +542,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
@Override
public void updateFinalShift() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationTargets != null) {
LiveTileOverlay.INSTANCE.update(
mTaskViewSimulator.getCurrentCropRect(),
mTaskViewSimulator.getCurrentCornerRadius());
}
}
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed;
@@ -573,6 +552,14 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
updateSysUiFlags(mCurrentShift.value);
applyWindowTransform();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationTargets != null) {
LiveTileOverlay.INSTANCE.update(
mTaskViewSimulator.getCurrentRect(),
mTaskViewSimulator.getCurrentCornerRadius());
}
}
updateLauncherTransitionProgress();
}
@@ -261,16 +261,6 @@ public final class LauncherActivityInterface extends
return true;
}
@Override
public void updateOverviewPredictionState() {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
return;
}
PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
PredictionUiStateManager.Client.OVERVIEW);
}
@Override
public int getContainerType() {
final Launcher launcher = getVisibleLauncher();
@@ -29,7 +29,6 @@ import android.view.ViewConfiguration;
import androidx.annotation.BinderThread;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -165,7 +164,7 @@ public class OverviewCommandHelper {
mActivityInterface = mOverviewComponentObserver.getActivityInterface();
mCreateTime = SystemClock.elapsedRealtime();
mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,
RecentsModel.getRunningTaskId(), mDeviceState);
ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
// Preload the plan
mRecentsModel.getTasks(null);
@@ -227,10 +226,6 @@ public class OverviewCommandHelper {
LauncherLogProto.ContainerType.TASKSWITCHER);
mUserEventLogged = true;
}
// Switch prediction client to overview
PredictionUiStateManager.INSTANCE.get(activity).switchClient(
PredictionUiStateManager.Client.OVERVIEW);
return mAnimationProvider.onActivityReady(activity, wasVisible);
}
@@ -76,7 +76,7 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity>
*/
public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) {
mHomeTaskInfo = homeTaskInfo;
onGestureAnimationStart(homeTaskInfo == null ? -1 : homeTaskInfo.taskId);
onGestureAnimationStart(homeTaskInfo);
}
/**
@@ -107,14 +107,15 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity>
}
@Override
protected boolean shouldAddDummyTaskView(int runningTaskId) {
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId
protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
if (mHomeTaskInfo != null && runningTaskInfo != null &&
mHomeTaskInfo.taskId == runningTaskInfo.taskId
&& getTaskViewCount() == 0) {
// Do not add a dummy task if we are running over home with empty recents, so that we
// show the empty recents message instead of showing a dummy task and later removing it.
return false;
}
return super.shouldAddDummyTaskView(runningTaskId);
return super.shouldAddDummyTaskView(runningTaskInfo);
}
@Override
@@ -205,13 +205,15 @@ public class StaggeredWorkspaceAnim {
ResourceProvider rp = DynamicResource.provider(v.getContext());
float stiffness = rp.getFloat(R.dimen.staggered_stiffness);
float damping = rp.getFloat(R.dimen.staggered_damping_ratio);
float endTransY = 0;
float springVelocity = Math.abs(mVelocity) * Math.signum(endTransY - mSpringTransY);
ValueAnimator springTransY = new SpringAnimationBuilder(v.getContext())
.setStiffness(stiffness)
.setDampingRatio(damping)
.setMinimumVisibleChange(1f)
.setStartValue(mSpringTransY)
.setEndValue(0)
.setStartVelocity(mVelocity)
.setEndValue(endTransY)
.setStartVelocity(springVelocity)
.build(v, VIEW_TRANSLATE_Y);
springTransY.setStartDelay(startDelay);
springTransY.addListener(new AnimatorListenerAdapter() {
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.util;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
@@ -197,6 +198,15 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
return mTempRectF;
}
/**
* Returns the current task bounds in the Launcher coordinate space.
*/
public RectF getCurrentRect() {
RectF result = getCurrentCropRect();
mMatrix.mapRect(result);
return result;
}
public RecentsOrientedState getOrientationState() {
return mOrientationState;
}
@@ -295,6 +305,10 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
builder.withMatrix(mMatrix)
.withWindowCrop(mTmpCropRect)
.withCornerRadius(getCurrentCornerRadius());
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
builder.withRelativeLayerTo(params.getRecentsSurface(), Integer.MAX_VALUE);
}
}
/**
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
import android.util.FloatProperty;
import android.view.SurfaceControl;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -58,6 +59,7 @@ public class TransformParams {
private float mCornerRadius;
private RemoteAnimationTargets mTargetSet;
private SurfaceTransactionApplier mSyncTransactionApplier;
private SurfaceControl mRecentsSurface;
private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
@@ -138,6 +140,8 @@ public class TransformParams {
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet;
SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length];
mRecentsSurface = getRecentsSurface(targets);
for (int i = 0; i < targets.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
@@ -165,6 +169,20 @@ public class TransformParams {
return surfaceParams;
}
private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
for (int i = 0; i < targets.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
if (app.mode == targets.targetMode) {
if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) {
return app.leash.getSurfaceControl();
}
} else {
return app.leash.getSurfaceControl();
}
}
return null;
}
// Pubic getters so outside packages can read the values.
public float getProgress() {
@@ -179,6 +197,10 @@ public class TransformParams {
return mCornerRadius;
}
public SurfaceControl getRecentsSurface() {
return mRecentsSurface;
}
public RemoteAnimationTargets getTargetSet() {
return mTargetSet;
}
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -42,8 +43,10 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.Themes;
@@ -139,7 +142,12 @@ public class AllAppsEduView extends AbstractFloatingView {
config.userControlled = false;
AnimatorPlaybackController stateAnimationController =
mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, config);
float maxAllAppsProgress = 0.15f;
float maxAllAppsProgress = mLauncher.getDeviceProfile().isLandscape ? 0.35f : 0.15f;
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
PendingAnimation allAppsAlpha = new PendingAnimation(config.duration);
allAppsController.setAlphas(ALL_APPS, config, allAppsAlpha);
mAnimation.play(allAppsAlpha.buildAnim());
ValueAnimator intro = ValueAnimator.ofFloat(0, 1f);
intro.setInterpolator(LINEAR);
@@ -191,7 +199,8 @@ public class AllAppsEduView extends AbstractFloatingView {
@Override
public void onAnimationEnd(Animator animation) {
mAnimation = null;
stateAnimationController.dispatchOnCancel();
// Handles cancelling the animation used to hint towards All Apps.
mLauncher.getStateManager().goToState(NORMAL, false);
handleClose(false);
}
});
@@ -41,8 +41,6 @@ import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener;
@@ -237,9 +235,6 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
super.reset();
setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
// We are moving to home or some other UI with no recents. Switch back to the home client,
// the home predictions should have been updated when the activity was resumed.
PredictionUiStateManager.INSTANCE.get(getContext()).switchClient(Client.HOME);
}
@Override
@@ -56,6 +56,7 @@ import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -135,6 +136,7 @@ import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.LauncherEventUtil;
@@ -147,7 +149,7 @@ import java.util.function.Consumer;
/**
* A list of recent tasks.
*/
@TargetApi(Build.VERSION_CODES.P)
@TargetApi(Build.VERSION_CODES.R)
public abstract class RecentsView<T extends StatefulActivity> extends PagedView implements
Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
@@ -1041,13 +1043,13 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
/**
* Called when a gesture from an app is starting.
*/
public void onGestureAnimationStart(int runningTaskId) {
public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
updateOrientationHandler();
}
showCurrentTask(runningTaskId);
showCurrentTask(runningTaskInfo);
setEnableFreeScroll(false);
setEnableDrawingLiveTile(false);
setRunningTaskHidden(true);
@@ -1127,8 +1129,8 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
/**
* Returns true if we should add a dummy taskView for the running task id
*/
protected boolean shouldAddDummyTaskView(int runningTaskId) {
return getTaskView(runningTaskId) == null;
protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
return runningTaskInfo != null && getTaskView(runningTaskInfo.taskId) == null;
}
/**
@@ -1137,8 +1139,8 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
* is called. Also scrolls the view to this task.
*/
public void showCurrentTask(int runningTaskId) {
if (shouldAddDummyTaskView(runningTaskId)) {
public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
if (shouldAddDummyTaskView(runningTaskInfo)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = mTaskViewPool.getView();
@@ -1148,10 +1150,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
}
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(),
new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
false, true, false, false, new ActivityManager.TaskDescription(), 0,
new ComponentName("", ""), false);
mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false);
taskView.bind(mTmpRunningTask, mOrientationState);
// Measure and layout immediately so that the scroll values is updated instantly
@@ -1162,7 +1161,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
setCurrentTask(runningTaskId);
setCurrentTask(runningTaskInfo == null ? -1 : runningTaskInfo.taskId);
setCurrentPage(getRunningTaskIndex());
setRunningTaskViewShowScreenshot(false);
setRunningTaskHidden(runningTaskTileHidden);
@@ -1680,7 +1679,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
: View.LAYOUT_DIRECTION_RTL);
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
mActivity.getDragLayer().recreateControllers();
boolean isInLandscape = mOrientationState.getTouchRotation() != 0
boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
|| mOrientationState.getRecentsActivityRotation() != ROTATION_0;
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
!mOrientationState.canRecentsActivityRotate() && isInLandscape);
@@ -17,6 +17,7 @@ package com.android.quickstep;
import static com.android.launcher3.util.LauncherUIHelper.doLayout;
import android.app.ActivityManager.RunningTaskInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -50,7 +51,7 @@ public class RecentsActivityTest {
}
@Test
public void testRecets_showCurrentTask() {
public void testRecents_showCurrentTask() {
ActivityController<RecentsActivity> controller =
Robolectric.buildActivity(RecentsActivity.class);
@@ -58,7 +59,10 @@ public class RecentsActivityTest {
doLayout(activity);
FallbackRecentsView frv = activity.getOverviewPanel();
frv.showCurrentTask(22);
RunningTaskInfo dummyTask = new RunningTaskInfo();
dummyTask.taskId = 22;
frv.showCurrentTask(dummyTask);
doLayout(activity);
ThumbnailData thumbnailData = new ThumbnailData();
@@ -157,6 +157,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
protected void onDeferredResumed() {
super.onDeferredResumed();
handlePendingActivityRequest();
}
@Override
protected void handlePendingActivityRequest() {
super.handlePendingActivityRequest();
if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
// Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
@@ -16,8 +16,6 @@
package com.android.launcher3;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
@@ -62,7 +60,6 @@ import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.util.Pair;
import android.util.TypedValue;
import android.view.View;
import androidx.annotation.NonNull;
@@ -879,10 +876,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
}
});
} else {
float velocityDpPerS = DynamicResource.provider(mLauncher)
float velocityPxPerS = DynamicResource.provider(mLauncher)
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
float velocityPxPerS = TypedValue.applyDimension(COMPLEX_UNIT_DIP,
velocityDpPerS, mLauncher.getResources().getDisplayMetrics());
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocityPxPerS, false)
.getAnimators());
}
@@ -153,13 +153,6 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
public abstract void onExitOverview(RecentsAnimationDeviceState deviceState,
Runnable exitRunnable);
/**
* Updates the prediction state to the overview state.
*/
public void updateOverviewPredictionState() {
// By public overview predictions are not supported
}
/**
* Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
*/
@@ -92,15 +92,6 @@ public class RecentsModel extends TaskStackChangeListener {
return mTaskList.getTasks(false /* loadKeysOnly */, callback);
}
/**
* @return The task id of the running task, or -1 if there is no current running task.
*/
public static int getRunningTaskId() {
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
return runningTask != null ? runningTask.id : -1;
}
/**
* @return Whether the provided {@param changeId} is the latest recent tasks list id.
*/
@@ -140,7 +131,9 @@ public class RecentsModel extends TaskStackChangeListener {
}
// Keep the cache up to date with the latest thumbnails
int runningTaskId = RecentsModel.getRunningTaskId();
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
int runningTaskId = runningTask != null ? runningTask.id : -1;
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
for (Task task : tasks) {
if (task.key.id == runningTaskId) {
@@ -49,7 +49,7 @@ public class LayoutUtils {
Rect taskSize = new Rect();
LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize,
orientationHandler);
return (dp.heightPx - taskSize.height()) / 2;
return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
}
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
+2 -1
View File
@@ -17,7 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:focusable="false">
<com.android.launcher3.CellLayout
android:id="@+id/workspace"
+1 -1
View File
@@ -142,7 +142,7 @@
<item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
<item name="staggered_stiffness" type="dimen" format="float">150</item>
<dimen name="unlock_staggered_velocity_dp_per_s">3dp</dimen>
<dimen name="unlock_staggered_velocity_dp_per_s">4dp</dimen>
<item name="hint_scale_damping_ratio" type="dimen" format="float">0.7</item>
<item name="hint_scale_stiffness" type="dimen" format="float">200</item>
@@ -183,6 +183,10 @@ public abstract class BaseRecyclerView extends RecyclerView {
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onScrollStateChanged: " + state);
}
if (state == SCROLL_STATE_IDLE) {
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
}
@@ -192,6 +196,10 @@ public abstract class BaseRecyclerView extends RecyclerView {
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (isLayoutSuppressed()) info.setScrollable(false);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS,
"onInitializeAccessibilityNodeInfo, scrollable: " + info.isScrollable());
}
}
@Override
@@ -199,8 +207,12 @@ public abstract class BaseRecyclerView extends RecyclerView {
final boolean changing = frozen != isLayoutSuppressed();
super.setLayoutFrozen(frozen);
if (changing) {
ActivityContext.lookupContext(getContext()).getDragLayer()
.sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "setLayoutFrozen " + frozen
+ " @ " + Log.getStackTraceString(new Throwable()));
ActivityContext.lookupContext(getContext()).getDragLayer()
.sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
}
}
}
}
+3 -1
View File
@@ -923,6 +923,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
DiscoveryBounce.showForHomeIfNeeded(this);
}
protected void handlePendingActivityRequest() { }
private void logStopAndResume(int command) {
int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
@@ -1423,7 +1424,8 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
if (!isInState(NORMAL)) {
// Only change state, if not already the same. This prevents cancelling any
// animations running as part of resume
mStateManager.goToState(NORMAL);
mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange(),
this::handlePendingActivityRequest);
}
// Reset the apps view
+4 -4
View File
@@ -58,7 +58,7 @@ import com.android.launcher3.pm.InstallSessionTracker;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageUserKey;
@@ -410,7 +410,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>();
final IntSet removedIds = new IntSet();
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo
@@ -418,13 +418,13 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
&& user.equals(info.user)
&& info.getIntent() != null
&& TextUtils.equals(packageName, info.getIntent().getPackage())) {
removedIds.put(info.id, true /* remove */);
removedIds.add(info.id);
}
}
}
if (!removedIds.isEmpty()) {
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds));
}
}
});
+2 -1
View File
@@ -250,7 +250,8 @@ public abstract class LauncherState implements BaseState<LauncherState> {
}
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
if (this != NORMAL || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
if ((this != NORMAL && this != HINT_STATE)
|| !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
return DEFAULT_ALPHA_PROVIDER;
}
final int centerPage = launcher.getWorkspace().getNextPage();
@@ -83,7 +83,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
protected final BaseDraggingActivity mLauncher;
protected final AdapterHolder[] mAH;
private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
private final ItemInfoMatcher mWorkMatcher = mPersonalMatcher.negate();
private final AllAppsStore mAllAppsStore = new AllAppsStore();
private final Paint mNavBarScrimPaint;
@@ -75,6 +75,9 @@ public class AccessibilityManagerCompat {
}
public static void sendScrollFinishedEventToTest(Context context) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "sendScrollFinishedEventToTest");
}
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return;
@@ -94,6 +97,9 @@ public class AccessibilityManagerCompat {
AccessibilityEvent.TYPE_ANNOUNCEMENT);
e.setClassName(eventTag);
e.setParcelableData(data);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "sendEventToTest " + e);
}
accessibilityManager.sendAccessibilityEvent(e);
}
+2 -2
View File
@@ -241,9 +241,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
mFolderName.setSelectAllOnFocus(true);
mFolderName.setInputType(mFolderName.getInputType()
& ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
& ~InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
| InputType.TYPE_TEXT_FLAG_CAP_WORDS);
mFolderName.forceDisableSuggestions(!FeatureFlags.FOLDER_NAME_SUGGEST.get());
mFolderName.forceDisableSuggestions(true);
mFooter = findViewById(R.id.folder_footer);
@@ -56,6 +56,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
@@ -348,6 +349,19 @@ public class BgDataModel {
}
}
/**
* Calls the provided {@code op} for all workspaceItems in the in-memory model (both persisted
* items and dynamic/predicted items for the provided {@code userHandle}.
* Note the call is not synchronized over the model, that should be handled by the called.
*/
public void forAllWorkspaceItemInfos(UserHandle userHandle, Consumer<WorkspaceItemInfo> op) {
for (ItemInfo info : itemsIdMap) {
if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
op.accept((WorkspaceItemInfo) info);
}
}
}
public interface Callbacks {
// If the launcher has permission to access deep shortcuts.
int FLAG_HAS_SHORTCUT_PERMISSION = 1 << 0;
@@ -21,7 +21,6 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import java.util.ArrayList;
@@ -48,23 +47,18 @@ public class CacheDataUpdatedTask extends BaseModelUpdateTask {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IconCache iconCache = app.getIconCache();
ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
WorkspaceItemInfo si = (WorkspaceItemInfo) info;
ComponentName cn = si.getTargetComponent();
if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
&& isValidShortcut(si) && cn != null
&& mPackages.contains(cn.getPackageName())) {
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
updatedShortcuts.add(si);
}
dataModel.forAllWorkspaceItemInfos(mUser, si -> {
ComponentName cn = si.getTargetComponent();
if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
&& isValidShortcut(si) && cn != null
&& mPackages.contains(cn.getPackageName())) {
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
updatedShortcuts.add(si);
}
}
});
apps.updateIconsAndLabels(mPackages, mUser);
}
bindUpdatedWorkspaceItems(updatedShortcuts);
@@ -20,8 +20,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.PromiseAppInfo;
@@ -70,21 +68,18 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
synchronized (dataModel) {
final HashSet<ItemInfo> updates = new HashSet<>();
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo) {
WorkspaceItemInfo si = (WorkspaceItemInfo) info;
ComponentName cn = si.getTargetComponent();
if (si.hasPromiseIconUi() && (cn != null)
&& mInstallInfo.packageName.equals(cn.getPackageName())) {
si.setInstallProgress(mInstallInfo.progress);
if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
// Mark this info as broken.
si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
}
updates.add(si);
dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
ComponentName cn = si.getTargetComponent();
if (si.hasPromiseIconUi() && (cn != null)
&& mInstallInfo.packageName.equals(cn.getPackageName())) {
si.setInstallProgress(mInstallInfo.progress);
if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
// Mark this info as broken.
si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
}
updates.add(si);
}
}
});
for (LauncherAppWidgetInfo widget : dataModel.appWidgets) {
if (widget.providerName.getPackageName().equals(mInstallInfo.packageName)) {
@@ -94,12 +89,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
}
if (!updates.isEmpty()) {
scheduleCallbackTask(new CallbackTask() {
@Override
public void execute(Callbacks callbacks) {
callbacks.bindRestoreItemsChange(updates);
}
});
scheduleCallbackTask(callbacks -> callbacks.bindRestoreItemsChange(updates));
}
}
}
@@ -45,7 +45,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
@@ -92,9 +92,11 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
final String[] packages = mPackages;
final int N = packages.length;
FlagOp flagOp = FlagOp.NO_OP;
final FlagOp flagOp;
final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
final ItemInfoMatcher matcher = mOp == OP_USER_AVAILABILITY_CHANGE
? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user
: ItemInfoMatcher.ofPackages(packageSet, mUser);
final HashSet<ComponentName> removedComponents = new HashSet<>();
switch (mOp) {
@@ -158,19 +160,22 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
flagOp = ums.isUserQuiet(mUser)
? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
: FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
// We want to update all packages for this user.
matcher = ItemInfoMatcher.ofUser(mUser);
appsList.updateDisabledFlags(matcher, flagOp);
// We are not synchronizing here, as int operations are atomic
appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled());
break;
}
default:
flagOp = FlagOp.NO_OP;
break;
}
bindApplicationsIfNeeded();
final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
final IntSet removedShortcuts = new IntSet();
// Shortcuts to keep even if the corresponding app was removed
final IntSet forceKeepShortcuts = new IntSet();
// Update shortcut infos
if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
@@ -180,118 +185,118 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
// For system apps, package manager send OP_UPDATE when an app is enabled.
final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
WorkspaceItemInfo si = (WorkspaceItemInfo) info;
boolean infoUpdated = false;
boolean shortcutUpdated = false;
dataModel.forAllWorkspaceItemInfos(mUser, si -> {
// Update shortcuts which use iconResource.
if ((si.iconResource != null)
&& packageSet.contains(si.iconResource.packageName)) {
LauncherIcons li = LauncherIcons.obtain(context);
BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
li.recycle();
if (iconInfo != null) {
si.bitmap = iconInfo;
infoUpdated = true;
boolean infoUpdated = false;
boolean shortcutUpdated = false;
// Update shortcuts which use iconResource.
if ((si.iconResource != null)
&& packageSet.contains(si.iconResource.packageName)) {
LauncherIcons li = LauncherIcons.obtain(context);
BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
li.recycle();
if (iconInfo != null) {
si.bitmap = iconInfo;
infoUpdated = true;
}
}
ComponentName cn = si.getTargetComponent();
if (cn != null && matcher.matches(si, cn)) {
String packageName = cn.getPackageName();
if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
forceKeepShortcuts.add(si.id);
if (mOp == OP_REMOVE) {
return;
}
}
ComponentName cn = si.getTargetComponent();
if (cn != null && matcher.matches(si, cn)) {
String packageName = cn.getPackageName();
if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
removedShortcuts.put(si.id, false);
if (mOp == OP_REMOVE) {
continue;
}
}
if (si.isPromise() && isNewApkAvailable) {
boolean isTargetValid = true;
if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
List<ShortcutInfo> shortcut =
new ShortcutRequest(context, mUser)
.forPackage(cn.getPackageName(),
si.getDeepShortcutId())
.query(ShortcutRequest.PINNED);
if (shortcut.isEmpty()) {
isTargetValid = false;
} else {
si.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true;
}
} else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
isTargetValid = context.getSystemService(LauncherApps.class)
.isActivityEnabled(cn, mUser);
}
if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
} else if (si.hasPromiseIconUi()) {
removedShortcuts.put(si.id, true);
continue;
}
} else if (!isTargetValid) {
removedShortcuts.put(si.id, true);
FileLog.e(TAG, "Restored shortcut no longer valid "
+ si.getIntent());
continue;
if (si.isPromise() && isNewApkAvailable) {
boolean isTargetValid = true;
if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
List<ShortcutInfo> shortcut =
new ShortcutRequest(context, mUser)
.forPackage(cn.getPackageName(),
si.getDeepShortcutId())
.query(ShortcutRequest.PINNED);
if (shortcut.isEmpty()) {
isTargetValid = false;
} else {
si.status = WorkspaceItemInfo.DEFAULT;
si.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true;
}
} else if (isNewApkAvailable && removedComponents.contains(cn)) {
} else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
isTargetValid = context.getSystemService(LauncherApps.class)
.isActivityEnabled(cn, mUser);
}
if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
} else if (si.hasPromiseIconUi()) {
removedShortcuts.add(si.id);
return;
}
}
if (isNewApkAvailable &&
si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
} else if (!isTargetValid) {
removedShortcuts.add(si.id);
FileLog.e(TAG, "Restored shortcut no longer valid "
+ si.getIntent());
return;
} else {
si.status = WorkspaceItemInfo.DEFAULT;
infoUpdated = true;
}
int oldRuntimeFlags = si.runtimeStatusFlags;
si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
if (si.runtimeStatusFlags != oldRuntimeFlags) {
shortcutUpdated = true;
} else if (isNewApkAvailable && removedComponents.contains(cn)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
}
}
if (infoUpdated || shortcutUpdated) {
updatedWorkspaceItems.add(si);
if (isNewApkAvailable
&& si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
infoUpdated = true;
}
if (infoUpdated) {
getModelWriter().updateItemInDatabase(si);
}
} else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
if (mUser.equals(widgetInfo.user)
&& widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
&& packageSet.contains(widgetInfo.providerName.getPackageName())) {
widgetInfo.restoreStatus &=
~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
// adding this flag ensures that launcher shows 'click to setup'
// if the widget has a config activity. In case there is no config
// activity, it will be marked as 'restored' during bind.
widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
widgets.add(widgetInfo);
getModelWriter().updateItemInDatabase(widgetInfo);
int oldRuntimeFlags = si.runtimeStatusFlags;
si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
if (si.runtimeStatusFlags != oldRuntimeFlags) {
shortcutUpdated = true;
}
}
if (infoUpdated || shortcutUpdated) {
updatedWorkspaceItems.add(si);
}
if (infoUpdated && si.id != ItemInfo.NO_ID) {
getModelWriter().updateItemInDatabase(si);
}
});
for (LauncherAppWidgetInfo widgetInfo : dataModel.appWidgets) {
if (mUser.equals(widgetInfo.user)
&& widgetInfo.hasRestoreFlag(
LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
&& packageSet.contains(widgetInfo.providerName.getPackageName())) {
widgetInfo.restoreStatus &=
~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY
& ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
// adding this flag ensures that launcher shows 'click to setup'
// if the widget has a config activity. In case there is no config
// activity, it will be marked as 'restored' during bind.
widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
widgets.add(widgetInfo);
getModelWriter().updateItemInDatabase(widgetInfo);
}
}
}
bindUpdatedWorkspaceItems(updatedWorkspaceItems);
if (!removedShortcuts.isEmpty()) {
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts));
}
if (!widgets.isEmpty()) {
@@ -319,7 +324,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
.or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
.and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));
.and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate());
deleteAndBindComponentsRemoved(removeMatch);
// Remove any queued items from the install queue
@@ -21,7 +21,6 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -58,14 +57,14 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask {
MultiHashMap<ShortcutKey, WorkspaceItemInfo> keyToShortcutInfo = new MultiHashMap<>();
HashSet<String> allIds = new HashSet<>();
for (ItemInfo itemInfo : dataModel.itemsIdMap) {
if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
if (mPackageName.equals(si.getIntent().getPackage()) && si.user.equals(mUser)) {
synchronized (dataModel) {
dataModel.forAllWorkspaceItemInfos(mUser, si -> {
if ((si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
&& mPackageName.equals(si.getIntent().getPackage())) {
keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
allIds.add(si.getDeepShortcutId());
}
}
});
}
final ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
@@ -23,7 +23,6 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -73,27 +72,27 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask {
ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
HashSet<ShortcutKey> removedKeys = new HashSet<>();
for (ItemInfo itemInfo : dataModel.itemsIdMap) {
if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& mUser.equals(itemInfo.user)) {
WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
if (mIsUserUnlocked) {
ShortcutKey key = ShortcutKey.fromItemInfo(si);
ShortcutInfo shortcut = pinnedShortcuts.get(key);
// We couldn't verify the shortcut during loader. If its no longer available
// (probably due to clear data), delete the workspace item as well
if (shortcut == null) {
removedKeys.add(key);
continue;
synchronized (dataModel) {
dataModel.forAllWorkspaceItemInfos(mUser, si -> {
if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
if (mIsUserUnlocked) {
ShortcutKey key = ShortcutKey.fromItemInfo(si);
ShortcutInfo shortcut = pinnedShortcuts.get(key);
// We couldn't verify the shortcut during loader. If its no longer available
// (probably due to clear data), delete the workspace item as well
if (shortcut == null) {
removedKeys.add(key);
return;
}
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
app.getIconCache().getShortcutIcon(si, shortcut);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
app.getIconCache().getShortcutIcon(si, shortcut);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
updatedWorkspaceItemInfos.add(si);
}
updatedWorkspaceItemInfos.add(si);
}
});
}
bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
if (!removedKeys.isEmpty()) {
@@ -33,6 +33,7 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.widget.WidgetsFullSheet;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
@@ -92,6 +93,11 @@ public class TestInformationHandler implements ResourceBasedOverride {
l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
}
case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
return getLauncherUIProperty(Bundle::putInt,
l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
}
case TestProtocol.REQUEST_WINDOW_INSETS: {
return getUIProperty(Bundle::putParcelable, a -> {
WindowInsets insets = a.getWindow()
@@ -81,6 +81,7 @@ public final class TestProtocol {
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
public static final String REQUEST_WINDOW_INSETS = "window-insets";
public static final String REQUEST_PID = "pid";
public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
@@ -106,4 +107,5 @@ public final class TestProtocol {
public static final String PAUSE_NOT_DETECTED = "b/139891609";
public static final String OVERIEW_NOT_ALLAPPS = "b/156095088";
public static final String NO_SWIPE_TO_HOME = "b/158017601";
public static final String NO_SCROLL_END_WIDGETS = "b/160238801";
}
@@ -17,6 +17,7 @@
package com.android.launcher3.touch;
import static android.widget.ListPopupWindow.WRAP_CONTENT;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
@@ -33,6 +34,7 @@ import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller;
@@ -260,4 +262,10 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
}
return new ChildBounds(childHeight, childWidth, childBottom, childLeft);
}
@SuppressWarnings("SuspiciousNameCombination")
@Override
public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
return rect.left;
}
}
@@ -96,6 +96,7 @@ public interface PagedOrientationHandler {
int getTaskMenuWidth(View view);
int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout);
void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp);
int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
/**
* Maps the velocity from the coordinate plane of the foreground app to that
@@ -32,6 +32,7 @@ import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller;
@@ -257,4 +258,9 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
}
return new ChildBounds(childWidth, childHeight, childRight, childTop);
}
@Override
public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
return dp.heightPx - rect.bottom;
}
}
@@ -18,9 +18,11 @@ package com.android.launcher3.touch;
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Surface;
import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
@@ -77,4 +79,9 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
view.setTranslationX(0);
view.setTranslationY(translation);
}
@Override
public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
return dp.widthPx - rect.right;
}
}
@@ -81,11 +81,10 @@ public interface ItemInfoMatcher {
}
/**
* Returns a new matcher which returns the opposite boolean value of the provided
* {@param matcher}.
* Returns a new matcher with returns the opposite value of this.
*/
static ItemInfoMatcher not(ItemInfoMatcher matcher) {
return (info, cn) -> !matcher.matches(info, cn);
default ItemInfoMatcher negate() {
return (info, cn) -> !matches(info, cn);
}
static ItemInfoMatcher ofUser(UserHandle user) {
@@ -105,7 +104,10 @@ public interface ItemInfoMatcher {
keys.contains(ShortcutKey.fromItemInfo(info));
}
static ItemInfoMatcher ofItemIds(IntSparseArrayMap<Boolean> ids, Boolean matchDefault) {
return (info, cn) -> ids.get(info.id, matchDefault);
/**
* Returns a matcher for items with provided ids
*/
static ItemInfoMatcher ofItemIds(IntSet ids) {
return (info, cn) -> ids.contains(info.id);
}
}
@@ -64,7 +64,7 @@ public class OnboardingPrefs<T extends Launcher> {
private static final Map<String, Integer> MAX_COUNTS;
static {
Map<String, Integer> maxCounts = new ArrayMap<>(3);
Map<String, Integer> maxCounts = new ArrayMap<>(4);
maxCounts.put(HOME_BOUNCE_COUNT, 3);
maxCounts.put(SHELF_BOUNCE_COUNT, 3);
maxCounts.put(ALL_APPS_COUNT, 5);
@@ -292,6 +292,9 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "BaseDragLayer: " + ev);
}
switch (ev.getAction()) {
case ACTION_DOWN: {
if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) {
@@ -20,8 +20,10 @@ import android.util.Log;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
import java.util.ArrayList;
@@ -32,8 +34,8 @@ import java.util.Iterator;
* methods accordingly.
*/
public class WidgetsDiffReporter {
private static final boolean DEBUG = false;
private static final String TAG = "WidgetsDiffReporter";
private static final boolean DEBUG = Utilities.IS_RUNNING_IN_TEST_HARNESS; // b/160238801
private static final String TAG = TestProtocol.NO_SCROLL_END_WIDGETS;
private final IconCache mIconCache;
private final RecyclerView.Adapter mListener;
@@ -24,6 +24,7 @@ import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -38,8 +39,10 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.TopRoundedCornerView;
@@ -68,6 +71,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsFullSheet: " + ev);
}
return super.dispatchTouchEvent(ev);
}
public WidgetsFullSheet(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -120,6 +120,9 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
public int getCurrentScrollY() {
// Skip early if widgets are not bound.
if (isModelNotReady() || getChildCount() == 0) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "getCurrentScrollY: -1");
}
return -1;
}
@@ -128,6 +131,10 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
int y = (child.getMeasuredHeight() * rowIndex);
int offset = getLayoutManager().getDecoratedTop(child);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS,
"getCurrentScrollY: " + (getPaddingTop() + y - offset));
}
return getPaddingTop() + y - offset;
}
@@ -158,13 +165,23 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
}
if (mTouchDownOnScroller) {
return mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
final boolean result = mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 1 " + result);
}
return result;
}
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 2 false");
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView.onTouchEvent");
}
if (mTouchDownOnScroller) {
mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
}
@@ -172,5 +189,31 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onRequestDisallowInterceptTouchEvent "
+ disallowIntercept);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final boolean result = super.dispatchTouchEvent(ev);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView: state: "
+ getScrollState()
+ " can scroll: " + getLayoutManager().canScrollVertically()
+ " result: " + result
+ " layout suppressed: " + isLayoutSuppressed()
+ " event: " + ev);
}
return result;
}
@Override
public void stopNestedScroll() {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "stopNestedScroll");
}
super.stopNestedScroll();
}
}
@@ -73,20 +73,12 @@ public class ActivityLeakTracker implements Application.ActivityLifecycleCallbac
}
public boolean noLeakedActivities() {
int liveActivities = 0;
int destroyedActivities = 0;
for (Activity activity : mActivities.keySet()) {
if (activity.isDestroyed()) {
++destroyedActivities;
} else {
++liveActivities;
return false;
}
}
if (liveActivities > 2) return false;
// It's OK to have 1 leaked activity if no active activities exist.
return liveActivities == 0 ? destroyedActivities <= 1 : destroyedActivities == 0;
return mActivities.size() <= 2;
}
}
@@ -635,9 +635,11 @@ public final class LauncherInstrumentation {
Parcelable executeAndWaitForEvent(Runnable command,
UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
try {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "executeAndWaitForEvent: before");
final AccessibilityEvent event =
mInstrumentation.getUiAutomation().executeAndWaitForEvent(
command, eventFilter, WAIT_TIME_MS);
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "executeAndWaitForEvent: after");
assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event);
final Parcelable parcelableData = event.getParcelableData();
event.recycle();
@@ -1094,7 +1096,10 @@ public final class LauncherInstrumentation {
executeAndWaitForEvent(
() -> linearGesture(
startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE),
event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
event -> {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "scroll: received event: " + event);
return TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName());
},
() -> "Didn't receive a scroll end message: " + startX + ", " + startY
+ ", " + endX + ", " + endY);
}
@@ -57,6 +57,8 @@ public class LogEventChecker {
while (true) {
rawEvents = mLauncher.getTestInfo(TestProtocol.REQUEST_GET_TEST_EVENTS)
.getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
if (rawEvents == null) return null;
final int expectedCount = mExpectedEvents.entrySet()
.stream().mapToInt(e -> e.getValue().size()).sum();
if (rawEvents.size() >= expectedCount
@@ -83,6 +85,7 @@ public class LogEventChecker {
String verify(long waitForExpectedCountMs, boolean successfulGesture) {
final ListMap<String> actualEvents = finishSync(waitForExpectedCountMs);
if (actualEvents == null) return "null event sequences because launcher likely died";
final StringBuilder sb = new StringBuilder();
boolean hasMismatches = false;
@@ -91,8 +94,7 @@ public class LogEventChecker {
List<String> actual = new ArrayList<>(actualEvents.getNonNull(sequence));
final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
hasMismatches = hasMismatches
|| mismatchPosition != -1 && !ignoreMistatch(successfulGesture, sequence);
hasMismatches = hasMismatches || mismatchPosition != -1;
formatSequenceWithMismatch(
sb,
sequence,
@@ -103,8 +105,7 @@ public class LogEventChecker {
// Check for unexpected event sequences in the actual data.
for (String actualNamedSequence : actualEvents.keySet()) {
if (!mExpectedEvents.containsKey(actualNamedSequence)) {
hasMismatches = hasMismatches
|| !ignoreMistatch(successfulGesture, actualNamedSequence);
hasMismatches = true;
formatSequenceWithMismatch(
sb,
actualNamedSequence,
@@ -117,13 +118,6 @@ public class LogEventChecker {
return hasMismatches ? "mismatching events: " + sb.toString() : null;
}
// Workaround for b/154157191
private static boolean ignoreMistatch(boolean successfulGesture, String sequence) {
// b/156287114
return false;
// return TestProtocol.SEQUENCE_TIS.equals(sequence) && successfulGesture;
}
// If the list of actual events matches the list of expected events, returns -1, otherwise
// the position of the mismatch.
private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {
@@ -28,6 +28,7 @@ import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
import com.android.launcher3.tapl.LauncherInstrumentation.GestureScope;
import com.android.launcher3.testing.TestProtocol;
import java.util.Collection;
@@ -90,6 +91,12 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
return LauncherInstrumentation.ContainerType.WIDGETS;
}
private int getWidgetsScroll() {
return mLauncher.getTestInfo(
TestProtocol.REQUEST_WIDGETS_SCROLL_Y)
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
public Widget getWidget(String labelText) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
@@ -136,7 +143,13 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
}
mLauncher.assertTrue("Too many attempts", ++i <= 40);
final int scroll = getWidgetsScroll();
mLauncher.scrollToLastVisibleRow(widgetsContainer, cells, 0);
final int newScroll = getWidgetsScroll();
mLauncher.assertTrue(
"Scrolled in a wrong direction in Widgets: from " + scroll + " to "
+ newScroll, newScroll >= scroll);
mLauncher.assertTrue("Unable to scroll to the widget", newScroll != scroll);
}
}
}