Merging from ub-launcher3-master @ build 6925377
Test: manual, presubmit on the source branch x20/teams/android-launcher/merge/ub-launcher3-master_master_6925377.html Change-Id: I928b100c8f41abff34047df69d988622123f9939
This commit is contained in:
@@ -16,14 +16,10 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
|
||||
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
@@ -32,11 +28,8 @@ import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.quickstep.TaskViewUtils;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
/**
|
||||
@@ -53,60 +46,16 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
||||
protected boolean isLaunchingFromRecents(@NonNull View v,
|
||||
@Nullable RemoteAnimationTargetCompat[] targets) {
|
||||
return mLauncher.getStateManager().getState().overviewUi
|
||||
&& findTaskViewToLaunch(mLauncher, v, targets) != null;
|
||||
&& findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
|
||||
@NonNull RemoteAnimationTargetCompat[] appTargets,
|
||||
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
|
||||
RecentsView recentsView = mLauncher.getOverviewPanel();
|
||||
boolean skipLauncherChanges = !launcherClosing;
|
||||
|
||||
TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
|
||||
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
|
||||
createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
|
||||
mLauncher.getDepthController(), pa);
|
||||
anim.play(pa.buildAnim());
|
||||
|
||||
Animator childStateAnimation = null;
|
||||
// Found a visible recents task that matches the opening app, lets launch the app from there
|
||||
Animator launcherAnim;
|
||||
final AnimatorListenerAdapter windowAnimEndListener;
|
||||
if (launcherClosing) {
|
||||
launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
|
||||
launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
|
||||
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
|
||||
|
||||
// Make sure recents gets fixed up by resetting task alphas and scales, etc.
|
||||
windowAnimEndListener = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mLauncher.getStateManager().moveToRestState();
|
||||
mLauncher.getStateManager().reapplyState();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
AnimatorPlaybackController controller =
|
||||
mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL,
|
||||
RECENTS_LAUNCH_DURATION);
|
||||
controller.dispatchOnStart();
|
||||
childStateAnimation = controller.getTarget();
|
||||
launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
|
||||
windowAnimEndListener = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mLauncher.getStateManager().goToState(NORMAL, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
anim.play(launcherAnim);
|
||||
|
||||
// Set the current animation first, before adding windowAnimEndListener. Setting current
|
||||
// animation adds some listeners which need to be called before windowAnimEndListener
|
||||
// (the ordering of listeners matter in this case).
|
||||
mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation);
|
||||
anim.addListener(windowAnimEndListener);
|
||||
TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
|
||||
launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(),
|
||||
mLauncher.getDepthController());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -153,6 +153,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
private final float mContentTransY;
|
||||
private final float mWorkspaceTransY;
|
||||
private final float mClosingWindowTransY;
|
||||
private final float mMaxShadowRadius;
|
||||
|
||||
private DeviceProfile mDeviceProfile;
|
||||
|
||||
@@ -186,6 +187,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
|
||||
mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
|
||||
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
|
||||
mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
|
||||
|
||||
mLauncher.addOnDeviceProfileChangeListener(this);
|
||||
}
|
||||
@@ -538,6 +540,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
EXAGGERATED_EASE);
|
||||
FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
|
||||
RADIUS_DURATION, EXAGGERATED_EASE);
|
||||
FloatProp mShadowRadius = new FloatProp(0, mMaxShadowRadius, 0,
|
||||
APP_LAUNCH_DURATION, EXAGGERATED_EASE);
|
||||
|
||||
@Override
|
||||
public void onUpdate(float percent) {
|
||||
@@ -600,7 +604,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
builder.withMatrix(matrix)
|
||||
.withWindowCrop(crop)
|
||||
.withAlpha(1f - mIconAlpha.value)
|
||||
.withCornerRadius(mWindowRadius.value);
|
||||
.withCornerRadius(mWindowRadius.value)
|
||||
.withShadowRadius(mShadowRadius.value);
|
||||
} else {
|
||||
tmpPos.set(target.position.x, target.position.y);
|
||||
if (target.localBounds != null) {
|
||||
@@ -752,6 +757,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
|
||||
FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
|
||||
FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
|
||||
FloatProp mShadowRadius = new FloatProp(mMaxShadowRadius, 0, 0, duration,
|
||||
DEACCEL_1_7);
|
||||
|
||||
@Override
|
||||
public void onUpdate(float percent) {
|
||||
@@ -773,7 +780,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
matrix.postTranslate(tmpPos.x, tmpPos.y);
|
||||
builder.withMatrix(matrix)
|
||||
.withAlpha(mAlpha.value)
|
||||
.withCornerRadius(windowCornerRadius);
|
||||
.withCornerRadius(windowCornerRadius)
|
||||
.withShadowRadius(mShadowRadius.value);
|
||||
} else {
|
||||
matrix.setTranslate(tmpPos.x, tmpPos.y);
|
||||
builder.withMatrix(matrix)
|
||||
@@ -798,33 +806,31 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
}
|
||||
|
||||
private void addCujInstrumentation(Animator anim, int cuj, String transition) {
|
||||
if (Trace.isEnabled()) {
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
Trace.beginAsyncSection(transition, 0);
|
||||
InteractionJankMonitorWrapper.begin(cuj);
|
||||
super.onAnimationStart(animation);
|
||||
}
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
Trace.beginAsyncSection(transition, 0);
|
||||
InteractionJankMonitorWrapper.begin(cuj);
|
||||
super.onAnimationStart(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
super.onAnimationCancel(animation);
|
||||
InteractionJankMonitorWrapper.cancel(cuj);
|
||||
}
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
super.onAnimationCancel(animation);
|
||||
InteractionJankMonitorWrapper.cancel(cuj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
InteractionJankMonitorWrapper.end(cuj);
|
||||
}
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
InteractionJankMonitorWrapper.end(cuj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -108,6 +108,8 @@ public class PredictionRowView extends LinearLayout implements
|
||||
|
||||
AllAppsSectionDecorator.SectionDecorationHandler mDecorationHandler;
|
||||
|
||||
@Nullable private List<ItemInfo> mPendingPredictedItems;
|
||||
|
||||
public PredictionRowView(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -164,6 +166,7 @@ public class PredictionRowView extends LinearLayout implements
|
||||
}
|
||||
mDecorationHandler.onDraw(canvas);
|
||||
mDecorationHandler.onFocusDraw(canvas, getFocusedChild());
|
||||
mLauncher.getAppsView().getActiveRecyclerView().invalidateItemDecorations();
|
||||
}
|
||||
mFocusHelper.draw(canvas);
|
||||
super.dispatchDraw(canvas);
|
||||
@@ -203,6 +206,16 @@ public class PredictionRowView extends LinearLayout implements
|
||||
* we can optimize by swapping them in place.
|
||||
*/
|
||||
public void setPredictedApps(List<ItemInfo> items) {
|
||||
if (!mLauncher.isWorkspaceLoading() && isShown() && getWindowVisibility() == View.VISIBLE) {
|
||||
mPendingPredictedItems = items;
|
||||
return;
|
||||
}
|
||||
|
||||
applyPredictedApps(items);
|
||||
}
|
||||
|
||||
private void applyPredictedApps(List<ItemInfo> items) {
|
||||
mPendingPredictedItems = null;
|
||||
mPredictedApps.clear();
|
||||
items.stream()
|
||||
.filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
|
||||
@@ -341,4 +354,13 @@ public class PredictionRowView extends LinearLayout implements
|
||||
public View getFocusedChild() {
|
||||
return getChildAt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisibilityAggregated(boolean isVisible) {
|
||||
super.onVisibilityAggregated(isVisible);
|
||||
|
||||
if (mPendingPredictedItems != null && !isVisible) {
|
||||
applyPredictedApps(mPendingPredictedItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +251,28 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
* Sets or updates the predicted items
|
||||
*/
|
||||
public void setPredictedItems(FixedContainerItems items) {
|
||||
if (!mLauncher.isWorkspaceLoading()
|
||||
&& mHotseat.isShown()
|
||||
&& mHotseat.getWindowVisibility() == View.VISIBLE) {
|
||||
mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> {
|
||||
if (isVisible) {
|
||||
return;
|
||||
}
|
||||
mHotseat.setOnVisibilityAggregatedCallback(null);
|
||||
|
||||
applyPredictedItems(items);
|
||||
});
|
||||
} else {
|
||||
mHotseat.setOnVisibilityAggregatedCallback(null);
|
||||
|
||||
applyPredictedItems(items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or updates the predicted items only once the hotseat becomes hidden to the user
|
||||
*/
|
||||
private void applyPredictedItems(FixedContainerItems items) {
|
||||
mPredictedItems = items.items;
|
||||
if (mPredictedItems.isEmpty()) {
|
||||
HotseatRestoreHelper.restoreBackup(mLauncher);
|
||||
|
||||
@@ -250,6 +250,9 @@ public class AppEventProducer implements StatsLogConsumer {
|
||||
case PREDICTION_CONTAINER: {
|
||||
return "predictions";
|
||||
}
|
||||
case SHORTCUTS_CONTAINER: {
|
||||
return "deep-shortcuts";
|
||||
}
|
||||
case FOLDER: {
|
||||
FolderContainer fc = ci.getFolder();
|
||||
switch (fc.getParentContainerCase()) {
|
||||
|
||||
@@ -18,8 +18,7 @@ package com.android.launcher3.model;
|
||||
|
||||
import static android.content.ContentResolver.SCHEME_CONTENT;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
|
||||
import static com.android.launcher3.Utilities.newContentObserver;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.RemoteAction;
|
||||
@@ -35,7 +34,7 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
@@ -43,7 +42,7 @@ import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.MainThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
@@ -55,6 +54,7 @@ import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.popup.RemoteActionShortcut;
|
||||
import com.android.launcher3.popup.SystemShortcut;
|
||||
import com.android.launcher3.util.BgObjectWithLooper;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
@@ -68,15 +68,11 @@ import java.util.Map;
|
||||
* Data model for digital wellbeing status of apps.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.Q)
|
||||
public final class WellbeingModel {
|
||||
public final class WellbeingModel extends BgObjectWithLooper {
|
||||
private static final String TAG = "WellbeingModel";
|
||||
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final int MSG_PACKAGE_ADDED = 1;
|
||||
private static final int MSG_PACKAGE_REMOVED = 2;
|
||||
private static final int MSG_FULL_REFRESH = 3;
|
||||
|
||||
private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0;
|
||||
private static final int IN_MINIMAL_DEVICE = 2;
|
||||
|
||||
@@ -98,9 +94,9 @@ public final class WellbeingModel {
|
||||
|
||||
private final Context mContext;
|
||||
private final String mWellbeingProviderPkg;
|
||||
private final Handler mWorkerHandler;
|
||||
|
||||
private final ContentObserver mContentObserver;
|
||||
private Handler mWorkerHandler;
|
||||
private ContentObserver mContentObserver;
|
||||
|
||||
private final Object mModelLock = new Object();
|
||||
// Maps the action Id to the corresponding RemoteAction
|
||||
@@ -111,60 +107,73 @@ public final class WellbeingModel {
|
||||
|
||||
private WellbeingModel(final Context context) {
|
||||
mContext = context;
|
||||
mWorkerHandler =
|
||||
new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
|
||||
|
||||
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
|
||||
mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
if (DEBUG || mIsInTest) {
|
||||
Log.d(TAG, "ContentObserver.onChange() called with: selfChange = ["
|
||||
+ selfChange + "], uri = [" + uri + "]");
|
||||
}
|
||||
Preconditions.assertUIThread();
|
||||
|
||||
if (uri.getPath().contains(PATH_ACTIONS)) {
|
||||
// Wellbeing reports that app actions have changed.
|
||||
updateWellbeingData();
|
||||
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
|
||||
// Wellbeing reports that minimal device state or config is changed.
|
||||
updateLauncherModel(context);
|
||||
}
|
||||
}
|
||||
};
|
||||
FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () ->
|
||||
updateLauncherModel(context));
|
||||
initializeInBackground("WellbeingHandler");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialized(Looper looper) {
|
||||
mWorkerHandler = new Handler(looper);
|
||||
mContentObserver = newContentObserver(mWorkerHandler, this::onWellbeingUriChanged);
|
||||
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
|
||||
context.registerReceiver(
|
||||
new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
|
||||
mContext.registerReceiver(
|
||||
new SimpleBroadcastReceiver(t -> restartObserver()),
|
||||
PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
|
||||
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
|
||||
Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
|
||||
Intent.ACTION_PACKAGE_RESTARTED));
|
||||
Intent.ACTION_PACKAGE_RESTARTED),
|
||||
null, mWorkerHandler);
|
||||
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addDataScheme("package");
|
||||
context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
|
||||
filter);
|
||||
mContext.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
|
||||
filter, null, mWorkerHandler);
|
||||
|
||||
restartObserver();
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void onWellbeingUriChanged(Uri uri) {
|
||||
Preconditions.assertNonUiThread();
|
||||
if (DEBUG || mIsInTest) {
|
||||
Log.d(TAG, "ContentObserver.onChange() called with: uri = [" + uri + "]");
|
||||
}
|
||||
if (uri.getPath().contains(PATH_ACTIONS)) {
|
||||
// Wellbeing reports that app actions have changed.
|
||||
updateAllPackages();
|
||||
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
|
||||
// Wellbeing reports that minimal device state or config is changed.
|
||||
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Temporary bug fix for b/169771796. Wellbeing provides the layout configuration when
|
||||
// minimal device is enabled. We always want to reload the configuration from Wellbeing
|
||||
// since the layout configuration might have changed.
|
||||
mContext.deleteDatabase(DB_NAME_MINIMAL_DEVICE);
|
||||
|
||||
final Bundle extras = new Bundle();
|
||||
String dbFile;
|
||||
if (isInMinimalDeviceMode()) {
|
||||
dbFile = DB_NAME_MINIMAL_DEVICE;
|
||||
extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
|
||||
mWellbeingProviderPkg + ".api");
|
||||
} else {
|
||||
dbFile = InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
|
||||
}
|
||||
LauncherSettings.Settings.call(mContext.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
|
||||
dbFile, extras);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInTest(boolean inTest) {
|
||||
mIsInTest = inTest;
|
||||
}
|
||||
|
||||
protected void onWellbeingProviderChanged(Intent intent) {
|
||||
if (DEBUG || mIsInTest) {
|
||||
Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
|
||||
}
|
||||
restartObserver();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void restartObserver() {
|
||||
final ContentResolver resolver = mContext.getContentResolver();
|
||||
resolver.unregisterContentObserver(mContentObserver);
|
||||
@@ -179,7 +188,7 @@ public final class WellbeingModel {
|
||||
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
|
||||
if (mIsInTest) throw new RuntimeException(e);
|
||||
}
|
||||
updateWellbeingData();
|
||||
updateAllPackages();
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@@ -212,39 +221,6 @@ public final class WellbeingModel {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWellbeingData() {
|
||||
mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
|
||||
}
|
||||
|
||||
private void updateLauncherModel(@NonNull final Context context) {
|
||||
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
|
||||
reloadLauncherInNormalMode(context);
|
||||
return;
|
||||
}
|
||||
mWorkerHandler.post(() -> {
|
||||
if (isInMinimalDeviceMode()) {
|
||||
reloadLauncherInMinimalMode(context);
|
||||
} else {
|
||||
reloadLauncherInNormalMode(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void reloadLauncherInNormalMode(@NonNull final Context context) {
|
||||
LauncherSettings.Settings.call(context.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
|
||||
InvariantDeviceProfile.INSTANCE.get(context).dbFile);
|
||||
}
|
||||
|
||||
private void reloadLauncherInMinimalMode(@NonNull final Context context) {
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
|
||||
mWellbeingProviderPkg + ".api");
|
||||
LauncherSettings.Settings.call(context.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
|
||||
DB_NAME_MINIMAL_DEVICE, extras);
|
||||
}
|
||||
|
||||
private Uri.Builder apiBuilder() {
|
||||
return new Uri.Builder()
|
||||
.scheme(SCHEME_CONTENT)
|
||||
@@ -277,7 +253,8 @@ public final class WellbeingModel {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean updateActions(String... packageNames) {
|
||||
@WorkerThread
|
||||
private boolean updateActions(String[] packageNames) {
|
||||
if (packageNames.length == 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -340,68 +317,51 @@ public final class WellbeingModel {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_PACKAGE_REMOVED: {
|
||||
String packageName = (String) msg.obj;
|
||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||
synchronized (mModelLock) {
|
||||
mPackageToActionId.remove(packageName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case MSG_PACKAGE_ADDED: {
|
||||
String packageName = (String) msg.obj;
|
||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||
if (!updateActions(packageName)) {
|
||||
scheduleRefreshRetry(msg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@WorkerThread
|
||||
private void updateActionsWithRetry(int retryCount, @Nullable String packageName) {
|
||||
String[] packageNames = TextUtils.isEmpty(packageName)
|
||||
? mContext.getSystemService(LauncherApps.class)
|
||||
.getActivityList(null, Process.myUserHandle()).stream()
|
||||
.map(li -> li.getApplicationInfo().packageName).distinct()
|
||||
.toArray(String[]::new)
|
||||
: new String[] { packageName };
|
||||
|
||||
case MSG_FULL_REFRESH: {
|
||||
// Remove all existing messages
|
||||
mWorkerHandler.removeCallbacksAndMessages(null);
|
||||
final String[] packageNames = mContext.getSystemService(LauncherApps.class)
|
||||
.getActivityList(null, Process.myUserHandle()).stream()
|
||||
.map(li -> li.getApplicationInfo().packageName).distinct()
|
||||
.toArray(String[]::new);
|
||||
if (!updateActions(packageNames)) {
|
||||
scheduleRefreshRetry(msg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||
if (updateActions(packageNames)) {
|
||||
return;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void scheduleRefreshRetry(Message originalMsg) {
|
||||
int retryCount = originalMsg.arg1;
|
||||
if (retryCount >= RETRY_TIMES_MS.length) {
|
||||
// To many retries, skip
|
||||
return;
|
||||
}
|
||||
|
||||
Message msg = Message.obtain(originalMsg);
|
||||
msg.arg1 = retryCount + 1;
|
||||
mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
|
||||
mWorkerHandler.postDelayed(
|
||||
() -> updateActionsWithRetry(retryCount + 1, packageName),
|
||||
packageName, RETRY_TIMES_MS[retryCount]);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void updateAllPackages() {
|
||||
updateActionsWithRetry(0, null);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void onAppPackageChanged(Intent intent) {
|
||||
if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
|
||||
Preconditions.assertUIThread();
|
||||
Preconditions.assertNonUiThread();
|
||||
|
||||
final String packageName = intent.getData().getSchemeSpecificPart();
|
||||
if (packageName == null || packageName.length() == 0) {
|
||||
// they sent us a bad intent
|
||||
return;
|
||||
}
|
||||
|
||||
final String action = intent.getAction();
|
||||
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
|
||||
Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget();
|
||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||
synchronized (mModelLock) {
|
||||
mPackageToActionId.remove(packageName);
|
||||
}
|
||||
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
|
||||
Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
|
||||
updateActionsWithRetry(0, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,11 +41,14 @@ 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.TaskUtils.taskIsATargetWithMode;
|
||||
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
@@ -78,6 +81,8 @@ import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.tracing.InputConsumerProto;
|
||||
import com.android.launcher3.tracing.SwipeHandlerProto;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.launcher3.util.VibratorWrapper;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
@@ -90,6 +95,7 @@ import com.android.quickstep.util.AnimatorControllerWithResistance;
|
||||
import com.android.quickstep.util.InputConsumerProxy;
|
||||
import com.android.quickstep.util.MotionPauseDetector;
|
||||
import com.android.quickstep.util.RecentsOrientedState;
|
||||
import com.android.quickstep.util.ProtoTracer;
|
||||
import com.android.quickstep.util.RectFSpringAnim;
|
||||
import com.android.quickstep.util.SurfaceTransactionApplier;
|
||||
import com.android.quickstep.util.SwipePipToHomeAnimator;
|
||||
@@ -107,6 +113,7 @@ import com.android.systemui.shared.system.TaskInfoCompat;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -614,13 +621,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||
|
||||
updateSysUiFlags(mCurrentShift.value);
|
||||
applyWindowTransform();
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
if (mRecentsAnimationTargets != null) {
|
||||
LiveTileOverlay.INSTANCE.update(
|
||||
mTaskViewSimulator.getCurrentRect(),
|
||||
mTaskViewSimulator.getCurrentCornerRadius());
|
||||
}
|
||||
}
|
||||
|
||||
updateLauncherTransitionProgress();
|
||||
}
|
||||
@@ -719,6 +719,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||
|
||||
@UiThread
|
||||
public void onGestureStarted(boolean isLikelyToStartNewTask) {
|
||||
InteractionJankMonitorWrapper.begin(
|
||||
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
|
||||
notifyGestureStartedAsync();
|
||||
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
|
||||
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
|
||||
@@ -794,7 +796,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||
}
|
||||
|
||||
private void onSettledOnEndTarget() {
|
||||
switch (mGestureState.getEndTarget()) {
|
||||
final GestureEndTarget endTarget = mGestureState.getEndTarget();
|
||||
switch (endTarget) {
|
||||
case HOME:
|
||||
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
|
||||
// Notify swipe-to-home (recents animation) is finished
|
||||
@@ -811,7 +814,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||
mStateCallback.setState(STATE_RESUME_LAST_TASK);
|
||||
break;
|
||||
}
|
||||
ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget());
|
||||
ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget);
|
||||
if (endTarget != NEW_TASK) {
|
||||
InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
|
||||
@@ -1388,7 +1394,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||
if (taskView != null && !mCanceled) {
|
||||
// Defer finishing the animation until the next launcher frame with the
|
||||
// new thumbnail
|
||||
finishTransitionPosted = ViewUtils.postDraw(taskView,
|
||||
finishTransitionPosted = ViewUtils.postFrameDrawn(taskView,
|
||||
() -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
|
||||
this::isCanceled);
|
||||
}
|
||||
@@ -1450,17 +1456,64 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
|
||||
endLauncherTransitionController();
|
||||
mActivityInterface.onSwipeUpToRecentsComplete();
|
||||
if (mRecentsAnimationController != null) {
|
||||
mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
|
||||
true /* screenshot */);
|
||||
}
|
||||
mRecentsView.onSwipeUpAnimationSuccess();
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler(
|
||||
this::launchOtherTaskInLiveTileMode);
|
||||
}
|
||||
|
||||
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
|
||||
doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
|
||||
reset();
|
||||
}
|
||||
|
||||
private void launchOtherTaskInLiveTileMode(RemoteAnimationTargetCompat appearedTaskTarget) {
|
||||
TaskView taskView = mRecentsView.getTaskView(appearedTaskTarget.taskId);
|
||||
if (taskView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
RemoteAnimationTargetCompat[] apps = Arrays.copyOf(
|
||||
mRecentsAnimationTargets.apps,
|
||||
mRecentsAnimationTargets.apps.length + 1);
|
||||
apps[apps.length - 1] = appearedTaskTarget;
|
||||
boolean launcherClosing =
|
||||
taskIsATargetWithMode(apps, mActivity.getTaskId(), MODE_CLOSING);
|
||||
|
||||
AnimatorSet anim = new AnimatorSet();
|
||||
TaskViewUtils.composeRecentsLaunchAnimator(
|
||||
anim, taskView, apps,
|
||||
mRecentsAnimationTargets.wallpapers, launcherClosing,
|
||||
mActivity.getStateManager(), mRecentsView,
|
||||
mActivityInterface.getDepthController());
|
||||
anim.addListener(new AnimatorListenerAdapter(){
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
cleanUp(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {
|
||||
cleanUp(true);
|
||||
}
|
||||
|
||||
private void cleanUp(boolean canceled) {
|
||||
if (mRecentsAnimationController != null) {
|
||||
mRecentsAnimationController.finish(false /* toRecents */,
|
||||
null /* onFinishComplete */);
|
||||
if (canceled) {
|
||||
mRecentsAnimationController = null;
|
||||
} else {
|
||||
mActivityInterface.onLaunchTaskSuccess();
|
||||
}
|
||||
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
|
||||
}
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
}
|
||||
|
||||
private void addLiveTileOverlay() {
|
||||
if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) {
|
||||
mRecentsView.setLiveTileOverlayAttached(true);
|
||||
@@ -1523,39 +1576,33 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||
|
||||
protected void startNewTask(Consumer<Boolean> resultCallback) {
|
||||
// Launch the task user scrolled to (mRecentsView.getNextPage()).
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
// We finish recents animation inside launchTask() when live tile is enabled.
|
||||
mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
|
||||
true /* freezeTaskList */);
|
||||
} else {
|
||||
if (!mCanceled) {
|
||||
TaskView nextTask = mRecentsView.getNextPageTaskView();
|
||||
if (nextTask != null) {
|
||||
int taskId = nextTask.getTask().key.id;
|
||||
mGestureState.updateLastStartedTaskId(taskId);
|
||||
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
|
||||
.contains(taskId);
|
||||
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
|
||||
success -> {
|
||||
resultCallback.accept(success);
|
||||
if (success) {
|
||||
if (hasTaskPreviouslyAppeared) {
|
||||
onRestartPreviouslyAppearedTask();
|
||||
}
|
||||
} else {
|
||||
mActivityInterface.onLaunchTaskFailed();
|
||||
nextTask.notifyTaskLaunchFailed(TAG);
|
||||
mRecentsAnimationController.finish(true /* toRecents */, null);
|
||||
if (!mCanceled) {
|
||||
TaskView nextTask = mRecentsView.getNextPageTaskView();
|
||||
if (nextTask != null) {
|
||||
int taskId = nextTask.getTask().key.id;
|
||||
mGestureState.updateLastStartedTaskId(taskId);
|
||||
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
|
||||
.contains(taskId);
|
||||
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
|
||||
success -> {
|
||||
resultCallback.accept(success);
|
||||
if (success) {
|
||||
if (hasTaskPreviouslyAppeared) {
|
||||
onRestartPreviouslyAppearedTask();
|
||||
}
|
||||
}, MAIN_EXECUTOR.getHandler());
|
||||
} else {
|
||||
mActivityInterface.onLaunchTaskFailed();
|
||||
Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
|
||||
mRecentsAnimationController.finish(true /* toRecents */, null);
|
||||
}
|
||||
} else {
|
||||
mActivityInterface.onLaunchTaskFailed();
|
||||
nextTask.notifyTaskLaunchFailed(TAG);
|
||||
mRecentsAnimationController.finish(true /* toRecents */, null);
|
||||
}
|
||||
}, MAIN_EXECUTOR.getHandler());
|
||||
} else {
|
||||
mActivityInterface.onLaunchTaskFailed();
|
||||
Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
|
||||
mRecentsAnimationController.finish(true /* toRecents */, null);
|
||||
}
|
||||
mCanceled = false;
|
||||
}
|
||||
mCanceled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1641,6 +1688,32 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||
}
|
||||
mTaskViewSimulator.apply(mTransformParams);
|
||||
}
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mRecentsAnimationTargets != null) {
|
||||
LiveTileOverlay.INSTANCE.update(
|
||||
mTaskViewSimulator.getCurrentRect(),
|
||||
mTaskViewSimulator.getCurrentCornerRadius());
|
||||
}
|
||||
ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for winscope tracing, see launcher_trace.proto
|
||||
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
|
||||
* @param inputConsumerProto The parent of this proto message.
|
||||
*/
|
||||
public void writeToProto(InputConsumerProto.Builder inputConsumerProto) {
|
||||
SwipeHandlerProto.Builder swipeHandlerProto = SwipeHandlerProto.newBuilder();
|
||||
|
||||
mGestureState.writeToProto(swipeHandlerProto);
|
||||
|
||||
swipeHandlerProto.setIsRecentsAttachedToAppWindow(
|
||||
mAnimationFactory.isRecentsAttachedToAppWindow());
|
||||
swipeHandlerProto.setScrollOffset(mRecentsView == null
|
||||
? 0
|
||||
: mRecentsView.getScrollOffset());
|
||||
swipeHandlerProto.setAppToOverviewProgress(mCurrentShift.value);
|
||||
|
||||
inputConsumerProto.setSwipeHandler(swipeHandlerProto);
|
||||
}
|
||||
|
||||
public interface Factory {
|
||||
|
||||
@@ -297,6 +297,10 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
|
||||
* @param animate Whether to animate recents to/from its new attached state.
|
||||
*/
|
||||
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
|
||||
|
||||
default boolean isRecentsAttachedToAppWindow() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultAnimationFactory implements AnimationFactory {
|
||||
@@ -388,6 +392,11 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
|
||||
fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecentsAttachedToAppWindow() {
|
||||
return mIsAttachedToWindow;
|
||||
}
|
||||
|
||||
protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
|
||||
// Scale down recents from being full screen to being in overview.
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
|
||||
@@ -26,6 +26,8 @@ import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.tracing.GestureStateProto;
|
||||
import com.android.launcher3.tracing.SwipeHandlerProto;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
@@ -46,19 +48,22 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||
* Defines the end targets of a gesture and the associated state.
|
||||
*/
|
||||
public enum GestureEndTarget {
|
||||
HOME(true, LAUNCHER_STATE_HOME, false),
|
||||
HOME(true, LAUNCHER_STATE_HOME, false, GestureStateProto.GestureEndTarget.HOME),
|
||||
|
||||
RECENTS(true, LAUNCHER_STATE_OVERVIEW, true),
|
||||
RECENTS(true, LAUNCHER_STATE_OVERVIEW, true, GestureStateProto.GestureEndTarget.RECENTS),
|
||||
|
||||
NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true),
|
||||
NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
|
||||
GestureStateProto.GestureEndTarget.NEW_TASK),
|
||||
|
||||
LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true);
|
||||
LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
|
||||
GestureStateProto.GestureEndTarget.LAST_TASK);
|
||||
|
||||
GestureEndTarget(boolean isLauncher, int containerType,
|
||||
boolean recentsAttachedToAppWindow) {
|
||||
GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow,
|
||||
GestureStateProto.GestureEndTarget protoEndTarget) {
|
||||
this.isLauncher = isLauncher;
|
||||
this.containerType = containerType;
|
||||
this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
|
||||
this.protoEndTarget = protoEndTarget;
|
||||
}
|
||||
|
||||
/** Whether the target is in the launcher activity. Implicitly, if the end target is going
|
||||
@@ -68,6 +73,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||
public final int containerType;
|
||||
/** Whether RecentsView should be attached to the window as we animate to this target */
|
||||
public final boolean recentsAttachedToAppWindow;
|
||||
/** The GestureStateProto enum value, used for winscope tracing. See launcher_trace.proto */
|
||||
public final GestureStateProto.GestureEndTarget protoEndTarget;
|
||||
}
|
||||
|
||||
private static final String TAG = "GestureState";
|
||||
@@ -345,4 +352,17 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||
pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
|
||||
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for winscope tracing, see launcher_trace.proto
|
||||
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
|
||||
* @param swipeHandlerProto The parent of this proto message.
|
||||
*/
|
||||
public void writeToProto(SwipeHandlerProto.Builder swipeHandlerProto) {
|
||||
GestureStateProto.Builder gestureStateProto = GestureStateProto.newBuilder();
|
||||
gestureStateProto.setEndTarget(mEndTarget == null
|
||||
? GestureStateProto.GestureEndTarget.UNSET
|
||||
: mEndTarget.protoEndTarget);
|
||||
swipeHandlerProto.setGestureState(gestureStateProto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ import android.view.InputEvent;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.tracing.InputConsumerProto;
|
||||
import com.android.launcher3.tracing.TouchInteractionServiceProto;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public interface InputConsumer {
|
||||
|
||||
@@ -116,4 +119,21 @@ public interface InputConsumer {
|
||||
}
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for winscope tracing, see launcher_trace.proto
|
||||
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
|
||||
* @param serviceProto The parent of this proto message.
|
||||
*/
|
||||
default void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
|
||||
InputConsumerProto.Builder inputConsumerProto = InputConsumerProto.newBuilder();
|
||||
inputConsumerProto.setName(getName());
|
||||
writeToProtoInternal(inputConsumerProto);
|
||||
serviceProto.setInputConsumer(inputConsumerProto);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #writeToProto - allows subclasses to write additional info to the proto.
|
||||
*/
|
||||
default void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||
import com.android.systemui.shared.system.LatencyTrackerCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
@@ -174,6 +175,9 @@ public class OverviewCommandHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
InteractionJankMonitorWrapper.begin(
|
||||
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timout */);
|
||||
|
||||
// Otherwise, start overview.
|
||||
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
|
||||
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
|
||||
|
||||
@@ -35,6 +35,8 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import com.android.launcher3.tracing.OverviewComponentObserverProto;
|
||||
import com.android.launcher3.tracing.TouchInteractionServiceProto;
|
||||
import com.android.launcher3.util.SimpleBroadcastReceiver;
|
||||
import com.android.systemui.shared.system.PackageManagerWrapper;
|
||||
|
||||
@@ -262,4 +264,17 @@ public final class OverviewComponentObserver {
|
||||
pw.println(" overviewIntent=" + mOverviewIntent);
|
||||
pw.println(" homeIntent=" + mCurrentHomeIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for winscope tracing, see launcher_trace.proto
|
||||
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
|
||||
* @param serviceProto The parent of this proto message.
|
||||
*/
|
||||
public void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
|
||||
OverviewComponentObserverProto.Builder overviewComponentObserver =
|
||||
OverviewComponentObserverProto.newBuilder();
|
||||
overviewComponentObserver.setOverviewActivityStarted(mActivityInterface.isStarted());
|
||||
overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed());
|
||||
serviceProto.setOverviewComponentObvserver(overviewComponentObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
@@ -90,24 +91,6 @@ public class RecentsAnimationController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the controller that we want to defer cancel until the next app transition starts.
|
||||
* If {@param screenshot} is set, then we will receive a screenshot on the next
|
||||
* {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call
|
||||
* {@link #cleanupScreenshot()} when that screenshot is no longer used.
|
||||
*/
|
||||
public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
|
||||
mController.setDeferCancelUntilNextTransition(defer, screenshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the screenshot previously returned from
|
||||
* {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}.
|
||||
*/
|
||||
public void cleanupScreenshot() {
|
||||
UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove task remote animation target from
|
||||
* {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}.
|
||||
@@ -151,6 +134,7 @@ public class RecentsAnimationController {
|
||||
mOnFinishedListener.accept(this);
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
mController.finish(toRecents, sendUserLeaveHint);
|
||||
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
|
||||
if (callback != null) {
|
||||
MAIN_EXECUTOR.execute(callback);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
@@ -66,6 +67,8 @@ public abstract class SwipeUpAnimationLogic {
|
||||
// How much further we can drag past recents, as a factor of mTransitionDragLength.
|
||||
protected float mDragLengthFactor = 1;
|
||||
|
||||
protected final float mMaxShadowRadius;
|
||||
|
||||
protected AnimatorControllerWithResistance mWindowTransitionController;
|
||||
|
||||
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
|
||||
@@ -80,6 +83,9 @@ public abstract class SwipeUpAnimationLogic {
|
||||
mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
|
||||
mDeviceState.getRotationTouchHelper().getDisplayRotation());
|
||||
mTaskViewSimulator.setDrawsBelowRecents(true);
|
||||
|
||||
mMaxShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.max_shadow_radius);
|
||||
mTransformParams.setShadowRadius(mMaxShadowRadius);
|
||||
}
|
||||
|
||||
protected void initTransitionEndpoints(DeviceProfile dp) {
|
||||
@@ -259,9 +265,11 @@ public abstract class SwipeUpAnimationLogic {
|
||||
|
||||
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
|
||||
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
|
||||
float shadowRadius = Utilities.mapRange(progress, mMaxShadowRadius, 0);
|
||||
mTransformParams
|
||||
.setTargetAlpha(getWindowAlpha(progress))
|
||||
.setCornerRadius(cornerRadius);
|
||||
.setCornerRadius(cornerRadius)
|
||||
.setShadowRadius(shadowRadius);
|
||||
|
||||
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
|
||||
mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
|
||||
@@ -272,7 +280,8 @@ public abstract class SwipeUpAnimationLogic {
|
||||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mCropRect)
|
||||
.withCornerRadius(params.getCornerRadius());
|
||||
.withCornerRadius(params.getCornerRadius())
|
||||
.withShadowRadius(params.getShadowRadius());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.quickstep;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
|
||||
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
|
||||
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
|
||||
|
||||
@@ -31,6 +32,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
|
||||
|
||||
private RecentsAnimationController mController;
|
||||
@@ -39,6 +42,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
// Temporary until we can hook into gesture state events
|
||||
private GestureState mLastGestureState;
|
||||
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
|
||||
private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
|
||||
|
||||
/**
|
||||
* Preloads the recents animation.
|
||||
@@ -88,22 +92,21 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
|
||||
if (thumbnailData != null) {
|
||||
// If a screenshot is provided, switch to the screenshot before cleaning up
|
||||
activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
|
||||
() -> cleanUpRecentsAnimation(thumbnailData));
|
||||
} else {
|
||||
cleanUpRecentsAnimation(null /* canceledThumbnail */);
|
||||
}
|
||||
cleanUpRecentsAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
|
||||
cleanUpRecentsAnimation(null /* canceledThumbnail */);
|
||||
cleanUpRecentsAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
|
||||
if (mLaunchOtherTaskHandler != null
|
||||
&& mLastGestureState.getEndTarget() == RECENTS) {
|
||||
mLaunchOtherTaskHandler.accept(appearedTaskTarget);
|
||||
return;
|
||||
}
|
||||
if (mController != null) {
|
||||
if (mLastAppearedTaskTarget == null
|
||||
|| appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
|
||||
@@ -138,6 +141,15 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
return mCallbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* The passed-in handler is used to render side task launch animation in recents in live tile
|
||||
* mode.
|
||||
*/
|
||||
public void setLaunchOtherTaskInLiveTileModeHandler(
|
||||
Consumer<RemoteAnimationTargetCompat> handler) {
|
||||
mLaunchOtherTaskHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the running recents animation.
|
||||
*/
|
||||
@@ -147,7 +159,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
|
||||
? mController::finishAnimationToHome
|
||||
: mController::finishAnimationToApp);
|
||||
cleanUpRecentsAnimation(null /* canceledThumbnail */);
|
||||
cleanUpRecentsAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,12 +186,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
/**
|
||||
* Cleans up the recents animation entirely.
|
||||
*/
|
||||
private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) {
|
||||
// Clean up the screenshot if necessary
|
||||
if (mController != null && canceledThumbnail != null) {
|
||||
mController.cleanupScreenshot();
|
||||
}
|
||||
|
||||
private void cleanUpRecentsAnimation() {
|
||||
// Release all the target leashes
|
||||
if (mTargets != null) {
|
||||
mTargets.release();
|
||||
@@ -195,6 +202,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
mTargets = null;
|
||||
mLastGestureState = null;
|
||||
mLastAppearedTaskTarget = null;
|
||||
mLaunchOtherTaskHandler = null;
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
|
||||
@@ -21,12 +21,14 @@ import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_REC
|
||||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
@@ -34,6 +36,7 @@ import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.FastBitmapDrawable;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
@@ -42,7 +45,6 @@ import com.android.quickstep.util.CancellableTask;
|
||||
import com.android.quickstep.util.TaskKeyLruCache;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.Task.TaskKey;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.PackageManagerWrapper;
|
||||
import com.android.systemui.shared.system.TaskDescriptionCompat;
|
||||
|
||||
@@ -163,9 +165,8 @@ public class TaskIconCache {
|
||||
key.getComponent(), key.userId);
|
||||
}
|
||||
if (activityInfo != null) {
|
||||
entry.contentDescription = ActivityManagerWrapper.getInstance()
|
||||
.getBadgedContentDescription(activityInfo, task.key.userId,
|
||||
task.taskDescription);
|
||||
entry.contentDescription = getBadgedContentDescription(
|
||||
activityInfo, task.key.userId, task.taskDescription);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +174,21 @@ public class TaskIconCache {
|
||||
return entry;
|
||||
}
|
||||
|
||||
private String getBadgedContentDescription(ActivityInfo info, int userId, TaskDescription td) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
String taskLabel = td == null ? null : Utilities.trim(td.getLabel());
|
||||
if (TextUtils.isEmpty(taskLabel)) {
|
||||
taskLabel = Utilities.trim(info.loadLabel(pm));
|
||||
}
|
||||
|
||||
String applicationLabel = Utilities.trim(info.applicationInfo.loadLabel(pm));
|
||||
String badgedApplicationLabel = userId != UserHandle.myUserId()
|
||||
? pm.getUserBadgedLabel(applicationLabel, UserHandle.of(userId)).toString()
|
||||
: applicationLabel;
|
||||
return applicationLabel.equals(taskLabel)
|
||||
? badgedApplicationLabel : badgedApplicationLabel + " " + taskLabel;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private Drawable getDefaultIcon(int userId) {
|
||||
synchronized (mDefaultIcons) {
|
||||
|
||||
@@ -17,15 +17,20 @@ package com.android.quickstep;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
|
||||
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
|
||||
import static com.android.launcher3.anim.Interpolators.clampToProgress;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -35,12 +40,18 @@ import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.statehandlers.DepthController;
|
||||
import com.android.launcher3.statemanager.StateManager;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
import com.android.quickstep.util.SurfaceTransactionApplier;
|
||||
import com.android.quickstep.util.TaskViewSimulator;
|
||||
@@ -49,6 +60,7 @@ import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskThumbnailView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
/**
|
||||
@@ -67,8 +79,7 @@ public final class TaskViewUtils {
|
||||
* opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
|
||||
*/
|
||||
public static TaskView findTaskViewToLaunch(
|
||||
BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
|
||||
if (v instanceof TaskView) {
|
||||
TaskView taskView = (TaskView) v;
|
||||
return recentsView.isTaskViewVisible(taskView) ? taskView : null;
|
||||
@@ -119,6 +130,21 @@ public final class TaskViewUtils {
|
||||
return taskView;
|
||||
}
|
||||
|
||||
public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
|
||||
RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
|
||||
PendingAnimation out) {
|
||||
boolean isRunningTask = v.isRunningTask();
|
||||
TransformParams params = null;
|
||||
TaskViewSimulator tsv = null;
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
|
||||
params = v.getRecentsView().getLiveTileParams();
|
||||
tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
|
||||
}
|
||||
createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets,
|
||||
depthController, out, params, tsv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an animation that controls the window of the opening targets for the recents launch
|
||||
* animation.
|
||||
@@ -126,16 +152,25 @@ public final class TaskViewUtils {
|
||||
public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
|
||||
RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
|
||||
PendingAnimation out) {
|
||||
PendingAnimation out, @Nullable TransformParams params,
|
||||
@Nullable TaskViewSimulator tsv) {
|
||||
boolean isQuickSwitch = v.isEndQuickswitchCuj();
|
||||
v.setEndQuickswitchCuj(false);
|
||||
|
||||
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
|
||||
boolean inLiveTileMode =
|
||||
ENABLE_QUICKSTEP_LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
|
||||
final RemoteAnimationTargets targets =
|
||||
new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING);
|
||||
targets.addReleaseCheck(applier);
|
||||
new RemoteAnimationTargets(appTargets, wallpaperTargets,
|
||||
inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
|
||||
|
||||
TransformParams params = new TransformParams()
|
||||
if (params == null) {
|
||||
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
|
||||
targets.addReleaseCheck(applier);
|
||||
|
||||
params = new TransformParams()
|
||||
.setSyncTransactionApplier(applier)
|
||||
.setTargetSet(targets);
|
||||
}
|
||||
|
||||
final RecentsView recentsView = v.getRecentsView();
|
||||
int taskIndex = recentsView.indexOfChild(v);
|
||||
@@ -149,8 +184,9 @@ public final class TaskViewUtils {
|
||||
int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
|
||||
|
||||
TaskViewSimulator topMostSimulator = null;
|
||||
if (targets.apps.length > 0) {
|
||||
TaskViewSimulator tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
|
||||
|
||||
if (tsv == null && targets.apps.length > 0) {
|
||||
tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
|
||||
tsv.setDp(dp);
|
||||
tsv.setLayoutRotation(displayRotation, displayRotation);
|
||||
tsv.setPreview(targets.apps[targets.apps.length - 1]);
|
||||
@@ -158,19 +194,24 @@ public final class TaskViewUtils {
|
||||
tsv.recentsViewScale.value = 1;
|
||||
tsv.setScroll(startScroll);
|
||||
|
||||
// Fade in the task during the initial 20% of the animation
|
||||
out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
|
||||
clampToProgress(LINEAR, 0, 0.2f));
|
||||
}
|
||||
|
||||
if (tsv != null) {
|
||||
out.setFloat(tsv.fullScreenProgress,
|
||||
AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
|
||||
out.setFloat(tsv.recentsViewScale,
|
||||
AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
|
||||
out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
|
||||
|
||||
out.addOnFrameCallback(() -> tsv.apply(params));
|
||||
TaskViewSimulator finalTsv = tsv;
|
||||
TransformParams finalParams = params;
|
||||
out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
|
||||
topMostSimulator = tsv;
|
||||
}
|
||||
|
||||
// Fade in the task during the initial 20% of the animation
|
||||
out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, clampToProgress(LINEAR, 0, 0.2f));
|
||||
|
||||
if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
|
||||
out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
|
||||
|
||||
@@ -223,10 +264,19 @@ public final class TaskViewUtils {
|
||||
});
|
||||
}
|
||||
|
||||
out.addListener(new AnimatorListenerAdapter() {
|
||||
out.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
if (isQuickSwitch) {
|
||||
InteractionJankMonitorWrapper.end(
|
||||
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
targets.release();
|
||||
super.onAnimationEnd(animation);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -235,4 +285,57 @@ public final class TaskViewUtils {
|
||||
TOUCH_RESPONSE_INTERPOLATOR);
|
||||
}
|
||||
}
|
||||
|
||||
public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
|
||||
@NonNull RemoteAnimationTargetCompat[] appTargets,
|
||||
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing,
|
||||
@NonNull StateManager stateManager, @NonNull RecentsView recentsView,
|
||||
@NonNull DepthController depthController) {
|
||||
boolean skipLauncherChanges = !launcherClosing;
|
||||
|
||||
TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets);
|
||||
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
|
||||
createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
|
||||
depthController, pa);
|
||||
anim.play(pa.buildAnim());
|
||||
|
||||
Animator childStateAnimation = null;
|
||||
// Found a visible recents task that matches the opening app, lets launch the app from there
|
||||
Animator launcherAnim;
|
||||
final AnimatorListenerAdapter windowAnimEndListener;
|
||||
if (launcherClosing) {
|
||||
launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
|
||||
launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
|
||||
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
|
||||
|
||||
// Make sure recents gets fixed up by resetting task alphas and scales, etc.
|
||||
windowAnimEndListener = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
stateManager.moveToRestState();
|
||||
stateManager.reapplyState();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
AnimatorPlaybackController controller =
|
||||
stateManager.createAnimationToNewWorkspace(NORMAL,
|
||||
RECENTS_LAUNCH_DURATION);
|
||||
controller.dispatchOnStart();
|
||||
childStateAnimation = controller.getTarget();
|
||||
launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
|
||||
windowAnimEndListener = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
stateManager.goToState(NORMAL, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
anim.play(launcherAnim);
|
||||
|
||||
// Set the current animation first, before adding windowAnimEndListener. Setting current
|
||||
// animation adds some listeners which need to be called before windowAnimEndListener
|
||||
// (the ordering of listeners matter in this case).
|
||||
stateManager.setCurrentAnimation(anim, childStateAnimation);
|
||||
anim.addListener(windowAnimEndListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,8 +737,11 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
private void reset() {
|
||||
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
|
||||
mGestureState = DEFAULT_STATE;
|
||||
// By default, use batching of the input events
|
||||
mInputEventReceiver.setBatchingEnabled(true);
|
||||
// By default, use batching of the input events, but check receiver before using in the rare
|
||||
// case that the monitor was disposed before the swipe settled
|
||||
if (mInputEventReceiver != null) {
|
||||
mInputEventReceiver.setBatchingEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void preloadOverview(boolean fromInit) {
|
||||
@@ -895,6 +898,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
TouchInteractionServiceProto.Builder serviceProto =
|
||||
TouchInteractionServiceProto.newBuilder();
|
||||
serviceProto.setServiceConnected(true);
|
||||
|
||||
if (mOverviewComponentObserver != null) {
|
||||
mOverviewComponentObserver.writeToProto(serviceProto);
|
||||
}
|
||||
mConsumer.writeToProto(serviceProto);
|
||||
|
||||
proto.setTouchInteractionService(serviceProto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,21 +15,23 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.shared.system.WindowCallbacksCompat;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.systemui.shared.system.ViewRootImplCompat;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
/**
|
||||
* Utility class for helpful methods related to {@link View} objects.
|
||||
*/
|
||||
public class ViewUtils {
|
||||
|
||||
/** See {@link #postDraw(View, Runnable, BooleanSupplier)}} */
|
||||
public static boolean postDraw(View view, Runnable onFinishRunnable) {
|
||||
return postDraw(view, onFinishRunnable, () -> false);
|
||||
/** See {@link #postFrameDrawn(View, Runnable, BooleanSupplier)}} */
|
||||
public static boolean postFrameDrawn(View view, Runnable onFinishRunnable) {
|
||||
return postFrameDrawn(view, onFinishRunnable, () -> false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,37 +40,55 @@ public class ViewUtils {
|
||||
*
|
||||
* @param onFinishRunnable runnable to be run right after the view finishes drawing.
|
||||
*/
|
||||
public static boolean postDraw(View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
|
||||
// Defer finishing the animation until the next launcher frame with the
|
||||
// new thumbnail
|
||||
return new WindowCallbacksCompat(view) {
|
||||
// The number of frames to defer until we actually finish the animation
|
||||
private int mDeferFrameCount = 2;
|
||||
public static boolean postFrameDrawn(
|
||||
View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
|
||||
return new FrameHandler(view, onFinishRunnable, canceled).schedule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostDraw(Canvas canvas) {
|
||||
// If we were cancelled after this was attached, do not update
|
||||
// the state.
|
||||
if (canceled.getAsBoolean()) {
|
||||
detach();
|
||||
return;
|
||||
}
|
||||
private static class FrameHandler implements LongConsumer {
|
||||
|
||||
if (mDeferFrameCount > 0) {
|
||||
mDeferFrameCount--;
|
||||
// Workaround, detach and reattach to invalidate the root node for
|
||||
// another draw
|
||||
detach();
|
||||
attach();
|
||||
view.invalidate();
|
||||
return;
|
||||
}
|
||||
final ViewRootImplCompat mViewRoot;
|
||||
final Runnable mFinishCallback;
|
||||
final BooleanSupplier mCancelled;
|
||||
final Handler mHandler;
|
||||
|
||||
if (onFinishRunnable != null) {
|
||||
onFinishRunnable.run();
|
||||
}
|
||||
detach();
|
||||
int mDeferFrameCount = 1;
|
||||
|
||||
FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
|
||||
mViewRoot = new ViewRootImplCompat(view);
|
||||
mFinishCallback = finishCallback;
|
||||
mCancelled = cancelled;
|
||||
mHandler = new Handler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(long l) {
|
||||
Utilities.postAsyncCallback(mHandler, this::onFrame);
|
||||
}
|
||||
|
||||
private void onFrame() {
|
||||
if (mCancelled.getAsBoolean()) {
|
||||
return;
|
||||
}
|
||||
}.attach();
|
||||
|
||||
if (mDeferFrameCount > 0) {
|
||||
mDeferFrameCount--;
|
||||
schedule();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFinishCallback != null) {
|
||||
mFinishCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean schedule() {
|
||||
if (mViewRoot.isValid()) {
|
||||
mViewRoot.registerRtFrameCallback(this);
|
||||
mViewRoot.getView().invalidate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.tracing.InputConsumerProto;
|
||||
import com.android.quickstep.InputConsumer;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
|
||||
@@ -53,4 +54,9 @@ public abstract class DelegateInputConsumer implements InputConsumer {
|
||||
mDelegate.onMotionEvent(event);
|
||||
event.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
|
||||
mDelegate.writeToProtoInternal(inputConsumerProto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import androidx.annotation.UiThread;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.tracing.InputConsumerProto;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.quickstep.AbsSwipeUpHandler;
|
||||
@@ -478,4 +479,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
||||
public boolean allowInterceptByParent() {
|
||||
return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
|
||||
if (mInteractionHandler != null) {
|
||||
mInteractionHandler.writeToProto(inputConsumerProto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -104,7 +104,13 @@ final class AssistantGestureTutorialController extends TutorialController {
|
||||
hideFeedback();
|
||||
hideHandCoachingAnimation();
|
||||
showRippleEffect(
|
||||
() -> mTutorialFragment.changeController(ASSISTANT_COMPLETE));
|
||||
() -> {
|
||||
if (mTutorialFragment.isTutorialComplete()) {
|
||||
mTutorialFragment.changeController(ASSISTANT_COMPLETE);
|
||||
} else {
|
||||
mTutorialFragment.continueTutorial();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case ASSISTANT_NOT_STARTED_BAD_ANGLE:
|
||||
showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);
|
||||
|
||||
@@ -130,7 +130,13 @@ final class BackGestureTutorialController extends TutorialController {
|
||||
hideFeedback();
|
||||
hideHandCoachingAnimation();
|
||||
showRippleEffect(
|
||||
() -> mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE));
|
||||
() -> {
|
||||
if (mTutorialFragment.isTutorialComplete()) {
|
||||
mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE);
|
||||
} else {
|
||||
mTutorialFragment.continueTutorial();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case BACK_CANCELLED_FROM_LEFT:
|
||||
showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package com.android.quickstep.interaction;
|
||||
|
||||
import static com.android.quickstep.interaction.TutorialFragment.KEY_TUTORIAL_TYPE;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
@@ -25,11 +23,14 @@ import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.interaction.TutorialController.TutorialType;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
/** Shows the gesture interactive sandbox in full screen mode. */
|
||||
@@ -37,6 +38,9 @@ public class GestureSandboxActivity extends FragmentActivity {
|
||||
|
||||
private static final String LOG_TAG = "GestureSandboxActivity";
|
||||
|
||||
private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
|
||||
|
||||
private Deque<TutorialType> mTutorialSteps;
|
||||
private TutorialFragment mFragment;
|
||||
|
||||
@Override
|
||||
@@ -45,7 +49,9 @@ public class GestureSandboxActivity extends FragmentActivity {
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.gesture_tutorial_activity);
|
||||
|
||||
mFragment = TutorialFragment.newInstance(getTutorialType(getIntent().getExtras()));
|
||||
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
|
||||
mTutorialSteps = getTutorialSteps(args);
|
||||
mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.gesture_tutorial_fragment_container, mFragment)
|
||||
.commit();
|
||||
@@ -72,17 +78,65 @@ public class GestureSandboxActivity extends FragmentActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private TutorialType getTutorialType(Bundle extras) {
|
||||
TutorialType defaultType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
if (extras == null || !extras.containsKey(KEY_TUTORIAL_TYPE)) {
|
||||
return defaultType;
|
||||
/** Returns true iff there aren't anymore tutorial types to display to the user. */
|
||||
public boolean isTutorialComplete() {
|
||||
return mTutorialSteps.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
|
||||
*
|
||||
* If there is no following step, the tutorial is closed.
|
||||
*/
|
||||
public void continueTutorial() {
|
||||
if (isTutorialComplete()) {
|
||||
mFragment.closeTutorial();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
return TutorialType.valueOf(extras.getString(KEY_TUTORIAL_TYPE, ""));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return defaultType;
|
||||
mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.gesture_tutorial_fragment_container, mFragment)
|
||||
.runOnCommit(() -> mFragment.onAttachedToWindow())
|
||||
.commit();
|
||||
}
|
||||
|
||||
private String[] getTutorialStepNames() {
|
||||
String[] tutorialStepNames = new String[mTutorialSteps.size()];
|
||||
|
||||
int i = 0;
|
||||
for (TutorialType tutorialStep : mTutorialSteps) {
|
||||
tutorialStepNames[i++] = tutorialStep.name();
|
||||
}
|
||||
|
||||
return tutorialStepNames;
|
||||
}
|
||||
|
||||
private Deque<TutorialType> getTutorialSteps(Bundle extras) {
|
||||
Deque<TutorialType> defaultSteps = new ArrayDeque<>();
|
||||
defaultSteps.push(TutorialType.RIGHT_EDGE_BACK_NAVIGATION);
|
||||
|
||||
if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
|
||||
return defaultSteps;
|
||||
}
|
||||
|
||||
String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS);
|
||||
|
||||
if (tutorialStepNames == null) {
|
||||
return defaultSteps;
|
||||
}
|
||||
|
||||
Deque<TutorialType> tutorialSteps = new ArrayDeque<>();
|
||||
for (String tutorialStepName : tutorialStepNames) {
|
||||
tutorialSteps.addLast(TutorialType.valueOf(tutorialStepName));
|
||||
}
|
||||
|
||||
return tutorialSteps;
|
||||
}
|
||||
|
||||
private void hideSystemUI() {
|
||||
|
||||
@@ -94,8 +94,13 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
|
||||
case HOME_NAVIGATION:
|
||||
switch (result) {
|
||||
case HOME_GESTURE_COMPLETED: {
|
||||
animateFakeTaskViewHome(finalVelocity, () ->
|
||||
mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE));
|
||||
animateFakeTaskViewHome(finalVelocity, () -> {
|
||||
if (mTutorialFragment.isTutorialComplete()) {
|
||||
mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE);
|
||||
} else {
|
||||
mTutorialFragment.continueTutorial();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
|
||||
|
||||
+7
-2
@@ -104,8 +104,13 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
|
||||
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
|
||||
break;
|
||||
case OVERVIEW_GESTURE_COMPLETED:
|
||||
fadeOutFakeTaskView(true, () ->
|
||||
mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE));
|
||||
fadeOutFakeTaskView(true, () -> {
|
||||
if (mTutorialFragment.isTutorialComplete()) {
|
||||
mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE);
|
||||
} else {
|
||||
mTutorialFragment.continueTutorial();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
|
||||
case HOME_OR_OVERVIEW_CANCELLED:
|
||||
|
||||
@@ -54,6 +54,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
||||
fragment = new BackGestureTutorialFragment();
|
||||
tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
|
||||
}
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
|
||||
fragment.setArguments(args);
|
||||
@@ -197,6 +198,20 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
||||
return mHandCoachingAnimation;
|
||||
}
|
||||
|
||||
void continueTutorial() {
|
||||
if (!(getContext() instanceof GestureSandboxActivity)) {
|
||||
closeTutorial();
|
||||
return;
|
||||
}
|
||||
GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
|
||||
|
||||
if (gestureSandboxActivity == null) {
|
||||
closeTutorial();
|
||||
return;
|
||||
}
|
||||
gestureSandboxActivity.continueTutorial();
|
||||
}
|
||||
|
||||
void closeTutorial() {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
@@ -207,4 +222,13 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
||||
void startSystemNavigationSetting() {
|
||||
startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
|
||||
}
|
||||
|
||||
boolean isTutorialComplete() {
|
||||
if (!(getContext() instanceof GestureSandboxActivity)) {
|
||||
return true;
|
||||
}
|
||||
GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
|
||||
|
||||
return gestureSandboxActivity == null || gestureSandboxActivity.isTutorialComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import static android.view.Surface.ROTATION_180;
|
||||
import static android.view.Surface.ROTATION_270;
|
||||
import static android.view.Surface.ROTATION_90;
|
||||
|
||||
import static com.android.launcher3.Utilities.newContentObserver;
|
||||
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
|
||||
@@ -73,12 +74,9 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String DELIMITER_DOT = "\\.";
|
||||
|
||||
private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateAutoRotateSetting();
|
||||
}
|
||||
};
|
||||
private ContentObserver mSystemAutoRotateObserver =
|
||||
newContentObserver(new Handler(), t -> updateAutoRotateSetting());
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
|
||||
public @interface SurfaceRotation {}
|
||||
@@ -541,6 +539,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
||||
* @return "MyObject@1234"
|
||||
*/
|
||||
private static String extractObjectNameAndAddress(String stringToExtract) {
|
||||
return stringToExtract.substring(stringToExtract.lastIndexOf(DELIMITER_DOT));
|
||||
int index = stringToExtract.lastIndexOf(DELIMITER_DOT);
|
||||
return index >= 0 ? stringToExtract.substring(index) : "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +322,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mTmpCropRect)
|
||||
.withCornerRadius(getCurrentCornerRadius());
|
||||
.withCornerRadius(getCurrentCornerRadius())
|
||||
.withShadowRadius(params.getShadowRadius());
|
||||
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
|
||||
// When relativeLayer = 0, it reverts the surfaces back to the original order.
|
||||
|
||||
@@ -57,6 +57,7 @@ public class TransformParams {
|
||||
private float mProgress;
|
||||
private float mTargetAlpha;
|
||||
private float mCornerRadius;
|
||||
private float mShadowRadius;
|
||||
private RemoteAnimationTargets mTargetSet;
|
||||
private SurfaceTransactionApplier mSyncTransactionApplier;
|
||||
private SurfaceControl mRecentsSurface;
|
||||
@@ -68,6 +69,7 @@ public class TransformParams {
|
||||
mProgress = 0;
|
||||
mTargetAlpha = 1;
|
||||
mCornerRadius = -1;
|
||||
mShadowRadius = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +92,14 @@ public class TransformParams {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the shadow radius of the transformed window, in pixels.
|
||||
*/
|
||||
public TransformParams setShadowRadius(float shadowRadius) {
|
||||
mShadowRadius = shadowRadius;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the alpha of the transformed window. Default is 1.
|
||||
*/
|
||||
@@ -197,6 +207,10 @@ public class TransformParams {
|
||||
return mCornerRadius;
|
||||
}
|
||||
|
||||
public float getShadowRadius() {
|
||||
return mShadowRadius;
|
||||
}
|
||||
|
||||
public SurfaceControl getRecentsSurface() {
|
||||
return mRecentsSurface;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,10 @@ public class LiveTileOverlay extends Drawable {
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
public void update(float left, float top, float right, float bottom) {
|
||||
mCurrentRect.set(left, top, right, bottom);
|
||||
}
|
||||
|
||||
public void setIcon(Drawable icon) {
|
||||
mIcon = icon;
|
||||
}
|
||||
@@ -94,18 +98,16 @@ public class LiveTileOverlay extends Drawable {
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mCurrentRect != null) {
|
||||
canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
|
||||
if (mIcon != null && mIconAnimationProgress > 0f) {
|
||||
canvas.save();
|
||||
float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
|
||||
1f).getInterpolation(mIconAnimationProgress);
|
||||
canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
|
||||
mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
|
||||
canvas.scale(scale, scale);
|
||||
mIcon.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
|
||||
if (mIcon != null && mIconAnimationProgress > 0f) {
|
||||
canvas.save();
|
||||
float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
|
||||
1f).getInterpolation(mIconAnimationProgress);
|
||||
canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
|
||||
mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
|
||||
canvas.scale(scale, scale);
|
||||
mIcon.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -230,6 +230,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
view.setScaleX(scale);
|
||||
view.setScaleY(scale);
|
||||
view.mLastComputedTaskPushOutDistance = null;
|
||||
view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
|
||||
view.updatePageOffsets();
|
||||
view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
|
||||
}
|
||||
@@ -539,6 +540,9 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
@Override
|
||||
protected void onWindowVisibilityChanged(int visibility) {
|
||||
super.onWindowVisibilityChanged(visibility);
|
||||
if (visibility == GONE && ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
finishRecentsAnimation(true /* toRecents */, null);
|
||||
}
|
||||
updateTaskStackListenerState();
|
||||
}
|
||||
|
||||
@@ -873,6 +877,10 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
|
||||
mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
|
||||
mLiveTileTaskViewSimulator.setOffsetY(0);
|
||||
|
||||
// Reset the live tile rect
|
||||
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
|
||||
LiveTileOverlay.INSTANCE.update(0, 0, deviceProfile.widthPx, deviceProfile.heightPx);
|
||||
}
|
||||
if (mRunningTaskTileHidden) {
|
||||
setRunningTaskHidden(mRunningTaskTileHidden);
|
||||
@@ -1292,19 +1300,26 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
}
|
||||
|
||||
public void showNextTask() {
|
||||
TaskView runningTaskView = getRunningTaskView();
|
||||
final TaskView runningTaskView = getRunningTaskView();
|
||||
final TaskView targetTask;
|
||||
|
||||
if (runningTaskView == null) {
|
||||
// Launch the first task
|
||||
if (getTaskViewCount() > 0) {
|
||||
getTaskViewAt(0).launchTask(true);
|
||||
targetTask = getTaskViewAt(0);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (getNextTaskView() != null) {
|
||||
getNextTaskView().launchTask(true);
|
||||
final TaskView nextTask = getNextTaskView();
|
||||
if (nextTask != null) {
|
||||
targetTask = nextTask;
|
||||
} else {
|
||||
runningTaskView.launchTask(true);
|
||||
targetTask = runningTaskView;
|
||||
}
|
||||
}
|
||||
targetTask.setEndQuickswitchCuj(true);
|
||||
targetTask.launchTask(true);
|
||||
}
|
||||
|
||||
public void setRunningTaskIconScaledDown(boolean isScaledDown) {
|
||||
@@ -1976,6 +1991,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
TaskView task = getTaskViewAt(i);
|
||||
mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
|
||||
}
|
||||
mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2254,6 +2270,10 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
return mLiveTileTaskViewSimulator;
|
||||
}
|
||||
|
||||
public TransformParams getLiveTileParams() {
|
||||
return mLiveTileParams;
|
||||
}
|
||||
|
||||
// TODO: To be removed in a follow up CL
|
||||
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
|
||||
RecentsAnimationTargets recentsAnimationTargets) {
|
||||
@@ -2442,7 +2462,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
} else {
|
||||
taskView.getThumbnail().refresh();
|
||||
}
|
||||
ViewUtils.postDraw(taskView, onFinishRunnable);
|
||||
ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
|
||||
} else {
|
||||
onFinishRunnable.run();
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
@@ -65,7 +66,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.R;
|
||||
@@ -75,6 +75,7 @@ import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.popup.SystemShortcut;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
@@ -82,10 +83,12 @@ import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.TransformingTouchDelegate;
|
||||
import com.android.launcher3.util.ViewPool.Reusable;
|
||||
import com.android.quickstep.RecentsModel;
|
||||
import com.android.quickstep.RemoteAnimationTargets;
|
||||
import com.android.quickstep.TaskIconCache;
|
||||
import com.android.quickstep.TaskOverlayFactory;
|
||||
import com.android.quickstep.TaskThumbnailCache;
|
||||
import com.android.quickstep.TaskUtils;
|
||||
import com.android.quickstep.TaskViewUtils;
|
||||
import com.android.quickstep.util.CancellableTask;
|
||||
import com.android.quickstep.util.RecentsOrientedState;
|
||||
import com.android.quickstep.util.TaskCornerRadius;
|
||||
@@ -175,7 +178,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
private float mCurveScale;
|
||||
private float mFullscreenProgress;
|
||||
private final FullscreenDrawParams mCurrentFullscreenParams;
|
||||
private final BaseDraggingActivity mActivity;
|
||||
private final StatefulActivity mActivity;
|
||||
|
||||
private ObjectAnimator mIconAndDimAnimator;
|
||||
private float mIconScaleAnimStartProgress = 0;
|
||||
@@ -189,6 +192,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
private CancellableTask mThumbnailLoadRequest;
|
||||
private CancellableTask mIconLoadRequest;
|
||||
|
||||
private boolean mEndQuickswitchCuj;
|
||||
|
||||
// Order in which the footers appear. Lower order appear below higher order.
|
||||
public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
|
||||
private final FooterWrapper[] mFooters = new FooterWrapper[2];
|
||||
@@ -210,18 +215,31 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
|
||||
public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mActivity = BaseDraggingActivity.fromContext(context);
|
||||
mActivity = StatefulActivity.fromContext(context);
|
||||
setOnClickListener((view) -> {
|
||||
if (getTask() == null) {
|
||||
return;
|
||||
}
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
if (isRunningTask()) {
|
||||
// TODO: Replace this animation with createRecentsWindowAnimator
|
||||
createLaunchAnimationForRunningTask().start();
|
||||
} else {
|
||||
launchTask(true /* animate */);
|
||||
}
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
|
||||
RecentsView recentsView = getRecentsView();
|
||||
RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
|
||||
recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
|
||||
|
||||
AnimatorSet anim = new AnimatorSet();
|
||||
TaskViewUtils.composeRecentsLaunchAnimator(
|
||||
anim, this, targets.apps,
|
||||
targets.wallpapers, true /* launcherClosing */,
|
||||
mActivity.getStateManager(), recentsView,
|
||||
recentsView.getDepthController());
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
|
||||
recentsView.finishRecentsAnimation(false, null);
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
} else {
|
||||
launchTask(true /* animate */);
|
||||
}
|
||||
@@ -801,6 +819,14 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEndQuickswitchCuj() {
|
||||
return mEndQuickswitchCuj;
|
||||
}
|
||||
|
||||
public void setEndQuickswitchCuj(boolean endQuickswitchCuj) {
|
||||
mEndQuickswitchCuj = endQuickswitchCuj;
|
||||
}
|
||||
|
||||
private static final class TaskOutlineProvider extends ViewOutlineProvider {
|
||||
|
||||
private final int mMarginTop;
|
||||
|
||||
Reference in New Issue
Block a user