Fixing touches getting ignored just after swipe-up am: 1a286a189e
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/12240767
Change-Id: Iabaa1f7f6320c7ccc359a41dc40ca50d2ec169a3
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b031ffb..bc0cd97 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -49,7 +49,7 @@
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 60afddb..d680507 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -49,7 +49,7 @@
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index e49f2ec..bb83b76 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -60,7 +60,7 @@
android:stateNotNeeded="true"
android:theme="@style/LauncherTheme"
android:screenOrientation="unspecified"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity="" />
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
index c15a596..20e1edc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
@@ -71,10 +71,9 @@
}
private PrintWriter getWriter() {
- String fName = FILE_NAME_PREFIX + (LOG_DAYS % 10);
- if (fName.equals(mFileName)) return mCurrentWriter;
-
Calendar cal = Calendar.getInstance();
+ String fName = FILE_NAME_PREFIX + (cal.get(Calendar.DAY_OF_YEAR) % 10);
+ if (fName.equals(mFileName)) return mCurrentWriter;
boolean append = false;
File logFile = new File(mLogsDir, fName);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
index 8c1db4e..9e7c9fb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
@@ -30,7 +30,6 @@
*/
public class HotseatRestoreHelper {
private final Launcher mLauncher;
- private boolean mBackupRestored = false;
HotseatRestoreHelper(Launcher context) {
mLauncher = context;
@@ -62,7 +61,6 @@
* Finds and restores a previously saved snapshow of Favorites table
*/
public void restoreBackup() {
- if (mBackupRestored) return;
MODEL_EXECUTOR.execute(() -> {
try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
LauncherSettings.Settings.call(
@@ -78,7 +76,6 @@
idp.numRows);
backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true);
transaction.commit();
- mBackupRestored = true;
mLauncher.getModel().forceReload();
}
});
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index de83caf..9310685 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -24,6 +24,7 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.app.ActivityManager.RunningTaskInfo;
import android.util.Log;
import android.view.animation.Interpolator;
@@ -52,17 +53,17 @@
private final BaseActivityInterface<?, T> mActivityInterface;
// The id of the currently running task that is transitioning to overview.
- private final int mTargetTaskId;
+ private final RunningTaskInfo mTargetTask;
private final RecentsAnimationDeviceState mDeviceState;
private T mActivity;
private RecentsView mRecentsView;
AppToOverviewAnimationProvider(
- BaseActivityInterface<?, T> activityInterface, int targetTaskId,
+ BaseActivityInterface<?, T> activityInterface, RunningTaskInfo targetTask,
RecentsAnimationDeviceState deviceState) {
mActivityInterface = activityInterface;
- mTargetTaskId = targetTaskId;
+ mTargetTask = targetTask;
mDeviceState = deviceState;
}
@@ -73,7 +74,7 @@
* @param wasVisible true if it was visible before
*/
boolean onActivityReady(T activity, Boolean wasVisible) {
- activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
+ activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTask);
AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
mDeviceState,
@@ -122,7 +123,8 @@
wallpaperTargets, MODE_CLOSING);
// Use the top closing app to determine the insets for the animation
- RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
+ RemoteAnimationTargetCompat runningTaskTarget = mTargetTask == null ? null
+ : targets.findTask(mTargetTask.taskId);
if (runningTaskTarget == null) {
Log.e(TAG, "No closing app");
return pa.buildAnim();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index aba5ab6..cbef67b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static android.widget.Toast.LENGTH_SHORT;
+
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
@@ -27,11 +29,13 @@
import android.os.Build;
import android.util.Log;
import android.view.MotionEvent;
+import android.widget.Toast;
import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.VibratorWrapper;
@@ -140,10 +144,10 @@
mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
true /* freezeTaskList */);
} else {
- int taskId = mRecentsView.getNextPageTaskView().getTask().key.id;
if (!mCanceled) {
- TaskView nextTask = mRecentsView.getTaskView(taskId);
+ TaskView nextTask = mRecentsView.getNextPageTaskView();
if (nextTask != null) {
+ int taskId = nextTask.getTask().key.id;
mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId);
@@ -160,6 +164,10 @@
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;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
index f0f3d0f..a8a49b03 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
@@ -266,10 +266,6 @@
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
this::notifyTransitionCancelled);
- mGestureState.runOnceAtState(STATE_END_TARGET_SET,
- () -> mDeviceState.onEndTargetCalculated(mGestureState.getEndTarget(),
- mActivityInterface));
-
if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
| STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
@@ -337,7 +333,7 @@
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return;
}
- mTaskViewSimulator.setRecentsConfiguration(mActivity.getResources().getConfiguration());
+ mTaskViewSimulator.setRecentsRotation(mActivity.getDisplay().getRotation());
// If we've already ended the gesture and are going home, don't prepare recents UI,
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
@@ -400,6 +396,11 @@
mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback(
mOnDeferredActivityLaunch);
+ mGestureState.runOnceAtState(STATE_END_TARGET_SET,
+ () -> mDeviceState.getRotationTouchHelper().
+ onEndTargetCalculated(mGestureState.getEndTarget(),
+ mActivityInterface));
+
notifyGestureStartedAsync();
}
@@ -423,7 +424,7 @@
}
protected void notifyGestureAnimationStartToRecents() {
- mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId());
+ mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
}
private void launcherFrameDrawn() {
@@ -1087,6 +1088,7 @@
anim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
if (mActivity != null) {
removeLiveTileOverlay();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index 33b9cde..d1da0c1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -140,7 +140,7 @@
}
@Override
- public void onExitOverview(RecentsAnimationDeviceState deviceState, Runnable exitRunnable) {
+ public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
// no-op, fake landscape not supported for 3P
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index fc7a119..f60a50b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -15,14 +15,35 @@
*/
package com.android.quickstep;
+import static android.content.Intent.EXTRA_COMPONENT_NAME;
+import static android.content.Intent.EXTRA_USER;
+
+import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT;
+import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION;
+import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
+import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.ObjectAnimator;
+import android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.ParcelUuid;
+import android.os.UserHandle;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import androidx.annotation.NonNull;
@@ -32,19 +53,33 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import java.lang.ref.WeakReference;
+import java.util.UUID;
+import java.util.function.Consumer;
+
/**
* Handles the navigation gestures when a 3rd party launcher is the default home activity.
*/
+@TargetApi(Build.VERSION_CODES.R)
public class FallbackSwipeHandler extends
BaseSwipeUpHandlerV2<RecentsActivity, FallbackRecentsView> {
+ /**
+ * Message used for receiving gesture nav contract information. We use a static messenger to
+ * avoid leaking too make binders in case the receiving launcher does not handle the contract
+ * properly.
+ */
+ private static StaticMessageReceiver sMessageReceiver = null;
+
private FallbackHomeAnimationFactory mActiveAnimationFactory;
private final boolean mRunningOverHome;
@@ -89,7 +124,9 @@
protected HomeAnimationFactory createHomeAnimationFactory(long duration) {
mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
- mContext.startActivity(new Intent(mGestureState.getHomeIntent()), options.toBundle());
+ Intent intent = new Intent(mGestureState.getHomeIntent());
+ mActiveAnimationFactory.addGestureContract(intent);
+ mContext.startActivity(intent, options.toBundle());
return mActiveAnimationFactory;
}
@@ -130,17 +167,20 @@
}
private class FallbackHomeAnimationFactory extends HomeAnimationFactory {
-
+ private final Rect mTempRect = new Rect();
private final TransformParams mHomeAlphaParams = new TransformParams();
private final AnimatedFloat mHomeAlpha;
private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat();
-
private final AnimatedFloat mRecentsAlpha = new AnimatedFloat();
+ private final RectF mTargetRect = new RectF();
+ private SurfaceControl mSurfaceControl;
+
private final long mDuration;
+
+ private RectFSpringAnim mSpringAnim;
FallbackHomeAnimationFactory(long duration) {
- super(null);
mDuration = duration;
if (mRunningOverHome) {
@@ -162,6 +202,15 @@
this::updateRecentsActivityTransformDuringHomeAnim);
}
+ @NonNull
+ @Override
+ public RectF getWindowTargetRect() {
+ if (mTargetRect.isEmpty()) {
+ mTargetRect.set(super.getWindowTargetRect());
+ }
+ return mTargetRect;
+ }
+
private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
RemoteAnimationTargetCompat app, TransformParams params) {
builder.withAlpha(mRecentsAlpha.value);
@@ -218,5 +267,87 @@
.start();
}
}
+
+ @Override
+ public void setAnimation(RectFSpringAnim anim) {
+ mSpringAnim = anim;
+ }
+
+ private void onMessageReceived(Message msg) {
+ try {
+ Bundle data = msg.getData();
+ RectF position = data.getParcelable(EXTRA_ICON_POSITION);
+ if (!position.isEmpty()) {
+ mSurfaceControl = data.getParcelable(EXTRA_ICON_SURFACE);
+ mTargetRect.set(position);
+ if (mSpringAnim != null) {
+ mSpringAnim.onTargetPositionChanged();
+ }
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
+ @Override
+ public void update(RectF currentRect, float progress, float radius) {
+ if (mSurfaceControl != null) {
+ currentRect.roundOut(mTempRect);
+ Transaction t = new Transaction();
+ t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
+ t.apply();
+ }
+ }
+
+ private void addGestureContract(Intent intent) {
+ if (mRunningOverHome || mGestureState.getRunningTask() == null) {
+ return;
+ }
+
+ TaskKey key = new TaskKey(mGestureState.getRunningTask());
+ if (key.getComponent() != null) {
+ if (sMessageReceiver == null) {
+ sMessageReceiver = new StaticMessageReceiver();
+ }
+
+ Bundle gestureNavContract = new Bundle();
+ gestureNavContract.putParcelable(EXTRA_COMPONENT_NAME, key.getComponent());
+ gestureNavContract.putParcelable(EXTRA_USER, UserHandle.of(key.userId));
+ gestureNavContract.putParcelable(EXTRA_REMOTE_CALLBACK,
+ sMessageReceiver.newCallback(this::onMessageReceived));
+ intent.putExtra(EXTRA_GESTURE_CONTRACT, gestureNavContract);
+ }
+ }
+ }
+
+ private static class StaticMessageReceiver implements Handler.Callback {
+
+ private final Messenger mMessenger =
+ new Messenger(new Handler(Looper.getMainLooper(), this));
+
+ private ParcelUuid mCurrentUID = new ParcelUuid(UUID.randomUUID());
+ private WeakReference<Consumer<Message>> mCurrentCallback = new WeakReference<>(null);
+
+ public Message newCallback(Consumer<Message> callback) {
+ mCurrentUID = new ParcelUuid(UUID.randomUUID());
+ mCurrentCallback = new WeakReference<>(callback);
+
+ Message msg = Message.obtain();
+ msg.replyTo = mMessenger;
+ msg.obj = mCurrentUID;
+ return msg;
+ }
+
+ @Override
+ public boolean handleMessage(@NonNull Message message) {
+ if (mCurrentUID.equals(message.obj)) {
+ Consumer<Message> consumer = mCurrentCallback.get();
+ if (consumer != null) {
+ consumer.accept(message);
+ return true;
+ }
+ }
+ return false;
+ }
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index edefbe1..7cd49fe 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -105,7 +105,7 @@
// recents, we assume the first task is invisible, making translation off by one task.
launcher.getStateManager().reapplyState();
launcher.getRootView().setForceHideBackArrow(false);
- notifyRecentsOfOrientation(deviceState);
+ notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
}
@Override
@@ -120,7 +120,7 @@
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
- notifyRecentsOfOrientation(deviceState);
+ notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
@@ -228,7 +228,7 @@
@Override
- public void onExitOverview(RecentsAnimationDeviceState deviceState, Runnable exitRunnable) {
+ public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
final StateManager<LauncherState> stateManager = getCreatedActivity().getStateManager();
stateManager.addStateListener(
new StateManager.StateListener<LauncherState>() {
@@ -244,11 +244,11 @@
});
}
- private void notifyRecentsOfOrientation(RecentsAnimationDeviceState deviceState) {
+ private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
// reset layout on swipe to home
RecentsView recentsView = getCreatedActivity().getOverviewPanel();
- recentsView.setLayoutRotation(deviceState.getCurrentActiveRotation(),
- deviceState.getDisplayRotation());
+ recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
+ rotationTouchHelper.getDisplayRotation());
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index fa7d268..052d0a6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.AnimatorSet;
import android.content.Context;
@@ -28,6 +29,7 @@
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.views.FloatingIconView;
+import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -72,36 +74,39 @@
mActivity.getRootView().setForceHideBackArrow(true);
mActivity.setHintUserWillBeActive();
- homeAnimFactory = new HomeAnimationFactory(floatingIconView) {
-
- @Override
- public RectF getWindowTargetRect() {
- if (canUseWorkspaceView) {
+ if (canUseWorkspaceView) {
+ // We want the window alpha to be 0 once this threshold is met, so that the
+ // FolderIconView can be seen morphing into the icon shape.
+ float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
+ homeAnimFactory = new LauncherHomeAnimationFactory() {
+ @Override
+ public RectF getWindowTargetRect() {
return iconLocation;
- } else {
- return super.getWindowTargetRect();
}
- }
- @NonNull
- @Override
- public AnimatorPlaybackController createActivityAnimationToHome() {
- // Return an empty APC here since we have an non-user controlled animation
- // to home.
- long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
- return mActivity.getStateManager().createAnimationToNewWorkspace(
- NORMAL, accuracy, 0 /* animComponents */);
- }
+ @Override
+ public void setAnimation(RectFSpringAnim anim) {
+ anim.addAnimatorListener(floatingIconView);
+ floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
+ floatingIconView.setFastFinishRunnable(anim::end);
+ }
- @Override
- public void playAtomicAnimation(float velocity) {
- new StaggeredWorkspaceAnim(mActivity, velocity,
- true /* animateOverviewScrim */).start();
- }
- };
+ @Override
+ public void update(RectF currentRect, float progress, float radius) {
+ floatingIconView.update(currentRect, 1f, progress, windowAlphaThreshold,
+ radius, false);
+ }
+ @Override
+ public void onCancel() {
+ floatingIconView.fastFinish();
+ }
+ };
+ } else {
+ homeAnimFactory = new LauncherHomeAnimationFactory();
+ }
} else {
- homeAnimFactory = new HomeAnimationFactory(null) {
+ homeAnimFactory = new HomeAnimationFactory() {
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
@@ -118,4 +123,22 @@
mRecentsAnimationController.finish(
true /* toRecents */, callback, true /* sendUserLeaveHint */);
}
+
+ private class LauncherHomeAnimationFactory extends HomeAnimationFactory {
+ @NonNull
+ @Override
+ public AnimatorPlaybackController createActivityAnimationToHome() {
+ // Return an empty APC here since we have an non-user controlled animation
+ // to home.
+ long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+ return mActivity.getStateManager().createAnimationToNewWorkspace(
+ NORMAL, accuracy, 0 /* animComponents */);
+ }
+
+ @Override
+ public void playAtomicAnimation(float velocity) {
+ new StaggeredWorkspaceAnim(mActivity, velocity,
+ true /* animateOverviewScrim */).start();
+ }
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index 434a929..4879db7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -165,7 +165,7 @@
mActivityInterface = mOverviewComponentObserver.getActivityInterface();
mCreateTime = SystemClock.elapsedRealtime();
mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,
- RecentsModel.getRunningTaskId(), mDeviceState);
+ ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
// Preload the plan
mRecentsModel.getTasks(null);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
index dc8f1c5..07faab7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.Animator;
import android.content.Context;
@@ -28,7 +27,6 @@
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
@@ -37,7 +35,6 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -85,7 +82,8 @@
mTransformParams = transformParams;
mTaskViewSimulator.setLayoutRotation(
- mDeviceState.getCurrentActiveRotation(), mDeviceState.getDisplayRotation());
+ mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
+ mDeviceState.getRotationTouchHelper().getDisplayRotation());
}
protected void initTransitionEndpoints(DeviceProfile dp) {
@@ -148,12 +146,6 @@
protected abstract class HomeAnimationFactory {
- public FloatingIconView mIconView;
-
- public HomeAnimationFactory(@Nullable FloatingIconView iconView) {
- mIconView = iconView;
- }
-
public @NonNull RectF getWindowTargetRect() {
PagedOrientationHandler orientationHandler = getOrientationHandler();
DeviceProfile dp = mDp;
@@ -174,6 +166,12 @@
public void playAtomicAnimation(float velocity) {
// No-op
}
+
+ public void setAnimation(RectFSpringAnim anim) { }
+
+ public void update(RectF currentRect, float progress, float radius) { }
+
+ public void onCancel() { }
}
/**
@@ -184,8 +182,6 @@
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
- final FloatingIconView fiv = homeAnimationFactory.mIconView;
- final boolean isFloatingIconView = fiv != null;
mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor);
mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
@@ -203,11 +199,7 @@
windowToHomePositionMap.mapRect(startRect);
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
- if (isFloatingIconView) {
- anim.addAnimatorListener(fiv);
- fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
- fiv.setFastFinishRunnable(anim::end);
- }
+ homeAnimationFactory.setAnimation(anim);
SpringAnimationRunner runner = new SpringAnimationRunner(
homeAnimationFactory, cropRectF, homeToWindowPositionMap);
@@ -242,32 +234,27 @@
final RectF mWindowCurrentRect = new RectF();
final Matrix mHomeToWindowPositionMap;
+ final HomeAnimationFactory mAnimationFactory;
- final FloatingIconView mFIV;
final AnimatorPlaybackController mHomeAnim;
final RectF mCropRectF;
final float mStartRadius;
final float mEndRadius;
- final float mWindowAlphaThreshold;
SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
Matrix homeToWindowPositionMap) {
+ mAnimationFactory = factory;
mHomeAnim = factory.createActivityAnimationToHome();
mCropRectF = cropRectF;
mHomeToWindowPositionMap = homeToWindowPositionMap;
cropRectF.roundOut(mCropRect);
- mFIV = factory.mIconView;
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
// rounding at the end of the animation.
mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
mEndRadius = cropRectF.width() / 2f;
-
- // We want the window alpha to be 0 once this threshold is met, so that the
- // FolderIconView can be seen morphing into the icon shape.
- mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f;
}
@Override
@@ -282,10 +269,7 @@
.setCornerRadius(cornerRadius);
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
- if (mFIV != null) {
- mFIV.update(currentRect, 1f, progress,
- mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false);
- }
+ mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
}
@Override
@@ -298,13 +282,12 @@
@Override
public void onCancel() {
- if (mFIV != null) {
- mFIV.fastFinish();
- }
+ mAnimationFactory.onCancel();
}
@Override
public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
mHomeAnim.dispatchOnStart();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index e9614d1..db512fa 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -19,6 +19,7 @@
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
import android.annotation.SuppressLint;
@@ -146,26 +147,29 @@
*/
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
boolean rotated) {
- final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+ getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
- getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
+ if (thumbnail != null) {
+ getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
+ final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
- getActionsView().setCallbacks(new OverlayUICallbacks() {
- @Override
- public void onShare() {
- if (isAllowedByPolicy) {
- mImageApi.startShareActivity();
- } else {
- showBlockedByPolicyMessage();
+ getActionsView().setCallbacks(new OverlayUICallbacks() {
+ @Override
+ public void onShare() {
+ if (isAllowedByPolicy) {
+ mImageApi.startShareActivity();
+ } else {
+ showBlockedByPolicyMessage();
+ }
}
- }
- @SuppressLint("NewApi")
- @Override
- public void onScreenshot() {
- saveScreenshot(task);
- }
- });
+ @SuppressLint("NewApi")
+ @Override
+ public void onScreenshot() {
+ saveScreenshot(task);
+ }
+ });
+ }
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 6e0b517..13adff5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -258,6 +258,7 @@
private static boolean sConnected = false;
private static boolean sIsInitialized = false;
+ private RotationTouchHelper mRotationTouchHelper;
public static boolean isConnected() {
return sConnected;
@@ -298,6 +299,7 @@
mDeviceState = new RecentsAnimationDeviceState(this);
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
+ mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
ProtoTracer.INSTANCE.get(this).add(this);
sConnected = true;
@@ -326,7 +328,7 @@
mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
mMainChoreographer, this::onInputEvent);
- mDeviceState.updateGestureTouchRegions();
+ mRotationTouchHelper.updateGestureTouchRegions();
}
/**
@@ -470,9 +472,9 @@
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SWIPE_TO_HOME, "TouchInteractionService.onInputEvent:DOWN");
}
- mDeviceState.setOrientationTransformIfNeeded(event);
+ mRotationTouchHelper.setOrientationTransformIfNeeded(event);
- if (mDeviceState.isInSwipeUpTouchRegion(event)) {
+ if (mRotationTouchHelper.isInSwipeUpTouchRegion(event)) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SWIPE_TO_HOME,
"TouchInteractionService.onInputEvent:isInSwipeUpTouchRegion");
@@ -509,7 +511,7 @@
// Other events
if (mUncheckedConsumer != InputConsumer.NO_OP) {
// Only transform the event if we are handling it in a proper consumer
- mDeviceState.setOrientationTransformIfNeeded(event);
+ mRotationTouchHelper.setOrientationTransformIfNeeded(event);
}
}
@@ -547,7 +549,7 @@
gestureState.updatePreviouslyAppearedTaskIds(
previousGestureState.getPreviouslyAppearedTaskIds());
} else {
- gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
+ gestureState.updateRunningTask(TraceHelper.allowIpcs("getRunningTask.0",
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
}
return gestureState;
@@ -660,7 +662,7 @@
if (AssistantUtilities.isExcludedAssistant(gestureState.getRunningTask())) {
// In the case where we are in the excluded assistant state, ignore it and treat the
// running activity as the task behind the assistant
- gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.assistant",
+ gestureState.updateRunningTask(TraceHelper.allowIpcs("getRunningTask.assistant",
() -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */)));
ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent();
ComponentName runningComponent =
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index d20bbe9..ffe9d6a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -76,7 +76,7 @@
*/
public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) {
mHomeTaskInfo = homeTaskInfo;
- onGestureAnimationStart(homeTaskInfo == null ? -1 : homeTaskInfo.taskId);
+ onGestureAnimationStart(homeTaskInfo);
}
/**
@@ -107,14 +107,15 @@
}
@Override
- protected boolean shouldAddDummyTaskView(int runningTaskId) {
- if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId
+ protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
+ if (mHomeTaskInfo != null && runningTaskInfo != null &&
+ mHomeTaskInfo.taskId == runningTaskInfo.taskId
&& getTaskViewCount() == 0) {
// Do not add a dummy task if we are running over home with empty recents, so that we
// show the empty recents message instead of showing a dummy task and later removing it.
return false;
}
- return super.shouldAddDummyTaskView(runningTaskId);
+ return super.shouldAddDummyTaskView(runningTaskInfo);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index 5ad48eb..0c2c92c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -99,7 +99,8 @@
case ACTION_POINTER_DOWN: {
if (mState == STATE_INACTIVE) {
int pointerIndex = ev.getActionIndex();
- if (mDeviceState.isInSwipeUpTouchRegion(ev, pointerIndex)
+ if (mDeviceState.getRotationTouchHelper()
+ .isInSwipeUpTouchRegion(ev, pointerIndex)
&& mDelegate.allowInterceptByParent()) {
setActive(ev);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 3a97216..a676390 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -147,7 +147,7 @@
if (!mThresholdCrossed) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) {
+ if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx)) {
int action = ev.getAction();
ev.setAction(ACTION_CANCEL);
finishTouchTracking(ev);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 26df9c7..6259f1f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -59,6 +59,7 @@
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
@@ -86,6 +87,7 @@
private final NavBarPosition mNavBarPosition;
private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
+ private final RotationTouchHelper mRotationTouchHelper;
private RecentsAnimationCallbacks mActiveCallbacks;
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
private final InputMonitorCompat mInputMonitorCompat;
@@ -163,6 +165,7 @@
mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
+ mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
}
@Override
@@ -230,7 +233,7 @@
if (!mPassedPilferInputSlop) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) {
+ if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx)) {
forceCancelGesture(ev);
}
}
@@ -424,7 +427,7 @@
@Override
public void notifyOrientationSetup() {
- mDeviceState.onStartGesture();
+ mRotationTouchHelper.onStartGesture();
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 3cafd42..4120331 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -205,13 +205,15 @@
ResourceProvider rp = DynamicResource.provider(v.getContext());
float stiffness = rp.getFloat(R.dimen.staggered_stiffness);
float damping = rp.getFloat(R.dimen.staggered_damping_ratio);
+ float endTransY = 0;
+ float springVelocity = Math.abs(mVelocity) * Math.signum(endTransY - mSpringTransY);
ValueAnimator springTransY = new SpringAnimationBuilder(v.getContext())
.setStiffness(stiffness)
.setDampingRatio(damping)
.setMinimumVisibleChange(1f)
.setStartValue(mSpringTransY)
- .setEndValue(0)
- .setStartVelocity(mVelocity)
+ .setEndValue(endTransY)
+ .setStartVelocity(springVelocity)
.build(v, VIEW_TRANSLATE_Y);
springTransY.setStartDelay(startDelay);
springTransY.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index c9ed498..8a6efe4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -129,8 +129,8 @@
/**
* @see com.android.quickstep.views.RecentsView#onConfigurationChanged(Configuration)
*/
- public void setRecentsConfiguration(Configuration configuration) {
- mOrientationState.setActivityConfiguration(configuration);
+ public void setRecentsRotation(int recentsRotation) {
+ mOrientationState.setRecentsRotation(recentsRotation);
mLayoutValid = false;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
index 0979c07..c06dd9c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -42,8 +43,10 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.Themes;
@@ -51,6 +54,7 @@
/**
* View used to educate the user on how to access All Apps when in No Nav Button navigation mode.
+ * Consumes all touches until after the animation is completed and the view is removed.
*/
public class AllAppsEduView extends AbstractFloatingView {
@@ -111,8 +115,18 @@
}
@Override
+ public boolean onBackPressed() {
+ return true;
+ }
+
+ @Override
+ public boolean canInterceptEventsInSystemGestureRegion() {
+ return true;
+ }
+
+ @Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- return mAnimation != null && mAnimation.isRunning();
+ return true;
}
private void playAnimation() {
@@ -139,7 +153,12 @@
config.userControlled = false;
AnimatorPlaybackController stateAnimationController =
mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, config);
- float maxAllAppsProgress = 0.15f;
+ float maxAllAppsProgress = mLauncher.getDeviceProfile().isLandscape ? 0.35f : 0.15f;
+
+ AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
+ PendingAnimation allAppsAlpha = new PendingAnimation(config.duration);
+ allAppsController.setAlphas(ALL_APPS, config, allAppsAlpha);
+ mAnimation.play(allAppsAlpha.buildAnim());
ValueAnimator intro = ValueAnimator.ofFloat(0, 1f);
intro.setInterpolator(LINEAR);
@@ -191,7 +210,8 @@
@Override
public void onAnimationEnd(Animator animation) {
mAnimation = null;
- stateAnimationController.dispatchOnCancel();
+ // Handles cancelling the animation used to hint towards All Apps.
+ mLauncher.getStateManager().goToState(NORMAL, false);
handleClose(false);
}
});
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 846b944..79028d1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -189,7 +189,7 @@
@Override
public boolean shouldUseMultiWindowTaskSizeStrategy() {
- return TraceHelper.whitelistIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode);
+ return TraceHelper.allowIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
index a2da398..79d57c5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
@@ -70,12 +70,14 @@
@IntDef(flag = true, value = {
DISABLED_SCROLLING,
- DISABLED_ROTATED})
+ DISABLED_ROTATED,
+ DISABLED_NO_THUMBNAIL})
@Retention(RetentionPolicy.SOURCE)
public @interface ActionsDisabledFlags { }
public static final int DISABLED_SCROLLING = 1 << 0;
public static final int DISABLED_ROTATED = 1 << 1;
+ public static final int DISABLED_NO_THUMBNAIL = 1 << 2;
private static final int INDEX_CONTENT_ALPHA = 0;
private static final int INDEX_VISIBILITY_ALPHA = 1;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 7b24b03..13a3f1a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -55,10 +55,8 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.app.ActivityManager;
-import android.content.ComponentName;
+import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Point;
@@ -80,6 +78,7 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -135,6 +134,7 @@
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.LauncherEventUtil;
@@ -147,7 +147,7 @@
/**
* A list of recent tasks.
*/
-@TargetApi(Build.VERSION_CODES.P)
+@TargetApi(Build.VERSION_CODES.R)
public abstract class RecentsView<T extends StatefulActivity> extends PagedView implements
Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
@@ -377,7 +377,7 @@
mOrientationState.setMultiWindowMode(inMultiWindowMode);
setLayoutRotation(mOrientationState.getTouchRotation(),
mOrientationState.getDisplayRotation());
- rotateAllChildTasks();
+ updateChildTaskOrientations();
}
if (!inMultiWindowMode && mOverviewStateEnabled) {
// TODO: Re-enable layout transitions for addition of the unpinned task
@@ -394,7 +394,7 @@
mActivity = BaseActivity.fromContext(context);
mOrientationState = new RecentsOrientedState(
context, mSizeStrategy, this::animateRecentsRotationInPlace);
- mOrientationState.setActivityConfiguration(context.getResources().getConfiguration());
+ mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation());
mFastFlingVelocity = getResources()
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
@@ -649,6 +649,16 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
+
+ TaskView taskView = getCurrentPageTaskView();
+ if (taskView != null) {
+ TouchDelegate mChildTouchDelegate = taskView.getIconTouchDelegate(ev);
+ if (mChildTouchDelegate != null && mChildTouchDelegate.onTouchEvent(ev)) {
+ // Keep consuming events to pass to delegate
+ return true;
+ }
+ }
+
final int x = (int) ev.getX();
final int y = (int) ev.getY();
switch (ev.getAction()) {
@@ -1041,13 +1051,13 @@
/**
* Called when a gesture from an app is starting.
*/
- public void onGestureAnimationStart(int runningTaskId) {
+ public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
updateOrientationHandler();
}
- showCurrentTask(runningTaskId);
+ showCurrentTask(runningTaskInfo);
setEnableFreeScroll(false);
setEnableDrawingLiveTile(false);
setRunningTaskHidden(true);
@@ -1078,7 +1088,7 @@
pa.addListener(AnimationSuccessListener.forRunnable(() -> {
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
mActivity.getDragLayer().recreateControllers();
- rotateAllChildTasks();
+ updateChildTaskOrientations();
setRecentsChangedOrientation(false).start();
}));
pa.start();
@@ -1099,7 +1109,7 @@
}
- private void rotateAllChildTasks() {
+ private void updateChildTaskOrientations() {
for (int i = 0; i < getTaskViewCount(); i++) {
getTaskViewAt(i).setOrientationState(mOrientationState);
}
@@ -1127,8 +1137,8 @@
/**
* Returns true if we should add a dummy taskView for the running task id
*/
- protected boolean shouldAddDummyTaskView(int runningTaskId) {
- return getTaskView(runningTaskId) == null;
+ protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
+ return runningTaskInfo != null && getTaskView(runningTaskInfo.taskId) == null;
}
/**
@@ -1137,8 +1147,8 @@
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
* is called. Also scrolls the view to this task.
*/
- public void showCurrentTask(int runningTaskId) {
- if (shouldAddDummyTaskView(runningTaskId)) {
+ public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
+ if (shouldAddDummyTaskView(runningTaskInfo)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = mTaskViewPool.getView();
@@ -1148,10 +1158,7 @@
}
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
- mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(),
- new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
- false, true, false, false, new ActivityManager.TaskDescription(), 0,
- new ComponentName("", ""), false);
+ mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false);
taskView.bind(mTmpRunningTask, mOrientationState);
// Measure and layout immediately so that the scroll values is updated instantly
@@ -1162,7 +1169,7 @@
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
- setCurrentTask(runningTaskId);
+ setCurrentTask(runningTaskInfo == null ? -1 : runningTaskInfo.taskId);
setCurrentPage(getRunningTaskIndex());
setRunningTaskViewShowScreenshot(false);
setRunningTaskHidden(runningTaskTileHidden);
@@ -1652,13 +1659,16 @@
super.setVisibility(visibility);
if (mActionsView != null) {
mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE);
+ if (visibility != VISIBLE) {
+ mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
+ }
}
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (mOrientationState.setActivityConfiguration(newConfig)) {
+ if (mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation())) {
updateOrientationHandler();
}
}
@@ -1680,10 +1690,11 @@
: View.LAYOUT_DIRECTION_RTL);
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
mActivity.getDragLayer().recreateControllers();
- boolean isInLandscape = mOrientationState.getTouchRotation() != 0
+ boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
|| mOrientationState.getRecentsActivityRotation() != ROTATION_0;
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
!mOrientationState.canRecentsActivityRotate() && isInLandscape);
+ updateChildTaskOrientations();
resetPaddingFromTaskSize();
requestLayout();
// Reapply the current page to update page scrolls.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
index ef66b7a..9b2048e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
@@ -169,7 +169,9 @@
}
if (mIsOpen) {
mOptionLayout.removeAllViews();
- populateAndLayoutMenu();
+ if (!populateAndLayoutMenu()) {
+ close(false);
+ }
}
}
@@ -186,14 +188,22 @@
}
mActivity.getDragLayer().addView(this);
mTaskView = taskView;
- populateAndLayoutMenu();
+ if (!populateAndLayoutMenu()) {
+ return false;
+ }
post(this::animateOpen);
return true;
}
- private void populateAndLayoutMenu() {
+ /** @return true if successfully able to populate task view menu, false otherwise */
+ private boolean populateAndLayoutMenu() {
+ if (mTaskView.getTask().icon == null) {
+ // Icon may not be loaded
+ return false;
+ }
addMenuOptions(mTaskView);
orientAroundTaskView(mTaskView);
+ return true;
}
private void addMenuOptions(TaskView taskView) {
@@ -240,8 +250,10 @@
setLayoutParams(params);
setScaleX(taskView.getScaleX());
setScaleY(taskView.getScaleY());
+ boolean canActivityRotate = taskView.getRecentsView()
+ .mOrientationState.canRecentsActivityRotate();
mOptionLayout.setOrientation(orientationHandler
- .getTaskMenuLayoutOrientation(mOptionLayout));
+ .getTaskMenuLayoutOrientation(canActivityRotate, mOptionLayout));
setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top,
taskView.getPagedOrientationHandler());
}
@@ -270,6 +282,7 @@
mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
setVisibility(VISIBLE);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index b2f937f..a8d6442 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -59,7 +59,6 @@
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ConfigurationCompat;
/**
* A task in the Recents view.
@@ -357,7 +356,7 @@
}
private void updateOverlay() {
- if (mOverlayEnabled && mBitmapShader != null && mThumbnailData != null) {
+ if (mOverlayEnabled) {
mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
mPreviewPositionHelper.mIsOrientationChanged);
} else {
@@ -385,8 +384,8 @@
if (mBitmapShader != null && mThumbnailData != null) {
mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
mThumbnailData.thumbnail.getHeight());
- int currentRotation = ConfigurationCompat.getWindowConfigurationRotation(
- mActivity.getResources().getConfiguration());
+ int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState()
+ .getRecentsActivityRotation();
mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
getMeasuredWidth(), getMeasuredHeight(), mActivity.getDeviceProfile(),
currentRotation);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 222f6e6..2058a7f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -22,16 +22,19 @@
import static android.view.Gravity.END;
import static android.view.Gravity.START;
import static android.view.Gravity.TOP;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.comp;
+import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
- .LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import android.animation.Animator;
@@ -53,7 +56,9 @@
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.Surface;
+import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -78,6 +83,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
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.TaskIconCache;
@@ -122,6 +128,13 @@
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
+ /**
+ * This technically can be a vanilla {@link TouchDelegate} class, however that class requires
+ * setting the touch bounds at construction, so we'd repeatedly be created many instances
+ * unnecessarily as scrolling occurs, whereas {@link TransformingTouchDelegate} allows touch
+ * delegated bounds only to be updated.
+ */
+ private TransformingTouchDelegate mIconTouchDelegate;
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
Collections.singletonList(new Rect());
@@ -186,6 +199,7 @@
private int mStackHeight;
private View mContextualChipWrapper;
private View mContextualChip;
+ private final float[] mIconCenterCoords = new float[2];
public TaskView(Context context) {
this(context, null);
@@ -246,6 +260,26 @@
super.onFinishInflate();
mSnapshotView = findViewById(R.id.snapshot);
mIconView = findViewById(R.id.icon);
+ mIconTouchDelegate = new TransformingTouchDelegate(mIconView);
+ }
+
+ public TouchDelegate getIconTouchDelegate(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ computeAndSetIconTouchDelegate();
+ }
+ return mIconTouchDelegate;
+ }
+
+ private void computeAndSetIconTouchDelegate() {
+ float iconHalfSize = mIconView.getWidth() / 2f;
+ mIconCenterCoords[0] = mIconCenterCoords[1] = iconHalfSize;
+ getDescendantCoordRelativeToAncestor(mIconView, mActivity.getDragLayer(), mIconCenterCoords,
+ false);
+ mIconTouchDelegate.setBounds(
+ (int) (mIconCenterCoords[0] - iconHalfSize),
+ (int) (mIconCenterCoords[1] - iconHalfSize),
+ (int) (mIconCenterCoords[0] + iconHalfSize),
+ (int) (mIconCenterCoords[1] + iconHalfSize));
}
/**
@@ -468,18 +502,18 @@
int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
switch (orientationHandler.getRotation()) {
- case Surface.ROTATION_90:
+ case ROTATION_90:
iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL;
iconParams.rightMargin = -thumbnailPadding;
iconParams.leftMargin = 0;
iconParams.topMargin = snapshotParams.topMargin / 2;
break;
- case Surface.ROTATION_180:
+ case ROTATION_180:
iconParams.gravity = BOTTOM | CENTER_HORIZONTAL;
iconParams.bottomMargin = -thumbnailPadding;
iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0;
break;
- case Surface.ROTATION_270:
+ case ROTATION_270:
iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
iconParams.leftMargin = -thumbnailPadding;
iconParams.rightMargin = 0;
diff --git a/quickstep/res/drawable/bg_circle.xml b/quickstep/res/drawable/bg_circle.xml
new file mode 100644
index 0000000..506177b
--- /dev/null
+++ b/quickstep/res/drawable/bg_circle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="#FFFFFFFF" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 459d65f..43bf0ea 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -24,6 +24,14 @@
android:layout_height="match_parent"
android:background="@drawable/gesture_tutorial_ripple"/>
+ <com.android.launcher3.views.ClipIconView
+ android:id="@+id/gesture_tutorial_fake_icon_view"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:background="@drawable/bg_circle"
+ android:backgroundTint="@color/gesture_tutorial_fake_task_view_color"
+ android:visibility="invisible" />
+
<View
android:id="@+id/gesture_tutorial_fake_task_view"
android:layout_width="match_parent"
@@ -41,81 +49,81 @@
android:id="@+id/gesture_tutorial_fragment_close_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="18dp"
- android:layout_marginTop="30dp"
- android:layout_marginStart="4dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
- android:background="@android:color/transparent"
+ android:layout_marginStart="4dp"
+ android:layout_marginTop="30dp"
android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_titles_container"
+ android:background="@android:color/transparent"
android:contentDescription="@string/gesture_tutorial_close_button_content_description"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/gesture_tutorial_close_button"/>
+ android:padding="18dp"
+ android:src="@drawable/gesture_tutorial_close_button"
+ android:tint="?android:attr/textColorPrimary"/>
<LinearLayout
android:id="@+id/gesture_tutorial_fragment_titles_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="70dp"
android:layout_alignParentTop="true"
+ android:layout_marginTop="70dp"
android:focusable="true"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/gesture_tutorial_fragment_title_view"
+ style="@style/TextAppearance.GestureTutorial.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/gesture_tutorial_title_margin_start_end"
- android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end"
- style="@style/TextAppearance.GestureTutorial.Title"/>
+ android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end"/>
<TextView
android:id="@+id/gesture_tutorial_fragment_subtitle_view"
+ style="@style/TextAppearance.GestureTutorial.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
android:layout_marginStart="@dimen/gesture_tutorial_subtitle_margin_start_end"
- android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end"
- style="@style/TextAppearance.GestureTutorial.Subtitle"/>
+ android:layout_marginTop="10dp"
+ android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end"/>
</LinearLayout>
<TextView
android:id="@+id/gesture_tutorial_fragment_feedback_view"
+ style="@style/TextAppearance.GestureTutorial.Feedback"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="10dp"
- android:layout_centerHorizontal="true"
android:layout_above="@id/gesture_tutorial_fragment_action_button"
+ android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end"
- style="@style/TextAppearance.GestureTutorial.Feedback"/>
+ android:layout_marginBottom="10dp"/>
<!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
of elevation and shadow) which is replaced by ripple effect in android:foreground -->
<Button
android:id="@+id/gesture_tutorial_fragment_action_button"
+ style="@style/TextAppearance.GestureTutorial.ButtonLabel"
android:layout_width="142dp"
android:layout_height="49dp"
- android:layout_marginEnd="@dimen/gesture_tutorial_button_margin_start_end"
- android:layout_marginBottom="48dp"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
- android:stateListAnimator="@null"
+ android:layout_marginEnd="@dimen/gesture_tutorial_button_margin_start_end"
+ android:layout_marginBottom="48dp"
android:background="@drawable/gesture_tutorial_action_button_background"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
- style="@style/TextAppearance.GestureTutorial.ButtonLabel"/>
+ android:stateListAnimator="@null"/>
<Button
android:id="@+id/gesture_tutorial_fragment_action_text_button"
+ style="@style/TextAppearance.GestureTutorial.TextButtonLabel"
android:layout_width="142dp"
android:layout_height="49dp"
- android:layout_marginStart="@dimen/gesture_tutorial_button_margin_start_end"
- android:layout_marginBottom="48dp"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
- android:stateListAnimator="@null"
+ android:layout_marginStart="@dimen/gesture_tutorial_button_margin_start_end"
+ android:layout_marginBottom="48dp"
android:background="@null"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
- style="@style/TextAppearance.GestureTutorial.TextButtonLabel"/>
+ android:stateListAnimator="@null"/>
</RelativeLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index e05688e..258f24a 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -17,9 +17,7 @@
<com.android.quickstep.views.OverviewActionsView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/overview_actions_height"
- android:layout_gravity="center_horizontal|bottom"
- android:layout_marginLeft="@dimen/overview_actions_horizontal_margin"
- android:layout_marginRight="@dimen/overview_actions_horizontal_margin">
+ android:layout_gravity="center_horizontal|bottom">
<LinearLayout
android:id="@+id/action_buttons"
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
index 93b64e6..c148a4b 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.util.LauncherUIHelper.doLayout;
+import android.app.ActivityManager.RunningTaskInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -50,7 +51,7 @@
}
@Test
- public void testRecets_showCurrentTask() {
+ public void testRecents_showCurrentTask() {
ActivityController<RecentsActivity> controller =
Robolectric.buildActivity(RecentsActivity.class);
@@ -58,7 +59,10 @@
doLayout(activity);
FallbackRecentsView frv = activity.getOverviewPanel();
- frv.showCurrentTask(22);
+
+ RunningTaskInfo dummyTask = new RunningTaskInfo();
+ dummyTask.taskId = 22;
+ frv.showCurrentTask(dummyTask);
doLayout(activity);
ThumbnailData thumbnailData = new ThumbnailData();
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 47ce320..235df42 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -111,6 +111,13 @@
}
@Override
+ protected void handleGestureContract(Intent intent) {
+ if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) {
+ super.handleGestureContract(intent);
+ }
+ }
+
+ @Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
RecentsModel.INSTANCE.get(this).onTrimMemory(level);
@@ -157,6 +164,12 @@
@Override
protected void onDeferredResumed() {
super.onDeferredResumed();
+ handlePendingActivityRequest();
+ }
+
+ @Override
+ protected void handlePendingActivityRequest() {
+ super.handlePendingActivityRequest();
if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
// Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index fe7b946..bd97f65 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -16,8 +16,6 @@
package com.android.launcher3;
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
@@ -62,7 +60,6 @@
import android.os.Handler;
import android.os.Looper;
import android.util.Pair;
-import android.util.TypedValue;
import android.view.View;
import androidx.annotation.NonNull;
@@ -875,10 +872,8 @@
}
});
} else {
- float velocityDpPerS = DynamicResource.provider(mLauncher)
+ float velocityPxPerS = DynamicResource.provider(mLauncher)
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
- float velocityPxPerS = TypedValue.applyDimension(COMPLEX_UNIT_DIP,
- velocityDpPerS, mLauncher.getResources().getDisplayMetrics());
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocityPxPerS, false)
.getAnimators());
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 2b698bd..5600589 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -150,7 +150,7 @@
return deviceState.isInDeferredGestureRegion(ev);
}
- public abstract void onExitOverview(RecentsAnimationDeviceState deviceState,
+ public abstract void onExitOverview(RotationTouchHelper deviceState,
Runnable exitRunnable);
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 0a70bd6..7a6bbb4 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -16,11 +16,9 @@
package com.android.quickstep;
import static android.content.Intent.ACTION_USER_UNLOCKED;
-import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
@@ -49,23 +47,19 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.view.MotionEvent;
-import android.view.OrientationEventListener;
import androidx.annotation.BinderThread;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.SecureSettingsObserver;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.util.NavBarPosition;
-import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
-import com.android.systemui.shared.system.TaskStackChangeListener;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -83,7 +77,7 @@
private final SysUINavigationMode mSysUiNavMode;
private final DefaultDisplay mDefaultDisplay;
private final int mDisplayId;
- private int mDisplayRotation;
+ private final RotationTouchHelper mRotationTouchHelper;
private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
@@ -107,76 +101,10 @@
}
};
- private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
- @Override
- public void onRecentTaskListFrozenChanged(boolean frozen) {
- mTaskListFrozen = frozen;
- if (frozen || mInOverview) {
- return;
- }
- enableMultipleRegions(false);
- }
-
- @Override
- public void onActivityRotation(int displayId) {
- super.onActivityRotation(displayId);
- // This always gets called before onDisplayInfoChanged() so we know how to process
- // the rotation in that method. This is done to avoid having a race condition between
- // the sensor readings and onDisplayInfoChanged() call
- if (displayId != mDisplayId) {
- return;
- }
-
- mPrioritizeDeviceRotation = true;
- if (mInOverview) {
- // reset, launcher must be rotating
- mExitOverviewRunnable.run();
- }
- }
- };
-
- private Runnable mExitOverviewRunnable = new Runnable() {
- @Override
- public void run() {
- mInOverview = false;
- enableMultipleRegions(false);
- }
- };
-
- private OrientationTouchTransformer mOrientationTouchTransformer;
- /**
- * Used to listen for when the device rotates into the orientation of the current
- * foreground app. For example, if a user quickswitches from a portrait to a fixed landscape
- * app and then rotates rotates the device to match that orientation, this triggers calls to
- * sysui to adjust the navbar.
- */
- private OrientationEventListener mOrientationListener;
- private int mSensorRotation = ROTATION_0;
- /**
- * This is the configuration of the foreground app or the app that will be in the foreground
- * once a quickstep gesture finishes.
- */
- private int mCurrentAppRotation = -1;
- /**
- * This flag is set to true when the device physically changes orientations. When true,
- * we will always report the current rotation of the foreground app whenever the display
- * changes, as it would indicate the user's intention to rotate the foreground app.
- */
- private boolean mPrioritizeDeviceRotation = false;
-
private Region mExclusionRegion;
private SystemGestureExclusionListenerCompat mExclusionListener;
private final List<ComponentName> mGestureBlockedActivities;
- private Runnable mOnDestroyFrozenTaskRunnable;
- /**
- * Set to true when user swipes to recents. In recents, we ignore the state of the recents
- * task list being frozen or not to allow the user to keep interacting with nav bar rotation
- * they went into recents with as opposed to defaulting to the default display rotation.
- * TODO: (b/156984037) For when user rotates after entering overview
- */
- private boolean mInOverview;
- private boolean mTaskListFrozen;
private boolean mIsUserSetupComplete;
@@ -186,6 +114,8 @@
mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
mDisplayId = mDefaultDisplay.getInfo().id;
runOnDestroy(() -> mDefaultDisplay.removeChangeListener(this));
+ mRotationTouchHelper = new RotationTouchHelper(context);
+ runOnDestroy(mRotationTouchHelper::destroy);
// Register for user unlocked if necessary
mIsUserUnlocked = context.getSystemService(UserManager.class)
@@ -207,10 +137,6 @@
};
runOnDestroy(mExclusionListener::unregister);
- Resources resources = mContext.getResources();
- mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
- () -> QuickStepContract.getWindowCornerRadius(resources));
-
// Register for navigation mode changes
onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
@@ -241,38 +167,6 @@
userSetupObserver.register();
runOnDestroy(userSetupObserver::unregister);
}
-
- mOrientationListener = new OrientationEventListener(context) {
- @Override
- public void onOrientationChanged(int degrees) {
- int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
- mSensorRotation);
- if (newRotation == mSensorRotation) {
- return;
- }
-
- mSensorRotation = newRotation;
- mPrioritizeDeviceRotation = true;
-
- if (newRotation == mCurrentAppRotation) {
- // When user rotates device to the orientation of the foreground app after
- // quickstepping
- toggleSecondaryNavBarsForRotation();
- }
- }
- };
- }
-
- private void setupOrientationSwipeHandler() {
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
- mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
- .unregisterTaskStackListener(mFrozenTaskListener);
- runOnDestroy(mOnDestroyFrozenTaskRunnable);
- }
-
- private void destroyOrientationSwipeHandlerCallback() {
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
- mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
}
private void runOnDestroy(Runnable action) {
@@ -311,13 +205,6 @@
mNavBarPosition = new NavBarPosition(newMode, mDefaultDisplay.getInfo());
- mOrientationTouchTransformer.setNavigationMode(newMode, mDefaultDisplay.getInfo());
- if (!mMode.hasGestures && newMode.hasGestures) {
- setupOrientationSwipeHandler();
- } else if (mMode.hasGestures && !newMode.hasGestures){
- destroyOrientationSwipeHandlerCallback();
- }
-
mMode = newMode;
}
@@ -328,28 +215,10 @@
return;
}
- mDisplayRotation = info.rotation;
-
if (!mMode.hasGestures) {
return;
}
mNavBarPosition = new NavBarPosition(mMode, info);
- updateGestureTouchRegions();
- mOrientationTouchTransformer.createOrAddTouchRegion(info);
- mCurrentAppRotation = mDisplayRotation;
-
- /* Update nav bars on the following:
- * a) if this is coming from an activity rotation OR
- * aa) we launch an app in the orientation that user is already in
- * b) We're not in overview, since overview will always be portrait (w/o home rotation)
- * c) We're actively in quickswitch mode
- */
- if ((mPrioritizeDeviceRotation
- || mCurrentAppRotation == mSensorRotation) // switch to an app of orientation user is in
- && !mInOverview
- && mTaskListFrozen) {
- toggleSecondaryNavBarsForRotation();
- }
}
/**
@@ -464,7 +333,7 @@
*/
public boolean canStartSystemGesture() {
boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
- || mTaskListFrozen;
+ || mRotationTouchHelper.isTaskListFrozen();
return canStartWithNavHidden
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
@@ -537,33 +406,6 @@
}
/**
- * Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
- */
- public void updateGestureTouchRegions() {
- if (!mMode.hasGestures) {
- return;
- }
-
- mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo());
- }
-
- /**
- * @return whether the coordinates of the {@param event} is in the swipe up gesture region.
- */
- public boolean isInSwipeUpTouchRegion(MotionEvent event) {
- return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY());
- }
-
- /**
- * @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
- * is in the swipe up gesture region.
- */
- public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
- return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
- event.getY(pointerIndex));
- }
-
- /**
* Sets the region in screen space where the gestures should be deferred (ie. due to specific
* nav bar ui).
*/
@@ -620,101 +462,13 @@
public boolean canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task) {
return mAssistantAvailable
&& !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
- && mOrientationTouchTransformer.touchInAssistantRegion(ev)
+ && mRotationTouchHelper.touchInAssistantRegion(ev)
&& !isLockToAppActive()
&& !isGestureBlockedActivity(task);
}
- /**
- * *May* apply a transform on the motion event if it lies in the nav bar region for another
- * orientation that is currently being tracked as a part of quickstep
- */
- void setOrientationTransformIfNeeded(MotionEvent event) {
- // negative coordinates bug b/143901881
- if (event.getX() < 0 || event.getY() < 0) {
- event.setLocation(Math.max(0, event.getX()), Math.max(0, event.getY()));
- }
- mOrientationTouchTransformer.transform(event);
- }
-
- private void enableMultipleRegions(boolean enable) {
- mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
- notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
- if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
- // Clear any previous state from sensor manager
- mSensorRotation = mCurrentAppRotation;
- mOrientationListener.enable();
- } else {
- mOrientationListener.disable();
- }
- }
-
- public void onStartGesture() {
- if (mTaskListFrozen) {
- // Prioritize whatever nav bar user touches once in quickstep
- // This case is specifically when user changes what nav bar they are using mid
- // quickswitch session before tasks list is unfrozen
- notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
- }
- }
-
- void onEndTargetCalculated(GestureState.GestureEndTarget endTarget,
- BaseActivityInterface activityInterface) {
- if (endTarget == GestureState.GestureEndTarget.RECENTS) {
- mInOverview = true;
- if (!mTaskListFrozen) {
- // If we're in landscape w/o ever quickswitching, show the navbar in landscape
- enableMultipleRegions(true);
- }
- activityInterface.onExitOverview(this, mExitOverviewRunnable);
- } else if (endTarget == GestureState.GestureEndTarget.HOME) {
- enableMultipleRegions(false);
- } else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) {
- if (mOrientationTouchTransformer.getQuickStepStartingRotation() == -1) {
- // First gesture to start quickswitch
- enableMultipleRegions(true);
- } else {
- notifySysuiOfCurrentRotation(
- mOrientationTouchTransformer.getCurrentActiveRotation());
- }
-
- // A new gesture is starting, reset the current device rotation
- // This is done under the assumption that the user won't rotate the phone and then
- // quickswitch in the old orientation.
- mPrioritizeDeviceRotation = false;
- } else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) {
- if (!mTaskListFrozen) {
- // touched nav bar but didn't go anywhere and not quickswitching, do nothing
- return;
- }
- notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
- }
- }
-
- private void notifySysuiOfCurrentRotation(int rotation) {
- UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
- .onQuickSwitchToNewTask(rotation));
- }
-
- /**
- * Disables/Enables multiple nav bars on {@link OrientationTouchTransformer} and then
- * notifies system UI of the primary rotation the user is interacting with
- */
- private void toggleSecondaryNavBarsForRotation() {
- mOrientationTouchTransformer.setSingleActiveRegion(mDefaultDisplay.getInfo());
- notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
- }
-
- public int getCurrentActiveRotation() {
- if (!mMode.hasGestures) {
- // touch rotation should always match that of display for 3 button
- return mDisplayRotation;
- }
- return mOrientationTouchTransformer.getCurrentActiveRotation();
- }
-
- public int getDisplayRotation() {
- return mDisplayRotation;
+ public RotationTouchHelper getRotationTouchHelper() {
+ return mRotationTouchHelper;
}
public void dump(PrintWriter pw) {
@@ -726,9 +480,7 @@
pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
- pw.println(" currentActiveRotation=" + getCurrentActiveRotation());
- pw.println(" displayRotation=" + getDisplayRotation());
pw.println(" isUserUnlocked=" + mIsUserUnlocked);
- mOrientationTouchTransformer.dump(pw);
+ mRotationTouchHelper.dump(pw);
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 517501a..6f54ba2 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -93,15 +93,6 @@
}
/**
- * @return The task id of the running task, or -1 if there is no current running task.
- */
- public static int getRunningTaskId() {
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- return runningTask != null ? runningTask.id : -1;
- }
-
- /**
* @return Whether the provided {@param changeId} is the latest recent tasks list id.
*/
public boolean isTaskListValid(int changeId) {
@@ -140,7 +131,9 @@
}
// Keep the cache up to date with the latest thumbnails
- int runningTaskId = RecentsModel.getRunningTaskId();
+ ActivityManager.RunningTaskInfo runningTask =
+ ActivityManagerWrapper.getInstance().getRunningTask();
+ int runningTaskId = runningTask != null ? runningTask.id : -1;
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
for (Task task : tasks) {
if (task.key.id == runningTaskId) {
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
new file mode 100644
index 0000000..5f3c022
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static android.view.Surface.ROTATION_0;
+
+import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
+import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.MotionEvent;
+import android.view.OrientationEventListener;
+
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class RotationTouchHelper implements
+ SysUINavigationMode.NavigationModeChangeListener,
+ DefaultDisplay.DisplayInfoChangeListener {
+
+ private final OrientationTouchTransformer mOrientationTouchTransformer;
+ private final DefaultDisplay mDefaultDisplay;
+ private final SysUINavigationMode mSysUiNavMode;
+ private final int mDisplayId;
+ private int mDisplayRotation;
+
+ private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
+
+ private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
+
+ private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
+ @Override
+ public void onRecentTaskListFrozenChanged(boolean frozen) {
+ mTaskListFrozen = frozen;
+ if (frozen || mInOverview) {
+ return;
+ }
+ enableMultipleRegions(false);
+ }
+
+ @Override
+ public void onActivityRotation(int displayId) {
+ super.onActivityRotation(displayId);
+ // This always gets called before onDisplayInfoChanged() so we know how to process
+ // the rotation in that method. This is done to avoid having a race condition between
+ // the sensor readings and onDisplayInfoChanged() call
+ if (displayId != mDisplayId) {
+ return;
+ }
+
+ mPrioritizeDeviceRotation = true;
+ if (mInOverview) {
+ // reset, launcher must be rotating
+ mExitOverviewRunnable.run();
+ }
+ }
+ };
+
+ private Runnable mExitOverviewRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mInOverview = false;
+ enableMultipleRegions(false);
+ }
+ };
+
+ /**
+ * Used to listen for when the device rotates into the orientation of the current foreground
+ * app. For example, if a user quickswitches from a portrait to a fixed landscape app and then
+ * rotates rotates the device to match that orientation, this triggers calls to sysui to adjust
+ * the navbar.
+ */
+ private OrientationEventListener mOrientationListener;
+ private int mSensorRotation = ROTATION_0;
+ /**
+ * This is the configuration of the foreground app or the app that will be in the foreground
+ * once a quickstep gesture finishes.
+ */
+ private int mCurrentAppRotation = -1;
+ /**
+ * This flag is set to true when the device physically changes orientations. When true, we will
+ * always report the current rotation of the foreground app whenever the display changes, as it
+ * would indicate the user's intention to rotate the foreground app.
+ */
+ private boolean mPrioritizeDeviceRotation = false;
+ private Runnable mOnDestroyFrozenTaskRunnable;
+ /**
+ * Set to true when user swipes to recents. In recents, we ignore the state of the recents
+ * task list being frozen or not to allow the user to keep interacting with nav bar rotation
+ * they went into recents with as opposed to defaulting to the default display rotation.
+ * TODO: (b/156984037) For when user rotates after entering overview
+ */
+ private boolean mInOverview;
+ private boolean mTaskListFrozen;
+
+
+ private final Context mContext;
+
+ public RotationTouchHelper(Context context) {
+ mContext = context;
+ Resources resources = mContext.getResources();
+ mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
+ mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
+ mDisplayId = mDefaultDisplay.getInfo().id;
+
+ mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
+ () -> QuickStepContract.getWindowCornerRadius(resources));
+
+ // Register for navigation mode changes
+ onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
+ runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
+
+ mOrientationListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int degrees) {
+ int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
+ mSensorRotation);
+ if (newRotation == mSensorRotation) {
+ return;
+ }
+
+ mSensorRotation = newRotation;
+ mPrioritizeDeviceRotation = true;
+
+ if (newRotation == mCurrentAppRotation) {
+ // When user rotates device to the orientation of the foreground app after
+ // quickstepping
+ toggleSecondaryNavBarsForRotation();
+ }
+ }
+ };
+ }
+
+ private void setupOrientationSwipeHandler() {
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
+ mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
+ .unregisterTaskStackListener(mFrozenTaskListener);
+ runOnDestroy(mOnDestroyFrozenTaskRunnable);
+ }
+
+ private void destroyOrientationSwipeHandlerCallback() {
+ ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
+ mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
+ }
+
+ private void runOnDestroy(Runnable action) {
+ mOnDestroyActions.add(action);
+ }
+
+ /**
+ * Cleans up all the registered listeners and receivers.
+ */
+ public void destroy() {
+ for (Runnable r : mOnDestroyActions) {
+ r.run();
+ }
+ }
+
+ public boolean isTaskListFrozen() {
+ return mTaskListFrozen;
+ }
+
+ public boolean touchInAssistantRegion(MotionEvent ev) {
+ return mOrientationTouchTransformer.touchInAssistantRegion(ev);
+ }
+
+ /**
+ * Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
+ */
+ public void updateGestureTouchRegions() {
+ if (!mMode.hasGestures) {
+ return;
+ }
+
+ mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo());
+ }
+
+ /**
+ * @return whether the coordinates of the {@param event} is in the swipe up gesture region.
+ */
+ public boolean isInSwipeUpTouchRegion(MotionEvent event) {
+ return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY());
+ }
+
+ /**
+ * @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
+ * is in the swipe up gesture region.
+ */
+ public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
+ return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
+ event.getY(pointerIndex));
+ }
+
+
+ @Override
+ public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
+ mDefaultDisplay.removeChangeListener(this);
+ mDefaultDisplay.addChangeListener(this);
+ onDisplayInfoChanged(mDefaultDisplay.getInfo(), CHANGE_ALL);
+
+ mOrientationTouchTransformer.setNavigationMode(newMode, mDefaultDisplay.getInfo());
+ if (!mMode.hasGestures && newMode.hasGestures) {
+ setupOrientationSwipeHandler();
+ } else if (mMode.hasGestures && !newMode.hasGestures){
+ destroyOrientationSwipeHandlerCallback();
+ }
+
+ mMode = newMode;
+ }
+
+ public int getDisplayRotation() {
+ return mDisplayRotation;
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
+ if (info.id != mDisplayId|| flags == CHANGE_FRAME_DELAY) {
+ // ignore displays that aren't running launcher and frame refresh rate changes
+ return;
+ }
+
+ mDisplayRotation = info.rotation;
+
+ if (!mMode.hasGestures) {
+ return;
+ }
+ updateGestureTouchRegions();
+ mOrientationTouchTransformer.createOrAddTouchRegion(info);
+ mCurrentAppRotation = mDisplayRotation;
+
+ /* Update nav bars on the following:
+ * a) if this is coming from an activity rotation OR
+ * aa) we launch an app in the orientation that user is already in
+ * b) We're not in overview, since overview will always be portrait (w/o home rotation)
+ * c) We're actively in quickswitch mode
+ */
+ if ((mPrioritizeDeviceRotation
+ || mCurrentAppRotation == mSensorRotation) // switch to an app of orientation user is in
+ && !mInOverview
+ && mTaskListFrozen) {
+ toggleSecondaryNavBarsForRotation();
+ }
+ }
+
+ /**
+ * *May* apply a transform on the motion event if it lies in the nav bar region for another
+ * orientation that is currently being tracked as a part of quickstep
+ */
+ void setOrientationTransformIfNeeded(MotionEvent event) {
+ // negative coordinates bug b/143901881
+ if (event.getX() < 0 || event.getY() < 0) {
+ event.setLocation(Math.max(0, event.getX()), Math.max(0, event.getY()));
+ }
+ mOrientationTouchTransformer.transform(event);
+ }
+
+ private void enableMultipleRegions(boolean enable) {
+ mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
+ notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
+ if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
+ // Clear any previous state from sensor manager
+ mSensorRotation = mCurrentAppRotation;
+ mOrientationListener.enable();
+ } else {
+ mOrientationListener.disable();
+ }
+ }
+
+ public void onStartGesture() {
+ if (mTaskListFrozen) {
+ // Prioritize whatever nav bar user touches once in quickstep
+ // This case is specifically when user changes what nav bar they are using mid
+ // quickswitch session before tasks list is unfrozen
+ notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
+ }
+ }
+
+ void onEndTargetCalculated(GestureState.GestureEndTarget endTarget,
+ BaseActivityInterface activityInterface) {
+ if (endTarget == GestureState.GestureEndTarget.RECENTS) {
+ mInOverview = true;
+ if (!mTaskListFrozen) {
+ // If we're in landscape w/o ever quickswitching, show the navbar in landscape
+ enableMultipleRegions(true);
+ }
+ activityInterface.onExitOverview(this, mExitOverviewRunnable);
+ } else if (endTarget == GestureState.GestureEndTarget.HOME) {
+ enableMultipleRegions(false);
+ } else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) {
+ if (mOrientationTouchTransformer.getQuickStepStartingRotation() == -1) {
+ // First gesture to start quickswitch
+ enableMultipleRegions(true);
+ } else {
+ notifySysuiOfCurrentRotation(
+ mOrientationTouchTransformer.getCurrentActiveRotation());
+ }
+
+ // A new gesture is starting, reset the current device rotation
+ // This is done under the assumption that the user won't rotate the phone and then
+ // quickswitch in the old orientation.
+ mPrioritizeDeviceRotation = false;
+ } else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) {
+ if (!mTaskListFrozen) {
+ // touched nav bar but didn't go anywhere and not quickswitching, do nothing
+ return;
+ }
+ notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
+ }
+ }
+
+ private void notifySysuiOfCurrentRotation(int rotation) {
+ UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
+ .onQuickSwitchToNewTask(rotation));
+ }
+
+ /**
+ * Disables/Enables multiple nav bars on {@link OrientationTouchTransformer} and then
+ * notifies system UI of the primary rotation the user is interacting with
+ */
+ private void toggleSecondaryNavBarsForRotation() {
+ mOrientationTouchTransformer.setSingleActiveRegion(mDefaultDisplay.getInfo());
+ notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
+ }
+
+ public int getCurrentActiveRotation() {
+ if (!mMode.hasGestures) {
+ // touch rotation should always match that of display for 3 button
+ return mDisplayRotation;
+ }
+ return mOrientationTouchTransformer.getCurrentActiveRotation();
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("RotationTouchHelper:");
+ pw.println(" currentActiveRotation=" + getCurrentActiveRotation());
+ pw.println(" displayRotation=" + getDisplayRotation());
+ mOrientationTouchTransformer.dump(pw);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 14e00dc..4110b33 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION;
import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
@@ -110,6 +111,7 @@
AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
+ mFakeIconView.setVisibility(View.INVISIBLE);
mFakeTaskView.setVisibility(View.INVISIBLE);
mFakeTaskView.setAlpha(1);
mRunningWindowAnim = null;
@@ -131,6 +133,7 @@
});
} else {
anim.setViewAlpha(mFakeTaskView, 0, ACCEL);
+ anim.setViewAlpha(mFakeIconView, 0, ACCEL);
anim.addListener(resetTaskView);
}
if (onEndRunnable != null) {
@@ -202,7 +205,7 @@
// derivative of the scroll interpolator at zero, ie. 2.
long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y));
long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
- HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) {
+ HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory() {
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
@@ -218,6 +221,24 @@
fakeHomeIconLeft + fakeHomeIconSizePx,
fakeHomeIconTop + fakeHomeIconSizePx);
}
+
+ @Override
+ public void update(RectF rect, float progress, float radius) {
+ mFakeIconView.setVisibility(View.VISIBLE);
+ mFakeIconView.update(rect, progress,
+ 1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */,
+ radius,
+ false, /* isOpening */
+ mFakeIconView, mDp,
+ false /* isVerticalBarLayout */);
+ mFakeIconView.setAlpha(1);
+ mFakeTaskView.setAlpha(getWindowAlpha(progress));
+ }
+
+ @Override
+ public void onCancel() {
+ mFakeIconView.setVisibility(View.INVISIBLE);
+ }
};
RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory);
windowAnim.start(mContext, velocityPxPerMs);
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index c1918c2..73f1f8c 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -28,6 +28,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
@@ -46,6 +47,7 @@
final TextView mTitleTextView;
final TextView mSubtitleTextView;
final TextView mFeedbackView;
+ final ClipIconView mFakeIconView;
final View mFakeTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
@@ -66,6 +68,7 @@
mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
+ mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index eac45e9..a89319e 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,6 +16,10 @@
package com.android.quickstep.logging;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.formatElapsedTime;
+
+import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
@@ -24,6 +28,8 @@
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
+import static java.lang.System.currentTimeMillis;
+
import android.content.Context;
import android.util.Log;
@@ -33,6 +39,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.FolderContainer.ParentContainerCase;
import com.android.launcher3.logger.LauncherAtom.FolderIcon;
import com.android.launcher3.logger.LauncherAtom.FromState;
import com.android.launcher3.logger.LauncherAtom.ToState;
@@ -59,17 +66,18 @@
* This class calls StatsLog compile time generated methods.
*
* To see if the logs are properly sent to statsd, execute following command.
+ * <ul>
* $ wwdebug (to turn on the logcat printout)
* $ wwlogcat (see logcat with grep filter on)
* $ statsd_testdrive (see how ww is writing the proto to statsd buffer)
+ * </ul>
*/
public class StatsLogCompatManager extends StatsLogManager {
private static final String TAG = "StatsLog";
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
- private static Context sContext;
-
+ private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
// from nano to lite, bake constant to prevent robo test failure.
@@ -77,8 +85,10 @@
private static final int FOLDER_HIERARCHY_OFFSET = 100;
private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
+ private final Context mContext;
+
public StatsLogCompatManager(Context context) {
- sContext = context;
+ mContext = context;
}
@Override
@@ -104,12 +114,14 @@
*/
@Override
public void logSnapshot() {
- LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
+ LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
new SnapshotWorker());
}
private class SnapshotWorker extends BaseModelUpdateTask {
+
private final InstanceId mInstanceId;
+
SnapshotWorker() {
mInstanceId = new InstanceIdSequence(
1 << 20 /*InstanceId.INSTANCE_ID_MAX*/).newInstanceId();
@@ -117,6 +129,20 @@
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ long lastSnapshotTimeMillis = getDevicePrefs(mContext)
+ .getLong(LAST_SNAPSHOT_TIME_MILLIS, 0);
+ // Log snapshot only if previous snapshot was older than a day
+ if (currentTimeMillis() - lastSnapshotTimeMillis < DAY_IN_MILLIS) {
+ if (IS_VERBOSE) {
+ String elapsedTime = formatElapsedTime(
+ (currentTimeMillis() - lastSnapshotTimeMillis) / 1000);
+ Log.d(TAG, String.format(
+ "Skipped snapshot logging since previous snapshot was %s old.",
+ elapsedTime));
+ }
+ return;
+ }
+
IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
ArrayList<ItemInfo> workspaceItems = (ArrayList) dataModel.workspaceItems.clone();
ArrayList<LauncherAppWidgetInfo> appWidgets = (ArrayList) dataModel.appWidgets.clone();
@@ -132,16 +158,19 @@
LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
writeSnapshot(atomInfo, mInstanceId);
}
- } catch (Exception e) { }
+ } catch (Exception e) {
+ }
}
for (ItemInfo info : appWidgets) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
writeSnapshot(atomInfo, mInstanceId);
}
+ getDevicePrefs(mContext).edit()
+ .putLong(LAST_SNAPSHOT_TIME_MILLIS, currentTimeMillis()).apply();
}
}
- private static void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
+ private void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
if (IS_VERBOSE) {
Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.getId(), info));
}
@@ -260,7 +289,7 @@
} else {
// Item is inside the folder, fetch folder info in a BG thread
// and then write to StatsLog.
- LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
+ LauncherAppState.getInstanceNoCreate().getModel().enqueueModelUpdateTask(
new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel,
@@ -337,7 +366,7 @@
}
private static int getCardinality(LauncherAtom.ItemInfo info) {
- switch (info.getContainerInfo().getContainerCase()){
+ switch (info.getContainerInfo().getContainerCase()) {
case PREDICTED_HOTSEAT_CONTAINER:
return info.getContainerInfo().getPredictedHotseatContainer().getCardinality();
case SEARCH_RESULT_CONTAINER:
@@ -402,9 +431,16 @@
}
private static int getPageId(LauncherAtom.ItemInfo info) {
+ if (info.hasTask()) {
+ return info.getTask().getIndex();
+ }
switch (info.getContainerInfo().getContainerCase()) {
case FOLDER:
return info.getContainerInfo().getFolder().getPageIndex();
+ case HOTSEAT:
+ return info.getContainerInfo().getHotseat().getIndex();
+ case PREDICTED_HOTSEAT_CONTAINER:
+ return info.getContainerInfo().getPredictedHotseatContainer().getIndex();
default:
return info.getContainerInfo().getWorkspace().getPageIndex();
}
@@ -413,6 +449,10 @@
private static int getParentPageId(LauncherAtom.ItemInfo info) {
switch (info.getContainerInfo().getContainerCase()) {
case FOLDER:
+ if (info.getContainerInfo().getFolder().getParentContainerCase()
+ == ParentContainerCase.HOTSEAT) {
+ return info.getContainerInfo().getFolder().getHotseat().getIndex();
+ }
return info.getContainerInfo().getFolder().getWorkspace().getPageIndex();
case SEARCH_RESULT_CONTAINER:
return info.getContainerInfo().getSearchResultContainer().getWorkspace()
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index ae19d73..f7bd1e2 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -49,7 +49,7 @@
Rect taskSize = new Rect();
LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize,
orientationHandler);
- return (dp.heightPx - taskSize.height()) / 2;
+ return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
}
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index d822b6c..81d24d7 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -33,7 +33,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Matrix;
@@ -48,7 +47,6 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
@@ -58,7 +56,6 @@
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.SysUINavigationMode;
-import com.android.systemui.shared.system.ConfigurationCompat;
import java.lang.annotation.Retention;
import java.util.function.IntConsumer;
@@ -91,6 +88,7 @@
private @SurfaceRotation int mTouchRotation = ROTATION_0;
private @SurfaceRotation int mDisplayRotation = ROTATION_0;
private @SurfaceRotation int mRecentsActivityRotation = ROTATION_0;
+ private @SurfaceRotation int mRecentsRotation = ROTATION_0 - 1;
// Launcher activity supports multiple orientation, but fallback activity does not
private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY = 1 << 0;
@@ -133,8 +131,6 @@
private int mFlags;
private int mPreviousRotation = ROTATION_0;
- @Nullable private Configuration mActivityConfiguration;
-
/**
* @param rotationChangeListener Callback for receiving rotation events when rotation watcher
* is enabled
@@ -170,11 +166,11 @@
}
/**
- * Sets the configuration for the recents activity, which could affect the activity's rotation
+ * Sets the rotation for the recents activity, which could affect the appearance of task view.
* @see #update(int, int)
*/
- public boolean setActivityConfiguration(Configuration activityConfiguration) {
- mActivityConfiguration = activityConfiguration;
+ public boolean setRecentsRotation(@SurfaceRotation int recentsRotation) {
+ mRecentsRotation = recentsRotation;
return update(mTouchRotation, mDisplayRotation);
}
@@ -231,9 +227,7 @@
@SurfaceRotation
private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) {
if (isRecentsActivityRotationAllowed()) {
- return mActivityConfiguration == null
- ? displayRotation
- : ConfigurationCompat.getWindowConfigurationRotation(mActivityConfiguration);
+ return mRecentsRotation < ROTATION_0 ? displayRotation : mRecentsRotation;
} else {
return ROTATION_0;
}
diff --git a/res/layout/floating_surface_view.xml b/res/layout/floating_surface_view.xml
new file mode 100644
index 0000000..434e84f
--- /dev/null
+++ b/res/layout/floating_surface_view.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.views.FloatingSurfaceView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
diff --git a/res/values/config.xml b/res/values/config.xml
index ca25325..75fcc90 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -142,7 +142,7 @@
<item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
<item name="staggered_stiffness" type="dimen" format="float">150</item>
- <dimen name="unlock_staggered_velocity_dp_per_s">3dp</dimen>
+ <dimen name="unlock_staggered_velocity_dp_per_s">4dp</dimen>
<item name="hint_scale_damping_ratio" type="dimen" format="float">0.7</item>
<item name="hint_scale_stiffness" type="dimen" format="float">200</item>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index cd27a2d..ce37a30 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -62,7 +62,8 @@
TYPE_ALL_APPS_EDU,
TYPE_TASK_MENU,
- TYPE_OPTIONS_POPUP
+ TYPE_OPTIONS_POPUP,
+ TYPE_ICON_SURFACE
})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
@@ -80,16 +81,18 @@
// Popups related to quickstep UI
public static final int TYPE_TASK_MENU = 1 << 10;
public static final int TYPE_OPTIONS_POPUP = 1 << 11;
+ public static final int TYPE_ICON_SURFACE = 1 << 12;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
| TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
- | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU;
+ | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
+ | TYPE_ICON_SURFACE;
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
- | TYPE_ALL_APPS_EDU;
+ | TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE;
// Usually we show the back button when a floating view is open. Instead, hide for these types.
public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 9cb8cf2..f2a5c65 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -82,7 +82,7 @@
super.onCreate(savedInstanceState);
- mIsSafeModeEnabled = TraceHelper.whitelistIpcs("isSafeMode",
+ mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
DefaultDisplay.INSTANCE.get(this).addChangeListener(this);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 60b6da6..198f13d 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -331,10 +331,7 @@
public boolean onTouchEvent(MotionEvent event) {
// ignore events if they happen in padding area
if (event.getAction() == MotionEvent.ACTION_DOWN
- && (event.getY() < getPaddingTop()
- || event.getX() < getPaddingLeft()
- || event.getY() > getHeight() - getPaddingBottom()
- || event.getX() > getWidth() - getPaddingRight())) {
+ && shouldIgnoreTouchDown(event.getX(), event.getY())) {
return false;
}
if (isLongClickable()) {
@@ -347,6 +344,16 @@
}
}
+ /**
+ * Returns true if the touch down at the provided position be ignored
+ */
+ protected boolean shouldIgnoreTouchDown(float x, float y) {
+ return y < getPaddingTop()
+ || x < getPaddingLeft()
+ || y > getHeight() - getPaddingBottom()
+ || x > getWidth() - getPaddingRight();
+ }
+
void setStayPressed(boolean stayPressed) {
mStayPressed = stayPressed;
refreshDrawableState();
@@ -607,6 +614,9 @@
@Override
public void setIconVisible(boolean visible) {
mIsIconVisible = visible;
+ if (!mIsIconVisible) {
+ resetIconScale();
+ }
Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
applyCompoundDrawables(icon);
}
@@ -746,11 +756,14 @@
@Override
public SafeCloseable prepareDrawDragView() {
- if (getIcon() instanceof FastBitmapDrawable) {
- FastBitmapDrawable icon = (FastBitmapDrawable) getIcon();
- icon.setScale(1f);
- }
+ resetIconScale();
setForceHideDot(true);
return () -> { };
}
+
+ private void resetIconScale() {
+ if (mIcon instanceof FastBitmapDrawable) {
+ ((FastBitmapDrawable) mIcon).setScale(1f);
+ }
+ }
}
diff --git a/src/com/android/launcher3/GestureNavContract.java b/src/com/android/launcher3/GestureNavContract.java
new file mode 100644
index 0000000..2a7e629
--- /dev/null
+++ b/src/com/android/launcher3/GestureNavContract.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3;
+
+import static android.content.Intent.EXTRA_COMPONENT_NAME;
+import static android.content.Intent.EXTRA_USER;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.SurfaceControl;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Class to encapsulate the handshake protocol between Launcher and gestureNav.
+ */
+public class GestureNavContract {
+
+ private static final String TAG = "GestureNavContract";
+
+ public static final String EXTRA_GESTURE_CONTRACT = "gesture_nav_contract_v1";
+ public static final String EXTRA_ICON_POSITION = "gesture_nav_contract_icon_position";
+ public static final String EXTRA_ICON_SURFACE = "gesture_nav_contract_surface_control";
+ public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
+
+ public final ComponentName componentName;
+ public final UserHandle user;
+
+ private final Message mCallback;
+
+ public GestureNavContract(ComponentName componentName, UserHandle user, Message callback) {
+ this.componentName = componentName;
+ this.user = user;
+ this.mCallback = callback;
+ }
+
+ /**
+ * Sends the position information to the receiver
+ */
+ @TargetApi(Build.VERSION_CODES.R)
+ public void sendEndPosition(RectF position, @Nullable SurfaceControl surfaceControl) {
+ Bundle result = new Bundle();
+ result.putParcelable(EXTRA_ICON_POSITION, position);
+ result.putParcelable(EXTRA_ICON_SURFACE, surfaceControl);
+
+ Message callback = Message.obtain();
+ callback.copyFrom(mCallback);
+ callback.setData(result);
+
+ try {
+ callback.replyTo.send(callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending icon position", e);
+ }
+ }
+
+ /**
+ * Clears and returns the GestureNavContract if it was present in the intent.
+ */
+ public static GestureNavContract fromIntent(Intent intent) {
+ if (!Utilities.ATLEAST_R) {
+ return null;
+ }
+ Bundle extras = intent.getBundleExtra(EXTRA_GESTURE_CONTRACT);
+ if (extras == null) {
+ return null;
+ }
+ intent.removeExtra(EXTRA_GESTURE_CONTRACT);
+
+ ComponentName componentName = extras.getParcelable(EXTRA_COMPONENT_NAME);
+ UserHandle userHandle = extras.getParcelable(EXTRA_USER);
+ Message callback = extras.getParcelable(EXTRA_REMOTE_CALLBACK);
+
+ if (componentName != null && userHandle != null && callback != null
+ && callback.replyTo != null) {
+ return new GestureNavContract(componentName, userHandle, callback);
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0970dae..4675362 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -21,6 +21,7 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
import static com.android.launcher3.InstallShortcutReceiver.FLAG_DRAG_AND_DROP;
@@ -168,6 +169,7 @@
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.FloatingSurfaceView;
import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.views.ScrimView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -509,6 +511,7 @@
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
+ AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE);
}
@Override
@@ -923,6 +926,7 @@
DiscoveryBounce.showForHomeIfNeeded(this);
}
+ protected void handlePendingActivityRequest() { }
private void logStopAndResume(int command) {
int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
@@ -1423,7 +1427,8 @@
if (!isInState(NORMAL)) {
// Only change state, if not already the same. This prevents cancelling any
// animations running as part of resume
- mStateManager.goToState(NORMAL);
+ mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange(),
+ this::handlePendingActivityRequest);
}
// Reset the apps view
@@ -1448,6 +1453,7 @@
mLauncherCallbacks.onHomeIntent(internalStateHandled);
}
mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
+ handleGestureContract(intent);
} else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
getStateManager().goToState(ALL_APPS, alreadyOnHome);
}
@@ -1456,6 +1462,17 @@
}
/**
+ * Handles gesture nav contract
+ */
+ protected void handleGestureContract(Intent intent) {
+ GestureNavContract gnc = GestureNavContract.fromIntent(intent);
+ if (gnc != null) {
+ AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE);
+ FloatingSurfaceView.show(this, gnc);
+ }
+ }
+
+ /**
* Hides the keyboard if visible
*/
public void hideKeyboard() {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index c78df62..39b0f2f 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -250,7 +250,8 @@
}
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
- if (this != NORMAL || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
+ if ((this != NORMAL && this != HINT_STATE)
+ || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
return DEFAULT_ALPHA_PROVIDER;
}
final int centerPage = launcher.getWorkspace().getNextPage();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 1441e0b..a6283ff 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -309,7 +309,9 @@
// In portrait, we want the pages spaced such that there is no
// overhang of the previous / next page into the current page viewport.
// We assume symmetrical padding in portrait mode.
- setPageSpacing(Math.max(grid.edgeMarginPx, padding.left + 1));
+ int maxInsets = Math.max(insets.left, insets.right);
+ int maxPadding = Math.max(grid.edgeMarginPx, padding.left + 1);
+ setPageSpacing(Math.max(maxInsets, maxPadding));
}
diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java
index eabd283..69716ea 100644
--- a/src/com/android/launcher3/anim/AlphaUpdateListener.java
+++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java
@@ -46,7 +46,8 @@
}
@Override
- public void onAnimationStart(Animator arg0) {
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
// We want the views to be visible for animation, so fade-in/out is visible
mView.setVisibility(View.VISIBLE);
}
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
index 9905e81..b83417c 100644
--- a/src/com/android/launcher3/anim/AnimationSuccessListener.java
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -19,6 +19,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import androidx.annotation.CallSuper;
+
/**
* Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations
*/
@@ -27,6 +29,12 @@
protected boolean mCancelled = false;
@Override
+ @CallSuper
+ public void onAnimationStart(Animator animation) {
+ mCancelled = false;
+ }
+
+ @Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index ea0ff8b..31e0418 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -335,6 +335,7 @@
@Override
public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
mCancelled = false;
mDispatched = false;
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 5c4a492..45116b6 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -177,6 +177,10 @@
public static final BooleanFlag USER_EVENT_DISPATCHER = new DeviceFlag(
"USER_EVENT_DISPATCHER", true, "User event dispatcher collects logs.");
+ public static final BooleanFlag ENABLE_MINIMAL_DEVICE = new DeviceFlag(
+ "ENABLE_MINIMAL_DEVICE", false,
+ "Allow user to toggle minimal device mode in launcher.");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 1c18402..b91d1c3 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -741,7 +741,8 @@
@Override
protected View getAccessibilityInitialFocusView() {
- return mContent.getFirstItem();
+ View firstItem = mContent.getFirstItem();
+ return firstItem != null ? firstItem : super.getAccessibilityInitialFocusView();
}
private void closeComplete(boolean wasAnimated) {
@@ -1134,6 +1135,9 @@
* Rearranges the children based on their rank.
*/
public void rearrangeChildren() {
+ if (!mContent.areViewsBound()) {
+ return;
+ }
mContent.arrangeChildren(getIconsInReadingOrder());
mItemsInvalidated = true;
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 75275b2..32d061c 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -129,6 +129,8 @@
private float mDotScale;
private Animator mDotScaleAnim;
+ private Rect mTouchArea = new Rect();
+
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
private float mScaleForReorderBounce = 1f;
@@ -711,6 +713,11 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN
+ && shouldIgnoreTouchDown(event.getX(), event.getY())) {
+ return false;
+ }
+
// Call the superclass onTouchEvent first, because sometimes it changes the state to
// isPressed() on an ACTION_UP
super.onTouchEvent(event);
@@ -719,6 +726,15 @@
return true;
}
+ /**
+ * Returns true if the touch down at the provided position be ignored
+ */
+ protected boolean shouldIgnoreTouchDown(float x, float y) {
+ mTouchArea.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(),
+ getHeight() - getPaddingBottom());
+ return !mTouchArea.contains((int) x, (int) y);
+ }
+
@Override
public void cancelLongPress() {
super.cancelLongPress();
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 32531c0..a08dd30 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -500,6 +500,9 @@
* Reorders the items such that the {@param empty} spot moves to {@param target}
*/
public void realTimeReorder(int empty, int target) {
+ if (!mViewsBound) {
+ return;
+ }
completePendingPageChanges();
int delay = 0;
float delayAmount = START_VIEW_REORDER_DELAY;
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 458ffa3..a424f84 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -126,7 +126,7 @@
*/
public static class PreviewContext extends ContextWrapper {
- private static final Set<MainThreadInitializedObject> WHITELIST = new HashSet<>(
+ private final Set<MainThreadInitializedObject> mAllowedObjects = new HashSet<>(
Arrays.asList(UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE));
@@ -160,7 +160,7 @@
*/
public <T> T getObject(MainThreadInitializedObject<T> mainThreadInitializedObject,
MainThreadInitializedObject.ObjectProvider<T> provider) {
- if (!WHITELIST.contains(mainThreadInitializedObject)) {
+ if (!mAllowedObjects.contains(mainThreadInitializedObject)) {
throw new IllegalStateException("Leaking unknown objects");
}
if (mainThreadInitializedObject == LauncherAppState.INSTANCE) {
diff --git a/src/com/android/launcher3/model/PredictionModel.java b/src/com/android/launcher3/model/PredictionModel.java
index 1429843..cb3903d 100644
--- a/src/com/android/launcher3/model/PredictionModel.java
+++ b/src/com/android/launcher3/model/PredictionModel.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
package com.android.launcher3.model;
+
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.ComponentName;
@@ -24,6 +25,7 @@
import androidx.annotation.AnyThread;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.pm.UserCache;
@@ -64,18 +66,28 @@
mUserCache = UserCache.INSTANCE.get(mContext);
}
+
/**
* Formats and stores a list of component key in device preferences.
*/
@AnyThread
public void cachePredictionComponentKeys(List<ComponentKey> componentKeys) {
MODEL_EXECUTOR.execute(() -> {
+ LauncherAppState appState = LauncherAppState.getInstance(mContext);
StringBuilder builder = new StringBuilder();
int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
for (int i = 0; i < count; i++) {
builder.append(serializeComponentKeyToString(componentKeys.get(i)));
builder.append("\n");
}
+ if (componentKeys.isEmpty() /* should invalidate loader items */) {
+ appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel model, AllAppsList apps) {
+ model.cachedPredictedItems.clear();
+ }
+ });
+ }
mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
});
}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index 2daa2fe..eb68592 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -23,7 +23,6 @@
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.view.MotionEvent;
import android.widget.Toast;
import com.android.launcher3.BubbleTextView;
@@ -106,12 +105,12 @@
}
@Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- // Show toast if user touches the drag handle (long clicks still start the drag).
- mShowInstructionToast = mDragHandleBounds.contains((int) ev.getX(), (int) ev.getY());
- }
- return super.onTouchEvent(ev);
+ protected boolean shouldIgnoreTouchDown(float x, float y) {
+ // Show toast if user touches the drag handle (long clicks still start the drag).
+ mShowInstructionToast = mDragHandleBounds.contains((int) x, (int) y);
+
+ // assume the whole view as clickable
+ return false;
}
@Override
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 60b87d9..f1e7f16 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -315,6 +315,7 @@
@Override
public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
// Change the internal state only when the transition actually starts
onStateTransitionStart(state);
}
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 48c7734..ac1ade2 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -17,6 +17,7 @@
package com.android.launcher3.touch;
import static android.widget.ListPopupWindow.WRAP_CONTENT;
+
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
@@ -33,6 +34,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller;
@@ -237,7 +239,8 @@
}
@Override
- public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
+ public int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate,
+ LinearLayout taskMenuLayout) {
return LinearLayout.HORIZONTAL;
}
@@ -260,4 +263,10 @@
}
return new ChildBounds(childHeight, childWidth, childBottom, childLeft);
}
+
+ @SuppressWarnings("SuspiciousNameCombination")
+ @Override
+ public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
+ return rect.left;
+ }
}
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 65b1a7a..d4f5cba 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -94,8 +94,9 @@
float getTaskMenuX(float x, View thumbnailView);
float getTaskMenuY(float y, View thumbnailView);
int getTaskMenuWidth(View view);
- int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout);
+ int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate, LinearLayout taskMenuLayout);
void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp);
+ int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
/**
* Maps the velocity from the coordinate plane of the foreground app to that
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 79e5c87..3341996 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -32,6 +32,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller;
@@ -236,8 +237,9 @@
}
@Override
- public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
- return taskMenuLayout.getOrientation();
+ public int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate,
+ LinearLayout taskMenuLayout) {
+ return canRecentsActivityRotate ? taskMenuLayout.getOrientation() : LinearLayout.VERTICAL;
}
@Override
@@ -257,4 +259,9 @@
}
return new ChildBounds(childWidth, childHeight, childRight, childTop);
}
+
+ @Override
+ public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
+ return dp.heightPx - rect.bottom;
+ }
}
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index d5ae2dc..e91f16d 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -18,9 +18,11 @@
import android.content.res.Resources;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.view.Surface;
import android.view.View;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
@@ -77,4 +79,9 @@
view.setTranslationX(0);
view.setTranslationY(translation);
}
+
+ @Override
+ public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
+ return dp.widthPx - rect.right;
+ }
}
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index fc9f8f7..f6003dd 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -46,7 +46,7 @@
if (mValue == null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
- mValue = TraceHelper.whitelistIpcs("main.thread.object",
+ mValue = TraceHelper.allowIpcs("main.thread.object",
() -> mProvider.get(context.getApplicationContext()));
} else {
try {
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index 168227d..c23df77 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -78,7 +78,7 @@
* Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
*/
@MainThread
- public static <T> T whitelistIpcs(String rpcName, Supplier<T> supplier) {
+ public static <T> T allowIpcs(String rpcName, Supplier<T> supplier) {
Object traceToken = INSTANCE.beginSection(rpcName, FLAG_IGNORE_BINDERS);
try {
return supplier.get();
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index b010b4b..357eeb8 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -602,13 +602,13 @@
*/
private boolean computeAllowSysuiScrims(@Nullable WallpaperInfo newWallpaperInfo) {
if (newWallpaperInfo == null) {
- // New wallpaper is static, not live. Thus, blacklist isn't applicable.
+ // Static wallpapers need scrim unless determined otherwise by wallpaperColors.
return true;
}
ComponentName newWallpaper = newWallpaperInfo.getComponent();
for (String wallpaperWithoutScrim : mWallpapersWithoutSysuiScrims) {
if (newWallpaper.equals(ComponentName.unflattenFromString(wallpaperWithoutScrim))) {
- // New wallpaper is blacklisted from showing a scrim.
+ // New wallpaper does not need a scrim.
return false;
}
}
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index 1a8e11b..fab0bd4 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -36,6 +36,7 @@
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewOutlineProvider;
import androidx.annotation.Nullable;
@@ -44,8 +45,6 @@
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InsettableFrameLayout.LayoutParams;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
@@ -94,7 +93,6 @@
}
};
- private final Launcher mLauncher;
private final int mBlurSizeOutline;
private final boolean mIsRtl;
@@ -128,7 +126,6 @@
public ClipIconView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mLauncher = Launcher.getLauncher(context);
mBlurSizeOutline = getResources().getDimensionPixelSize(
R.dimen.blur_size_medium_outline);
mIsRtl = Utilities.isRtl(getResources());
@@ -143,10 +140,41 @@
.setStiffness(SpringForce.STIFFNESS_LOW));
}
- void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
- boolean isOpening, float scale, float minSize, LayoutParams parentLp,
- boolean isVerticalBarLayout) {
- DeviceProfile dp = mLauncher.getDeviceProfile();
+ /**
+ * Update the icon UI to match the provided parameters during an animation frame
+ */
+ public void update(RectF rect, float progress, float shapeProgressStart,
+ float cornerRadius, boolean isOpening, View container,
+ DeviceProfile dp, boolean isVerticalBarLayout) {
+
+ MarginLayoutParams lp = (MarginLayoutParams) container.getLayoutParams();
+
+ float dX = mIsRtl
+ ? rect.left - (dp.widthPx - lp.getMarginStart() - lp.width)
+ : rect.left - lp.getMarginStart();
+ float dY = rect.top - lp.topMargin;
+ container.setTranslationX(dX);
+ container.setTranslationY(dY);
+
+ float minSize = Math.min(lp.width, lp.height);
+ float scaleX = rect.width() / minSize;
+ float scaleY = rect.height() / minSize;
+ float scale = Math.max(1f, Math.min(scaleX, scaleY));
+
+ update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
+ minSize, lp, isVerticalBarLayout, dp);
+
+ container.setPivotX(0);
+ container.setPivotY(0);
+ container.setScaleX(scale);
+ container.setScaleY(scale);
+
+ container.invalidate();
+ }
+
+ private void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
+ boolean isOpening, float scale, float minSize, MarginLayoutParams parentLp,
+ boolean isVerticalBarLayout, DeviceProfile dp) {
float dX = mIsRtl
? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
: rect.left - parentLp.getMarginStart();
@@ -228,8 +256,11 @@
}
}
- void setIcon(@Nullable Drawable drawable, int iconOffset, LayoutParams lp, boolean isOpening,
- boolean isVerticalBarLayout) {
+ /**
+ * Sets the icon for this view as part of initial setup
+ */
+ public void setIcon(@Nullable Drawable drawable, int iconOffset, MarginLayoutParams lp,
+ boolean isOpening, boolean isVerticalBarLayout, DeviceProfile dp) {
mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
if (mIsAdaptiveIcon) {
boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
@@ -264,15 +295,14 @@
Utilities.scaleRectAboutCenter(mStartRevealRect, IconShape.getNormalizationScale());
}
- float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
if (isVerticalBarLayout) {
- lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
+ lp.width = (int) Math.max(lp.width, lp.height * dp.aspectRatio);
} else {
- lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
+ lp.height = (int) Math.max(lp.height, lp.width * dp.aspectRatio);
}
int left = mIsRtl
- ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
+ ? dp.widthPx - lp.getMarginStart() - lp.width
: lp.leftMargin;
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 177aff4..8186dfa 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -47,7 +48,6 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -144,32 +144,8 @@
public void update(RectF rect, float alpha, float progress, float shapeProgressStart,
float cornerRadius, boolean isOpening) {
setAlpha(alpha);
-
- InsettableFrameLayout.LayoutParams lp =
- (InsettableFrameLayout.LayoutParams) getLayoutParams();
-
- DeviceProfile dp = mLauncher.getDeviceProfile();
- float dX = mIsRtl
- ? rect.left - (dp.widthPx - lp.getMarginStart() - lp.width)
- : rect.left - lp.getMarginStart();
- float dY = rect.top - lp.topMargin;
- setTranslationX(dX);
- setTranslationY(dY);
-
- float minSize = Math.min(lp.width, lp.height);
- float scaleX = rect.width() / minSize;
- float scaleY = rect.height() / minSize;
- float scale = Math.max(1f, Math.min(scaleX, scaleY));
-
- mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
- minSize, lp, mIsVerticalBarLayout);
-
- setPivotX(0);
- setPivotY(0);
- setScaleX(scale);
- setScaleY(scale);
-
- invalidate();
+ mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening,
+ this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout);
}
@Override
@@ -220,13 +196,18 @@
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
}
+ private static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
+ RectF outRect) {
+ getLocationBoundsForView(launcher, v, isOpening, outRect, new Rect());
+ }
+
/**
* Gets the location bounds of a view and returns the overall rotation.
* - For DeepShortcutView, we return the bounds of the icon view.
* - For BubbleTextView, we return the icon bounds.
*/
- private static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
- RectF outRect) {
+ public static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
+ RectF outRect, Rect outViewBounds) {
boolean ignoreTransform = !isOpening;
if (v instanceof DeepShortcutView) {
v = ((DeepShortcutView) v).getBubbleText();
@@ -239,17 +220,16 @@
return;
}
- Rect iconBounds = new Rect();
if (v instanceof BubbleTextView) {
- ((BubbleTextView) v).getIconBounds(iconBounds);
+ ((BubbleTextView) v).getIconBounds(outViewBounds);
} else if (v instanceof FolderIcon) {
- ((FolderIcon) v).getPreviewBounds(iconBounds);
+ ((FolderIcon) v).getPreviewBounds(outViewBounds);
} else {
- iconBounds.set(0, 0, v.getWidth(), v.getHeight());
+ outViewBounds.set(0, 0, v.getWidth(), v.getHeight());
}
- float[] points = new float[] {iconBounds.left, iconBounds.top, iconBounds.right,
- iconBounds.bottom};
+ float[] points = new float[] {outViewBounds.left, outViewBounds.top, outViewBounds.right,
+ outViewBounds.bottom};
Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points,
false, ignoreTransform);
outRect.set(
@@ -336,7 +316,8 @@
final InsettableFrameLayout.LayoutParams lp =
(InsettableFrameLayout.LayoutParams) getLayoutParams();
mBadge = badge;
- mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout);
+ mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout,
+ mLauncher.getDeviceProfile());
if (drawable instanceof AdaptiveIconDrawable) {
final int originalHeight = lp.height;
final int originalWidth = lp.width;
@@ -381,7 +362,7 @@
if (mIconLoadResult.isIconLoaded) {
setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
mIconLoadResult.iconOffset);
- hideOriginalView(originalView);
+ setIconAndDotVisible(originalView, false);
} else {
mIconLoadResult.onIconLoaded = () -> {
if (cancellationSignal.isCanceled()) {
@@ -392,22 +373,13 @@
mIconLoadResult.iconOffset);
setVisibility(VISIBLE);
- hideOriginalView(originalView);
+ setIconAndDotVisible(originalView, false);
};
mLoadIconSignal = cancellationSignal;
}
}
}
- private void hideOriginalView(View originalView) {
- if (originalView instanceof IconLabelDotView) {
- ((IconLabelDotView) originalView).setIconVisible(false);
- ((IconLabelDotView) originalView).setForceHideDot(true);
- } else {
- originalView.setVisibility(INVISIBLE);
- }
- }
-
@WorkerThread
@SuppressWarnings("WrongThread")
private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) {
@@ -477,7 +449,7 @@
}
if (!mIsOpening) {
// When closing an app, we want the item on the workspace to be invisible immediately
- hideOriginalView(mOriginalIcon);
+ setIconAndDotVisible(mOriginalIcon, false);
}
}
@@ -573,12 +545,7 @@
if (hideOriginal) {
if (isOpening) {
- if (originalView instanceof BubbleTextView) {
- ((BubbleTextView) originalView).setIconVisible(true);
- ((BubbleTextView) originalView).setForceHideDot(false);
- } else {
- originalView.setVisibility(VISIBLE);
- }
+ setIconAndDotVisible(originalView, true);
view.finish(dragLayer);
} else {
view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
@@ -615,12 +582,10 @@
});
if (originalView instanceof IconLabelDotView) {
- IconLabelDotView view = (IconLabelDotView) originalView;
fade.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- view.setIconVisible(true);
- view.setForceHideDot(false);
+ setIconAndDotVisible(originalView, true);
}
});
}
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
new file mode 100644
index 0000000..040619e
--- /dev/null
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import static com.android.launcher3.views.FloatingIconView.getLocationBoundsForView;
+import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Picture;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.GestureNavContract;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.Executors;
+
+/**
+ * Similar to {@link FloatingIconView} but displays a surface with the targetIcon. It then passes
+ * the surfaceHandle to the {@link GestureNavContract}.
+ */
+@TargetApi(Build.VERSION_CODES.R)
+public class FloatingSurfaceView extends AbstractFloatingView implements
+ OnGlobalLayoutListener, Insettable, SurfaceHolder.Callback2 {
+
+ private final RectF mTmpPosition = new RectF();
+
+ private final Launcher mLauncher;
+ private final RectF mIconPosition = new RectF();
+
+ private final Rect mIconBounds = new Rect();
+ private final Picture mPicture = new Picture();
+ private final Runnable mRemoveViewRunnable = this::removeViewFromParent;
+
+ private final SurfaceView mSurfaceView;
+
+
+ private View mIcon;
+ private GestureNavContract mContract;
+
+ public FloatingSurfaceView(Context context) {
+ this(context, null);
+ }
+
+ public FloatingSurfaceView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FloatingSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+
+ mSurfaceView = new SurfaceView(context);
+ mSurfaceView.setZOrderOnTop(true);
+
+ mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+ mSurfaceView.getHolder().addCallback(this);
+ mIsOpen = true;
+ addView(mSurfaceView);
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ setCurrentIconVisible(true);
+ mLauncher.getViewCache().recycleView(R.layout.floating_surface_view, this);
+ mContract = null;
+ mIcon = null;
+ mIsOpen = false;
+
+ // Remove after some time, to avoid flickering
+ Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable,
+ DefaultDisplay.INSTANCE.get(mLauncher).getInfo().singleFrameMs);
+ }
+
+ private void removeViewFromParent() {
+ mPicture.beginRecording(1, 1);
+ mPicture.endRecording();
+ mLauncher.getDragLayer().removeView(this);
+ }
+
+ /**
+ * Shows the surfaceView for the provided contract
+ */
+ public static void show(Launcher launcher, GestureNavContract contract) {
+ FloatingSurfaceView view = launcher.getViewCache().getView(R.layout.floating_surface_view,
+ launcher, launcher.getDragLayer());
+ view.mContract = contract;
+ view.mIsOpen = true;
+
+ // Cancel any pending remove
+ Executors.MAIN_EXECUTOR.getHandler().removeCallbacks(view.mRemoveViewRunnable);
+ view.removeViewFromParent();
+ launcher.getDragLayer().addView(view);
+ }
+
+ @Override
+ public void logActionCommand(int command) { }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_ICON_SURFACE) != 0;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ close(false);
+ return false;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnGlobalLayoutListener(this);
+ updateIconLocation();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ setCurrentIconVisible(true);
+ }
+
+ @Override
+ public void onGlobalLayout() {
+ updateIconLocation();
+ }
+
+ @Override
+ public void setInsets(Rect insets) { }
+
+ private void updateIconLocation() {
+ if (mContract == null) {
+ return;
+ }
+ View icon = mLauncher.getWorkspace().getFirstMatchForAppClose(
+ mContract.componentName.getPackageName(), mContract.user);
+
+ boolean iconChanged = mIcon != icon;
+ if (iconChanged) {
+ setCurrentIconVisible(true);
+ mIcon = icon;
+ setCurrentIconVisible(false);
+ }
+
+ if (icon != null && icon.isAttachedToWindow()) {
+ getLocationBoundsForView(mLauncher, icon, false, mTmpPosition, mIconBounds);
+
+ if (!mTmpPosition.equals(mIconPosition)) {
+ mIconPosition.set(mTmpPosition);
+ sendIconInfo();
+
+ LayoutParams lp = (LayoutParams) mSurfaceView.getLayoutParams();
+ lp.width = Math.round(mIconPosition.width());
+ lp.height = Math.round(mIconPosition.height());
+ lp.leftMargin = Math.round(mIconPosition.left);
+ lp.topMargin = Math.round(mIconPosition.top);
+ }
+ }
+ if (iconChanged && !mIconBounds.isEmpty()) {
+ // Record the icon display
+ setCurrentIconVisible(true);
+ Canvas c = mPicture.beginRecording(mIconBounds.width(), mIconBounds.height());
+ c.translate(-mIconBounds.left, -mIconBounds.top);
+ mIcon.draw(c);
+ mPicture.endRecording();
+ setCurrentIconVisible(false);
+ drawOnSurface();
+ }
+ }
+
+ private void sendIconInfo() {
+ if (mContract != null && !mIconPosition.isEmpty()) {
+ mContract.sendEndPosition(mIconPosition, mSurfaceView.getSurfaceControl());
+ }
+ }
+
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
+ drawOnSurface();
+ sendIconInfo();
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder,
+ int format, int width, int height) {
+ drawOnSurface();
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {}
+
+ @Override
+ public void surfaceRedrawNeeded(@NonNull SurfaceHolder surfaceHolder) {
+ drawOnSurface();
+ }
+
+ private void drawOnSurface() {
+ SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
+
+ Canvas c = surfaceHolder.lockHardwareCanvas();
+ if (c != null) {
+ mPicture.draw(c);
+ surfaceHolder.unlockCanvasAndPost(c);
+ }
+ }
+
+ private void setCurrentIconVisible(boolean isVisible) {
+ if (mIcon != null) {
+ setIconAndDotVisible(mIcon, isVisible);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/IconLabelDotView.java b/src/com/android/launcher3/views/IconLabelDotView.java
index 057caaf..e9113cf 100644
--- a/src/com/android/launcher3/views/IconLabelDotView.java
+++ b/src/com/android/launcher3/views/IconLabelDotView.java
@@ -15,10 +15,24 @@
*/
package com.android.launcher3.views;
+import android.view.View;
+
/**
* A view that has an icon, label, and notification dot.
*/
public interface IconLabelDotView {
void setIconVisible(boolean visible);
void setForceHideDot(boolean hide);
+
+ /**
+ * Sets the visibility of icon and dot of the view
+ */
+ static void setIconAndDotVisible(View view, boolean visible) {
+ if (view instanceof IconLabelDotView) {
+ ((IconLabelDotView) view).setIconVisible(visible);
+ ((IconLabelDotView) view).setForceHideDot(!visible);
+ } else {
+ view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
}
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 9021d9e..ca47728 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -52,7 +52,6 @@
private static final float MIN_SATUNATION = 0.7f;
private final Rect mRect = new Rect();
- private View mDefaultView;
private OnClickListener mClickListener;
private final LauncherAppWidgetInfo mInfo;
private final int mStartState;
@@ -111,12 +110,11 @@
@Override
protected View getDefaultView() {
- if (mDefaultView == null) {
- mDefaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false);
- mDefaultView.setOnClickListener(this);
- applyState();
- }
- return mDefaultView;
+ View defaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false);
+ defaultView.setOnClickListener(this);
+ applyState();
+ invalidate();
+ return defaultView;
}
@Override
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 1c8f095..33c8fbd 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -92,7 +92,7 @@
<activity
android:name="com.android.launcher3.testcomponent.TestLauncherActivity"
android:clearTaskOnLaunch="true"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:enabled="false"
android:label="Test launcher"
android:launchMode="singleTask"