Merging from ub-launcher3-rvc-dev @ build 6552182 am: e557bac0a1 am: b44fe59a6f am: cf0cd2a35a
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/11707957 Change-Id: Ic4fa00fe7be32b5584cd7221c7377bb35b975458
This commit is contained in:
@@ -51,6 +51,7 @@ message ContainerInfo {
|
||||
WidgetsContainer widgets_container = 5;
|
||||
PredictionContainer prediction_container = 6;
|
||||
SearchResultContainer search_result_container = 7;
|
||||
ShortcutsContainer shortcuts_container = 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +70,11 @@ message PredictionContainer {
|
||||
message SearchResultContainer {
|
||||
}
|
||||
|
||||
// Container for package specific shortcuts to deep links and notifications.
|
||||
// Typically shown as popup window by longpressing on an icon.
|
||||
message ShortcutsContainer {
|
||||
}
|
||||
|
||||
enum Origin {
|
||||
UNKNOWN = 0;
|
||||
DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat
|
||||
|
||||
+3
-70
@@ -15,17 +15,9 @@
|
||||
*/
|
||||
package com.android.launcher3.hybridhotseat;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.Hotseat;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
@@ -37,11 +29,8 @@ import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.launcher3.util.ActivityTracker;
|
||||
import com.android.launcher3.util.GridOccupancy;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.ArrowTipView;
|
||||
import com.android.launcher3.views.Snackbar;
|
||||
|
||||
@@ -54,18 +43,15 @@ import java.util.stream.IntStream;
|
||||
* Controller class for managing user onboaridng flow for hybrid hotseat
|
||||
*/
|
||||
public class HotseatEduController {
|
||||
|
||||
public static final String KEY_HOTSEAT_EDU_SEEN = "hotseat_edu_seen";
|
||||
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "launcher_onboarding";
|
||||
private static final int ONBOARDING_NOTIFICATION_ID = 7641;
|
||||
|
||||
public static final String HOTSEAT_EDU_ACTION =
|
||||
"com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU";
|
||||
private static final String SETTINGS_ACTION =
|
||||
"android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final Hotseat mHotseat;
|
||||
private final NotificationManager mNotificationManager;
|
||||
private final Notification mNotification;
|
||||
private List<WorkspaceItemInfo> mPredictedApps;
|
||||
private HotseatEduDialog mActiveDialog;
|
||||
|
||||
@@ -77,9 +63,6 @@ public class HotseatEduController {
|
||||
mLauncher = launcher;
|
||||
mHotseat = launcher.getHotseat();
|
||||
mOnOnboardingComplete = runnable;
|
||||
mNotificationManager = mLauncher.getSystemService(NotificationManager.class);
|
||||
createNotificationChannel();
|
||||
mNotification = createNotification();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,11 +199,6 @@ public class HotseatEduController {
|
||||
return pageId;
|
||||
}
|
||||
|
||||
|
||||
void removeNotification() {
|
||||
mNotificationManager.cancel(ONBOARDING_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
void moveHotseatItems() {
|
||||
mHotseat.removeAllViewsInLayout();
|
||||
if (!mNewItems.isEmpty()) {
|
||||
@@ -258,45 +236,9 @@ public class HotseatEduController {
|
||||
|
||||
void setPredictedApps(List<WorkspaceItemInfo> predictedApps) {
|
||||
mPredictedApps = predictedApps;
|
||||
if (!mPredictedApps.isEmpty()
|
||||
&& mLauncher.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
|
||||
mNotificationManager.notify(ONBOARDING_NOTIFICATION_ID, mNotification);
|
||||
}
|
||||
else {
|
||||
removeNotification();
|
||||
}
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
||||
CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title);
|
||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name,
|
||||
importance);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
private Notification createNotification() {
|
||||
Intent intent = new Intent(mLauncher.getApplicationContext(), mLauncher.getClass());
|
||||
intent = new NotificationHandler().addToIntent(intent);
|
||||
|
||||
CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title);
|
||||
String description = mLauncher.getString(R.string.hotseat_edu_prompt_content);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mLauncher,
|
||||
NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(name)
|
||||
.setOngoing(true)
|
||||
.setColor(Themes.getColorAccent(mLauncher))
|
||||
.setContentIntent(PendingIntent.getActivity(mLauncher, 0, intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT))
|
||||
.setSmallIcon(R.drawable.hotseat_edu_notification_icon)
|
||||
.setContentText(description);
|
||||
return builder.build();
|
||||
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
removeNotification();
|
||||
if (mActiveDialog != null) {
|
||||
mActiveDialog.setHotseatEduController(null);
|
||||
}
|
||||
@@ -334,14 +276,5 @@ public class HotseatEduController {
|
||||
mActiveDialog.setHotseatEduController(this);
|
||||
mActiveDialog.show(mPredictedApps);
|
||||
}
|
||||
|
||||
static class NotificationHandler implements
|
||||
ActivityTracker.SchedulerCallback<QuickstepLauncher> {
|
||||
@Override
|
||||
public boolean init(QuickstepLauncher activity, boolean alreadyOnHome) {
|
||||
activity.getHotseatPredictionController().showEdu();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -41,7 +41,6 @@ import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.AllAppsStore;
|
||||
@@ -148,8 +147,7 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
*/
|
||||
public void showEdu() {
|
||||
if (mHotseatEduController == null) return;
|
||||
mLauncher.getStateManager().goToState(LauncherState.NORMAL, true,
|
||||
() -> mHotseatEduController.showEdu());
|
||||
mHotseatEduController.showEdu();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+15
@@ -44,6 +44,7 @@ import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.hybridhotseat.HotseatEduController;
|
||||
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
@@ -98,6 +99,20 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
if (HotseatEduController.HOTSEAT_EDU_ACTION.equals(intent.getAction())
|
||||
&& mHotseatPredictionController != null) {
|
||||
boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags()
|
||||
& Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
|
||||
!= Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
|
||||
getStateManager().goToState(NORMAL, alreadyOnHome, () -> {
|
||||
mHotseatPredictionController.showEdu();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
|
||||
import static com.android.launcher3.anim.Interpolators.INSTANT;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
@@ -188,7 +189,7 @@ public class QuickstepAtomicAnimationFactory extends
|
||||
}
|
||||
} else if (toState == NORMAL && fromState == OVERVIEW_PEEK) {
|
||||
// Keep fully visible until the very end (when overview is offscreen) to make invisible.
|
||||
config.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1);
|
||||
config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
|
||||
} else if (toState == OVERVIEW_PEEK && fromState == NORMAL) {
|
||||
config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
|
||||
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
|
||||
|
||||
+1
-2
@@ -116,8 +116,7 @@ public class NavBarToHomeTouchController implements TouchController,
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED,
|
||||
"NavBarToHomeTouchController.canInterceptTouch true 2 "
|
||||
+ AbstractFloatingView.getTopOpenView(mLauncher).getClass()
|
||||
.getSimpleName());
|
||||
+ AbstractFloatingView.getTopOpenView(mLauncher), new Exception());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -15,57 +15,38 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
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;
|
||||
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Matrix.ScaleToFit;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.launcher3.util.VibratorWrapper;
|
||||
import com.android.launcher3.views.FloatingIconView;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.util.RectFSpringAnim;
|
||||
import com.android.quickstep.util.TaskViewSimulator;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.quickstep.util.TransformParams.BuilderProxy;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Consumer;
|
||||
@@ -75,40 +56,13 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.Q)
|
||||
public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
|
||||
implements RecentsAnimationListener {
|
||||
extends SwipeUpAnimationLogic implements RecentsAnimationListener {
|
||||
|
||||
private static final String TAG = "BaseSwipeUpHandler";
|
||||
protected static final Rect TEMP_RECT = new Rect();
|
||||
|
||||
public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
|
||||
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
|
||||
|
||||
// The distance needed to drag to reach the task size in recents.
|
||||
protected int mTransitionDragLength;
|
||||
// How much further we can drag past recents, as a factor of mTransitionDragLength.
|
||||
protected float mDragLengthFactor = 1;
|
||||
// Start resisting when swiping past this factor of mTransitionDragLength.
|
||||
private float mDragLengthFactorStartPullback = 1f;
|
||||
// This is how far down we can scale down, where 0f is full screen and 1f is recents.
|
||||
private float mDragLengthFactorMaxPullback = 1f;
|
||||
|
||||
protected final Context mContext;
|
||||
protected final RecentsAnimationDeviceState mDeviceState;
|
||||
protected final GestureState mGestureState;
|
||||
protected final BaseActivityInterface<?, T> mActivityInterface;
|
||||
protected final InputConsumerController mInputConsumer;
|
||||
|
||||
protected final TaskViewSimulator mTaskViewSimulator;
|
||||
private AnimatorPlaybackController mWindowTransitionController;
|
||||
|
||||
protected final TransformParams mTransformParams = new TransformParams();
|
||||
|
||||
// Shift in the range of [0, 1].
|
||||
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
|
||||
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
|
||||
// visible.
|
||||
protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
|
||||
|
||||
protected final ActivityInitListener mActivityInitListener;
|
||||
|
||||
protected RecentsAnimationController mRecentsAnimationController;
|
||||
@@ -119,7 +73,6 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
|
||||
|
||||
protected T mActivity;
|
||||
protected Q mRecentsView;
|
||||
protected DeviceProfile mDp;
|
||||
|
||||
protected Runnable mGestureEndCallback;
|
||||
|
||||
@@ -131,13 +84,10 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
|
||||
|
||||
protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
|
||||
GestureState gestureState, InputConsumerController inputConsumer) {
|
||||
mContext = context;
|
||||
mDeviceState = deviceState;
|
||||
mGestureState = gestureState;
|
||||
super(context, deviceState, gestureState, new TransformParams());
|
||||
mActivityInterface = gestureState.getActivityInterface();
|
||||
mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
|
||||
mInputConsumer = inputConsumer;
|
||||
mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,28 +107,6 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
|
||||
return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void updateDisplacement(float displacement) {
|
||||
// We are moving in the negative x/y direction
|
||||
displacement = -displacement;
|
||||
float shift;
|
||||
if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
|
||||
shift = mDragLengthFactor;
|
||||
} else {
|
||||
float translation = Math.max(displacement, 0);
|
||||
shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
|
||||
if (shift > mDragLengthFactorStartPullback) {
|
||||
float pullbackProgress = Utilities.getProgress(shift,
|
||||
mDragLengthFactorStartPullback, mDragLengthFactor);
|
||||
pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
|
||||
shift = mDragLengthFactorStartPullback + pullbackProgress
|
||||
* (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback);
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentShift.updateValue(shift);
|
||||
}
|
||||
|
||||
public void setGestureEndCallback(Runnable gestureEndCallback) {
|
||||
mGestureEndCallback = gestureEndCallback;
|
||||
}
|
||||
@@ -275,6 +203,7 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
|
||||
RecentsAnimationTargets targets) {
|
||||
mRecentsAnimationController = recentsAnimationController;
|
||||
mRecentsAnimationTargets = targets;
|
||||
mTransformParams.setTargetSet(mRecentsAnimationTargets);
|
||||
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
|
||||
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
|
||||
mGestureState.getRunningTaskId());
|
||||
@@ -282,7 +211,8 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
|
||||
if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
|
||||
Rect overviewStackBounds = mActivityInterface
|
||||
.getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
|
||||
dp = dp.getMultiWindowProfile(mContext, overviewStackBounds);
|
||||
dp = dp.getMultiWindowProfile(mContext,
|
||||
new WindowBounds(overviewStackBounds, targets.homeContentInsets));
|
||||
} else {
|
||||
// If we are not in multi-window mode, home insets should be same as system insets.
|
||||
dp = dp.copy(mContext);
|
||||
@@ -355,35 +285,6 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
|
||||
return mGestureState.getLastStartedTaskId() != -1;
|
||||
}
|
||||
|
||||
protected void initTransitionEndpoints(DeviceProfile dp) {
|
||||
mDp = dp;
|
||||
|
||||
mTaskViewSimulator.setDp(dp);
|
||||
mTaskViewSimulator.setLayoutRotation(
|
||||
mDeviceState.getCurrentActiveRotation(),
|
||||
mDeviceState.getDisplayRotation());
|
||||
mTransitionDragLength = mActivityInterface.getSwipeUpDestinationAndLength(
|
||||
dp, mContext, TEMP_RECT,
|
||||
mTaskViewSimulator.getOrientationState().getOrientationHandler());
|
||||
|
||||
if (mDeviceState.isFullyGesturalNavMode()) {
|
||||
// We can drag all the way to the top of the screen.
|
||||
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
|
||||
|
||||
float startScale = mTaskViewSimulator.getFullScreenScale();
|
||||
// Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f.
|
||||
mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale);
|
||||
mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale);
|
||||
} else {
|
||||
mDragLengthFactor = 1;
|
||||
mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
|
||||
}
|
||||
|
||||
PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
|
||||
mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor);
|
||||
mWindowTransitionController = pa.createPlaybackController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the window should be translated horizontally if the recents view scrolls
|
||||
*/
|
||||
@@ -455,7 +356,6 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
|
||||
if (mWindowTransitionController != null) {
|
||||
float progress = mCurrentShift.value / mDragLengthFactor;
|
||||
mWindowTransitionController.setPlayFraction(progress);
|
||||
mTransformParams.setTargetSet(mRecentsAnimationTargets);
|
||||
|
||||
if (mRecentsViewScrollLinked) {
|
||||
mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
|
||||
@@ -464,217 +364,9 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
|
||||
}
|
||||
}
|
||||
|
||||
protected PagedOrientationHandler getOrientationHandler() {
|
||||
return mTaskViewSimulator.getOrientationState().getOrientationHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an animation that transforms the current app window into the home app.
|
||||
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
|
||||
* @param homeAnimationFactory The home animation factory.
|
||||
*/
|
||||
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)
|
||||
.setTargetSet(mRecentsAnimationTargets));
|
||||
RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
|
||||
|
||||
// Matrix to map a rect in Launcher space to window space
|
||||
Matrix homeToWindowPositionMap = new Matrix();
|
||||
mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap);
|
||||
|
||||
final RectF startRect = new RectF(cropRectF);
|
||||
mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
|
||||
// Move the startRect to Launcher space as floatingIconView runs in Launcher
|
||||
Matrix windowToHomePositionMap = new Matrix();
|
||||
homeToWindowPositionMap.invert(windowToHomePositionMap);
|
||||
windowToHomePositionMap.mapRect(startRect);
|
||||
|
||||
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
|
||||
if (isFloatingIconView) {
|
||||
anim.addAnimatorListener(fiv);
|
||||
fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
|
||||
fiv.setFastFinishRunnable(anim::end);
|
||||
}
|
||||
|
||||
SpringAnimationRunner runner = new SpringAnimationRunner(
|
||||
homeAnimationFactory, cropRectF, homeToWindowPositionMap);
|
||||
anim.addOnUpdateListener(runner);
|
||||
anim.addAnimatorListener(runner);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public interface Factory {
|
||||
|
||||
BaseSwipeUpHandler newHandler(
|
||||
GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
|
||||
}
|
||||
|
||||
protected interface RunningWindowAnim {
|
||||
void end();
|
||||
|
||||
void cancel();
|
||||
|
||||
static RunningWindowAnim wrap(Animator animator) {
|
||||
return new RunningWindowAnim() {
|
||||
@Override
|
||||
public void end() {
|
||||
animator.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
animator.cancel();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
|
||||
return new RunningWindowAnim() {
|
||||
@Override
|
||||
public void end() {
|
||||
rectFSpringAnim.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
rectFSpringAnim.cancel();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param progress The progress of the animation to the home screen.
|
||||
* @return The current alpha to set on the animating app window.
|
||||
*/
|
||||
protected float getWindowAlpha(float progress) {
|
||||
// Alpha interpolates between [1, 0] between progress values [start, end]
|
||||
final float start = 0f;
|
||||
final float end = 0.85f;
|
||||
|
||||
if (progress <= start) {
|
||||
return 1f;
|
||||
}
|
||||
if (progress >= end) {
|
||||
return 0f;
|
||||
}
|
||||
return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
|
||||
}
|
||||
|
||||
protected abstract class HomeAnimationFactory {
|
||||
|
||||
private FloatingIconView mIconView;
|
||||
|
||||
public HomeAnimationFactory(@Nullable FloatingIconView iconView) {
|
||||
mIconView = iconView;
|
||||
}
|
||||
|
||||
public @NonNull RectF getWindowTargetRect() {
|
||||
PagedOrientationHandler orientationHandler = getOrientationHandler();
|
||||
DeviceProfile dp = mDp;
|
||||
final int halfIconSize = dp.iconSizePx / 2;
|
||||
float primaryDimension = orientationHandler
|
||||
.getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
|
||||
float secondaryDimension = orientationHandler
|
||||
.getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
|
||||
final float targetX = primaryDimension / 2f;
|
||||
final float targetY = secondaryDimension - dp.hotseatBarSizePx;
|
||||
// Fallback to animate to center of screen.
|
||||
return new RectF(targetX - halfIconSize, targetY - halfIconSize,
|
||||
targetX + halfIconSize, targetY + halfIconSize);
|
||||
}
|
||||
|
||||
public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome();
|
||||
|
||||
public void playAtomicAnimation(float velocity) {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
private class SpringAnimationRunner extends AnimationSuccessListener
|
||||
implements RectFSpringAnim.OnUpdateListener, BuilderProxy {
|
||||
|
||||
final Rect mCropRect = new Rect();
|
||||
final Matrix mMatrix = new Matrix();
|
||||
|
||||
final RectF mWindowCurrentRect = new RectF();
|
||||
final Matrix mHomeToWindowPositionMap;
|
||||
|
||||
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) {
|
||||
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
|
||||
public void onUpdate(RectF currentRect, float progress) {
|
||||
mHomeAnim.setPlayFraction(progress);
|
||||
mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect);
|
||||
|
||||
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
|
||||
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
|
||||
mTransformParams
|
||||
.setTargetAlpha(getWindowAlpha(progress))
|
||||
.setCornerRadius(cornerRadius);
|
||||
|
||||
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
|
||||
if (mFIV != null) {
|
||||
mFIV.update(currentRect, 1f, progress,
|
||||
mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuildTargetParams(
|
||||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mCropRect)
|
||||
.withCornerRadius(params.getCornerRadius());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (mFIV != null) {
|
||||
mFIV.fastFinish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mHomeAnim.dispatchOnStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mHomeAnim.getAnimationPlayer().end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
|
||||
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
|
||||
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
|
||||
import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
|
||||
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
|
||||
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
|
||||
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
||||
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
|
||||
@@ -253,6 +254,10 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
|
||||
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,
|
||||
@@ -1035,7 +1040,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
|
||||
}
|
||||
// Make sure recents is in its final state
|
||||
maybeUpdateRecentsAttachedState(false);
|
||||
mActivityInterface.onSwipeUpToHomeComplete();
|
||||
mActivityInterface.onSwipeUpToHomeComplete(mDeviceState);
|
||||
}
|
||||
});
|
||||
return anim;
|
||||
@@ -1232,7 +1237,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
|
||||
}
|
||||
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
|
||||
doLogGesture(HOME);
|
||||
mDeviceState.enableMultipleRegions(false);
|
||||
}
|
||||
|
||||
protected abstract void finishRecentsControllerToHome(Runnable callback);
|
||||
@@ -1248,7 +1252,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
|
||||
|
||||
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
|
||||
doLogGesture(RECENTS);
|
||||
mDeviceState.onSwipeUpToOverview(mActivityInterface);
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ public final class FallbackActivityInterface extends
|
||||
|
||||
/** 4 */
|
||||
@Override
|
||||
public void onSwipeUpToHomeComplete() {
|
||||
public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {
|
||||
onSwipeUpToRecentsComplete();
|
||||
}
|
||||
|
||||
|
||||
+10
-6
@@ -96,7 +96,7 @@ public final class LauncherActivityInterface extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpToHomeComplete() {
|
||||
public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
if (launcher == null) {
|
||||
return;
|
||||
@@ -105,6 +105,7 @@ public final class LauncherActivityInterface extends
|
||||
// recents, we assume the first task is invisible, making translation off by one task.
|
||||
launcher.getStateManager().reapplyState();
|
||||
launcher.getRootView().setForceHideBackArrow(false);
|
||||
notifyRecentsOfOrientation(deviceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -235,17 +236,20 @@ public final class LauncherActivityInterface extends
|
||||
// Are we going from Recents to Workspace?
|
||||
if (toState == LauncherState.NORMAL) {
|
||||
exitRunnable.run();
|
||||
|
||||
// reset layout on swipe to home
|
||||
RecentsView recentsView = getCreatedActivity().getOverviewPanel();
|
||||
recentsView.setLayoutRotation(deviceState.getCurrentActiveRotation(),
|
||||
deviceState.getDisplayRotation());
|
||||
notifyRecentsOfOrientation(deviceState);
|
||||
stateManager.removeStateListener(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void notifyRecentsOfOrientation(RecentsAnimationDeviceState deviceState) {
|
||||
// reset layout on swipe to home
|
||||
RecentsView recentsView = getCreatedActivity().getOverviewPanel();
|
||||
recentsView.setLayoutRotation(deviceState.getCurrentActiveRotation(),
|
||||
deviceState.getDisplayRotation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
|
||||
return homeBounds;
|
||||
|
||||
+3
-2
@@ -79,12 +79,13 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
|
||||
|
||||
@Override
|
||||
protected Activity getCurrentActivity() {
|
||||
OverviewComponentObserver observer = new OverviewComponentObserver(mContext,
|
||||
new RecentsAnimationDeviceState(mContext));
|
||||
RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(mContext);
|
||||
OverviewComponentObserver observer = new OverviewComponentObserver(mContext, rads);
|
||||
try {
|
||||
return observer.getActivityInterface().getCreatedActivity();
|
||||
} finally {
|
||||
observer.onDestroy();
|
||||
rads.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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 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;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Matrix.ScaleToFit;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
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;
|
||||
import com.android.quickstep.util.TransformParams.BuilderProxy;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
|
||||
|
||||
public abstract class SwipeUpAnimationLogic {
|
||||
|
||||
protected static final Rect TEMP_RECT = new Rect();
|
||||
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
|
||||
|
||||
protected DeviceProfile mDp;
|
||||
|
||||
protected final Context mContext;
|
||||
protected final RecentsAnimationDeviceState mDeviceState;
|
||||
protected final GestureState mGestureState;
|
||||
protected final TaskViewSimulator mTaskViewSimulator;
|
||||
|
||||
protected final TransformParams mTransformParams;
|
||||
|
||||
// Shift in the range of [0, 1].
|
||||
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
|
||||
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
|
||||
// visible.
|
||||
protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
|
||||
|
||||
// The distance needed to drag to reach the task size in recents.
|
||||
protected int mTransitionDragLength;
|
||||
// How much further we can drag past recents, as a factor of mTransitionDragLength.
|
||||
protected float mDragLengthFactor = 1;
|
||||
// Start resisting when swiping past this factor of mTransitionDragLength.
|
||||
private float mDragLengthFactorStartPullback = 1f;
|
||||
// This is how far down we can scale down, where 0f is full screen and 1f is recents.
|
||||
private float mDragLengthFactorMaxPullback = 1f;
|
||||
|
||||
protected AnimatorPlaybackController mWindowTransitionController;
|
||||
|
||||
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
|
||||
GestureState gestureState, TransformParams transformParams) {
|
||||
mContext = context;
|
||||
mDeviceState = deviceState;
|
||||
mGestureState = gestureState;
|
||||
mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
|
||||
mTransformParams = transformParams;
|
||||
}
|
||||
|
||||
protected void initTransitionEndpoints(DeviceProfile dp) {
|
||||
mDp = dp;
|
||||
|
||||
mTaskViewSimulator.setDp(dp);
|
||||
mTaskViewSimulator.setLayoutRotation(
|
||||
mDeviceState.getCurrentActiveRotation(),
|
||||
mDeviceState.getDisplayRotation());
|
||||
mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
|
||||
dp, mContext, TEMP_RECT,
|
||||
mTaskViewSimulator.getOrientationState().getOrientationHandler());
|
||||
|
||||
if (mDeviceState.isFullyGesturalNavMode()) {
|
||||
// We can drag all the way to the top of the screen.
|
||||
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
|
||||
|
||||
float startScale = mTaskViewSimulator.getFullScreenScale();
|
||||
// Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f.
|
||||
mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale);
|
||||
mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale);
|
||||
} else {
|
||||
mDragLengthFactor = 1;
|
||||
mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
|
||||
}
|
||||
|
||||
PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
|
||||
mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor);
|
||||
mWindowTransitionController = pa.createPlaybackController();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void updateDisplacement(float displacement) {
|
||||
// We are moving in the negative x/y direction
|
||||
displacement = -displacement;
|
||||
float shift;
|
||||
if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
|
||||
shift = mDragLengthFactor;
|
||||
} else {
|
||||
float translation = Math.max(displacement, 0);
|
||||
shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
|
||||
if (shift > mDragLengthFactorStartPullback) {
|
||||
float pullbackProgress = Utilities.getProgress(shift,
|
||||
mDragLengthFactorStartPullback, mDragLengthFactor);
|
||||
pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
|
||||
shift = mDragLengthFactorStartPullback + pullbackProgress
|
||||
* (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback);
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentShift.updateValue(shift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the value of {@link #mCurrentShift} changes
|
||||
*/
|
||||
@UiThread
|
||||
public abstract void updateFinalShift();
|
||||
|
||||
protected PagedOrientationHandler getOrientationHandler() {
|
||||
return mTaskViewSimulator.getOrientationState().getOrientationHandler();
|
||||
}
|
||||
|
||||
protected abstract class HomeAnimationFactory {
|
||||
|
||||
public FloatingIconView mIconView;
|
||||
|
||||
public HomeAnimationFactory(@Nullable FloatingIconView iconView) {
|
||||
mIconView = iconView;
|
||||
}
|
||||
|
||||
public @NonNull RectF getWindowTargetRect() {
|
||||
PagedOrientationHandler orientationHandler = getOrientationHandler();
|
||||
DeviceProfile dp = mDp;
|
||||
final int halfIconSize = dp.iconSizePx / 2;
|
||||
float primaryDimension = orientationHandler
|
||||
.getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
|
||||
float secondaryDimension = orientationHandler
|
||||
.getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
|
||||
final float targetX = primaryDimension / 2f;
|
||||
final float targetY = secondaryDimension - dp.hotseatBarSizePx;
|
||||
// Fallback to animate to center of screen.
|
||||
return new RectF(targetX - halfIconSize, targetY - halfIconSize,
|
||||
targetX + halfIconSize, targetY + halfIconSize);
|
||||
}
|
||||
|
||||
public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome();
|
||||
|
||||
public void playAtomicAnimation(float velocity) {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an animation that transforms the current app window into the home app.
|
||||
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
|
||||
* @param homeAnimationFactory The home animation factory.
|
||||
*/
|
||||
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));
|
||||
RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
|
||||
|
||||
// Matrix to map a rect in Launcher space to window space
|
||||
Matrix homeToWindowPositionMap = new Matrix();
|
||||
mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap);
|
||||
|
||||
final RectF startRect = new RectF(cropRectF);
|
||||
mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
|
||||
// Move the startRect to Launcher space as floatingIconView runs in Launcher
|
||||
Matrix windowToHomePositionMap = new Matrix();
|
||||
homeToWindowPositionMap.invert(windowToHomePositionMap);
|
||||
windowToHomePositionMap.mapRect(startRect);
|
||||
|
||||
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
|
||||
if (isFloatingIconView) {
|
||||
anim.addAnimatorListener(fiv);
|
||||
fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
|
||||
fiv.setFastFinishRunnable(anim::end);
|
||||
}
|
||||
|
||||
SpringAnimationRunner runner = new SpringAnimationRunner(
|
||||
homeAnimationFactory, cropRectF, homeToWindowPositionMap);
|
||||
anim.addOnUpdateListener(runner);
|
||||
anim.addAnimatorListener(runner);
|
||||
return anim;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param progress The progress of the animation to the home screen.
|
||||
* @return The current alpha to set on the animating app window.
|
||||
*/
|
||||
protected float getWindowAlpha(float progress) {
|
||||
// Alpha interpolates between [1, 0] between progress values [start, end]
|
||||
final float start = 0f;
|
||||
final float end = 0.85f;
|
||||
|
||||
if (progress <= start) {
|
||||
return 1f;
|
||||
}
|
||||
if (progress >= end) {
|
||||
return 0f;
|
||||
}
|
||||
return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
|
||||
}
|
||||
|
||||
protected class SpringAnimationRunner extends AnimationSuccessListener
|
||||
implements RectFSpringAnim.OnUpdateListener, BuilderProxy {
|
||||
|
||||
final Rect mCropRect = new Rect();
|
||||
final Matrix mMatrix = new Matrix();
|
||||
|
||||
final RectF mWindowCurrentRect = new RectF();
|
||||
final Matrix mHomeToWindowPositionMap;
|
||||
|
||||
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) {
|
||||
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
|
||||
public void onUpdate(RectF currentRect, float progress) {
|
||||
mHomeAnim.setPlayFraction(progress);
|
||||
mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect);
|
||||
|
||||
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
|
||||
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
|
||||
mTransformParams
|
||||
.setTargetAlpha(getWindowAlpha(progress))
|
||||
.setCornerRadius(cornerRadius);
|
||||
|
||||
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
|
||||
if (mFIV != null) {
|
||||
mFIV.update(currentRect, 1f, progress,
|
||||
mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuildTargetParams(
|
||||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mCropRect)
|
||||
.withCornerRadius(params.getCornerRadius());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (mFIV != null) {
|
||||
mFIV.fastFinish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mHomeAnim.dispatchOnStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mHomeAnim.getAnimationPlayer().end();
|
||||
}
|
||||
}
|
||||
|
||||
public interface RunningWindowAnim {
|
||||
void end();
|
||||
|
||||
void cancel();
|
||||
|
||||
static RunningWindowAnim wrap(Animator animator) {
|
||||
return new RunningWindowAnim() {
|
||||
@Override
|
||||
public void end() {
|
||||
animator.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
animator.cancel();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
|
||||
return new RunningWindowAnim() {
|
||||
@Override
|
||||
public void end() {
|
||||
rectFSpringAnim.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
rectFSpringAnim.cancel();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -81,6 +81,7 @@ import com.android.quickstep.inputconsumers.OverviewInputConsumer;
|
||||
import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
|
||||
import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
|
||||
import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
|
||||
import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.quickstep.util.AssistantUtilities;
|
||||
import com.android.quickstep.util.ProtoTracer;
|
||||
@@ -588,6 +589,13 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
}
|
||||
}
|
||||
|
||||
// If Bubbles is expanded, use the overlay input consumer, which will close Bubbles
|
||||
// instead of going all the way home when a swipe up is detected.
|
||||
if (mDeviceState.isBubblesExpanded()) {
|
||||
base = new SysUiOverlayInputConsumer(
|
||||
getBaseContext(), mDeviceState, mInputMonitorCompat);
|
||||
}
|
||||
|
||||
if (mDeviceState.isScreenPinningActive()) {
|
||||
// Note: we only allow accessibility to wrap this, and it replaces the previous
|
||||
// base input consumer (which should be NO_OP anyway since topTaskLocked == true).
|
||||
@@ -613,7 +621,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
if (!isFixedRotationTransformEnabled()) {
|
||||
return;
|
||||
}
|
||||
mDeviceState.enableMultipleRegions(baseInputConsumer instanceof OtherActivityInputConsumer);
|
||||
baseInputConsumer.notifyOrientationSetup();
|
||||
}
|
||||
|
||||
private InputConsumer newBaseConsumer(GestureState previousGestureState,
|
||||
@@ -725,12 +733,13 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
if (!mDeviceState.isUserUnlocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
|
||||
// Prevent the overview from being started before the real home on first boot.
|
||||
return;
|
||||
}
|
||||
|
||||
if (RestoreDbTask.isPending(this)) {
|
||||
if (RestoreDbTask.isPending(this) || !mDeviceState.isUserSetupComplete()) {
|
||||
// Preloading while a restore is pending may cause launcher to start the restore
|
||||
// too early.
|
||||
return;
|
||||
|
||||
+9
-3
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.quickstep.fallback;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -30,7 +31,8 @@ import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
|
||||
/**
|
||||
* In 0-button mode, intercepts swipe up from the nav bar on FallbackRecentsView to go home.
|
||||
*/
|
||||
public class FallbackNavBarTouchController implements TouchController {
|
||||
public class FallbackNavBarTouchController implements TouchController,
|
||||
TriggerSwipeUpTouchTracker.OnSwipeUpListener {
|
||||
|
||||
private final RecentsActivity mActivity;
|
||||
@Nullable
|
||||
@@ -44,7 +46,7 @@ public class FallbackNavBarTouchController implements TouchController {
|
||||
DefaultDisplay.INSTANCE.get(mActivity).getInfo());
|
||||
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(mActivity,
|
||||
true /* disableHorizontalSwipe */, navBarPosition,
|
||||
null /* onInterceptTouch */, this::onSwipeUp);
|
||||
null /* onInterceptTouch */, this);
|
||||
} else {
|
||||
mTriggerSwipeUpTracker = null;
|
||||
}
|
||||
@@ -72,7 +74,11 @@ public class FallbackNavBarTouchController implements TouchController {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onSwipeUp(boolean wasFling) {
|
||||
@Override
|
||||
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
|
||||
mActivity.<FallbackRecentsView>getOverviewPanel().startHome();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpCancelled() {}
|
||||
}
|
||||
|
||||
+5
@@ -403,6 +403,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
||||
TraceHelper.INSTANCE.endSection(traceToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyOrientationSetup() {
|
||||
mDeviceState.onStartGesture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConsumerAboutToBeSwitched() {
|
||||
Preconditions.assertUIThread();
|
||||
|
||||
+9
-3
@@ -17,6 +17,7 @@ package com.android.quickstep.inputconsumers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PointF;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
@@ -33,7 +34,8 @@ import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
|
||||
public class OverviewWithoutFocusInputConsumer implements InputConsumer {
|
||||
public class OverviewWithoutFocusInputConsumer implements InputConsumer,
|
||||
TriggerSwipeUpTouchTracker.OnSwipeUpListener {
|
||||
|
||||
private final Context mContext;
|
||||
private final InputMonitorCompat mInputMonitor;
|
||||
@@ -45,7 +47,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer {
|
||||
mContext = context;
|
||||
mInputMonitor = inputMonitor;
|
||||
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, disableHorizontalSwipe,
|
||||
deviceState.getNavBarPosition(), this::onInterceptTouch, this::onSwipeUp);
|
||||
deviceState.getNavBarPosition(), this::onInterceptTouch, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,7 +72,8 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
private void onSwipeUp(boolean wasFling) {
|
||||
@Override
|
||||
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
|
||||
mContext.startActivity(new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
@@ -83,4 +86,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer {
|
||||
wasFling ? Touch.FLING : Touch.SWIPE, Direction.UP, containerType, pageIndex);
|
||||
activity.getUserEventDispatcher().setPreviousHomeGesture(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpCancelled() {}
|
||||
}
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.inputconsumers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PointF;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.quickstep.InputConsumer;
|
||||
import com.android.quickstep.RecentsAnimationDeviceState;
|
||||
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
|
||||
/**
|
||||
* Input consumer used when a fullscreen System UI overlay is showing (such as the expanded Bubbles
|
||||
* UI).
|
||||
*
|
||||
* This responds to swipes up by sending a closeSystemDialogs broadcast (causing overlays to close)
|
||||
* rather than closing the app behind the overlay and sending the user all the way home.
|
||||
*/
|
||||
public class SysUiOverlayInputConsumer implements InputConsumer,
|
||||
TriggerSwipeUpTouchTracker.OnSwipeUpListener {
|
||||
|
||||
private final Context mContext;
|
||||
private final InputMonitorCompat mInputMonitor;
|
||||
private final TriggerSwipeUpTouchTracker mTriggerSwipeUpTracker;
|
||||
|
||||
public SysUiOverlayInputConsumer(
|
||||
Context context,
|
||||
RecentsAnimationDeviceState deviceState,
|
||||
InputMonitorCompat inputMonitor) {
|
||||
mContext = context;
|
||||
mInputMonitor = inputMonitor;
|
||||
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, true,
|
||||
deviceState.getNavBarPosition(), this::onInterceptTouch, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return TYPE_SYSUI_OVERLAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowInterceptByParent() {
|
||||
return !mTriggerSwipeUpTracker.interceptedTouch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent ev) {
|
||||
mTriggerSwipeUpTracker.onMotionEvent(ev);
|
||||
}
|
||||
|
||||
private void onInterceptTouch() {
|
||||
if (mInputMonitor != null) {
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
|
||||
mInputMonitor.pilferPointers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
|
||||
// Close system dialogs when a swipe up is detected.
|
||||
mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpCancelled() {
|
||||
|
||||
}
|
||||
}
|
||||
+13
-8
@@ -23,6 +23,7 @@ import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
@@ -74,7 +75,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
private DeviceProfile mDp;
|
||||
|
||||
private final Matrix mMatrix = new Matrix();
|
||||
private RemoteAnimationTargetCompat mRunningTarget;
|
||||
private final Point mRunningTargetWindowPosition = new Point();
|
||||
|
||||
// Thumbnail view properties
|
||||
private final Rect mThumbnailPosition = new Rect();
|
||||
@@ -139,13 +140,19 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
* Sets the targets which the simulator will control
|
||||
*/
|
||||
public void setPreview(RemoteAnimationTargetCompat runningTarget) {
|
||||
mRunningTarget = runningTarget;
|
||||
setPreviewBounds(runningTarget.screenSpaceBounds, runningTarget.contentInsets);
|
||||
mRunningTargetWindowPosition.set(runningTarget.position.x, runningTarget.position.y);
|
||||
}
|
||||
|
||||
mThumbnailData.insets.set(mRunningTarget.contentInsets);
|
||||
/**
|
||||
* Sets the targets which the simulator will control
|
||||
*/
|
||||
public void setPreviewBounds(Rect bounds, Rect insets) {
|
||||
mThumbnailData.insets.set(insets);
|
||||
// TODO: What is this?
|
||||
mThumbnailData.windowingMode = WINDOWING_MODE_FULLSCREEN;
|
||||
|
||||
mThumbnailPosition.set(runningTarget.screenSpaceBounds);
|
||||
mThumbnailPosition.set(bounds);
|
||||
mLayoutValid = false;
|
||||
}
|
||||
|
||||
@@ -199,16 +206,14 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
postDisplayRotation(deltaRotation(
|
||||
mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()),
|
||||
mDp.widthPx, mDp.heightPx, matrix);
|
||||
if (mRunningTarget != null) {
|
||||
matrix.postTranslate(-mRunningTarget.position.x, -mRunningTarget.position.y);
|
||||
}
|
||||
matrix.postTranslate(-mRunningTargetWindowPosition.x, -mRunningTargetWindowPosition.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the target to the previously set parameters
|
||||
*/
|
||||
public void apply(TransformParams params) {
|
||||
if (mDp == null || mRunningTarget == null) {
|
||||
if (mDp == null || mThumbnailPosition.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!mLayoutValid) {
|
||||
|
||||
+11
-3
@@ -149,8 +149,12 @@ public class TriggerSwipeUpTouchTracker {
|
||||
isSwipeUp = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop;
|
||||
}
|
||||
|
||||
if (isSwipeUp && mOnSwipeUp != null) {
|
||||
mOnSwipeUp.onSwipeUp(wasFling);
|
||||
if (mOnSwipeUp != null) {
|
||||
if (isSwipeUp) {
|
||||
mOnSwipeUp.onSwipeUp(wasFling, new PointF(velocityX, velocityY));
|
||||
} else {
|
||||
mOnSwipeUp.onSwipeUpCancelled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +165,11 @@ public class TriggerSwipeUpTouchTracker {
|
||||
/**
|
||||
* Called on touch up if a swipe up was detected.
|
||||
* @param wasFling Whether the swipe was a fling, or just passed touch slop at low velocity.
|
||||
* @param finalVelocity The final velocity of the swipe.
|
||||
*/
|
||||
void onSwipeUp(boolean wasFling);
|
||||
void onSwipeUp(boolean wasFling, PointF finalVelocity);
|
||||
|
||||
/** Called on touch up if a swipe up was not detected. */
|
||||
void onSwipeUpCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
-2
@@ -45,8 +45,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||
public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout
|
||||
implements OnClickListener {
|
||||
|
||||
public static final long VISIBILITY_TRANSITION_DURATION_MS = 80;
|
||||
|
||||
@IntDef(flag = true, value = {
|
||||
HIDDEN_UNSUPPORTED_NAVIGATION,
|
||||
HIDDEN_DISABLED_FEATURE,
|
||||
|
||||
@@ -1039,6 +1039,11 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
|
||||
private void animateRecentsRotationInPlace(int newRotation) {
|
||||
if (mOrientationState.canLauncherRotate()) {
|
||||
// Update the rotation but let system take care of the rotation animation
|
||||
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
|
||||
return;
|
||||
}
|
||||
AnimatorSet pa = setRecentsChangedOrientation(true);
|
||||
pa.addListener(AnimationSuccessListener.forRunnable(() -> {
|
||||
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/gesture_tutorial_ripple"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/gesture_tutorial_fake_task_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/gesture_tutorial_fake_task_view_color"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/gesture_tutorial_fragment_hand_coaching"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -63,12 +63,6 @@
|
||||
<!-- Content description for a close button. [CHAR LIMIT=NONE] -->
|
||||
<string name="gesture_tutorial_close_button_content_description" translatable="false">Close</string>
|
||||
|
||||
|
||||
<!-- Hotseat migration notification title -->
|
||||
<string name="hotseat_edu_prompt_title">Easily access your most-used apps</string>
|
||||
<!-- Hotseat migration notification content -->
|
||||
<string name="hotseat_edu_prompt_content">Pixel predicts apps you\’ll need next, right on your Home screen. Tap to set up.</string>
|
||||
|
||||
<!-- Hotseat educational strings for users who don't qualify for migration -->
|
||||
<string name="hotseat_edu_title_migrate">Get app suggestions on the bottom row of your Home screen</string>
|
||||
|
||||
|
||||
@@ -102,11 +102,30 @@ public class DepthController implements StateHandler<LauncherState> {
|
||||
*/
|
||||
private float mDepth;
|
||||
|
||||
private View.OnAttachStateChangeListener mOnAttachListener;
|
||||
|
||||
public DepthController(Launcher l) {
|
||||
mLauncher = l;
|
||||
}
|
||||
|
||||
private void ensureDependencies() {
|
||||
if (mLauncher.getRootView() != null && mOnAttachListener == null) {
|
||||
mOnAttachListener = new View.OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View view) {
|
||||
// To handle the case where window token is invalid during last setDepth call.
|
||||
IBinder windowToken = mLauncher.getRootView().getWindowToken();
|
||||
if (windowToken != null) {
|
||||
mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View view) {
|
||||
}
|
||||
};
|
||||
mLauncher.getRootView().addOnAttachStateChangeListener(mOnAttachListener);
|
||||
}
|
||||
if (mWallpaperManager != null) {
|
||||
return;
|
||||
}
|
||||
@@ -184,10 +203,10 @@ public class DepthController implements StateHandler<LauncherState> {
|
||||
return;
|
||||
}
|
||||
|
||||
mDepth = depthF;
|
||||
if (mSurface == null || !mSurface.isValid()) {
|
||||
return;
|
||||
}
|
||||
mDepth = depthF;
|
||||
ensureDependencies();
|
||||
IBinder windowToken = mLauncher.getRootView().getWindowToken();
|
||||
if (windowToken != null) {
|
||||
|
||||
@@ -101,7 +101,7 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
|
||||
activity.getStateManager().reapplyState();
|
||||
}
|
||||
|
||||
public abstract void onSwipeUpToHomeComplete();
|
||||
public abstract void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState);
|
||||
|
||||
public abstract void onAssistantVisibilityChanged(float visibility);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public interface InputConsumer {
|
||||
int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
|
||||
int TYPE_RESET_GESTURE = 1 << 8;
|
||||
int TYPE_OVERSCROLL = 1 << 9;
|
||||
int TYPE_SYSUI_OVERLAY = 1 << 10;
|
||||
|
||||
String[] NAMES = new String[] {
|
||||
"TYPE_NO_OP", // 0
|
||||
@@ -46,6 +47,7 @@ public interface InputConsumer {
|
||||
"TYPE_OVERVIEW_WITHOUT_FOCUS", // 7
|
||||
"TYPE_RESET_GESTURE", // 8
|
||||
"TYPE_OVERSCROLL", // 9
|
||||
"TYPE_SYSUI_OVERLAY" // 10
|
||||
};
|
||||
|
||||
InputConsumer NO_OP = () -> TYPE_NO_OP;
|
||||
@@ -69,6 +71,11 @@ public interface InputConsumer {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle and specific setup necessary based on the orientation of the device
|
||||
*/
|
||||
default void notifyOrientationSetup() {}
|
||||
|
||||
/**
|
||||
* Returns the active input consumer is in the hierarchy of this input consumer.
|
||||
*/
|
||||
|
||||
@@ -37,7 +37,6 @@ import android.view.Surface;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
import com.android.launcher3.util.DefaultDisplay;
|
||||
import com.android.quickstep.util.RecentsOrientedState.SurfaceRotation;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@@ -67,6 +66,14 @@ class OrientationTouchTransformer {
|
||||
private boolean mEnableMultipleRegions;
|
||||
private Resources mResources;
|
||||
private OrientationRectF mLastRectTouched;
|
||||
/**
|
||||
* The rotation of the last touched nav bar. Derived from {@link #mLastRectTouched}, but has a
|
||||
* longer lifetime than the rect. Note this is different than {@link #mQuickStepStartingRotation}
|
||||
* as it always updates its value on every touch whereas mQuickstepStartingRotation only
|
||||
* updates when device rotation matches touch rotation. Maybe this will be only one necessary
|
||||
* after TODO(b/154580671) is in. TBD.
|
||||
*/
|
||||
private int mLastRectRotation;
|
||||
private SysUINavigationMode.Mode mMode;
|
||||
private QuickStepContractInfo mContractInfo;
|
||||
|
||||
@@ -143,15 +150,18 @@ class OrientationTouchTransformer {
|
||||
* ALSO, you BETTER call this with {@param enableMultipleRegions} set to false once you're done.
|
||||
*
|
||||
* @param enableMultipleRegions Set to true to start tracking multiple nav bar regions
|
||||
* @param info The current displayInfo
|
||||
* @param info The current displayInfo which will be the start of the quickswitch gesture
|
||||
*/
|
||||
void enableMultipleRegions(boolean enableMultipleRegions, DefaultDisplay.Info info) {
|
||||
mEnableMultipleRegions = enableMultipleRegions &&
|
||||
mMode != SysUINavigationMode.Mode.TWO_BUTTONS;
|
||||
if (!enableMultipleRegions) {
|
||||
if (mEnableMultipleRegions) {
|
||||
mQuickStepStartingRotation = info.rotation;
|
||||
} else {
|
||||
mLastRectRotation = 0;
|
||||
mQuickStepStartingRotation = QUICKSTEP_ROTATION_UNINITIALIZED;
|
||||
resetSwipeRegions(info);
|
||||
}
|
||||
resetSwipeRegions(info);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,11 +253,7 @@ class OrientationTouchTransformer {
|
||||
}
|
||||
|
||||
int getCurrentActiveRotation() {
|
||||
if (mLastRectTouched == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return mLastRectTouched.mRotation;
|
||||
}
|
||||
return mLastRectRotation;
|
||||
}
|
||||
|
||||
int getQuickStepStartingRotation() {
|
||||
@@ -286,7 +292,9 @@ class OrientationTouchTransformer {
|
||||
}
|
||||
if (rect.applyTransform(event, false)) {
|
||||
mLastRectTouched = rect;
|
||||
if (mCurrentDisplayRotation == mLastRectTouched.mRotation) {
|
||||
mLastRectRotation = rect.mRotation;
|
||||
if (mEnableMultipleRegions && mCurrentDisplayRotation == mLastRectRotation) {
|
||||
// TODO(b/154580671) might make this block unnecessary
|
||||
// Start a touch session for the default nav region for the display
|
||||
mQuickStepStartingRotation = mLastRectTouched.mRotation;
|
||||
resetSwipeRegions();
|
||||
|
||||
@@ -40,21 +40,20 @@ import java.util.function.Consumer;
|
||||
/**
|
||||
* Manages the recent task list from the system, caching it as necessary.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.P)
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
public class RecentTasksList extends TaskStackChangeListener {
|
||||
|
||||
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
|
||||
|
||||
private final KeyguardManagerCompat mKeyguardManager;
|
||||
private final LooperExecutor mMainThreadExecutor;
|
||||
private final ActivityManagerWrapper mActivityManagerWrapper;
|
||||
|
||||
// The list change id, increments as the task list changes in the system
|
||||
private int mChangeId;
|
||||
// The last change id when the list was last loaded completely, must be <= the list change id
|
||||
private int mLastLoadedId;
|
||||
// The last change id was loaded with keysOnly = true
|
||||
private boolean mLastLoadHadKeysOnly;
|
||||
|
||||
ArrayList<Task> mTasks = new ArrayList<>();
|
||||
private TaskLoadResult mResultsBg = INVALID_RESULT;
|
||||
private TaskLoadResult mResultsUi = INVALID_RESULT;
|
||||
|
||||
public RecentTasksList(LooperExecutor mainThreadExecutor,
|
||||
KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) {
|
||||
@@ -71,7 +70,7 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
|
||||
// Kick off task loading in the background
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
ArrayList<Task> tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */);
|
||||
ArrayList<Task> tasks = loadTasksInBackground(numTasks, -1, true /* loadKeysOnly */);
|
||||
mMainThreadExecutor.execute(() -> callback.accept(tasks));
|
||||
});
|
||||
}
|
||||
@@ -85,26 +84,30 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
*/
|
||||
public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
|
||||
final int requestLoadId = mChangeId;
|
||||
Runnable resultCallback = callback == null
|
||||
? () -> { }
|
||||
: () -> callback.accept(copyOf(mTasks));
|
||||
|
||||
if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
|
||||
if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
|
||||
// The list is up to date, send the callback on the next frame,
|
||||
// so that requestID can be returned first.
|
||||
mMainThreadExecutor.post(resultCallback);
|
||||
if (callback != null) {
|
||||
// Copy synchronously as the changeId might change by next frame
|
||||
ArrayList<Task> result = copyOf(mResultsUi);
|
||||
mMainThreadExecutor.post(() -> callback.accept(result));
|
||||
}
|
||||
|
||||
return requestLoadId;
|
||||
}
|
||||
|
||||
// Kick off task loading in the background
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
ArrayList<Task> tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly);
|
||||
|
||||
if (!mResultsBg.isValidForRequest(requestLoadId, loadKeysOnly)) {
|
||||
mResultsBg = loadTasksInBackground(Integer.MAX_VALUE, requestLoadId, loadKeysOnly);
|
||||
}
|
||||
TaskLoadResult loadResult = mResultsBg;
|
||||
mMainThreadExecutor.execute(() -> {
|
||||
mTasks = tasks;
|
||||
mLastLoadedId = requestLoadId;
|
||||
mLastLoadHadKeysOnly = loadKeysOnly;
|
||||
resultCallback.run();
|
||||
mResultsUi = loadResult;
|
||||
if (callback != null) {
|
||||
ArrayList<Task> result = copyOf(mResultsUi);
|
||||
callback.accept(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,8 +122,8 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onTaskStackChanged() {
|
||||
mChangeId++;
|
||||
public void onTaskStackChanged() {
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -131,22 +134,28 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
// callback (those are for changes to the active tasks), but the task list is still updated,
|
||||
// so we should also invalidate the change id to ensure we load a new list instead of
|
||||
// reusing a stale list.
|
||||
mChangeId++;
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskRemoved(int taskId) {
|
||||
mTasks = loadTasksInBackground(Integer.MAX_VALUE, false);
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityPinned(String packageName, int userId, int taskId,
|
||||
int stackId) {
|
||||
mChangeId++;
|
||||
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityUnpinned() {
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
private synchronized void invalidateLoadedTasks() {
|
||||
UI_HELPER_EXECUTOR.execute(() -> mResultsBg = INVALID_RESULT);
|
||||
mResultsUi = INVALID_RESULT;
|
||||
mChangeId++;
|
||||
}
|
||||
|
||||
@@ -154,9 +163,8 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
* Loads and creates a list of all the recent tasks.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
ArrayList<Task> loadTasksInBackground(int numTasks, boolean loadKeysOnly) {
|
||||
TaskLoadResult loadTasksInBackground(int numTasks, int requestId, boolean loadKeysOnly) {
|
||||
int currentUserId = Process.myUserHandle().getIdentifier();
|
||||
ArrayList<Task> allTasks = new ArrayList<>();
|
||||
List<ActivityManager.RecentTaskInfo> rawTasks =
|
||||
mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId);
|
||||
// The raw tasks are given in most-recent to least-recent order, we need to reverse it
|
||||
@@ -173,6 +181,7 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
}
|
||||
};
|
||||
|
||||
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
|
||||
for (ActivityManager.RecentTaskInfo rawTask : rawTasks) {
|
||||
Task.TaskKey taskKey = new Task.TaskKey(rawTask);
|
||||
Task task;
|
||||
@@ -197,4 +206,22 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
}
|
||||
return newTasks;
|
||||
}
|
||||
|
||||
private static class TaskLoadResult extends ArrayList<Task> {
|
||||
|
||||
final int mId;
|
||||
|
||||
// If the result was loaded with keysOnly = true
|
||||
final boolean mKeysOnly;
|
||||
|
||||
TaskLoadResult(int id, boolean keysOnly, int size) {
|
||||
super(size);
|
||||
mId = id;
|
||||
mKeysOnly = keysOnly;
|
||||
}
|
||||
|
||||
boolean isValidForRequest(int requestId, boolean loadKeysOnly) {
|
||||
return mId == requestId && (!mKeysOnly || loadKeysOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
@@ -58,8 +57,7 @@ public class RecentsAnimationController {
|
||||
private boolean mUseLauncherSysBarFlags = false;
|
||||
private boolean mSplitScreenMinimized = false;
|
||||
private boolean mTouchInProgress;
|
||||
private boolean mFinishPending;
|
||||
private @Nullable Runnable mFinishPendingCallback;
|
||||
private boolean mDisableInputProxyPending;
|
||||
|
||||
public RecentsAnimationController(RecentsAnimationControllerCompat controller,
|
||||
boolean allowMinimizeSplitScreen,
|
||||
@@ -138,12 +136,12 @@ public class RecentsAnimationController {
|
||||
|
||||
@UiThread
|
||||
public void finishAnimationToHome() {
|
||||
finishAndClear(true /* toRecents */, null, false /* sendUserLeaveHint */);
|
||||
finishAndDisableInputProxy(true /* toRecents */, null, false /* sendUserLeaveHint */);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void finishAnimationToApp() {
|
||||
finishAndClear(false /* toRecents */, null, false /* sendUserLeaveHint */);
|
||||
finishAndDisableInputProxy(false /* toRecents */, null, false /* sendUserLeaveHint */);
|
||||
}
|
||||
|
||||
/** See {@link #finish(boolean, Runnable, boolean)} */
|
||||
@@ -162,19 +160,16 @@ public class RecentsAnimationController {
|
||||
@UiThread
|
||||
public void finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) {
|
||||
Preconditions.assertUIThread();
|
||||
if (!toRecents) {
|
||||
finishAndClear(false, onFinishComplete, sendUserLeaveHint);
|
||||
if (toRecents && mTouchInProgress) {
|
||||
// Finish the controller as requested, but don't disable input proxy yet.
|
||||
mDisableInputProxyPending = true;
|
||||
finishController(toRecents, onFinishComplete, sendUserLeaveHint);
|
||||
} else {
|
||||
if (mTouchInProgress) {
|
||||
mFinishPending = true;
|
||||
mFinishPendingCallback = onFinishComplete;
|
||||
} else {
|
||||
finishAndClear(true, onFinishComplete, sendUserLeaveHint);
|
||||
}
|
||||
finishAndDisableInputProxy(toRecents, onFinishComplete, sendUserLeaveHint);
|
||||
}
|
||||
}
|
||||
|
||||
private void finishAndClear(boolean toRecents, Runnable onFinishComplete,
|
||||
private void finishAndDisableInputProxy(boolean toRecents, Runnable onFinishComplete,
|
||||
boolean sendUserLeaveHint) {
|
||||
disableInputProxy();
|
||||
finishController(toRecents, onFinishComplete, sendUserLeaveHint);
|
||||
@@ -262,11 +257,9 @@ public class RecentsAnimationController {
|
||||
} else if (action == ACTION_CANCEL || action == ACTION_UP) {
|
||||
// Finish any pending actions
|
||||
mTouchInProgress = false;
|
||||
if (mFinishPending) {
|
||||
mFinishPending = false;
|
||||
finishAndClear(true /* toRecents */, mFinishPendingCallback,
|
||||
false /* sendUserLeaveHint */);
|
||||
mFinishPendingCallback = null;
|
||||
if (mDisableInputProxyPending) {
|
||||
mDisableInputProxyPending = false;
|
||||
disableInputProxy();
|
||||
}
|
||||
}
|
||||
if (mInputConsumer != null) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.quickstep;
|
||||
import static android.content.Intent.ACTION_USER_UNLOCKED;
|
||||
|
||||
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;
|
||||
@@ -44,6 +45,7 @@ import android.content.res.Resources;
|
||||
import android.graphics.Region;
|
||||
import android.os.Process;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
@@ -52,6 +54,7 @@ import androidx.annotation.BinderThread;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
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.systemui.shared.system.ActivityManagerWrapper;
|
||||
@@ -103,7 +106,8 @@ public class RecentsAnimationDeviceState implements
|
||||
private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
|
||||
@Override
|
||||
public void onRecentTaskListFrozenChanged(boolean frozen) {
|
||||
if (frozen) {
|
||||
mTaskListFrozen = frozen;
|
||||
if (frozen || mInOverview) {
|
||||
return;
|
||||
}
|
||||
enableMultipleRegions(false);
|
||||
@@ -124,6 +128,9 @@ public class RecentsAnimationDeviceState implements
|
||||
* TODO: (b/156984037) For when user rotates after entering overview
|
||||
*/
|
||||
private boolean mInOverview;
|
||||
private boolean mTaskListFrozen;
|
||||
|
||||
private boolean mIsUserSetupComplete;
|
||||
|
||||
public RecentsAnimationDeviceState(Context context) {
|
||||
mContext = context;
|
||||
@@ -175,6 +182,17 @@ public class RecentsAnimationDeviceState implements
|
||||
ComponentName.unflattenFromString(blockingActivity));
|
||||
}
|
||||
}
|
||||
|
||||
SecureSettingsObserver userSetupObserver = new SecureSettingsObserver(
|
||||
context.getContentResolver(),
|
||||
e -> mIsUserSetupComplete = e,
|
||||
Settings.Secure.USER_SETUP_COMPLETE,
|
||||
0);
|
||||
mIsUserSetupComplete = userSetupObserver.getValue();
|
||||
if (!mIsUserSetupComplete) {
|
||||
userSetupObserver.register();
|
||||
runOnDestroy(userSetupObserver::unregister);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupOrientationSwipeHandler() {
|
||||
@@ -243,7 +261,8 @@ public class RecentsAnimationDeviceState implements
|
||||
|
||||
@Override
|
||||
public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
|
||||
if (info.id != getDisplayId()) {
|
||||
if (info.id != getDisplayId() || (flags & CHANGE_FRAME_DELAY) == CHANGE_FRAME_DELAY) {
|
||||
// ignore displays that aren't running launcher and frame refresh rate changes
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -314,6 +333,13 @@ public class RecentsAnimationDeviceState implements
|
||||
return mIsUserUnlocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the user has completed setup wizard
|
||||
*/
|
||||
public boolean isUserSetupComplete() {
|
||||
return mIsUserSetupComplete;
|
||||
}
|
||||
|
||||
private void notifyUserUnlocked() {
|
||||
for (Runnable action : mUserUnlockedActions) {
|
||||
action.run();
|
||||
@@ -360,7 +386,6 @@ public class RecentsAnimationDeviceState implements
|
||||
return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
|
||||
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
|
||||
&& (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
|
||||
&& (mSystemUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) == 0
|
||||
&& ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
|
||||
|| (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
|
||||
}
|
||||
@@ -380,6 +405,13 @@ public class RecentsAnimationDeviceState implements
|
||||
return (mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the bubble stack is expanded
|
||||
*/
|
||||
public boolean isBubblesExpanded() {
|
||||
return (mSystemUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether lock-task mode is active
|
||||
*/
|
||||
@@ -506,7 +538,7 @@ public class RecentsAnimationDeviceState implements
|
||||
* *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
|
||||
*/
|
||||
public void setOrientationTransformIfNeeded(MotionEvent event) {
|
||||
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()));
|
||||
@@ -514,25 +546,54 @@ public class RecentsAnimationDeviceState implements
|
||||
mOrientationTouchTransformer.transform(event);
|
||||
}
|
||||
|
||||
void onSwipeUpToOverview(BaseActivityInterface activityInterface) {
|
||||
mInOverview = true;
|
||||
activityInterface.onExitOverview(this, () -> {
|
||||
mInOverview = false;
|
||||
enableMultipleRegions(false);
|
||||
});
|
||||
void enableMultipleRegions(boolean enable) {
|
||||
mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
|
||||
notifySysuiForRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
|
||||
}
|
||||
|
||||
void enableMultipleRegions(boolean enable) {
|
||||
if (mInOverview) {
|
||||
return;
|
||||
private void notifySysuiForRotation(int rotation) {
|
||||
UI_HELPER_EXECUTOR.execute(() ->
|
||||
SystemUiProxy.INSTANCE.get(mContext).onQuickSwitchToNewTask(rotation));
|
||||
}
|
||||
|
||||
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
|
||||
notifySysuiForRotation(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, () -> {
|
||||
mInOverview = false;
|
||||
enableMultipleRegions(false);
|
||||
});
|
||||
} 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 {
|
||||
notifySysuiForRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
|
||||
}
|
||||
} else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) {
|
||||
if (!mTaskListFrozen) {
|
||||
// touched nav bar but didn't go anywhere and not quickswitching, do nothing
|
||||
return;
|
||||
}
|
||||
notifySysuiForRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
|
||||
}
|
||||
mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
int quickStepStartingRotation =
|
||||
mOrientationTouchTransformer.getQuickStepStartingRotation();
|
||||
SystemUiProxy.INSTANCE.get(mContext)
|
||||
.onQuickSwitchToNewTask(quickStepStartingRotation);
|
||||
});
|
||||
}
|
||||
|
||||
public int getCurrentActiveRotation() {
|
||||
|
||||
@@ -18,8 +18,11 @@ package com.android.quickstep.interaction;
|
||||
import static com.android.quickstep.interaction.TutorialController.TutorialType.BACK_NAVIGATION_COMPLETE;
|
||||
import static com.android.quickstep.interaction.TutorialController.TutorialType.LEFT_EDGE_BACK_NAVIGATION;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
|
||||
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
|
||||
@@ -154,11 +157,14 @@ final class BackGestureTutorialController extends TutorialController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNavBarGestureAttempted(NavBarGestureResult result) {
|
||||
public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
|
||||
if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
|
||||
if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) {
|
||||
mTutorialFragment.closeTutorial();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavBarGestureProgress(@Nullable Float displacement) {}
|
||||
}
|
||||
|
||||
+223
-4
@@ -15,19 +15,135 @@
|
||||
*/
|
||||
package com.android.quickstep.interaction;
|
||||
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
|
||||
import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION;
|
||||
import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.view.WindowInsets.Type;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.quickstep.AnimatedFloat;
|
||||
import com.android.quickstep.GestureState;
|
||||
import com.android.quickstep.OverviewComponentObserver;
|
||||
import com.android.quickstep.RecentsAnimationDeviceState;
|
||||
import com.android.quickstep.SwipeUpAnimationLogic;
|
||||
import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
|
||||
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
|
||||
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
|
||||
import com.android.quickstep.util.RectFSpringAnim;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
|
||||
|
||||
/** A {@link TutorialController} for the Home tutorial. */
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
final class HomeGestureTutorialController extends TutorialController {
|
||||
|
||||
private float mFakeTaskViewRadius;
|
||||
private Rect mFakeTaskViewRect = new Rect();
|
||||
|
||||
private final ViewSwipeUpAnimation mViewSwipeUpAnimation;
|
||||
private RunningWindowAnim mRunningWindowAnim;
|
||||
|
||||
HomeGestureTutorialController(HomeGestureTutorialFragment fragment, TutorialType tutorialType) {
|
||||
super(fragment, tutorialType);
|
||||
|
||||
RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext);
|
||||
OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState);
|
||||
mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
|
||||
new GestureState(observer, -1));
|
||||
observer.onDestroy();
|
||||
deviceState.destroy();
|
||||
|
||||
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext)
|
||||
.getDeviceProfile(mContext)
|
||||
.copy(mContext);
|
||||
Insets insets = mContext.getSystemService(WindowManager.class)
|
||||
.getCurrentWindowMetrics()
|
||||
.getWindowInsets()
|
||||
.getInsets(Type.systemBars());
|
||||
dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom));
|
||||
mViewSwipeUpAnimation.initDp(dp);
|
||||
|
||||
mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources());
|
||||
|
||||
mFakeTaskView.setClipToOutline(true);
|
||||
mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void cancelRunningAnimation() {
|
||||
if (mRunningWindowAnim != null) {
|
||||
mRunningWindowAnim.cancel();
|
||||
}
|
||||
mRunningWindowAnim = null;
|
||||
}
|
||||
|
||||
/** Fades the task view, optionally after animating to a fake Overview. */
|
||||
private void fadeOutFakeTaskView(boolean toOverviewFirst, @Nullable Runnable onEndRunnable) {
|
||||
cancelRunningAnimation();
|
||||
PendingAnimation anim = new PendingAnimation(300);
|
||||
AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation, boolean isReverse) {
|
||||
mFakeTaskView.setVisibility(View.INVISIBLE);
|
||||
mFakeTaskView.setAlpha(1);
|
||||
mRunningWindowAnim = null;
|
||||
}
|
||||
};
|
||||
if (toOverviewFirst) {
|
||||
anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation, boolean isReverse) {
|
||||
PendingAnimation fadeAnim = new PendingAnimation(300);
|
||||
fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL);
|
||||
fadeAnim.addListener(resetTaskView);
|
||||
AnimatorSet animset = fadeAnim.buildAnim();
|
||||
animset.setStartDelay(100);
|
||||
animset.start();
|
||||
mRunningWindowAnim = RunningWindowAnim.wrap(animset);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
anim.setViewAlpha(mFakeTaskView, 0, ACCEL);
|
||||
anim.addListener(resetTaskView);
|
||||
}
|
||||
if (onEndRunnable != null) {
|
||||
anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable));
|
||||
}
|
||||
AnimatorSet animset = anim.buildAnim();
|
||||
animset.start();
|
||||
mRunningWindowAnim = RunningWindowAnim.wrap(animset);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,22 +201,35 @@ final class HomeGestureTutorialController extends TutorialController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNavBarGestureAttempted(NavBarGestureResult result) {
|
||||
public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
|
||||
switch (mTutorialType) {
|
||||
case HOME_NAVIGATION:
|
||||
switch (result) {
|
||||
case HOME_GESTURE_COMPLETED:
|
||||
case HOME_GESTURE_COMPLETED: {
|
||||
hideFeedback();
|
||||
cancelRunningAnimation();
|
||||
hideHandCoachingAnimation();
|
||||
mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE);
|
||||
RectFSpringAnim rectAnim =
|
||||
mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity);
|
||||
// After home animation finishes, fade out and then move to the next screen.
|
||||
rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable(
|
||||
() -> fadeOutFakeTaskView(false,
|
||||
() -> mTutorialFragment.changeController(
|
||||
HOME_NAVIGATION_COMPLETE))));
|
||||
mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim);
|
||||
break;
|
||||
}
|
||||
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
|
||||
case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
|
||||
showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
|
||||
break;
|
||||
case OVERVIEW_GESTURE_COMPLETED:
|
||||
showFeedback(R.string.home_gesture_feedback_overview_detected);
|
||||
fadeOutFakeTaskView(true, () ->
|
||||
showFeedback(R.string.home_gesture_feedback_overview_detected));
|
||||
break;
|
||||
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
|
||||
case HOME_OR_OVERVIEW_CANCELLED:
|
||||
fadeOutFakeTaskView(false, null);
|
||||
showFeedback(R.string.home_gesture_feedback_wrong_swipe_direction);
|
||||
break;
|
||||
}
|
||||
@@ -112,4 +241,94 @@ final class HomeGestureTutorialController extends TutorialController {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavBarGestureProgress(@Nullable Float displacement) {
|
||||
if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE) {
|
||||
mFakeTaskView.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
mFakeTaskView.setVisibility(View.VISIBLE);
|
||||
if (mRunningWindowAnim == null) {
|
||||
mViewSwipeUpAnimation.updateDisplacement(displacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewSwipeUpAnimation extends SwipeUpAnimationLogic {
|
||||
|
||||
ViewSwipeUpAnimation(Context context, RecentsAnimationDeviceState deviceState,
|
||||
GestureState gestureState) {
|
||||
super(context, deviceState, gestureState, new FakeTransformParams());
|
||||
}
|
||||
|
||||
void initDp(DeviceProfile dp) {
|
||||
initTransitionEndpoints(dp);
|
||||
mTaskViewSimulator.setPreviewBounds(
|
||||
new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFinalShift() {
|
||||
float progress = mCurrentShift.value / mDragLengthFactor;
|
||||
mWindowTransitionController.setPlayFraction(progress);
|
||||
mTaskViewSimulator.apply(mTransformParams);
|
||||
}
|
||||
|
||||
AnimatedFloat getCurrentShift() {
|
||||
return mCurrentShift;
|
||||
}
|
||||
|
||||
RectFSpringAnim handleSwipeUpToHome(PointF velocity) {
|
||||
PointF velocityPxPerMs = new PointF(velocity.x, velocity.y);
|
||||
float currentShift = mCurrentShift.value;
|
||||
final float startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y
|
||||
* getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor);
|
||||
float distanceToTravel = (1 - currentShift) * mTransitionDragLength;
|
||||
|
||||
// we want the page's snap velocity to approximately match the velocity at
|
||||
// which the user flings, so we scale the duration by a value near to the
|
||||
// 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) {
|
||||
@Override
|
||||
public AnimatorPlaybackController createActivityAnimationToHome() {
|
||||
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RectF getWindowTargetRect() {
|
||||
int fakeHomeIconSizePx = mDp.allAppsIconSizePx;
|
||||
int fakeHomeIconLeft = (mDp.widthPx - fakeHomeIconSizePx) / 2;
|
||||
int fakeHomeIconTop = mDp.heightPx - (mDp.allAppsCellHeightPx * 3);
|
||||
return new RectF(fakeHomeIconLeft, fakeHomeIconTop,
|
||||
fakeHomeIconLeft + fakeHomeIconSizePx,
|
||||
fakeHomeIconTop + fakeHomeIconSizePx);
|
||||
}
|
||||
};
|
||||
RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory);
|
||||
windowAnim.start(mContext, velocityPxPerMs);
|
||||
return windowAnim;
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeTransformParams extends TransformParams {
|
||||
|
||||
@Override
|
||||
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
|
||||
SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
|
||||
proxy.onBuildTargetParams(builder, null, this);
|
||||
return new SurfaceParams[] {builder.build()};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applySurfaceParams(SurfaceParams[] params) {
|
||||
SurfaceParams p = params[0];
|
||||
mFakeTaskView.setAnimationMatrix(p.matrix);
|
||||
mFakeTaskViewRect.set(p.windowCrop);
|
||||
mFakeTaskViewRadius = p.cornerRadius;
|
||||
mFakeTaskView.invalidateOutline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.quickstep.interaction;
|
||||
|
||||
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_GESTURE_COMPLETED;
|
||||
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_NOT_STARTED_TOO_FAR_FROM_EDGE;
|
||||
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_CANCELLED;
|
||||
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION;
|
||||
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_GESTURE_COMPLETED;
|
||||
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE;
|
||||
@@ -24,19 +25,23 @@ import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestu
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.view.Display;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
import com.android.quickstep.util.NavBarPosition;
|
||||
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
|
||||
|
||||
/** Utility class to handle home gestures. */
|
||||
public class NavBarGestureHandler implements OnTouchListener {
|
||||
public class NavBarGestureHandler implements OnTouchListener,
|
||||
TriggerSwipeUpTouchTracker.OnSwipeUpListener {
|
||||
|
||||
private static final String LOG_TAG = "NavBarGestureHandler";
|
||||
|
||||
@@ -44,6 +49,7 @@ public class NavBarGestureHandler implements OnTouchListener {
|
||||
private final TriggerSwipeUpTouchTracker mSwipeUpTouchTracker;
|
||||
private int mBottomGestureHeight;
|
||||
private boolean mTouchCameFromNavBar;
|
||||
private float mDownY;
|
||||
private NavBarGestureAttemptCallback mGestureCallback;
|
||||
|
||||
NavBarGestureHandler(Context context) {
|
||||
@@ -55,10 +61,11 @@ public class NavBarGestureHandler implements OnTouchListener {
|
||||
displayRotation = display.getRotation();
|
||||
display.getRealSize(mDisplaySize);
|
||||
}
|
||||
mDownY = mDisplaySize.y;
|
||||
mSwipeUpTouchTracker =
|
||||
new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/,
|
||||
new NavBarPosition(Mode.NO_BUTTON, displayRotation),
|
||||
null /*onInterceptTouch*/, this::onSwipeUp);
|
||||
null /*onInterceptTouch*/, this);
|
||||
|
||||
final Resources resources = context.getResources();
|
||||
mBottomGestureHeight =
|
||||
@@ -73,16 +80,26 @@ public class NavBarGestureHandler implements OnTouchListener {
|
||||
mGestureCallback = null;
|
||||
}
|
||||
|
||||
private void onSwipeUp(boolean wasFling) {
|
||||
@Override
|
||||
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
|
||||
if (mGestureCallback == null) {
|
||||
return;
|
||||
}
|
||||
finalVelocity.set(finalVelocity.x / 1000, finalVelocity.y / 1000);
|
||||
if (mTouchCameFromNavBar) {
|
||||
mGestureCallback.onNavBarGestureAttempted(wasFling
|
||||
? HOME_GESTURE_COMPLETED : OVERVIEW_GESTURE_COMPLETED);
|
||||
? HOME_GESTURE_COMPLETED : OVERVIEW_GESTURE_COMPLETED, finalVelocity);
|
||||
} else {
|
||||
mGestureCallback.onNavBarGestureAttempted(wasFling
|
||||
? HOME_NOT_STARTED_TOO_FAR_FROM_EDGE : OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE);
|
||||
? HOME_NOT_STARTED_TOO_FAR_FROM_EDGE : OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE,
|
||||
finalVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpCancelled() {
|
||||
if (mGestureCallback != null) {
|
||||
mGestureCallback.onNavBarGestureAttempted(HOME_OR_OVERVIEW_CANCELLED, new PointF());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,15 +108,22 @@ public class NavBarGestureHandler implements OnTouchListener {
|
||||
int action = motionEvent.getAction();
|
||||
boolean intercepted = mSwipeUpTouchTracker.interceptedTouch();
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mTouchCameFromNavBar = motionEvent.getRawY() >= mDisplaySize.y - mBottomGestureHeight;
|
||||
mDownY = motionEvent.getY();
|
||||
mTouchCameFromNavBar = mDownY >= mDisplaySize.y - mBottomGestureHeight;
|
||||
if (!mTouchCameFromNavBar) {
|
||||
mGestureCallback.setNavBarGestureProgress(null);
|
||||
}
|
||||
mSwipeUpTouchTracker.init();
|
||||
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||
if (mGestureCallback != null && !intercepted && mTouchCameFromNavBar) {
|
||||
mGestureCallback.onNavBarGestureAttempted(
|
||||
HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION);
|
||||
HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, new PointF());
|
||||
intercepted = true;
|
||||
}
|
||||
}
|
||||
if (mTouchCameFromNavBar && mGestureCallback != null) {
|
||||
mGestureCallback.setNavBarGestureProgress(motionEvent.getY() - mDownY);
|
||||
}
|
||||
mSwipeUpTouchTracker.onMotionEvent(motionEvent);
|
||||
return intercepted;
|
||||
}
|
||||
@@ -110,12 +134,16 @@ public class NavBarGestureHandler implements OnTouchListener {
|
||||
OVERVIEW_GESTURE_COMPLETED,
|
||||
HOME_NOT_STARTED_TOO_FAR_FROM_EDGE,
|
||||
OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE,
|
||||
HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION // Side swipe on nav bar.
|
||||
HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, // Side swipe on nav bar.
|
||||
HOME_OR_OVERVIEW_CANCELLED
|
||||
}
|
||||
|
||||
/** Callback to let the UI react to attempted nav bar gestures. */
|
||||
interface NavBarGestureAttemptCallback {
|
||||
/** Called whenever any touch is completed. */
|
||||
void onNavBarGestureAttempted(NavBarGestureResult result);
|
||||
void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity);
|
||||
|
||||
/** Indicates how far a touch originating in the nav bar has moved from the nav bar. */
|
||||
void setNavBarGestureProgress(@Nullable Float displacement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.quickstep.interaction;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
@@ -39,11 +40,13 @@ abstract class TutorialController implements BackGestureAttemptCallback,
|
||||
|
||||
final TutorialFragment mTutorialFragment;
|
||||
TutorialType mTutorialType;
|
||||
final Context mContext;
|
||||
|
||||
final ImageButton mCloseButton;
|
||||
final TextView mTitleTextView;
|
||||
final TextView mSubtitleTextView;
|
||||
final TextView mFeedbackView;
|
||||
final View mFakeTaskView;
|
||||
final View mRippleView;
|
||||
final RippleDrawable mRippleDrawable;
|
||||
final TutorialHandAnimation mHandCoachingAnimation;
|
||||
@@ -55,6 +58,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
|
||||
TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
|
||||
mTutorialFragment = tutorialFragment;
|
||||
mTutorialType = tutorialType;
|
||||
mContext = mTutorialFragment.getContext();
|
||||
|
||||
View rootView = tutorialFragment.getRootView();
|
||||
mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
|
||||
@@ -62,6 +66,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
|
||||
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);
|
||||
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
|
||||
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
|
||||
mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
|
||||
mHandCoachingAnimation = tutorialFragment.getHandAnimation();
|
||||
|
||||
@@ -37,11 +37,6 @@ import com.android.quickstep.interaction.TutorialController.TutorialType;
|
||||
abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
||||
|
||||
private static final String LOG_TAG = "TutorialFragment";
|
||||
private static final String SYSTEM_NAVIGATION_SETTING_INTENT =
|
||||
"#Intent;action=com.android.settings.SEARCH_RESULT_TRAMPOLINE;S"
|
||||
+ ".:settings:fragment_args_key=gesture_system_navigation_input_summary;S"
|
||||
+ ".:settings:show_fragment=com.android.settings.gestures"
|
||||
+ ".SystemNavigationGestureSettings;end";
|
||||
static final String KEY_TUTORIAL_TYPE = "tutorial_type";
|
||||
|
||||
TutorialType mTutorialType;
|
||||
|
||||
@@ -65,10 +65,10 @@ public class StatsLogCompatManager extends StatsLogManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a {@link LauncherEvent}.
|
||||
* Logs a {@link EventEnum}.
|
||||
*/
|
||||
@Override
|
||||
public void log(LauncherEvent event) {
|
||||
public void log(EventEnum event) {
|
||||
log(event, DEFAULT_INSTANCE_ID, LauncherAtom.ItemInfo.getDefaultInstance());
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public class StatsLogCompatManager extends StatsLogManager {
|
||||
* Logs an event and accompanying {@link InstanceId}.
|
||||
*/
|
||||
@Override
|
||||
public void log(LauncherEvent event, InstanceId instanceId) {
|
||||
public void log(EventEnum event, InstanceId instanceId) {
|
||||
log(event, instanceId, LauncherAtom.ItemInfo.getDefaultInstance());
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ public class StatsLogCompatManager extends StatsLogManager {
|
||||
* Logs an event and accompanying {@link ItemInfo}.
|
||||
*/
|
||||
@Override
|
||||
public void log(LauncherEvent event, @Nullable LauncherAtom.ItemInfo info) {
|
||||
public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo info) {
|
||||
log(event, DEFAULT_INSTANCE_ID, info);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class StatsLogCompatManager extends StatsLogManager {
|
||||
* Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
|
||||
*/
|
||||
@Override
|
||||
public void log(LauncherEvent event, InstanceId instanceId,
|
||||
public void log(EventEnum event, InstanceId instanceId,
|
||||
@Nullable LauncherAtom.ItemInfo info) {
|
||||
logInternal(event, instanceId, info,
|
||||
LAUNCHER_UICHANGED__DST_STATE__HOME,
|
||||
@@ -102,14 +102,17 @@ public class StatsLogCompatManager extends StatsLogManager {
|
||||
/**
|
||||
* Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
|
||||
*/
|
||||
private void logInternal(LauncherEvent event, InstanceId instanceId,
|
||||
private void logInternal(EventEnum event, InstanceId instanceId,
|
||||
@Nullable LauncherAtom.ItemInfo info, int startState, int endState) {
|
||||
info = info == null ? LauncherAtom.ItemInfo.getDefaultInstance() : info;
|
||||
|
||||
if (IS_VERBOSE) {
|
||||
String name = (event instanceof LauncherEvent) ? ((LauncherEvent) event).name() :
|
||||
event.getId() + "";
|
||||
|
||||
Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
|
||||
? String.format("\n%s\n%s", event.name(), info)
|
||||
: String.format("%s(InstanceId:%s)\n%s", event.name(), instanceId, info));
|
||||
? String.format("\n%s\n%s", name, info)
|
||||
: String.format("%s(InstanceId:%s)\n%s", name, instanceId, info));
|
||||
}
|
||||
|
||||
if (!Utilities.ATLEAST_R) {
|
||||
|
||||
@@ -208,7 +208,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
||||
mDisplayRotation = displayRotation;
|
||||
mTouchRotation = touchRotation;
|
||||
|
||||
if (mLauncherRotation == mTouchRotation) {
|
||||
if (mLauncherRotation == mTouchRotation || canLauncherRotate()) {
|
||||
mOrientationHandler = PagedOrientationHandler.HOME_ROTATED;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "current RecentsOrientedState: " + this);
|
||||
@@ -240,7 +240,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
||||
|
||||
private void setFlag(int mask, boolean enabled) {
|
||||
boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
|
||||
&& mFlags == VALUE_ROTATION_WATCHER_ENABLED;
|
||||
&& (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED;
|
||||
if (enabled) {
|
||||
mFlags |= mask;
|
||||
} else {
|
||||
@@ -248,7 +248,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
||||
}
|
||||
|
||||
boolean isRotationEnabled = !TestProtocol.sDisableSensorRotation
|
||||
&& mFlags == VALUE_ROTATION_WATCHER_ENABLED;
|
||||
&& (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED;
|
||||
if (wasRotationEnabled != isRotationEnabled) {
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
if (isRotationEnabled) {
|
||||
|
||||
@@ -58,9 +58,9 @@ public class RecentTasksListTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTaskRemoved_reloadsAllTasks() {
|
||||
public void onTaskRemoved_doesNotFetchTasks() {
|
||||
mRecentTasksList.onTaskRemoved(0);
|
||||
verify(mockActivityManagerWrapper, times(1))
|
||||
verify(mockActivityManagerWrapper, times(0))
|
||||
.getRecentTasks(anyInt(), anyInt());
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class RecentTasksListTest {
|
||||
when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(Collections.singletonList(recentTaskInfo));
|
||||
|
||||
List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, true);
|
||||
List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, true);
|
||||
|
||||
assertEquals(1, taskList.size());
|
||||
assertNull(taskList.get(0).taskDescription.getLabel());
|
||||
@@ -91,7 +91,7 @@ public class RecentTasksListTest {
|
||||
when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(Collections.singletonList(recentTaskInfo));
|
||||
|
||||
List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, false);
|
||||
List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, false);
|
||||
|
||||
assertEquals(1, taskList.size());
|
||||
assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel());
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
<color name="gesture_tutorial_subtitle_color">#99000000</color> <!-- 60% black -->
|
||||
<color name="gesture_tutorial_feedback_color">#FF000000</color>
|
||||
<color name="gesture_tutorial_ripple_color">#A0C2F9</color> <!-- Light Blue -->
|
||||
<color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
|
||||
<color name="gesture_tutorial_action_button_label_color">#FFFFFFFF</color>
|
||||
<color name="gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
@@ -35,6 +36,8 @@ import android.view.ActionMode;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.WindowInsets.Type;
|
||||
import android.view.WindowMetrics;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -51,6 +54,7 @@ import com.android.launcher3.util.DefaultDisplay.Info;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
|
||||
/**
|
||||
* Extension of BaseActivity allowing support for drag-n-drop
|
||||
@@ -272,15 +276,19 @@ public abstract class BaseDraggingActivity extends BaseActivity
|
||||
|
||||
protected abstract void reapplyUi();
|
||||
|
||||
protected Rect getMultiWindowDisplaySize() {
|
||||
protected WindowBounds getMultiWindowDisplaySize() {
|
||||
if (Utilities.ATLEAST_R) {
|
||||
return new Rect(getWindowManager().getCurrentWindowMetrics().getBounds());
|
||||
WindowMetrics wm = getWindowManager().getCurrentWindowMetrics();
|
||||
|
||||
Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
|
||||
return new WindowBounds(wm.getBounds(),
|
||||
new Rect(insets.left, insets.top, insets.right, insets.bottom));
|
||||
}
|
||||
// Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return
|
||||
// the app window size
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
Point mwSize = new Point();
|
||||
display.getSize(mwSize);
|
||||
return new Rect(0, 0, mwSize.x, mwSize.y);
|
||||
return new WindowBounds(new Rect(0, 0, mwSize.x, mwSize.y), new Rect());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.android.launcher3.graphics.IconShape;
|
||||
import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.launcher3.icons.IconNormalizer;
|
||||
import com.android.launcher3.util.DefaultDisplay;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
|
||||
public class DeviceProfile {
|
||||
|
||||
@@ -265,19 +266,16 @@ public class DeviceProfile {
|
||||
/**
|
||||
* TODO: Move this to the builder as part of setMultiWindowMode
|
||||
*/
|
||||
public DeviceProfile getMultiWindowProfile(Context context, Rect windowPosition) {
|
||||
public DeviceProfile getMultiWindowProfile(Context context, WindowBounds windowBounds) {
|
||||
// We take the minimum sizes of this profile and it's multi-window variant to ensure that
|
||||
// the system decor is always excluded.
|
||||
Point mwSize = new Point(Math.min(availableWidthPx, windowPosition.width()),
|
||||
Math.min(availableHeightPx, windowPosition.height()));
|
||||
Point mwSize = new Point(Math.min(availableWidthPx, windowBounds.availableSize.x),
|
||||
Math.min(availableHeightPx, windowBounds.availableSize.y));
|
||||
|
||||
// In multi-window mode, we can have widthPx = availableWidthPx
|
||||
// and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
|
||||
// widthPx and heightPx values where it's needed.
|
||||
DeviceProfile profile = toBuilder(context)
|
||||
.setSizeRange(mwSize, mwSize)
|
||||
.setSize(mwSize.x, mwSize.y)
|
||||
.setWindowPosition(windowPosition.left, windowPosition.top)
|
||||
.setSize(windowBounds.bounds.width(), windowBounds.bounds.height())
|
||||
.setWindowPosition(windowBounds.bounds.left, windowBounds.bounds.top)
|
||||
.setMultiWindowMode(true)
|
||||
.build();
|
||||
|
||||
@@ -299,7 +297,7 @@ public class DeviceProfile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverse of {@link #getMultiWindowProfile(Context, Rect)}
|
||||
* Inverse of {@link #getMultiWindowProfile(Context, WindowBounds)}
|
||||
* @return device profile corresponding to the current orientation in non multi-window mode.
|
||||
*/
|
||||
public DeviceProfile getFullScreenProfile() {
|
||||
|
||||
@@ -200,7 +200,7 @@ public class InvariantDeviceProfile {
|
||||
DefaultDisplay.INSTANCE.get(context).getInfo(),
|
||||
getPredefinedDeviceProfiles(context, gridName));
|
||||
|
||||
Info myInfo = new Info(display);
|
||||
Info myInfo = new Info(context, display);
|
||||
DisplayOption myDisplayOption = invDistWeightedInterpolate(
|
||||
myInfo, getPredefinedDeviceProfiles(context, gridName));
|
||||
|
||||
|
||||
@@ -154,9 +154,9 @@ public class LauncherSettings {
|
||||
public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
|
||||
public static final int CONTAINER_ALL_APPS = -104;
|
||||
public static final int CONTAINER_WIDGETS_TRAY = -105;
|
||||
|
||||
// Represents search results view.
|
||||
public static final int CONTAINER_SEARCH_RESULTS = -106;
|
||||
public static final int CONTAINER_SHORTCUTS = -107;
|
||||
|
||||
public static final String containerToString(int container) {
|
||||
switch (container) {
|
||||
@@ -166,6 +166,7 @@ public class LauncherSettings {
|
||||
case CONTAINER_ALL_APPS: return "all_apps";
|
||||
case CONTAINER_WIDGETS_TRAY: return "widgets_tray";
|
||||
case CONTAINER_SEARCH_RESULTS: return "search_result";
|
||||
case CONTAINER_SHORTCUTS: return "shortcuts";
|
||||
default: return String.valueOf(container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
|
||||
import static com.android.launcher3.anim.Interpolators.INSTANT;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
|
||||
@@ -227,7 +229,9 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
||||
setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
|
||||
(visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade);
|
||||
|
||||
setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0, allAppsFade);
|
||||
// Set visibility of the container at the very beginning or end of the transition.
|
||||
setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0,
|
||||
hasAnyVisibleItem ? INSTANT : FINAL_FRAME);
|
||||
}
|
||||
|
||||
public AnimatorListenerAdapter getProgressAnimatorListener() {
|
||||
|
||||
@@ -61,6 +61,11 @@ public class Interpolators {
|
||||
public static final Interpolator EXAGGERATED_EASE;
|
||||
|
||||
public static final Interpolator INSTANT = t -> 1;
|
||||
/**
|
||||
* All values of t map to 0 until t == 1. This is primarily useful for setting view visibility,
|
||||
* which should only happen at the very end of the animation (when it's already hidden).
|
||||
*/
|
||||
public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1;
|
||||
|
||||
private static final int MIN_SETTLE_DURATION = 200;
|
||||
private static final float OVERSHOOT_FACTOR = 0.9f;
|
||||
|
||||
@@ -466,6 +466,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
if (!isEmpty(firstLabel)) {
|
||||
mFolderName.setHint("");
|
||||
mFolderName.setText(firstLabel);
|
||||
mFolderName.selectAll();
|
||||
}
|
||||
}
|
||||
mFolderName.showKeyboard();
|
||||
|
||||
@@ -32,7 +32,7 @@ import com.android.launcher3.util.ResourceBasedOverride;
|
||||
*/
|
||||
public class StatsLogManager implements ResourceBasedOverride {
|
||||
|
||||
interface EventEnum {
|
||||
public interface EventEnum {
|
||||
int getId();
|
||||
}
|
||||
|
||||
@@ -63,6 +63,18 @@ public class StatsLogManager implements ResourceBasedOverride {
|
||||
+ "new/same value.")
|
||||
LAUNCHER_FOLDER_LABEL_UPDATED(460),
|
||||
|
||||
@UiEvent(doc = "User long pressed on the workspace empty space.")
|
||||
LAUNCHER_WORKSPACE_LONGPRESS(461),
|
||||
|
||||
@UiEvent(doc = "User tapped or long pressed on a wallpaper icon inside launcher settings.")
|
||||
LAUNCHER_WALLPAPER_BUTTON_TAP_OR_LONGPRESS(462),
|
||||
|
||||
@UiEvent(doc = "User tapped or long pressed on settings icon inside launcher settings.")
|
||||
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS(463),
|
||||
|
||||
@UiEvent(doc = "User tapped or long pressed on widget tray icon inside launcher settings.")
|
||||
LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS(464),
|
||||
|
||||
@UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
|
||||
LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
|
||||
|
||||
@@ -82,7 +94,15 @@ public class StatsLogManager implements ResourceBasedOverride {
|
||||
|
||||
@UiEvent(doc = "User cancelled uninstalling the package after dropping on "
|
||||
+ "the icon onto 'Uninstall' button in the target bar")
|
||||
LAUNCHER_ITEM_UNINSTALL_CANCELLED(470);
|
||||
LAUNCHER_ITEM_UNINSTALL_CANCELLED(470),
|
||||
|
||||
@UiEvent(doc = "User opened package specific widgets list by tapping on widgets system "
|
||||
+ "shortcut within longpress popup window.")
|
||||
LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP(514),
|
||||
|
||||
@UiEvent(doc = "User opened app info of the package by tapping on appinfo system shortcut "
|
||||
+ "within longpress popup window.")
|
||||
LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP(515);
|
||||
// ADD MORE
|
||||
|
||||
private final int mId;
|
||||
@@ -113,27 +133,27 @@ public class StatsLogManager implements ResourceBasedOverride {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a {@link LauncherEvent}.
|
||||
* Logs a {@link EventEnum}.
|
||||
*/
|
||||
public void log(LauncherEvent event) {
|
||||
public void log(EventEnum event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link InstanceId}.
|
||||
*/
|
||||
public void log(LauncherEvent event, InstanceId instanceId) {
|
||||
public void log(EventEnum event, InstanceId instanceId) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link ItemInfo}.
|
||||
*/
|
||||
public void log(LauncherEvent event, @Nullable ItemInfo info) {
|
||||
public void log(EventEnum event, @Nullable ItemInfo itemInfo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
|
||||
*/
|
||||
public void log(LauncherEvent event, InstanceId instanceId, @Nullable ItemInfo info) {
|
||||
public void log(EventEnum event, InstanceId instanceId, @Nullable ItemInfo itemInfo) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SEARCH_RESULTS;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
|
||||
@@ -45,6 +46,7 @@ import com.android.launcher3.logger.LauncherAtom.AllAppsContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
|
||||
import com.android.launcher3.logger.LauncherAtom.PredictionContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
|
||||
import com.android.launcher3.util.ContentWriter;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -363,6 +365,10 @@ public class ItemInfo {
|
||||
return ContainerInfo.newBuilder()
|
||||
.setSearchResultContainer(SearchResultContainer.getDefaultInstance())
|
||||
.build();
|
||||
case CONTAINER_SHORTCUTS:
|
||||
return ContainerInfo.newBuilder()
|
||||
.setShortcutsContainer(ShortcutsContainer.getDefaultInstance())
|
||||
.build();
|
||||
}
|
||||
return ContainerInfo.getDefaultInstance();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.launcher3.popup;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
|
||||
import static com.android.launcher3.Utilities.squaredHypot;
|
||||
import static com.android.launcher3.Utilities.squaredTouchSlop;
|
||||
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
|
||||
@@ -61,6 +62,7 @@ import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.notification.NotificationInfo;
|
||||
import com.android.launcher3.notification.NotificationItemView;
|
||||
import com.android.launcher3.notification.NotificationKeyData;
|
||||
@@ -675,8 +677,10 @@ public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends Arr
|
||||
iconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
|
||||
|
||||
DraggableView draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_ICON);
|
||||
WorkspaceItemInfo itemInfo = sv.getFinalInfo();
|
||||
itemInfo.container = CONTAINER_SHORTCUTS;
|
||||
DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getIconView(), draggableView,
|
||||
mContainer, sv.getFinalInfo(),
|
||||
mContainer, itemInfo,
|
||||
new ShortcutDragPreviewProvider(sv.getIconView(), iconShift),
|
||||
new DragOptions());
|
||||
dv.animateShift(-iconShift.x, -iconShift.y);
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.launcher3.popup;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.os.Handler;
|
||||
@@ -160,6 +162,7 @@ public class PopupPopulator {
|
||||
final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
|
||||
cache.getUnbadgedShortcutIcon(si, shortcut);
|
||||
si.rank = i;
|
||||
si.container = CONTAINER_SHORTCUTS;
|
||||
|
||||
final DeepShortcutView view = shortcutViews.get(i);
|
||||
uiHandler.post(() -> view.applyShortcutInfo(si, shortcut, container));
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.android.launcher3.popup;
|
||||
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP;
|
||||
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
@@ -103,7 +105,6 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
|
||||
};
|
||||
|
||||
public static class Widgets extends SystemShortcut<Launcher> {
|
||||
|
||||
public Widgets(Launcher target, ItemInfo itemInfo) {
|
||||
super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo);
|
||||
}
|
||||
@@ -117,6 +118,9 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
|
||||
widgetsBottomSheet.populateAndShow(mItemInfo);
|
||||
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
|
||||
ControlType.WIDGETS_BUTTON, view);
|
||||
// TODO(thiruram): Fix missing container info when item is inside folder.
|
||||
mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP,
|
||||
mItemInfo.buildProto());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +141,9 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
|
||||
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
|
||||
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
|
||||
ControlType.APPINFO_TARGET, view);
|
||||
// TODO(thiruram): Fix missing container info when item is inside folder.
|
||||
mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP,
|
||||
mItemInfo.buildProto());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.view.MotionEvent.ACTION_POINTER_UP;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_LONGPRESS;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
@@ -41,8 +42,6 @@ import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.views.OptionsPopupView;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
|
||||
/**
|
||||
* Helper class to handle touch on empty space in workspace and show options popup on long press
|
||||
@@ -175,9 +174,7 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe
|
||||
|
||||
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
|
||||
Action.Direction.NONE, ContainerType.WORKSPACE,
|
||||
mWorkspace.getCurrentPage());
|
||||
mLauncher.getStatsLogManager().log(LAUNCHER_WORKSPACE_LONGPRESS);
|
||||
OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
|
||||
} else {
|
||||
cancelLongPress();
|
||||
|
||||
@@ -145,10 +145,11 @@ public class DefaultDisplay implements DisplayListener {
|
||||
}
|
||||
|
||||
private Info(Context context) {
|
||||
this(context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
|
||||
this(context, context.getSystemService(DisplayManager.class)
|
||||
.getDisplay(DEFAULT_DISPLAY));
|
||||
}
|
||||
|
||||
public Info(Display display) {
|
||||
public Info(Context context, Display display) {
|
||||
id = display.getDisplayId();
|
||||
rotation = display.getRotation();
|
||||
|
||||
@@ -161,8 +162,8 @@ public class DefaultDisplay implements DisplayListener {
|
||||
display.getRealSize(realSize);
|
||||
display.getCurrentSizeRange(smallestSize, largestSize);
|
||||
|
||||
metrics = new DisplayMetrics();
|
||||
display.getMetrics(metrics);
|
||||
Context defaultDisplayContext = context.createDisplayContext(display);
|
||||
metrics = defaultDisplayContext.getResources().getDisplayMetrics();
|
||||
}
|
||||
|
||||
private boolean hasDifferentSize(Info info) {
|
||||
|
||||
@@ -181,6 +181,11 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
|
||||
}
|
||||
|
||||
private TouchController findControllerToHandleTouch(MotionEvent ev) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "findControllerToHandleTouch ev=" + ev
|
||||
+ ", isEventInLauncher=" + isEventInLauncher(ev)
|
||||
+ ", topOpenView=" + AbstractFloatingView.getTopOpenView(mActivity));
|
||||
}
|
||||
if (isEventInLauncher(ev)) {
|
||||
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
|
||||
if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
|
||||
|
||||
@@ -59,6 +59,7 @@ import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.popup.SystemShortcut;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
|
||||
/**
|
||||
* A view that is created to look like another view with the purpose of creating fluid animations.
|
||||
@@ -560,6 +561,11 @@ public class FloatingIconView extends FrameLayout implements
|
||||
view.setVisibility(INVISIBLE);
|
||||
parent.addView(view);
|
||||
dragLayer.addView(view.mListenerView);
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "getFloatingIconView. listenerView "
|
||||
+ "added to dragLayer. listenerView=" + view.mListenerView + ", fiv=" + view,
|
||||
new Exception());
|
||||
}
|
||||
view.mListenerView.setListener(view::fastFinish);
|
||||
|
||||
view.mEndRunnable = () -> {
|
||||
@@ -639,6 +645,10 @@ public class FloatingIconView extends FrameLayout implements
|
||||
private void finish(DragLayer dragLayer) {
|
||||
((ViewGroup) dragLayer.getParent()).removeView(this);
|
||||
dragLayer.removeView(mListenerView);
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "listenerView removed from dragLayer. "
|
||||
+ "listenerView=" + mListenerView + ", fiv=" + this, new Exception());
|
||||
}
|
||||
recycle();
|
||||
mLauncher.getViewCache().recycleView(R.layout.floating_icon_view, this);
|
||||
}
|
||||
|
||||
@@ -17,18 +17,20 @@ package com.android.launcher3.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
|
||||
/**
|
||||
* An invisible AbstractFloatingView that can run a callback when it is being closed.
|
||||
*/
|
||||
public class ListenerView extends AbstractFloatingView {
|
||||
|
||||
public Runnable mCloseListener;
|
||||
private Runnable mCloseListener;
|
||||
|
||||
public ListenerView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -36,12 +38,20 @@ public class ListenerView extends AbstractFloatingView {
|
||||
}
|
||||
|
||||
public void setListener(Runnable listener) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView setListener lv=" + this
|
||||
+ ", listener=" + listener, new Exception());
|
||||
}
|
||||
mCloseListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView onAttachedToWindow lv=" + this,
|
||||
new Exception());
|
||||
}
|
||||
mIsOpen = true;
|
||||
}
|
||||
|
||||
@@ -49,10 +59,19 @@ public class ListenerView extends AbstractFloatingView {
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mIsOpen = false;
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView onDetachedFromView lv=" + this,
|
||||
new Exception());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView handeClose lv=" + this
|
||||
+ ", mIsOpen=" + mIsOpen + ", mCloseListener=" + mCloseListener
|
||||
+ ", getParent()=" + getParent(), new Exception());
|
||||
}
|
||||
if (mIsOpen) {
|
||||
if (mCloseListener != null) {
|
||||
mCloseListener.run();
|
||||
@@ -77,6 +96,10 @@ public class ListenerView extends AbstractFloatingView {
|
||||
|
||||
@Override
|
||||
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView touchEvent lv=" + this
|
||||
+ ", ev=" + ev, new Exception());
|
||||
}
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
handleClose(false);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ package com.android.launcher3.views;
|
||||
|
||||
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
|
||||
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WALLPAPER_BUTTON_TAP_OR_LONGPRESS;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -37,13 +40,12 @@ import androidx.annotation.VisibleForTesting;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.logging.StatsLogManager.EventEnum;
|
||||
import com.android.launcher3.model.WidgetsModel;
|
||||
import com.android.launcher3.popup.ArrowPopup;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
|
||||
import com.android.launcher3.widget.WidgetsFullSheet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -68,21 +70,21 @@ public class OptionsPopupView extends ArrowPopup
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
handleViewClick(view, Action.Touch.TAP);
|
||||
handleViewClick(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
return handleViewClick(view, Action.Touch.LONGPRESS);
|
||||
return handleViewClick(view);
|
||||
}
|
||||
|
||||
private boolean handleViewClick(View view, int action) {
|
||||
private boolean handleViewClick(View view) {
|
||||
OptionItem item = mItemMap.get(view);
|
||||
if (item == null) {
|
||||
return false;
|
||||
}
|
||||
if (item.mControlTypeForLog > 0) {
|
||||
logTap(action, item.mControlTypeForLog);
|
||||
if (item.mEventId.getId() > 0) {
|
||||
mLauncher.getStatsLogManager().log(item.mEventId);
|
||||
}
|
||||
if (item.mClickListener.onLongClick(view)) {
|
||||
close(true);
|
||||
@@ -91,10 +93,6 @@ public class OptionsPopupView extends ArrowPopup
|
||||
return false;
|
||||
}
|
||||
|
||||
private void logTap(int action, int controlType) {
|
||||
mLauncher.getUserEventDispatcher().logActionOnControl(action, controlType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() != MotionEvent.ACTION_DOWN) {
|
||||
@@ -159,13 +157,16 @@ public class OptionsPopupView extends ArrowPopup
|
||||
int resDrawable = Utilities.existsStyleWallpapers(launcher) ?
|
||||
R.drawable.ic_palette : R.drawable.ic_wallpaper;
|
||||
options.add(new OptionItem(resString, resDrawable,
|
||||
ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker));
|
||||
LAUNCHER_WALLPAPER_BUTTON_TAP_OR_LONGPRESS,
|
||||
OptionsPopupView::startWallpaperPicker));
|
||||
if (!WidgetsModel.GO_DISABLE_WIDGETS) {
|
||||
options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
|
||||
ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked));
|
||||
LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS,
|
||||
OptionsPopupView::onWidgetsClicked));
|
||||
}
|
||||
options.add(new OptionItem(R.string.settings_button_text, R.drawable.ic_setting,
|
||||
ControlType.SETTINGS_BUTTON, OptionsPopupView::startSettings));
|
||||
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
|
||||
OptionsPopupView::startSettings));
|
||||
|
||||
show(launcher, target, options);
|
||||
}
|
||||
@@ -224,14 +225,14 @@ public class OptionsPopupView extends ArrowPopup
|
||||
|
||||
private final int mLabelRes;
|
||||
private final int mIconRes;
|
||||
private final int mControlTypeForLog;
|
||||
private final EventEnum mEventId;
|
||||
private final OnLongClickListener mClickListener;
|
||||
|
||||
public OptionItem(int labelRes, int iconRes, int controlTypeForLog,
|
||||
public OptionItem(int labelRes, int iconRes, EventEnum eventId,
|
||||
OnLongClickListener clickListener) {
|
||||
mLabelRes = labelRes;
|
||||
mIconRes = iconRes;
|
||||
mControlTypeForLog = controlTypeForLog;
|
||||
mEventId = eventId;
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* 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.uioverrides;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
/** Render preview using surface view. */
|
||||
public class PreviewSurfaceRenderer {
|
||||
|
||||
/** Handle a received surface view request. */
|
||||
public static void render(Context context, Bundle bundle) { }
|
||||
}
|
||||
Reference in New Issue
Block a user