Merge "[OneSearch] Update the plugin interface to include more answer data." into sc-v2-dev
diff --git a/Android.bp b/Android.bp
index 43d28c9..c8d9186 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,9 @@
android_library {
name: "launcher-aosp-tapl",
+ libs: [
+ "framework-statsd",
+ ],
static_libs: [
"androidx.annotation_annotation",
"androidx.test.runner",
@@ -192,6 +195,9 @@
resource_dirs: [
"quickstep/res",
],
+ libs: [
+ "framework-statsd",
+ ],
static_libs: [
"Launcher3ResLib",
"SystemUISharedLib",
@@ -261,6 +267,9 @@
resource_dirs: [
"quickstep/res",
],
+ libs: [
+ "framework-statsd",
+ ],
static_libs: [
"SystemUI-statsd",
"SystemUISharedLib",
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index d67b23b..31c0f5f 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -30,6 +30,7 @@
determines how many thumbnails will be fetched in the background. -->
<integer name="recentsThumbnailCacheSize">3</integer>
<integer name="recentsIconCacheSize">12</integer>
+ <integer name="recentsScrollHapticMinGapMillis">20</integer>
<!-- Assistant Gesture -->
<integer name="assistant_gesture_min_time_threshold">200</integer>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 2534699..1f4be5b 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -90,6 +90,8 @@
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
import java.util.stream.Stream;
@@ -619,4 +621,12 @@
recentsView.finishRecentsAnimation(/* toRecents= */ true, null);
}
}
+
+ @Override
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ if (mDepthController != null) {
+ mDepthController.dump(prefix, writer);
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 5769f0b..55a140d 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -25,7 +25,9 @@
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.hybridhotseat.HotseatPredictionModel.convertDataModelToAppTargetBundle;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import android.app.StatsManager;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionManager;
import android.app.prediction.AppPredictor;
@@ -39,12 +41,14 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import android.util.Log;
+import android.util.StatsEvent;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -53,10 +57,10 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.util.Executors;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.PersistedItemArray;
import com.android.quickstep.logging.StatsLogCompatManager;
+import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.Collections;
import java.util.List;
@@ -85,6 +89,7 @@
private final InvariantDeviceProfile mIDP;
private final AppEventProducer mAppEventProducer;
+ private final StatsManager mStatsManager;
protected boolean mActive = false;
@@ -93,6 +98,7 @@
mIDP = InvariantDeviceProfile.INSTANCE.get(context);
StatsLogCompatManager.LOGS_CONSUMER.add(mAppEventProducer);
+ mStatsManager = context.getSystemService(StatsManager.class);
}
@Override
@@ -155,10 +161,60 @@
additionalSnapshotEvents(instanceId);
prefs.edit().putLong(LAST_SNAPSHOT_TIME_MILLIS, now).apply();
}
+
+ // Only register for launcher snapshot logging if this is the primary ModelDelegate
+ // instance, as there will be additional instances that may be destroyed at any time.
+ if (mIsPrimaryInstance) {
+ registerSnapshotLoggingCallback();
+ }
}
protected void additionalSnapshotEvents(InstanceId snapshotInstanceId){}
+ /**
+ * Registers a callback to log launcher workspace layout using Statsd pulled atom.
+ */
+ protected void registerSnapshotLoggingCallback() {
+ if (mStatsManager == null) {
+ Log.d(TAG, "Failed to get StatsManager");
+ }
+
+ try {
+ mStatsManager.setPullAtomCallback(
+ SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT,
+ null /* PullAtomMetadata */,
+ MODEL_EXECUTOR,
+ (i, eventList) -> {
+ InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+ IntSparseArrayMap<ItemInfo> itemsIdMap;
+ synchronized (mDataModel) {
+ itemsIdMap = mDataModel.itemsIdMap.clone();
+ }
+
+ for (ItemInfo info : itemsIdMap) {
+ FolderInfo parent = info.container > 0
+ ? (FolderInfo) itemsIdMap.get(info.container) : null;
+ LauncherAtom.ItemInfo itemInfo = info.buildProto(parent);
+ Log.d(TAG, itemInfo.toString());
+ StatsEvent statsEvent = StatsLogCompatManager.buildStatsEvent(itemInfo,
+ instanceId);
+ eventList.add(statsEvent);
+ }
+ Log.d(TAG,
+ String.format(
+ "Successfully logged %d workspace items with instanceId=%d",
+ itemsIdMap.size(), instanceId.getId()));
+ additionalSnapshotEvents(instanceId);
+ return StatsManager.PULL_SUCCESS;
+ }
+ );
+ Log.d(TAG, "Successfully registered for launcher snapshot logging!");
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to register launcher snapshot logging callback with StatsManager",
+ e);
+ }
+ }
+
@Override
public void validateData() {
super.validateData();
@@ -175,7 +231,9 @@
super.destroy();
mActive = false;
StatsLogCompatManager.LOGS_CONSUMER.remove(mAppEventProducer);
-
+ if (mIsPrimaryInstance) {
+ mStatsManager.clearPullAtomCallback(SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT);
+ }
destroyPredictors();
}
@@ -221,7 +279,7 @@
private void registerPredictor(PredictorState state, AppPredictor predictor) {
state.predictor = predictor;
state.predictor.registerPredictionUpdates(
- Executors.MODEL_EXECUTOR, t -> handleUpdate(state, t));
+ MODEL_EXECUTOR, t -> handleUpdate(state, t));
state.predictor.requestPredictionUpdate();
}
@@ -236,7 +294,7 @@
private void registerWidgetsPredictor(AppPredictor predictor) {
mWidgetsRecommendationState.predictor = predictor;
mWidgetsRecommendationState.predictor.registerPredictionUpdates(
- Executors.MODEL_EXECUTOR, targets -> {
+ MODEL_EXECUTOR, targets -> {
if (mWidgetsRecommendationState.setTargets(targets)) {
// No diff, skip
return;
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 3e2fb63..9d70cfa 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -44,6 +44,8 @@
import com.android.systemui.shared.system.BlurUtils;
import com.android.systemui.shared.system.WallpaperManagerCompat;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.function.Consumer;
/**
@@ -138,6 +140,11 @@
*/
private float mDepth;
/**
+ * Last blur value, in pixels, that was applied.
+ * For debugging purposes.
+ */
+ private int mCurrentBlur;
+ /**
* If we're launching and app and should not be blurring the screen for performance reasons.
*/
private boolean mBlurDisabledForAppLaunch;
@@ -306,10 +313,10 @@
if (supportsBlur) {
boolean opaque = mLauncher.getScrimView().isFullyOpaque();
- int blur = !mCrossWindowBlursEnabled || mBlurDisabledForAppLaunch
+ mCurrentBlur = !mCrossWindowBlursEnabled || mBlurDisabledForAppLaunch
? 0 : (int) (depth * mMaxBlurRadius);
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
- .setBackgroundBlurRadius(mSurface, blur)
+ .setBackgroundBlurRadius(mSurface, mCurrentBlur)
.setOpaque(mSurface, opaque);
// Set early wake-up flags when we know we're executing an expensive operation, this way
@@ -348,4 +355,18 @@
mwAnimation.setAutoCancel(true);
mwAnimation.start();
}
+
+ public void dump(String prefix, PrintWriter writer) {
+ writer.println(prefix + this.getClass().getSimpleName());
+ writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
+ writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
+ writer.println(prefix + "\tmSurface=" + mSurface);
+ writer.println(prefix + "\tmOverlayScrollProgress=" + mOverlayScrollProgress);
+ writer.println(prefix + "\tmDepth=" + mDepth);
+ writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
+ writer.println(prefix + "\tmBlurDisabledForAppLaunch=" + mBlurDisabledForAppLaunch);
+ writer.println(prefix + "\tmInEarlyWakeUp=" + mInEarlyWakeUp);
+ writer.println(prefix + "\tmIgnoreStateChangesDuringMultiWindowAnimation="
+ + mIgnoreStateChangesDuringMultiWindowAnimation);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
index 43f015c..978bd47 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
@@ -77,6 +77,7 @@
@Override
public void onTransitionFinished() {
mMoveFromCenterAnimator.onTransitionFinished();
+ mMoveFromCenterAnimator.clearRegisteredViews();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
deleted file mode 100644
index 0e12e30..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 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.plugins;
-
-import android.content.Context;
-
-import com.android.launcher3.Utilities;
-import com.android.systemui.shared.plugins.PluginInitializer;
-
-public class PluginInitializerImpl implements PluginInitializer {
- @Override
- public String[] getPrivilegedPlugins(Context context) {
- return new String[0];
- }
-
- @Override
- public void handleWtfs() {
- }
-
- public boolean isDebuggable() {
- return Utilities.IS_DEBUG_DEVICE;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index e12f42e..df0ac7c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -24,17 +24,19 @@
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginInstanceManager;
+import com.android.systemui.shared.plugins.PluginActionManager;
+import com.android.systemui.shared.plugins.PluginInstance;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.plugins.PluginPrefs;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -52,29 +54,36 @@
private PluginManagerWrapper(Context c) {
mContext = c;
- PluginInitializerImpl pluginInitializer = new PluginInitializerImpl();
mPluginEnabler = new PluginEnablerImpl(c);
- PluginInstanceManager.Factory instanceManagerFactory = new PluginInstanceManager.Factory(
- c, c.getPackageManager(), c.getMainExecutor(), MODEL_EXECUTOR, pluginInitializer,
+ List<String> privilegedPlugins = Collections.emptyList();
+ PluginInstance.Factory instanceFactory = new PluginInstance.Factory(
+ getClass().getClassLoader(), new PluginInstance.InstanceFactory<>(),
+ new PluginInstance.VersionChecker(), privilegedPlugins,
+ Utilities.IS_DEBUG_DEVICE);
+ PluginActionManager.Factory instanceManagerFactory = new PluginActionManager.Factory(
+ c, c.getPackageManager(), c.getMainExecutor(), MODEL_EXECUTOR,
c.getSystemService(NotificationManager.class), mPluginEnabler,
- Arrays.asList(pluginInitializer.getPrivilegedPlugins(c)));
+ privilegedPlugins, instanceFactory);
mPluginManager = new PluginManagerImpl(c, instanceManagerFactory,
- pluginInitializer.isDebuggable(),
+ Utilities.IS_DEBUG_DEVICE,
Optional.ofNullable(Thread.getDefaultUncaughtExceptionHandler()), mPluginEnabler,
- new PluginPrefs(c), Arrays.asList(pluginInitializer.getPrivilegedPlugins(c)));
+ new PluginPrefs(c), privilegedPlugins);
}
public PluginEnablerImpl getPluginEnabler() {
return mPluginEnabler;
}
- public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass) {
+ /** */
+ public <T extends Plugin> void addPluginListener(
+ PluginListener<T> listener, Class<T> pluginClass) {
addPluginListener(listener, pluginClass, false);
}
- public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass,
- boolean allowMultiple) {
+ /** */
+ public <T extends Plugin> void addPluginListener(
+ PluginListener<T> listener, Class<T> pluginClass, boolean allowMultiple) {
mPluginManager.addPluginListener(listener, pluginClass, allowMultiple);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 283743d..ef6f53e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -24,7 +24,7 @@
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.ObjectAnimator;
@@ -38,11 +38,11 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.OverviewToHomeAnim;
+import com.android.quickstep.util.VibratorWrapper;
import com.android.quickstep.views.RecentsView;
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 40c3e02..ff3c517 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -41,7 +41,7 @@
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
@@ -67,14 +67,15 @@
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector;
import com.android.launcher3.util.TouchController;
-import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.util.VibratorWrapper;
import com.android.quickstep.util.WorkspaceRevealAnim;
import com.android.quickstep.views.LauncherRecentsView;
+import com.android.quickstep.views.RecentsView;
/**
* Handles quick switching to a recent task from the home screen. To give as much flexibility to
@@ -398,6 +399,14 @@
nonOverviewAnim.setFloatValues(startProgress, endProgress);
mNonOverviewAnim.dispatchOnStart();
}
+ if (targetState == QUICK_SWITCH) {
+ // Navigating to quick switch, add scroll feedback since the first time is not
+ // considered a scroll by the RecentsView.
+ VibratorWrapper.INSTANCE.get(mLauncher).vibrate(
+ RecentsView.SCROLL_VIBRATION_PRIMITIVE,
+ RecentsView.SCROLL_VIBRATION_PRIMITIVE_SCALE,
+ RecentsView.SCROLL_VIBRATION_FALLBACK);
+ }
nonOverviewAnim.setDuration(Math.max(xDuration, yDuration));
mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 0603ba5..010f463 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -22,6 +22,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.os.SystemClock;
+import android.os.VibrationEffect;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -41,6 +42,7 @@
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.util.VibratorWrapper;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -55,6 +57,12 @@
private static final long MIN_TASK_DISMISS_ANIMATION_DURATION = 300;
private static final long MAX_TASK_DISMISS_ANIMATION_DURATION = 600;
+ public static final int TASK_DISMISS_VIBRATION_PRIMITIVE =
+ Utilities.ATLEAST_R ? VibrationEffect.Composition.PRIMITIVE_TICK : -1;
+ public static final float TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE = 1f;
+ public static final VibrationEffect TASK_DISMISS_VIBRATION_FALLBACK =
+ VibratorWrapper.EFFECT_TEXTURE_TICK;
+
protected final T mActivity;
private final SingleAxisSwipeDetector mDetector;
private final RecentsView mRecentsView;
@@ -334,10 +342,10 @@
fling = false;
}
PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
+ boolean goingUp = orientationHandler.isGoingUp(velocity, mIsRtl);
float progress = mCurrentAnimation.getProgressFraction();
float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
if (fling) {
- boolean goingUp = orientationHandler.isGoingUp(velocity, mIsRtl);
goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
} else {
goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
@@ -357,6 +365,10 @@
mCurrentAnimation.startWithVelocity(mActivity, goingToEnd,
velocity * orientationHandler.getSecondaryTranslationDirectionFactor(),
mEndDisplacement, animationDuration);
+ if (goingUp && goingToEnd) {
+ VibratorWrapper.INSTANCE.get(mActivity).vibrate(TASK_DISMISS_VIBRATION_PRIMITIVE,
+ TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE, TASK_DISMISS_VIBRATION_FALLBACK);
+ }
}
private void clearState() {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index be0c980..1e841da 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -36,7 +36,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -46,6 +45,7 @@
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELED;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
@@ -92,10 +92,10 @@
import com.android.launcher3.tracing.InputConsumerProto;
import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.BaseActivityInterface.AnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -111,6 +111,7 @@
import com.android.quickstep.util.SwipePipToHomeAnimator;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.VibratorWrapper;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -434,7 +435,7 @@
// RecentsView never updates the display rotation until swipe-up, force update
// RecentsOrientedState before passing to TaskViewSimulator.
mRecentsView.updateRecentsRotation();
- runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
.setOrientationState(mRecentsView.getPagedViewOrientedState()));
// If we've already ended the gesture and are going home, don't prepare recents UI,
@@ -753,6 +754,8 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
+ ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
+ mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(targets);
mRecentsAnimationController = controller;
mRecentsAnimationTargets = targets;
@@ -763,7 +766,7 @@
// orientation state is independent of which remote target handle we use since both
// should be pointing to the same one. Just choose index 0 for now since that works for
// both split and non-split
- RecentsOrientedState orientationState = mRemoteTargetHandles[0].mTaskViewSimulator
+ RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
.getOrientationState();
DeviceProfile dp = orientationState.getLauncherDeviceProfile();
if (targets.minimizedHomeBounds != null && primaryTaskTarget != null) {
@@ -942,6 +945,7 @@
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify swipe-to-home (recents animation) is finished
SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished();
+ LauncherSplitScreenListener.INSTANCE.getNoCreate().notifySwipingToHome();
break;
case RECENTS:
mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
@@ -1350,7 +1354,7 @@
RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
- final RecentsOrientedState orientationState = mRemoteTargetHandles[0].mTaskViewSimulator
+ final RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
.getOrientationState();
final int windowRotation = calculateWindowRotation(runningTaskTarget, orientationState);
final int homeRotation = orientationState.getRecentsActivityRotation();
@@ -1386,7 +1390,7 @@
// is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
if (homeRotation == ROTATION_0
&& (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
- builder.setFromRotation(mRemoteTargetHandles[0].mTaskViewSimulator, windowRotation,
+ builder.setFromRotation(mRemoteTargetHandles[0].getTaskViewSimulator(), windowRotation,
taskInfo.displayCutoutInsets);
}
final SwipePipToHomeAnimator swipePipToHomeAnimator = builder.build();
@@ -1756,7 +1760,7 @@
* depend on proper class initialization.
*/
protected void initAfterSubclassConstructor() {
- initTransitionEndpoints(mRemoteTargetHandles[0].mTaskViewSimulator
+ initTransitionEndpoints(mRemoteTargetHandles[0].getTaskViewSimulator()
.getOrientationState().getLauncherDeviceProfile());
}
@@ -1774,7 +1778,7 @@
protected void linkRecentsViewScroll() {
SurfaceTransactionApplier.create(mRecentsView, applier -> {
- runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
.setSyncTransactionApplier(applier));
runOnRecentsAnimationStart(() ->
mRecentsAnimationTargets.addReleaseCheck(applier));
@@ -1802,8 +1806,13 @@
mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId);
+ boolean isOldTaskSplit = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+ .getRunningSplitTaskIds().length > 0;
nextTask.launchTask(success -> {
resultCallback.accept(success);
+ if (isOldTaskSplit) {
+ SystemUiProxy.INSTANCE.getNoCreate().exitSplitScreen(taskId);
+ }
if (success) {
if (hasTaskPreviouslyAppeared) {
onRestartPreviouslyAppearedTask();
@@ -1910,18 +1919,19 @@
boolean notSwipingPipToHome = mRecentsAnimationTargets != null && !mIsSwipingPipToHome;
boolean setRecentsScroll = mRecentsViewScrollLinked && mRecentsView != null;
for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
- AnimatorControllerWithResistance playbackController = remoteHandle.mPlaybackController;
+ AnimatorControllerWithResistance playbackController =
+ remoteHandle.getPlaybackController();
if (playbackController != null) {
playbackController.setProgress(Math.max(mCurrentShift.value,
getScaleProgressDueToScroll()), mDragLengthFactor);
}
if (notSwipingPipToHome) {
- TaskViewSimulator taskViewSimulator = remoteHandle.mTaskViewSimulator;
+ TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator();
if (setRecentsScroll) {
taskViewSimulator.setScroll(mRecentsView.getScrollOffset());
}
- taskViewSimulator.apply(remoteHandle.mTransformParams);
+ taskViewSimulator.apply(remoteHandle.getTransformParams());
}
}
ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 773817f..80ae65e 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -105,7 +105,7 @@
mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
if (mRunningOverHome) {
runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
+ remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
FallbackSwipeHandler.this::updateHomeActivityTransformDuringSwipeUp));
}
}
@@ -115,7 +115,8 @@
super.initTransitionEndpoints(dp);
if (mRunningOverHome) {
// Full screen scale should be independent of remote target handle
- mMaxLauncherScale = 1 / mRemoteTargetHandles[0].mTaskViewSimulator.getFullScreenScale();
+ mMaxLauncherScale = 1 / mRemoteTargetHandles[0].getTaskViewSimulator()
+ .getFullScreenScale();
}
}
@@ -214,21 +215,21 @@
mHomeAlpha.value = Utilities.boundToRange(1 - mCurrentShift.value, 0, 1);
mVerticalShiftForScale.value = mCurrentShift.value;
runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
+ remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
FallbackHomeAnimationFactory.this
::updateHomeActivityTransformDuringHomeAnim));
} else {
mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha);
mHomeAlpha.value = 0;
runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
+ remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
FallbackHomeAnimationFactory.this
::updateHomeActivityTransformDuringHomeAnim));
}
mRecentsAlpha.value = 1;
runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
+ remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
FallbackHomeAnimationFactory.this
::updateRecentsActivityTransformDuringHomeAnim));
}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index ce3406c..dc22a61 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -183,13 +183,13 @@
Rect crop = new Rect();
// We can assume there is only one remote target here because staged split never animates
// into the app icon, only into the homescreen
- mRemoteTargetHandles[0].mTaskViewSimulator.getCurrentCropRect().roundOut(crop);
+ mRemoteTargetHandles[0].getTaskViewSimulator().getCurrentCropRect().roundOut(crop);
Size windowSize = new Size(crop.width(), crop.height());
int fallbackBackgroundColor =
FloatingWidgetView.getDefaultBackgroundColor(mContext, runningTaskTarget);
FloatingWidgetView floatingWidgetView = FloatingWidgetView.getFloatingWidgetView(mActivity,
hostView, backgroundLocation, windowSize,
- mRemoteTargetHandles[0].mTaskViewSimulator.getCurrentCornerRadius(),
+ mRemoteTargetHandles[0].getTaskViewSimulator().getCurrentCornerRadius(),
isTargetTranslucent, fallbackBackgroundColor);
return new FloatingViewHomeAnimationFactory(floatingWidgetView) {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 8fb851c..b232464 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -32,6 +32,7 @@
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.util.RunnableList;
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
+import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -157,6 +158,7 @@
}
if (cmd.type == TYPE_HOME) {
mService.startActivity(mOverviewComponentObserver.getHomeIntent());
+ LauncherSplitScreenListener.INSTANCE.getNoCreate().notifySwipingToHome();
return true;
}
} else {
@@ -175,6 +177,7 @@
return launchTask(recents, getNextTask(recents), cmd);
case TYPE_HOME:
recents.startHome();
+ LauncherSplitScreenListener.INSTANCE.getNoCreate().notifySwipingToHome();
return true;
}
}
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
new file mode 100644
index 0000000..dc04016
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
+import com.android.quickstep.util.LauncherSplitScreenListener;
+import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.TransformParams;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Glues together the necessary components to animate a remote target using a
+ * {@link TaskViewSimulator}
+ */
+public class RemoteTargetGluer {
+ private final RemoteTargetHandle[] mRemoteTargetHandles;
+ private SplitConfigurationOptions.StagedSplitBounds mStagedSplitBounds;
+
+ /**
+ * Use this constructor if remote targets are split-screen independent
+ */
+ public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy,
+ RemoteAnimationTargets targets) {
+ mRemoteTargetHandles = createHandles(context, sizingStrategy, targets.apps.length);
+ }
+
+ /**
+ * Use this constructor if you want the number of handles created to match the number of active
+ * running tasks
+ */
+ public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) {
+ int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+ .getRunningSplitTaskIds();
+ mRemoteTargetHandles = createHandles(context, sizingStrategy, splitIds.length == 2 ? 2 : 1);
+ }
+
+ private RemoteTargetHandle[] createHandles(Context context,
+ BaseActivityInterface sizingStrategy, int numHandles) {
+ RemoteTargetHandle[] handles = new RemoteTargetHandle[numHandles];
+ for (int i = 0; i < numHandles; i++) {
+ TaskViewSimulator tvs = new TaskViewSimulator(context, sizingStrategy);
+ TransformParams transformParams = new TransformParams();
+ handles[i] = new RemoteTargetHandle(tvs, transformParams);
+ }
+ return handles;
+ }
+
+ /**
+ * Pairs together {@link TaskViewSimulator}s and {@link TransformParams} into a
+ * {@link RemoteTargetHandle}
+ * Assigns only the apps associated with {@param targets} into their own TaskViewSimulators.
+ * Length of targets.apps should match that of {@link #mRemoteTargetHandles}.
+ *
+ * If split screen may be active when this is called, you might want to use
+ * {@link #assignTargetsForSplitScreen(RemoteAnimationTargets)}
+ */
+ public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
+ for (int i = 0; i < mRemoteTargetHandles.length; i++) {
+ RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[i];
+ mRemoteTargetHandles[i].mTransformParams.setTargetSet(
+ createRemoteAnimationTargetsForTarget(primaryTaskTarget, targets));
+ mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
+ }
+ return mRemoteTargetHandles;
+ }
+
+ /**
+ * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this matches the
+ * apps in targets.apps to that of the split screened tasks. If split screen is active, then
+ * {@link #mRemoteTargetHandles} index 0 will be the left/top task, index one right/bottom
+ */
+ public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
+ int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+ .getRunningSplitTaskIds();
+ RemoteAnimationTargetCompat primaryTaskTarget;
+ RemoteAnimationTargetCompat secondaryTaskTarget;
+ if (mRemoteTargetHandles.length == 1) {
+ // If we're not in split screen, the splitIds count doesn't really matter since we
+ // should always hit this case. Right now there's no use case for multiple app targets
+ // without being in split screen
+ primaryTaskTarget = targets.apps[0];
+ mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
+ mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
+ } else {
+ // split screen
+ primaryTaskTarget = targets.findTask(splitIds[0]);
+ secondaryTaskTarget = targets.findTask(splitIds[1]);
+
+ mStagedSplitBounds = new SplitConfigurationOptions.StagedSplitBounds(
+ primaryTaskTarget.screenSpaceBounds,
+ secondaryTaskTarget.screenSpaceBounds);
+ mRemoteTargetHandles[0].mTransformParams.setTargetSet(
+ createRemoteAnimationTargetsForTarget(primaryTaskTarget, targets));
+ mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget,
+ mStagedSplitBounds);
+
+ mRemoteTargetHandles[1].mTransformParams.setTargetSet(
+ createRemoteAnimationTargetsForTarget(secondaryTaskTarget, targets));
+ mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget,
+ mStagedSplitBounds);
+ }
+ return mRemoteTargetHandles;
+ }
+
+ private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
+ RemoteAnimationTargetCompat target,
+ RemoteAnimationTargets targets) {
+ return new RemoteAnimationTargets(new RemoteAnimationTargetCompat[]{target},
+ targets.wallpapers, targets.nonApps, targets.targetMode);
+ }
+
+ public RemoteTargetHandle[] getRemoteTargetHandles() {
+ return mRemoteTargetHandles;
+ }
+
+ public SplitConfigurationOptions.StagedSplitBounds getStagedSplitBounds() {
+ return mStagedSplitBounds;
+ }
+
+ /**
+ * Container to keep together all the associated objects whose properties need to be updated to
+ * animate a single remote app target
+ */
+ public static class RemoteTargetHandle {
+ private final TaskViewSimulator mTaskViewSimulator;
+ private final TransformParams mTransformParams;
+ @Nullable
+ private AnimatorControllerWithResistance mPlaybackController;
+
+ public RemoteTargetHandle(TaskViewSimulator taskViewSimulator,
+ TransformParams transformParams) {
+ mTransformParams = transformParams;
+ mTaskViewSimulator = taskViewSimulator;
+ }
+
+ public TaskViewSimulator getTaskViewSimulator() {
+ return mTaskViewSimulator;
+ }
+
+ public TransformParams getTransformParams() {
+ return mTransformParams;
+ }
+
+ @Nullable
+ public AnimatorControllerWithResistance getPlaybackController() {
+ return mPlaybackController;
+ }
+
+ public void setPlaybackController(
+ @Nullable AnimatorControllerWithResistance playbackController) {
+ mPlaybackController = playbackController;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 78de300..ebe46fe 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -15,13 +15,10 @@
*/
package com.android.quickstep;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_SELECT;
import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
import android.content.Context;
@@ -40,8 +37,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.AppCloseConfig;
import com.android.quickstep.util.LauncherSplitScreenListener;
@@ -60,6 +56,7 @@
RecentsAnimationCallbacks.RecentsAnimationListener{
protected static final Rect TEMP_RECT = new Rect();
+ protected final RemoteTargetGluer mTargetGluer;
protected DeviceProfile mDp;
@@ -67,8 +64,7 @@
protected final RecentsAnimationDeviceState mDeviceState;
protected final GestureState mGestureState;
- protected final RemoteTargetHandle[] mRemoteTargetHandles;
- protected SplitConfigurationOptions.StagedSplitBounds mStagedSplitBounds;
+ protected RemoteTargetHandle[] mRemoteTargetHandles;
// Shift in the range of [0, 1].
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
@@ -93,44 +89,35 @@
LauncherSplitScreenListener.INSTANCE.getNoCreate()
.getRunningSplitTaskIds().length > 1;
- TaskViewSimulator primaryTVS = new TaskViewSimulator(context,
- gestureState.getActivityInterface());
- primaryTVS.getOrientationState().update(
- mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
- mDeviceState.getRotationTouchHelper().getDisplayRotation());
- mRemoteTargetHandles = new RemoteTargetHandle[mIsSwipeForStagedSplit ? 2 : 1];
- mRemoteTargetHandles[0] = new RemoteTargetHandle(primaryTVS, transformParams);
-
- if (mIsSwipeForStagedSplit) {
- TaskViewSimulator secondaryTVS = new TaskViewSimulator(context,
- gestureState.getActivityInterface());
- secondaryTVS.getOrientationState().update(
- mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
- mDeviceState.getRotationTouchHelper().getDisplayRotation());
- mRemoteTargetHandles[1] = new RemoteTargetHandle(secondaryTVS, new TransformParams());
- }
+ mTargetGluer = new RemoteTargetGluer(mContext, mGestureState.getActivityInterface());
+ mRemoteTargetHandles = mTargetGluer.getRemoteTargetHandles();
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTaskViewSimulator().getOrientationState().update(
+ mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
+ mDeviceState.getRotationTouchHelper().getDisplayRotation()
+ ));
}
protected void initTransitionEndpoints(DeviceProfile dp) {
mDp = dp;
mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
- dp, mContext, TEMP_RECT, mRemoteTargetHandles[0].mTaskViewSimulator
+ dp, mContext, TEMP_RECT, mRemoteTargetHandles[0].getTaskViewSimulator()
.getOrientationState().getOrientationHandler());
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
PendingAnimation pendingAnimation = new PendingAnimation(mTransitionDragLength * 2);
- TaskViewSimulator taskViewSimulator = remoteHandle.mTaskViewSimulator;
+ TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator();
taskViewSimulator.setDp(dp);
taskViewSimulator.addAppToOverviewAnim(pendingAnimation, LINEAR);
AnimatorPlaybackController playbackController =
pendingAnimation.createPlaybackController();
- remoteHandle.mPlaybackController = AnimatorControllerWithResistance.createForRecents(
+ remoteHandle.setPlaybackController(AnimatorControllerWithResistance.createForRecents(
playbackController, mContext, taskViewSimulator.getOrientationState(),
mDp, taskViewSimulator.recentsViewScale, AnimatedFloat.VALUE,
taskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE
- );
+ ));
}
}
@@ -157,7 +144,7 @@
protected PagedOrientationHandler getOrientationHandler() {
// OrientationHandler should be independent of remote target, can directly take one
- return mRemoteTargetHandles[0].mTaskViewSimulator
+ return mRemoteTargetHandles[0].getTaskViewSimulator()
.getOrientationState().getOrientationHandler();
}
@@ -246,8 +233,8 @@
for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length;
i < mRemoteTargetHandlesLength; i++) {
RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
- TaskViewSimulator tvs = remoteHandle.mTaskViewSimulator;
- tvs.apply(remoteHandle.mTransformParams.setProgress(startProgress));
+ TaskViewSimulator tvs = remoteHandle.getTaskViewSimulator();
+ tvs.apply(remoteHandle.getTransformParams().setProgress(startProgress));
startRects[i] = new RectF(tvs.getCurrentCropRect());
tvs.applyWindowToHomeRotation(outMatrix);
@@ -266,53 +253,10 @@
/** @return only the TaskViewSimulators from {@link #mRemoteTargetHandles} */
protected TaskViewSimulator[] getRemoteTaskViewSimulators() {
return Arrays.stream(mRemoteTargetHandles)
- .map(remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator)
+ .map(remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator())
.toArray(TaskViewSimulator[]::new);
}
- @Override
- public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
- ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
- RemoteAnimationTargetCompat dividerTarget = targets.getNonAppTargetOfType(
- TYPE_DOCK_DIVIDER);
- RemoteAnimationTargetCompat primaryTaskTarget;
- RemoteAnimationTargetCompat secondaryTaskTarget;
-
- // TODO(b/197568823) Determine if we need to exclude assistant as one of the targets we
- // animate
- if (!mIsSwipeForStagedSplit) {
- primaryTaskTarget = targets.findTask(mGestureState.getRunningTaskId());
- mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
-
- if (primaryTaskTarget != null) {
- mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget);
- }
- } else {
- int[] taskIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
- .getRunningSplitTaskIds();
- // We're in staged split
- primaryTaskTarget = targets.findTask(taskIds[0]);
- secondaryTaskTarget = targets.findTask(taskIds[1]);
- mStagedSplitBounds = new SplitConfigurationOptions.StagedSplitBounds(
- primaryTaskTarget.screenSpaceBounds,
- secondaryTaskTarget.screenSpaceBounds, dividerTarget.screenSpaceBounds);
- mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget,
- mStagedSplitBounds);
- mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget,
- mStagedSplitBounds);
- mRemoteTargetHandles[0].mTransformParams.setTargetSet(
- createRemoteAnimationTargetsForTarget(primaryTaskTarget));
- mRemoteTargetHandles[1].mTransformParams.setTargetSet(
- createRemoteAnimationTargetsForTarget(secondaryTaskTarget));
- }
- }
-
- private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
- RemoteAnimationTargetCompat target) {
- return new RemoteAnimationTargets(new RemoteAnimationTargetCompat[]{target},
- null, null, MODE_CLOSING);
- }
/**
* 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.
@@ -329,8 +273,8 @@
i < mRemoteTargetHandlesLength; i++) {
RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
out[i] = getWindowAnimationToHomeInternal(homeAnimationFactory,
- targetRect, remoteHandle.mTransformParams, remoteHandle.mTaskViewSimulator,
- startRects[i], homeToWindowPositionMap);
+ targetRect, remoteHandle.getTransformParams(),
+ remoteHandle.getTaskViewSimulator(), startRects[i], homeToWindowPositionMap);
}
return out;
}
@@ -445,21 +389,6 @@
}
}
- /**
- * Container to keep together all the associated objects whose properties need to be updated to
- * animate a single remote app target
- */
- public static class RemoteTargetHandle {
- public TaskViewSimulator mTaskViewSimulator;
- public TransformParams mTransformParams;
- public AnimatorControllerWithResistance mPlaybackController;
- public RemoteTargetHandle(TaskViewSimulator taskViewSimulator,
- TransformParams transformParams) {
- mTransformParams = transformParams;
- mTaskViewSimulator = taskViewSimulator;
- }
- }
-
public interface RunningWindowAnim {
void end();
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 7d2d413..aea2d4c 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -533,10 +533,17 @@
}
}
- public void exitSplitScreen() {
+ /**
+ * To be called whenever the user exits out of split screen apps (either by launching another
+ * app or by swiping home)
+ * @param topTaskId The taskId of the new app that was launched. System will then move this task
+ * to the front of what the user sees while removing all other split stages.
+ * If swiping to home (or there is no task to put at the top), can pass in -1.
+ */
+ public void exitSplitScreen(int topTaskId) {
if (mSplitScreen != null) {
try {
- mSplitScreen.exitSplitScreen();
+ mSplitScreen.exitSplitScreen(topTaskId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call exitSplitScreen");
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index d4c94db..23dc913 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -55,7 +55,6 @@
import android.window.TransitionInfo;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
@@ -67,6 +66,7 @@
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
@@ -151,28 +151,7 @@
RemoteAnimationTargetCompat[] wallpaperTargets,
RemoteAnimationTargetCompat[] nonAppTargets, DepthController depthController,
PendingAnimation out) {
- boolean isRunningTask = v.isRunningTask();
- TransformParams params = null;
- TaskViewSimulator tsv = null;
- // TODO(b/195675206) handle two TSVs here
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
- params = v.getRecentsView().getRemoteTargetHandles()[0].mTransformParams;
- tsv = v.getRecentsView().getRemoteTargetHandles()[0].mTaskViewSimulator;
- }
- createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets, nonAppTargets,
- depthController, out, params, tsv);
- }
-
- /**
- * Creates an animation that controls the window of the opening targets for the recents launch
- * animation.
- */
- public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, DepthController depthController,
- PendingAnimation out, @Nullable TransformParams params,
- @Nullable TaskViewSimulator tsv) {
+ RecentsView recentsView = v.getRecentsView();
boolean isQuickSwitch = v.isEndQuickswitchCuj();
v.setEndQuickswitchCuj(false);
@@ -183,16 +162,27 @@
inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
final RemoteAnimationTargetCompat navBarTarget = targets.getNavBarRemoteAnimationTarget();
- if (params == null) {
- SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
- targets.addReleaseCheck(applier);
+ SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+ targets.addReleaseCheck(applier);
- params = new TransformParams()
- .setSyncTransactionApplier(applier)
- .setTargetSet(targets);
+ RemoteTargetHandle[] remoteTargetHandles;
+ RemoteTargetHandle[] recentsViewHandles = recentsView.getRemoteTargetHandles();
+ if (v.isRunningTask()) {
+ // Re-use existing handles
+ remoteTargetHandles = recentsViewHandles;
+ } else {
+ RemoteTargetGluer gluer = new RemoteTargetGluer(v.getContext(),
+ recentsView.getSizeStrategy(), targets);
+ if (recentsViewHandles != null && recentsViewHandles.length > 1) {
+ remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets);
+ } else {
+ remoteTargetHandles = gluer.assignTargets(targets);
+ }
+ }
+ for (RemoteTargetHandle remoteTargetGluer : remoteTargetHandles) {
+ remoteTargetGluer.getTransformParams().setSyncTransactionApplier(applier);
}
- final RecentsView recentsView = v.getRecentsView();
int taskIndex = recentsView.indexOfChild(v);
Context context = v.getContext();
DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
@@ -202,45 +192,51 @@
float gridTranslationSecondary = recentsView.getGridTranslationSecondary(taskIndex);
int startScroll = recentsView.getScrollOffset(taskIndex);
- TaskViewSimulator topMostSimulator = null;
+ RemoteTargetHandle[] topMostSimulators = null;
- if (tsv == null && targets.apps.length > 0) {
- tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
- tsv.setDp(dp);
+ if (!v.isRunningTask()) {
+ // TVSs already initialized from the running task, no need to re-init
+ for (RemoteTargetHandle targetHandle : remoteTargetHandles) {
+ TaskViewSimulator tvsLocal = targetHandle.getTaskViewSimulator();
+ tvsLocal.setDp(dp);
- // RecentsView never updates the display rotation until swipe-up so the value may
- // be stale. Use the display value instead.
- int displayRotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
- tsv.getOrientationState().update(displayRotation, displayRotation);
+ // RecentsView never updates the display rotation until swipe-up so the value may
+ // be stale. Use the display value instead.
+ int displayRotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
+ tvsLocal.getOrientationState().update(displayRotation, displayRotation);
- tsv.setPreview(targets.apps[targets.apps.length - 1]);
- tsv.fullScreenProgress.value = 0;
- tsv.recentsViewScale.value = 1;
- if (showAsGrid) {
- tsv.taskSecondaryTranslation.value = gridTranslationSecondary;
+ tvsLocal.fullScreenProgress.value = 0;
+ tvsLocal.recentsViewScale.value = 1;
+ if (showAsGrid) {
+ tvsLocal.taskSecondaryTranslation.value = gridTranslationSecondary;
+ }
+ tvsLocal.setScroll(startScroll);
+
+ // Fade in the task during the initial 20% of the animation
+ out.addFloat(targetHandle.getTransformParams(), TransformParams.TARGET_ALPHA, 0, 1,
+ clampToProgress(LINEAR, 0, 0.2f));
}
- tsv.setScroll(startScroll);
-
- // Fade in the task during the initial 20% of the animation
- out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
- clampToProgress(LINEAR, 0, 0.2f));
}
- if (tsv != null) {
- out.setFloat(tsv.fullScreenProgress,
+ for (RemoteTargetHandle targetHandle : remoteTargetHandles) {
+ TaskViewSimulator tvsLocal = targetHandle.getTaskViewSimulator();
+ out.setFloat(tvsLocal.fullScreenProgress,
AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
- out.setFloat(tsv.recentsViewScale,
- AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
+ out.setFloat(tvsLocal.recentsViewScale,
+ AnimatedFloat.VALUE, tvsLocal.getFullScreenScale(),
+ TOUCH_RESPONSE_INTERPOLATOR);
if (showAsGrid) {
- out.setFloat(tsv.taskSecondaryTranslation, AnimatedFloat.VALUE, 0,
+ out.setFloat(tvsLocal.taskSecondaryTranslation, AnimatedFloat.VALUE, 0,
TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL);
}
- out.setFloat(tsv.recentsViewScroll, AnimatedFloat.VALUE, 0,
+ out.setFloat(tvsLocal.recentsViewScroll, AnimatedFloat.VALUE, 0,
TOUCH_RESPONSE_INTERPOLATOR);
- TaskViewSimulator finalTsv = tsv;
- TransformParams finalParams = params;
- out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
+ out.addOnFrameCallback(() -> {
+ for (RemoteTargetHandle handle : remoteTargetHandles) {
+ handle.getTaskViewSimulator().apply(handle.getTransformParams());
+ }
+ });
if (navBarTarget != null) {
final Rect cropRect = new Rect();
out.addOnFrameListener(new MultiValueUpdateListener() {
@@ -253,15 +249,20 @@
public void onUpdate(float percent, boolean initOnly) {
final SurfaceParams.Builder navBuilder =
new SurfaceParams.Builder(navBarTarget.leash);
- if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
- finalTsv.getCurrentCropRect().round(cropRect);
- navBuilder.withMatrix(finalTsv.getCurrentMatrix())
- .withWindowCrop(cropRect)
- .withAlpha(mNavFadeIn.value);
- } else {
- navBuilder.withAlpha(mNavFadeOut.value);
+
+ // TODO Do we need to operate over multiple TVSs for the navbar leash?
+ for (RemoteTargetHandle handle : remoteTargetHandles) {
+ if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
+ TaskViewSimulator taskViewSimulator = handle.getTaskViewSimulator();
+ taskViewSimulator.getCurrentCropRect().round(cropRect);
+ navBuilder.withMatrix(taskViewSimulator.getCurrentMatrix())
+ .withWindowCrop(cropRect)
+ .withAlpha(mNavFadeIn.value);
+ } else {
+ navBuilder.withAlpha(mNavFadeOut.value);
+ }
+ handle.getTransformParams().applySurfaceParams(navBuilder.build());
}
- finalParams.applySurfaceParams(navBuilder.build());
}
});
} else if (inLiveTileMode) {
@@ -273,14 +274,16 @@
controller.animateNavigationBarToApp(RECENTS_LAUNCH_DURATION);
}
}
- topMostSimulator = tsv;
+ topMostSimulators = remoteTargetHandles;
}
- if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
+ if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulators.length > 0) {
out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
- TaskViewSimulator simulatorToCopy = topMostSimulator;
- simulatorToCopy.apply(params);
+ RemoteTargetHandle[] simulatorCopies = topMostSimulators;
+ for (RemoteTargetHandle handle : simulatorCopies) {
+ handle.getTaskViewSimulator().apply(handle.getTransformParams());
+ }
// Mt represents the overall transformation on the thumbnailView relative to the
// Launcher's rootView
@@ -294,36 +297,49 @@
// During animation we apply transformation on the thumbnailView (and not the rootView)
// to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
// Mt K(0)` K(t) Mt`
- TaskThumbnailView ttv = v.getThumbnail();
- RectF tvBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
- float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
- getDescendantCoordRelativeToAncestor(ttv, ttv.getRootView(), tvBoundsMapped, false);
- RectF tvBoundsInRoot = new RectF(
- tvBoundsMapped[0], tvBoundsMapped[1],
- tvBoundsMapped[2], tvBoundsMapped[3]);
+ TaskThumbnailView[] thumbnails = v.getThumbnails();
+ Matrix[] mt = new Matrix[simulatorCopies.length];
+ Matrix[] mti = new Matrix[simulatorCopies.length];
+ for (int i = 0; i < thumbnails.length; i++) {
+ TaskThumbnailView ttv = thumbnails[i];
+ RectF localBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
+ float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
+ getDescendantCoordRelativeToAncestor(ttv, ttv.getRootView(), tvBoundsMapped, false);
+ RectF localBoundsInRoot = new RectF(
+ tvBoundsMapped[0], tvBoundsMapped[1],
+ tvBoundsMapped[2], tvBoundsMapped[3]);
+ Matrix localMt = new Matrix();
+ localMt.setRectToRect(localBounds, localBoundsInRoot, ScaleToFit.FILL);
+ mt[i] = localMt;
- Matrix mt = new Matrix();
- mt.setRectToRect(tvBounds, tvBoundsInRoot, ScaleToFit.FILL);
+ Matrix localMti = new Matrix();
+ localMti.invert(localMt);
+ mti[i] = localMti;
+ }
- Matrix mti = new Matrix();
- mt.invert(mti);
-
- Matrix k0i = new Matrix();
- simulatorToCopy.getCurrentMatrix().invert(k0i);
-
+ Matrix[] k0i = new Matrix[simulatorCopies.length];
+ for (int i = 0; i < simulatorCopies.length; i++) {
+ k0i[i] = new Matrix();
+ simulatorCopies[i].getTaskViewSimulator().getCurrentMatrix().invert(k0i[i]);
+ }
Matrix animationMatrix = new Matrix();
out.addOnFrameCallback(() -> {
- animationMatrix.set(mt);
- animationMatrix.postConcat(k0i);
- animationMatrix.postConcat(simulatorToCopy.getCurrentMatrix());
- animationMatrix.postConcat(mti);
- ttv.setAnimationMatrix(animationMatrix);
+ for (int i = 0; i < simulatorCopies.length; i++) {
+ animationMatrix.set(mt[i]);
+ animationMatrix.postConcat(k0i[i]);
+ animationMatrix.postConcat(simulatorCopies[i]
+ .getTaskViewSimulator().getCurrentMatrix());
+ animationMatrix.postConcat(mti[i]);
+ thumbnails[i].setAnimationMatrix(animationMatrix);
+ }
});
out.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- ttv.setAnimationMatrix(null);
+ for (TaskThumbnailView ttv : thumbnails) {
+ ttv.setAnimationMatrix(null);
+ }
}
});
}
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
index 7465db3..b2b2f59 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
@@ -43,7 +43,7 @@
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.util.VibratorWrapper;
+import com.android.quickstep.util.VibratorWrapper;
/** Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java. */
public class EdgeBackGesturePanel extends View {
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index fc1dd6f..851cccf 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -16,7 +16,6 @@
package com.android.quickstep.interaction;
import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_COMPLETED;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_BAD_ANGLE;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT;
@@ -26,6 +25,7 @@
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;
+import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -47,11 +47,11 @@
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
+import com.android.quickstep.util.VibratorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
/** Utility class to handle Home and Assistant gestures. */
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index c2c8f61..1f75936 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -46,7 +46,6 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.OverviewComponentObserver;
@@ -260,16 +259,16 @@
void initDp(DeviceProfile dp) {
initTransitionEndpoints(dp);
- mRemoteTargetHandles[0].mTaskViewSimulator.setPreviewBounds(
+ mRemoteTargetHandles[0].getTaskViewSimulator().setPreviewBounds(
new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets());
}
@Override
public void updateFinalShift() {
- mRemoteTargetHandles[0].mPlaybackController
+ mRemoteTargetHandles[0].getPlaybackController()
.setProgress(mCurrentShift.value, mDragLengthFactor);
- mRemoteTargetHandles[0].mTaskViewSimulator.apply(
- mRemoteTargetHandles[0].mTransformParams);
+ mRemoteTargetHandles[0].getTaskViewSimulator().apply(
+ mRemoteTargetHandles[0].getTransformParams());
}
AnimatedFloat getCurrentShift() {
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 6575996..7893f8d 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -31,9 +31,11 @@
import android.content.Context;
import android.util.Log;
+import android.util.StatsEvent;
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.slice.SliceItem;
@@ -135,6 +137,36 @@
}
/**
+ * Builds {@link StatsEvent} from {@link LauncherAtom.ItemInfo}. Used for pulled atom callback
+ * implementation.
+ */
+ public static StatsEvent buildStatsEvent(LauncherAtom.ItemInfo info,
+ @Nullable InstanceId instanceId) {
+ return SysUiStatsLog.buildStatsEvent(
+ SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT, // atom ID,
+ LAUNCHER_WORKSPACE_SNAPSHOT.getId(), // event_id = 1;
+ info.getAttribute().getNumber() * ATTRIBUTE_MULTIPLIER
+ + info.getItemCase().getNumber(), // item_id = 2;
+ instanceId == null ? 0 : instanceId.getId(), //instance_id = 3;
+ 0, //uid = 4 [(is_uid) = true];
+ getPackageName(info), // package_name = 5;
+ getComponentName(info), // component_name = 6;
+ getGridX(info, false), //grid_x = 7 [default = -1];
+ getGridY(info, false), //grid_y = 8 [default = -1];
+ getPageId(info), // page_id = 9 [default = -2];
+ getGridX(info, true), //grid_x_parent = 10 [default = -1];
+ getGridY(info, true), //grid_y_parent = 11 [default = -1];
+ getParentPageId(info), //page_id_parent = 12 [default = -2];
+ getHierarchy(info), // container_id = 13;
+ info.getIsWork(), // is_work_profile = 14;
+ info.getAttribute().getNumber(), // attribute_id = 15;
+ getCardinality(info), // cardinality = 16;
+ info.getWidget().getSpanX(), // span_x = 17 [default = 1];
+ info.getWidget().getSpanY() // span_y = 18 [default = 1];
+ );
+ }
+
+ /**
* Helps to construct and write statsd compatible log message.
*/
private static class StatsCompatLogger implements StatsLogger {
diff --git a/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java b/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
index 0f4ed01..fa4cddc 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
@@ -44,8 +44,6 @@
if (frozen) {
mPersistentGroupedIds = getRunningSplitTaskIds();
} else {
- // TODO(b/198310766) Need to also explicitly exit split screen if
- // we're not currently viewing split screened apps
mPersistentGroupedIds = EMPTY_ARRAY;
}
}
@@ -53,8 +51,11 @@
/**
* Gets set to current split taskIDs whenever the task list is frozen, and set to empty array
- * whenever task list unfreezes.
- * When not null, this indicates that we need to load a GroupedTaskView as the most recent
+ * whenever task list unfreezes. This also gets set to empty array whenever the user swipes to
+ * home - in that case the task list does not unfreeze immediately after the gesture, so it's
+ * done via {@link #notifySwipingToHome()}.
+ *
+ * When not empty, this indicates that we need to load a GroupedTaskView as the most recent
* page, so user can quickswitch back to a grouped task.
*/
private int[] mPersistentGroupedIds;
@@ -140,6 +141,18 @@
}
}
+ /** Notifies SystemUi to remove any split screen state */
+ public void notifySwipingToHome() {
+ boolean hasSplitTasks = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+ .getPersistentSplitIds().length > 0;
+ if (!hasSplitTasks) {
+ return;
+ }
+
+ SystemUiProxy.INSTANCE.getNoCreate().exitSplitScreen(-1);
+ mPersistentGroupedIds = EMPTY_ARRAY;
+ }
+
private void resetTaskId(StagedSplitTaskPosition taskPosition) {
taskPosition.taskId = -1;
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index a089e73..849a7bc 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -296,9 +296,8 @@
}
float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1);
- mCurrentFullscreenParams.setProgress(
- fullScreenProgress, recentsViewScale.value, /*taskViewScale=*/1f, mTaskRect.width(),
- mDp, mPositionHelper);
+ mCurrentFullscreenParams.setProgress(fullScreenProgress, recentsViewScale.value,
+ /* taskViewScale= */1f, mTaskRect.width(), mDp, mPositionHelper);
// Apply thumbnail matrix
RectF insets = mCurrentFullscreenParams.mCurrentDrawnInsets;
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/quickstep/src/com/android/quickstep/util/VibratorWrapper.java
similarity index 65%
rename from src/com/android/launcher3/util/VibratorWrapper.java
rename to quickstep/src/com/android/quickstep/util/VibratorWrapper.java
index b0defd4..211bd08 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/quickstep/src/com/android/quickstep/util/VibratorWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.util;
+package com.android.quickstep.util;
import static android.os.VibrationEffect.createPredefined;
import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
@@ -21,15 +21,20 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.media.AudioAttributes;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.MainThreadInitializedObject;
+
/**
* Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
*/
@@ -39,8 +44,15 @@
public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE =
new MainThreadInitializedObject<>(VibratorWrapper::new);
+ public static final AudioAttributes VIBRATION_ATTRS = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .build();
+
public static final VibrationEffect EFFECT_CLICK =
createPredefined(VibrationEffect.EFFECT_CLICK);
+ public static final VibrationEffect EFFECT_TEXTURE_TICK =
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TEXTURE_TICK);
/**
* Haptic when entering overview.
@@ -78,7 +90,27 @@
/** Vibrates with the given effect if haptic feedback is available and enabled. */
public void vibrate(VibrationEffect vibrationEffect) {
if (mHasVibrator && mIsHapticFeedbackEnabled) {
- UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect));
+ UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect, VIBRATION_ATTRS));
+ }
+ }
+
+ /**
+ * Vibrates with a single primitive, if supported, or use a fallback effect instead. This only
+ * vibrates if haptic feedback is available and enabled.
+ */
+ @SuppressLint("NewApi")
+ public void vibrate(int primitiveId, float primitiveScale, VibrationEffect fallbackEffect) {
+ if (mHasVibrator && mIsHapticFeedbackEnabled) {
+ UI_HELPER_EXECUTOR.execute(() -> {
+ if (Utilities.ATLEAST_R && primitiveId >= 0
+ && mVibrator.areAllPrimitivesSupported(primitiveId)) {
+ mVibrator.vibrate(VibrationEffect.startComposition()
+ .addPrimitive(primitiveId, primitiveScale)
+ .compose(), VIBRATION_ATTRS);
+ } else {
+ mVibrator.vibrate(fallbackEffect, VIBRATION_ATTRS);
+ }
+ });
}
}
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index a1befc5..5a86464 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -97,8 +97,9 @@
floatingView.mOrientationHandler =
originalView.getRecentsView().getPagedOrientationHandler();
- floatingView.mSplitPlaceholderView.setIcon(originalView.getIconView());
- floatingView.mSplitPlaceholderView.getIcon()
+ floatingView.mSplitPlaceholderView.setIconView(originalView.getIconView(),
+ launcher.getDeviceProfile().overviewTaskIconDrawableSizePx);
+ floatingView.mSplitPlaceholderView.getIconView()
.setRotation(floatingView.mOrientationHandler.getDegreesRotated());
parent.addView(floatingView);
return floatingView;
@@ -141,8 +142,8 @@
// TODO(194414938) seems like this scale value could be fine tuned, some stretchiness
mImageView.setScaleX(1f / scaleX + scaleX * progress);
mImageView.setScaleY(1f / scaleY + scaleY * progress);
- mOrientationHandler.setPrimaryScale(mSplitPlaceholderView.getIcon(), childScaleX);
- mOrientationHandler.setSecondaryScale(mSplitPlaceholderView.getIcon(), childScaleY);
+ mOrientationHandler.setPrimaryScale(mSplitPlaceholderView.getIconView(), childScaleX);
+ mOrientationHandler.setSecondaryScale(mSplitPlaceholderView.getIconView(), childScaleY);
}
protected void initPosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index d3077ad..8562719 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -113,6 +113,11 @@
}
@Override
+ public TaskThumbnailView[] getThumbnails() {
+ return new TaskThumbnailView[]{mSnapshotView, mSnapshotView2};
+ }
+
+ @Override
public void onRecycle() {
super.onRecycle();
mSnapshotView2.setThumbnail(mSecondaryTask, null);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index b690982..dd470e8 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -19,7 +19,6 @@
import static android.view.Surface.ROTATION_0;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
@@ -36,6 +35,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
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.LINEAR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -53,7 +53,6 @@
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -80,7 +79,9 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.VibrationEffect;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
@@ -146,7 +147,8 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
import com.android.quickstep.RemoteAnimationTargets;
-import com.android.quickstep.SwipeUpAnimationLogic.RemoteTargetHandle;
+import com.android.quickstep.RemoteTargetGluer;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
@@ -160,6 +162,7 @@
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.VibratorWrapper;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -244,6 +247,12 @@
}
};
+ public static final int SCROLL_VIBRATION_PRIMITIVE =
+ Utilities.ATLEAST_S ? VibrationEffect.Composition.PRIMITIVE_LOW_TICK : -1;
+ public static final float SCROLL_VIBRATION_PRIMITIVE_SCALE = 0.6f;
+ public static final VibrationEffect SCROLL_VIBRATION_FALLBACK =
+ VibratorWrapper.EFFECT_TEXTURE_TICK;
+
/**
* Can be used to tint the color of the RecentsView to simulate a scrim that can views
* excluded from. Really should be a proper scrim.
@@ -325,7 +334,7 @@
view.runActionOnRemoteHandles(new Consumer<RemoteTargetHandle>() {
@Override
public void accept(RemoteTargetHandle remoteTargetHandle) {
- remoteTargetHandle.mTaskViewSimulator.recentsViewScale.value =
+ remoteTargetHandle.getTaskViewSimulator().recentsViewScale.value =
scale;
}
});
@@ -397,6 +406,7 @@
protected final ACTIVITY_TYPE mActivity;
private final float mFastFlingVelocity;
+ private final int mScrollHapticMinGapMillis;
private final RecentsModel mModel;
private final int mGridSideMargin;
private final ClearAllButton mClearAllButton;
@@ -443,6 +453,7 @@
private ObjectAnimator mTintingAnimator;
private int mOverScrollShift = 0;
+ private long mScrollLastHapticTimestamp;
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -629,6 +640,8 @@
final int rotation = mActivity.getDisplay().getRotation();
mOrientationState.setRecentsRotation(rotation);
+ mScrollHapticMinGapMillis = getResources()
+ .getInteger(R.integer.recentsScrollHapticMinGapMillis);
mFastFlingVelocity = getResources()
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
mModel = RecentsModel.INSTANCE.get(context);
@@ -828,7 +841,7 @@
mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = new SurfaceTransactionApplier(this);
- runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
.setSyncTransactionApplier(mSyncTransactionApplier));
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
@@ -847,7 +860,7 @@
mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = null;
- runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
.setSyncTransactionApplier(null));
executeSideTaskLaunchCallback();
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
@@ -937,7 +950,7 @@
public void launchSideTaskInLiveTileModeForRestartedApp(int taskId) {
int runningTaskViewId = getTaskViewIdFromTaskId(taskId);
if (mRunningTaskViewId != -1 && mRunningTaskViewId == runningTaskViewId) {
- TransformParams params = mRemoteTargetHandles[0].mTransformParams;
+ TransformParams params = mRemoteTargetHandles[0].getTransformParams();
RemoteAnimationTargets targets = params.getTargetSet();
if (targets != null && targets.findTask(taskId) != null) {
launchSideTaskInLiveTileMode(taskId, targets.apps, targets.wallpapers,
@@ -1229,6 +1242,25 @@
}
@Override
+ protected void onEdgeAbsorbingScroll() {
+ vibrateForScroll();
+ }
+
+ @Override
+ protected void onScrollOverPageChanged() {
+ vibrateForScroll();
+ }
+
+ private void vibrateForScroll() {
+ long now = SystemClock.uptimeMillis();
+ if (now - mScrollLastHapticTimestamp > mScrollHapticMinGapMillis) {
+ mScrollLastHapticTimestamp = now;
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(SCROLL_VIBRATION_PRIMITIVE,
+ SCROLL_VIBRATION_PRIMITIVE_SCALE, SCROLL_VIBRATION_FALLBACK);
+ }
+ }
+
+ @Override
protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
// Enables swiping to the left or right only if the task overlay is not modal.
if (!isModal()) {
@@ -1481,10 +1513,11 @@
// to reset the params after it settles in Overview from swipe up so that we don't
// render with obsolete param values.
runActionOnRemoteHandles(remoteTargetHandle -> {
- remoteTargetHandle.mTaskViewSimulator.taskPrimaryTranslation.value = 0;
- remoteTargetHandle.mTaskViewSimulator.taskSecondaryTranslation.value = 0;
- remoteTargetHandle.mTaskViewSimulator.fullScreenProgress.value = 0;
- remoteTargetHandle.mTaskViewSimulator.recentsViewScale.value = 1;
+ TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
+ simulator.taskPrimaryTranslation.value = 0;
+ simulator.taskSecondaryTranslation.value = 0;
+ simulator.fullScreenProgress.value = 0;
+ simulator.recentsViewScale.value = 1;
});
// Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
@@ -1540,7 +1573,7 @@
// Propagate DeviceProfile change event.
runActionOnRemoteHandles(
- remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator.setDp(dp));
+ remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator().setDp(dp));
mActionsView.setDp(dp);
mOrientationState.setDeviceProfile(dp);
@@ -1877,7 +1910,7 @@
}
}
setEnableDrawingLiveTile(false);
- runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
.setTargetSet(null));
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
@@ -2001,9 +2034,6 @@
/**
* Called only when a swipe-up gesture from an app has completed. Only called after
* {@link #onGestureAnimationStart} and {@link #onGestureAnimationEnd()}.
- *
- * TODO(b/198310766) Need to also explicitly exit split screen if
- * the swipe up was to home
*/
public void onSwipeUpAnimationSuccess() {
animateUpTaskIconScale();
@@ -2590,12 +2620,15 @@
// alpha is set to 0 so that it can be recycled in the view pool properly
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && taskView.isRunningTask()) {
runActionOnRemoteHandles(remoteTargetHandle -> {
- TransformParams params = remoteTargetHandle.mTransformParams;
+ TransformParams params = remoteTargetHandle.getTransformParams();
anim.setFloat(params, TransformParams.TARGET_ALPHA, 0,
- clampToProgress(ACCEL, 0, 0.5f));
+ clampToProgress(FINAL_FRAME, 0, 0.5f));
});
}
- anim.setFloat(taskView, VIEW_ALPHA, 0, clampToProgress(ACCEL, 0, 0.5f));
+ boolean isTaskInBottomGridRow = showAsGrid() && !mTopRowIdSet.contains(
+ taskView.getTaskViewId()) && taskView.getTaskViewId() != mFocusedTaskViewId;
+ anim.setFloat(taskView, VIEW_ALPHA, 0,
+ clampToProgress(isTaskInBottomGridRow ? ACCEL : FINAL_FRAME, 0, 0.5f));
FloatProperty<TaskView> secondaryViewTranslate =
taskView.getSecondaryDissmissTranslationProperty();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
@@ -2613,7 +2646,7 @@
&& taskView.isRunningTask()) {
anim.addOnFrameCallback(() -> {
runActionOnRemoteHandles(
- remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
.taskSecondaryTranslation.value = mOrientationHandler
.getSecondaryValue(taskView.getTranslationX(),
taskView.getTranslationY()
@@ -2782,7 +2815,7 @@
anim.addOnFrameCallback(() -> {
runActionOnRemoteHandles(
remoteTargetHandle ->
- remoteTargetHandle.mTaskViewSimulator
+ remoteTargetHandle.getTaskViewSimulator()
.taskPrimaryTranslation.value =
TaskView.GRID_END_TRANSLATION_X.get(taskView));
redrawLiveTile();
@@ -2855,7 +2888,7 @@
anim.addOnFrameCallback(() -> {
runActionOnRemoteHandles(
remoteTargetHandle ->
- remoteTargetHandle.mTaskViewSimulator
+ remoteTargetHandle.getTaskViewSimulator()
.taskPrimaryTranslation.value =
mOrientationHandler.getPrimaryValue(
child.getTranslationX(),
@@ -3533,7 +3566,7 @@
mLastComputedTaskEndPushOutDistance = null;
updatePageOffsets();
runActionOnRemoteHandles(
- remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
.setScroll(getScrollOffset()));
setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
@@ -3599,7 +3632,7 @@
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
&& i == getRunningTaskIndex()) {
runActionOnRemoteHandles(
- remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
.taskPrimaryTranslation.value = totalTranslation);
redrawLiveTile();
}
@@ -3707,7 +3740,7 @@
task.getTaskResistanceTranslationProperty().set(task, translation / getScaleY());
}
runActionOnRemoteHandles(
- remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
.recentsViewSecondaryTranslation.value = translation);
}
@@ -4023,7 +4056,7 @@
&& runningTaskIndex != taskIndex) {
for (RemoteTargetHandle remoteHandle : recentsView.getRemoteTargetHandles()) {
anim.play(ObjectAnimator.ofFloat(
- remoteHandle.mTaskViewSimulator.taskPrimaryTranslation,
+ remoteHandle.getTaskViewSimulator().taskPrimaryTranslation,
AnimatedFloat.VALUE,
primaryTranslation));
}
@@ -4107,7 +4140,7 @@
mPendingAnimation.add(anim);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
runActionOnRemoteHandles(
- remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
.addOverviewToAppAnim(mPendingAnimation, interpolator));
mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
}
@@ -4201,9 +4234,9 @@
public void redrawLiveTile() {
runActionOnRemoteHandles(remoteTargetHandle -> {
- TransformParams params = remoteTargetHandle.mTransformParams;
+ TransformParams params = remoteTargetHandle.getTransformParams();
if (params.getTargetSet() != null) {
- remoteTargetHandle.mTaskViewSimulator.apply(params);
+ remoteTargetHandle.getTaskViewSimulator().apply(params);
}
});
}
@@ -4224,44 +4257,14 @@
recentsAnimationTargets.addReleaseCheck(mSyncTransactionApplier);
}
- RemoteAnimationTargetCompat dividerTarget =
- recentsAnimationTargets.getNonAppTargetOfType(TYPE_DOCK_DIVIDER);
- // TODO Consolidate this shared code with SwipeUpAnimationLogic (or maybe just reuse
- // what that class has and pass it into here)
- mRemoteTargetHandles = new RemoteTargetHandle[dividerTarget == null ? 1 : 2];
- TaskViewSimulator primaryTvs = createTaskViewSimulator();
- mRemoteTargetHandles[0] = new RemoteTargetHandle(primaryTvs, new TransformParams());
- if (dividerTarget == null) {
- mRemoteTargetHandles[0].mTaskViewSimulator
- .setPreview(recentsAnimationTargets.apps[0], null);
- mRemoteTargetHandles[0].mTransformParams.setTargetSet(recentsAnimationTargets);
- } else {
- TaskViewSimulator secondaryTvs = createTaskViewSimulator();
- secondaryTvs.setOrientationState(mOrientationState);
- secondaryTvs.recentsViewScale.value = 1;
-
- mRemoteTargetHandles[1] = new RemoteTargetHandle(secondaryTvs, new TransformParams());
- RemoteAnimationTargetCompat primaryTaskTarget = recentsAnimationTargets.apps[0];
- RemoteAnimationTargetCompat secondaryTaskTarget = recentsAnimationTargets.apps[1];
- mSplitBoundsConfig = new SplitConfigurationOptions.StagedSplitBounds(
- primaryTaskTarget.screenSpaceBounds,
- secondaryTaskTarget.screenSpaceBounds, dividerTarget.screenSpaceBounds);
- mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget,
- mSplitBoundsConfig);
- mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget,
- mSplitBoundsConfig);
- RemoteAnimationTargets rats = new RemoteAnimationTargets(
- new RemoteAnimationTargetCompat[]{primaryTaskTarget},
- recentsAnimationTargets.wallpapers, recentsAnimationTargets.nonApps,
- MODE_CLOSING
- );
- RemoteAnimationTargets splitRats = new RemoteAnimationTargets(
- new RemoteAnimationTargetCompat[]{secondaryTaskTarget},
- recentsAnimationTargets.wallpapers, recentsAnimationTargets.nonApps,
- MODE_CLOSING
- );
- mRemoteTargetHandles[0].mTransformParams.setTargetSet(rats);
- mRemoteTargetHandles[1].mTransformParams.setTargetSet(splitRats);
+ RemoteTargetGluer gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
+ mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(recentsAnimationTargets);
+ mSplitBoundsConfig = gluer.getStagedSplitBounds();
+ for (RemoteTargetHandle remoteTargetHandle : mRemoteTargetHandles) {
+ TaskViewSimulator tvs = remoteTargetHandle.getTaskViewSimulator();
+ tvs.setOrientationState(mOrientationState);
+ tvs.setDp(mActivity.getDeviceProfile());
+ tvs.recentsViewScale.value = 1;
}
}
@@ -4276,14 +4279,6 @@
}
}
- private TaskViewSimulator createTaskViewSimulator() {
- TaskViewSimulator tvs = new TaskViewSimulator(getContext(), getSizeStrategy());
- tvs.setOrientationState(mOrientationState);
- tvs.setDp(mActivity.getDeviceProfile());
- tvs.recentsViewScale.value = 1;
- return tvs;
- }
-
public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
finishRecentsAnimation(toRecents, true /* shouldPip */, onFinishComplete);
}
@@ -4778,7 +4773,7 @@
private void dispatchScrollChanged() {
runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.mTaskViewSimulator.setScroll(getScrollOffset()));
+ remoteTargetHandle.getTaskViewSimulator().setScroll(getScrollOffset()));
for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
mScrollListeners.get(i).onScrollChanged();
}
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
index a712d1a..845e13e 100644
--- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -24,8 +24,6 @@
import androidx.annotation.Nullable;
-import com.android.quickstep.util.SplitSelectStateController;
-
public class SplitPlaceholderView extends FrameLayout {
public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT =
@@ -42,34 +40,26 @@
}
};
- private SplitSelectStateController mSplitController;
- private IconView mIcon;
+ private IconView mIconView;
public SplitPlaceholderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
- public void init(SplitSelectStateController controller) {
- this.mSplitController = controller;
- }
-
- public SplitSelectStateController getSplitController() {
- return mSplitController;
- }
-
@Nullable
- public IconView getIcon() {
- return mIcon;
+ public IconView getIconView() {
+ return mIconView;
}
- public void setIcon(IconView icon) {
- if (mIcon == null) {
- mIcon = new IconView(getContext());
- addView(mIcon);
+ public void setIconView(IconView iconView, int iconSize) {
+ if (mIconView == null) {
+ mIconView = new IconView(getContext());
+ addView(mIconView);
}
- mIcon.setDrawable(icon.getDrawable());
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(icon.getLayoutParams());
+ mIconView.setDrawable(iconView.getDrawable());
+ mIconView.setDrawableSize(iconSize, iconSize);
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(iconView.getLayoutParams());
params.gravity = Gravity.CENTER;
- mIcon.setLayoutParams(params);
+ mIconView.setLayoutParams(params);
}
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 0e6c0fd..7c558c2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -91,7 +91,7 @@
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RemoteAnimationTargets;
-import com.android.quickstep.SwipeUpAnimationLogic.RemoteTargetHandle;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
@@ -99,6 +99,7 @@
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.util.CancellableTask;
+import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.util.TransformParams;
@@ -592,6 +593,11 @@
return mSnapshotView;
}
+ /** TODO(b/197033698) Remove all usages of above method and migrate to this one */
+ public TaskThumbnailView[] getThumbnails() {
+ return new TaskThumbnailView[]{mSnapshotView};
+ }
+
public IconView getIconView() {
return mIconView;
}
@@ -618,13 +624,12 @@
mIsClickableAsLiveTile = false;
RecentsView recentsView = getRecentsView();
RemoteAnimationTargets targets;
- RemoteTargetHandle[] remoteTargetHandles =
- recentsView.mRemoteTargetHandles;
+ RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
if (remoteTargetHandles.length == 1) {
- targets = remoteTargetHandles[0].mTransformParams.getTargetSet();
+ targets = remoteTargetHandles[0].getTransformParams().getTargetSet();
} else {
- TransformParams topLeftParams = remoteTargetHandles[0].mTransformParams;
- TransformParams rightBottomParams = remoteTargetHandles[1].mTransformParams;
+ TransformParams topLeftParams = remoteTargetHandles[0].getTransformParams();
+ TransformParams rightBottomParams = remoteTargetHandles[1].getTransformParams();
RemoteAnimationTargetCompat[] apps = Stream.concat(
Arrays.stream(topLeftParams.getTargetSet().apps),
Arrays.stream(rightBottomParams.getTargetSet().apps))
@@ -689,8 +694,13 @@
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
ActivityOptionsWrapper opts = mActivity.getActivityLaunchOptions(this, null);
+ boolean isOldTaskSplit = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+ .getPersistentSplitIds().length > 0;
if (ActivityManagerWrapper.getInstance()
.startActivityFromRecents(mTask.key, opts.options)) {
+ if (isOldTaskSplit) {
+ SystemUiProxy.INSTANCE.getNoCreate().exitSplitScreen(mTask.key.id);
+ }
RecentsView recentsView = getRecentsView();
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskViewId() != -1) {
recentsView.onTaskLaunchedInLiveTileMode();
@@ -1067,7 +1077,6 @@
private void setNonGridScale(float nonGridScale) {
mNonGridScale = nonGridScale;
- updateCornerRadius();
applyScale();
}
@@ -1098,6 +1107,7 @@
scale *= mDismissScale;
setScaleX(scale);
setScaleY(scale);
+ updateSnapshotRadius();
}
/**
@@ -1413,29 +1423,25 @@
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
mSnapshotView.getTaskOverlay().setFullscreenProgress(progress);
- updateCornerRadius();
+ updateSnapshotRadius();
- mSnapshotView.setFullscreenParams(mCurrentFullscreenParams);
mOutlineProvider.updateParams(
mCurrentFullscreenParams,
mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx);
invalidateOutline();
}
- private void updateCornerRadius() {
+ private void updateSnapshotRadius() {
updateCurrentFullscreenParams(mSnapshotView.getPreviewPositionHelper());
+ mSnapshotView.setFullscreenParams(mCurrentFullscreenParams);
}
void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
if (getRecentsView() == null) {
return;
}
- mCurrentFullscreenParams.setProgress(
- mFullscreenProgress,
- getRecentsView().getScaleX(),
- mNonGridScale,
- getWidth(), mActivity.getDeviceProfile(),
- previewPositionHelper);
+ mCurrentFullscreenParams.setProgress(mFullscreenProgress, getRecentsView().getScaleX(),
+ getScaleX(), getWidth(), mActivity.getDeviceProfile(), previewPositionHelper);
}
/**
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d1774e5..a6105eb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -153,6 +153,7 @@
<!-- All applications label -->
<string name="all_apps_button_label">Apps list</string>
+ <string name="all_apps_search_results">Search results</string>
<string name="all_apps_button_personal_label">Personal apps list</string>
<string name="all_apps_button_work_label">Work apps list</string>
diff --git a/res/xml/default_workspace_splitdisplay_5x5.xml b/res/xml/default_workspace_splitdisplay_5x5.xml
deleted file mode 100644
index 162367b..0000000
--- a/res/xml/default_workspace_splitdisplay_5x5.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
-
- <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
- <!-- Dialer, Messaging, [Maps/Music], Browser, Camera -->
- <resolve
- launcher:container="-101"
- launcher:screen="0"
- launcher:x="0"
- launcher:y="0" >
- <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
- <favorite launcher:uri="tel:123" />
- <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
- </resolve>
-
- <resolve
- launcher:container="-101"
- launcher:screen="1"
- launcher:x="1"
- launcher:y="0" >
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
- <favorite launcher:uri="sms:" />
- <favorite launcher:uri="smsto:" />
- <favorite launcher:uri="mms:" />
- <favorite launcher:uri="mmsto:" />
- </resolve>
-
- <resolve
- launcher:container="-101"
- launcher:screen="2"
- launcher:x="2"
- launcher:y="0" >
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;end" />
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MUSIC;end" />
- </resolve>
-
- <resolve
- launcher:container="-101"
- launcher:screen="3"
- launcher:x="3"
- launcher:y="0" >
- <favorite
- launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
- <favorite launcher:uri="http://www.example.com/" />
- </resolve>
-
- <resolve
- launcher:container="-101"
- launcher:screen="4"
- launcher:x="4"
- launcher:y="0" >
- <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
- <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
- </resolve>
-
- <!-- Bottom row -->
- <resolve
- launcher:screen="0"
- launcher:x="0"
- launcher:y="-1" >
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
- <favorite launcher:uri="mailto:" />
-
- </resolve>
-
- <resolve
- launcher:screen="0"
- launcher:x="1"
- launcher:y="-1" >
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
- <favorite launcher:uri="#Intent;type=images/*;end" />
-
- </resolve>
-
- <resolve
- launcher:screen="0"
- launcher:x="4"
- launcher:y="-1" >
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
- <favorite launcher:uri="market://details?id=com.android.launcher" />
- </resolve>
-
- <!-- Placeholder before we add page pairing in b/196376162 -->
- <resolve
- launcher:screen="1"
- launcher:x="0"
- launcher:y="-4" >
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
- <favorite launcher:uri="#Intent;type=images/*;end" />
- </resolve>
-
-</favorites>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index d8ee6f2..256999c 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -105,8 +105,7 @@
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
launcher:dbFile="launcher.db"
- launcher:defaultLayoutId="@xml/default_workspace_5x5"
- launcher:defaultSplitDisplayLayoutId="@xml/default_workspace_splitdisplay_5x5" >
+ launcher:defaultLayoutId="@xml/default_workspace_5x5" >
<display-option
launcher:name="Large Phone"
diff --git a/robolectric_tests/Android.bp b/robolectric_tests/Android.bp
deleted file mode 100644
index 6473d00..0000000
--- a/robolectric_tests/Android.bp
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2021 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.
-
-//
-// Launcher Robolectric test target.
-//
-// "robolectric_android-all-stub", not needed, we write our own stubs
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_apps_Launcher3_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_apps_Launcher3_license"],
-}
-
-filegroup {
- name: "launcher3-robolectric-resources",
- path: "resources",
- srcs: ["resources/*"],
-}
-
-filegroup {
- name: "launcher3-robolectric-src",
- srcs: ["src/**/*.java"],
-}
-
-android_robolectric_test {
- name: "LauncherRoboTests",
- srcs: [
- ":launcher3-robolectric-src",
- ],
- java_resources: [":launcher3-robolectric-resources"],
- static_libs: [
- "truth-prebuilt",
- "androidx.test.espresso.contrib",
- "androidx.test.espresso.core",
- "androidx.test.espresso.intents",
- "androidx.test.ext.junit",
- "androidx.test.runner",
- "androidx.test.rules",
- "mockito-robolectric-prebuilt",
- "SystemUISharedLib",
- ],
- robolectric_prebuilt_version: "4.5.1",
- instrumentation_for: "Launcher3",
-
- test_options: {
- timeout: 36000,
- },
-}
diff --git a/robolectric_tests/res/values/overlayable_icons_test.xml b/robolectric_tests/res/values/overlayable_icons_test.xml
deleted file mode 100644
index 5144e52..0000000
--- a/robolectric_tests/res/values/overlayable_icons_test.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- Copyright (C) 2019 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.
--->
-<resources>
- <!-- overlayable_icons references all of the drawables in this package
- that are being overlayed by resource overlays. If you remove/rename
- any of these resources, you must also change the resource overlay icons.-->
- <array name="overlayable_icons">
- <item>@drawable/ic_corp</item>
- <item>@drawable/ic_drag_handle</item>
- <item>@drawable/ic_hourglass_top</item>
- <item>@drawable/ic_info_no_shadow</item>
- <item>@drawable/ic_install_no_shadow</item>
- <item>@drawable/ic_palette</item>
- <item>@drawable/ic_pin</item>
- <item>@drawable/ic_remove_no_shadow</item>
- <item>@drawable/ic_setting</item>
- <item>@drawable/ic_smartspace_preferences</item>
- <item>@drawable/ic_split_screen</item>
- <item>@drawable/ic_uninstall_no_shadow</item>
- <item>@drawable/ic_warning</item>
- <item>@drawable/ic_widget</item>
- </array>
-</resources>
diff --git a/robolectric_tests/resources/robolectric.properties b/robolectric_tests/resources/robolectric.properties
deleted file mode 100644
index abb6968..0000000
--- a/robolectric_tests/resources/robolectric.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-sdk=30
-
-shadows= \
- com.android.launcher3.shadows.LShadowAppPredictionManager \
- com.android.launcher3.shadows.LShadowAppWidgetManager \
- com.android.launcher3.shadows.LShadowBackupManager \
- com.android.launcher3.shadows.LShadowDisplay \
- com.android.launcher3.shadows.LShadowLauncherApps \
- com.android.launcher3.shadows.ShadowDeviceFlag \
- com.android.launcher3.shadows.ShadowLooperExecutor \
- com.android.launcher3.shadows.ShadowMainThreadInitializedObject \
- com.android.launcher3.shadows.ShadowOverrides \
- com.android.launcher3.shadows.ShadowSurfaceTransactionApplier \
-
-application=com.android.launcher3.util.LauncherTestApplication
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppPredictionManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppPredictionManager.java
deleted file mode 100644
index ae051f7..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppPredictionManager.java
+++ /dev/null
@@ -1,38 +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.shadows;
-
-import static org.mockito.Mockito.mock;
-
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-/**
- * Shadow for {@link AppPredictionManager} which create mock predictors
- */
-@Implements(value = AppPredictionManager.class)
-public class LShadowAppPredictionManager {
-
- @Implementation
- public AppPredictor createAppPredictionSession(AppPredictionContext predictionContext) {
- return mock(AppPredictor.class);
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppWidgetManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppWidgetManager.java
deleted file mode 100644
index 696ffd0..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppWidgetManager.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 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.shadows;
-
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.os.Process;
-import android.os.UserHandle;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowAppWidgetManager;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Extension of {@link ShadowAppWidgetManager} with missing shadow methods
- */
-@Implements(value = AppWidgetManager.class)
-public class LShadowAppWidgetManager extends ShadowAppWidgetManager {
-
- @Override
- protected List<AppWidgetProviderInfo> getInstalledProviders() {
- return getInstalledProvidersForProfile(null);
- }
-
- @Implementation
- public List<AppWidgetProviderInfo> getInstalledProvidersForProfile(UserHandle profile) {
- UserHandle user = profile == null ? Process.myUserHandle() : profile;
- return super.getInstalledProviders().stream().filter(
- info -> user.equals(info.getProfile())).collect(Collectors.toList());
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBackupManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowBackupManager.java
deleted file mode 100644
index eae0101..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBackupManager.java
+++ /dev/null
@@ -1,45 +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.shadows;
-
-import android.app.backup.BackupManager;
-import android.os.UserHandle;
-import android.util.LongSparseArray;
-
-import androidx.annotation.Nullable;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowBackupManager;
-
-/**
- * Extension of {@link ShadowBackupManager} with missing shadow methods
- */
-@Implements(value = BackupManager.class)
-public class LShadowBackupManager extends ShadowBackupManager {
-
- private LongSparseArray<UserHandle> mProfileMapping = new LongSparseArray<>();
-
- public void addProfile(long userSerial, UserHandle userHandle) {
- mProfileMapping.put(userSerial, userHandle);
- }
-
- @Implementation
- @Nullable
- public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
- return mProfileMapping.get(ancestralSerialNumber);
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowDisplay.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowDisplay.java
deleted file mode 100644
index 3813fa1..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowDisplay.java
+++ /dev/null
@@ -1,54 +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.shadows;
-
-import static org.robolectric.shadow.api.Shadow.directlyOn;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.Display;
-
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-import org.robolectric.shadows.ShadowDisplay;
-
-/**
- * Extension of {@link ShadowDisplay} with missing shadow methods
- */
-@Implements(value = Display.class)
-public class LShadowDisplay extends ShadowDisplay {
-
- private final Rect mInsets = new Rect();
-
- @RealObject Display realObject;
-
- /**
- * Sets the insets for the display
- */
- public void setInsets(Rect insets) {
- mInsets.set(insets);
- }
-
- @Override
- protected void getCurrentSizeRange(Point outSmallestSize, Point outLargestSize) {
- directlyOn(realObject, Display.class).getCurrentSizeRange(outSmallestSize, outLargestSize);
- outSmallestSize.x -= mInsets.left + mInsets.right;
- outLargestSize.x -= mInsets.left + mInsets.right;
-
- outSmallestSize.y -= mInsets.top + mInsets.bottom;
- outLargestSize.y -= mInsets.top + mInsets.bottom;
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
deleted file mode 100644
index 6a6f0fb..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2019 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.shadows;
-
-import static org.robolectric.util.ReflectionHelpers.ClassParameter;
-import static org.robolectric.util.ReflectionHelpers.callConstructor;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ShortcutInfo;
-import android.os.UserHandle;
-import android.util.ArraySet;
-
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.PackageUserKey;
-
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowLauncherApps;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.stream.Collectors;
-
-/**
- * Extension of {@link ShadowLauncherApps} with missing shadow methods
- */
-@Implements(value = LauncherApps.class)
-public class LShadowLauncherApps extends ShadowLauncherApps {
-
- public final ArraySet<PackageUserKey> disabledApps = new ArraySet<>();
- public final ArraySet<ComponentKey> disabledActivities = new ArraySet<>();
-
- @Implementation
- @Override
- protected List<ShortcutInfo> getShortcuts(LauncherApps.ShortcutQuery query, UserHandle user) {
- try {
- return super.getShortcuts(query, user);
- } catch (UnsupportedOperationException e) {
- return Collections.emptyList();
- }
- }
-
- @Implementation
- protected boolean isPackageEnabled(String packageName, UserHandle user) {
- return !disabledApps.contains(new PackageUserKey(packageName, user));
- }
-
- @Implementation
- protected boolean isActivityEnabled(ComponentName component, UserHandle user) {
- return !disabledActivities.contains(new ComponentKey(component, user));
- }
-
- @Implementation
- protected LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
- ResolveInfo ri = RuntimeEnvironment.application.getPackageManager()
- .resolveActivity(intent, 0);
- return ri == null ? null : getLauncherActivityInfo(ri.activityInfo, user);
- }
-
- public LauncherActivityInfo getLauncherActivityInfo(
- ActivityInfo activityInfo, UserHandle user) {
- return callConstructor(LauncherActivityInfo.class,
- ClassParameter.from(Context.class, RuntimeEnvironment.application),
- ClassParameter.from(ActivityInfo.class, activityInfo),
- ClassParameter.from(UserHandle.class, user));
- }
-
- @Implementation
- public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user)
- throws PackageManager.NameNotFoundException {
- return RuntimeEnvironment.application.getPackageManager()
- .getApplicationInfo(packageName, flags);
- }
-
- @Implementation
- public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
- Intent intent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_LAUNCHER)
- .setPackage(packageName);
- return RuntimeEnvironment.application.getPackageManager().queryIntentActivities(intent, 0)
- .stream()
- .map(ri -> getLauncherActivityInfo(ri.activityInfo, user))
- .collect(Collectors.toList());
- }
-
- @Implementation
- public boolean hasShortcutHostPermission() {
- return true;
- }
-
- @Implementation
- public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
- return RuntimeEnvironment.application.getPackageManager().getPackageInstaller()
- .getAllSessions();
- }
-
- @Implementation
- public void registerPackageInstallerSessionCallback(
- Executor executor, PackageInstaller.SessionCallback callback) {
- }
-
- @Override
- protected List<LauncherActivityInfo> getShortcutConfigActivityList(String packageName,
- UserHandle user) {
- Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName);
- return RuntimeEnvironment.application.getPackageManager().queryIntentActivities(intent, 0)
- .stream()
- .map(ri -> getLauncherActivityInfo(ri.activityInfo, user))
- .collect(Collectors.toList());
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
deleted file mode 100644
index b58e4b7..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 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.shadows;
-
-import android.content.Context;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.uioverrides.DeviceFlag;
-import com.android.launcher3.util.LooperExecutor;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-import org.robolectric.shadow.api.Shadow;
-
-/**
- * Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
- */
-@Implements(value = DeviceFlag.class, isInAndroidSdk = false)
-public class ShadowDeviceFlag {
-
- @RealObject private DeviceFlag mRealObject;
- @Nullable private Boolean mValue;
-
- /**
- * Mock change listener as it uses internal system classes not available to robolectric
- */
- @Implementation
- protected void addChangeListener(Context context, Runnable r) { }
-
- @Implementation
- protected static boolean getDeviceValue(String key, boolean defaultValue) {
- return defaultValue;
- }
-
- @Implementation
- public boolean get() {
- if (mValue != null) {
- return mValue;
- }
- return Shadow.directlyOn(mRealObject, DeviceFlag.class, "get");
- }
-
- public void setValue(boolean value) {
- mValue = new Boolean(value);
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
deleted file mode 100644
index 57eda7e..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2019 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.shadows;
-
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-
-import static org.robolectric.shadow.api.Shadow.directlyOn;
-import static org.robolectric.util.ReflectionHelpers.setField;
-
-import android.os.Handler;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.util.LooperExecutor;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-
-/**
- * Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
- */
-@Implements(value = LooperExecutor.class, isInAndroidSdk = false)
-public class ShadowLooperExecutor {
-
- @RealObject private LooperExecutor mRealExecutor;
-
- private Handler mOverriddenHandler;
-
- @Implementation
- protected Handler getHandler() {
- if (mOverriddenHandler != null) {
- return mOverriddenHandler;
- }
- Handler handler = directlyOn(mRealExecutor, LooperExecutor.class, "getHandler");
- Thread thread = handler.getLooper().getThread();
- if (!thread.isAlive()) {
- // Robolectric destroys all loopers at the end of every test. Since Launcher maintains
- // some static threads, they need to be reinitialized in case they were destroyed.
- setField(mRealExecutor, "mHandler",
- new Handler(createAndStartNewLooper(thread.getName())));
- }
- return directlyOn(mRealExecutor, LooperExecutor.class, "getHandler");
- }
-
- public void setHandler(@Nullable Handler handler) {
- mOverriddenHandler = handler;
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowMainThreadInitializedObject.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowMainThreadInitializedObject.java
deleted file mode 100644
index 6e2ccf8..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowMainThreadInitializedObject.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2019 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.shadows;
-
-import static org.robolectric.shadow.api.Shadow.invokeConstructor;
-import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
-
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.MainThreadInitializedObject.ObjectProvider;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Set;
-import java.util.WeakHashMap;
-
-/**
- * Shadow for {@link MainThreadInitializedObject} to provide reset functionality for static sObjects
- */
-@Implements(value = MainThreadInitializedObject.class, isInAndroidSdk = false)
-public class ShadowMainThreadInitializedObject {
-
- // Keep reference to all created MainThreadInitializedObject so they can be cleared after test
- private static Set<MainThreadInitializedObject> sObjects =
- Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
-
- @RealObject private MainThreadInitializedObject mRealObject;
-
- @Implementation
- protected void __constructor__(ObjectProvider provider) {
- invokeConstructor(MainThreadInitializedObject.class, mRealObject,
- from(ObjectProvider.class, provider));
- sObjects.add(mRealObject);
- }
-
- /**
- * Resets all the initialized sObjects to be null
- */
- public static void resetInitializedObjects() {
- for (MainThreadInitializedObject object : new ArrayList<>(sObjects)) {
- object.initializeForTesting(null);
- }
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java
deleted file mode 100644
index 131f691..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java
+++ /dev/null
@@ -1,61 +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.shadows;
-
-import android.content.Context;
-
-import com.android.launcher3.util.MainThreadInitializedObject.ObjectProvider;
-import com.android.launcher3.util.ResourceBasedOverride;
-import com.android.launcher3.util.ResourceBasedOverride.Overrides;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.util.ReflectionHelpers.ClassParameter;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Shadow for {@link Overrides} to provide custom overrides for test
- */
-@Implements(value = Overrides.class, isInAndroidSdk = false)
-public class ShadowOverrides {
-
- private static Map<Class, ObjectProvider> sProviderMap = new HashMap<>();
-
- @Implementation
- public static <T extends ResourceBasedOverride> T getObject(
- Class<T> clazz, Context context, int resId) {
- ObjectProvider<T> provider = sProviderMap.get(clazz);
- if (provider != null) {
- return provider.get(context);
- }
- return Shadow.directlyOn(Overrides.class, "getObject",
- ClassParameter.from(Class.class, clazz),
- ClassParameter.from(Context.class, context),
- ClassParameter.from(int.class, resId));
- }
-
- public static <T> void setProvider(Class<T> clazz, ObjectProvider<T> provider) {
- sProviderMap.put(clazz, provider);
- }
-
- public static void clearProvider() {
- sProviderMap.clear();
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowSurfaceTransactionApplier.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowSurfaceTransactionApplier.java
deleted file mode 100644
index a9f2f27..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowSurfaceTransactionApplier.java
+++ /dev/null
@@ -1,42 +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.shadows;
-
-import static org.robolectric.shadow.api.Shadow.invokeConstructor;
-import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
-
-import android.view.View;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-
-/**
- * Shadow for SurfaceTransactionApplier to override default functionality
- */
-@Implements(className = "com.android.quickstep.util.SurfaceTransactionApplier",
- isInAndroidSdk = false)
-public class ShadowSurfaceTransactionApplier {
-
- @RealObject
- private Object mRealObject;
-
- @Implementation
- protected void __constructor__(View view) {
- invokeConstructor(mRealObject.getClass(), mRealObject, from(View.class, null));
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
deleted file mode 100644
index efac150..0000000
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
+++ /dev/null
@@ -1,50 +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.util;
-
-import static org.mockito.Mockito.mock;
-
-import android.app.Application;
-
-import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
-import com.android.launcher3.shadows.ShadowOverrides;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-
-import org.robolectric.TestLifecycleApplication;
-import org.robolectric.shadows.ShadowLog;
-
-import java.lang.reflect.Method;
-
-public class LauncherTestApplication extends Application implements TestLifecycleApplication {
-
- @Override
- public void beforeTest(Method method) {
- ShadowLog.stream = System.out;
-
- // Disable plugins
- PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
- }
-
- @Override
- public void prepareTest(Object test) { }
-
- @Override
- public void afterTest(Method method) {
- ShadowLog.stream = null;
- ShadowMainThreadInitializedObject.resetInitializedObjects();
- ShadowOverrides.clearProvider();
- }
-}
diff --git a/robolectric_tests/unstaged/SettingsCacheTest.java b/robolectric_tests/unstaged/SettingsCacheTest.java
deleted file mode 100644
index fbf4c63..0000000
--- a/robolectric_tests/unstaged/SettingsCacheTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2021 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.util;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.net.Uri;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.Collections;
-
-@RunWith(RobolectricTestRunner.class)
-public class SettingsCacheTest {
-
- public static final Uri KEY_SYSTEM_URI_TEST1 = Uri.parse("content://settings/system/test1");
- public static final Uri KEY_SYSTEM_URI_TEST2 = Uri.parse("content://settings/system/test2");;
-
- private SettingsCache.OnChangeListener mChangeListener;
- private SettingsCache mSettingsCache;
-
- @Before
- public void setup() {
- mChangeListener = mock(SettingsCache.OnChangeListener.class);
- Context targetContext = RuntimeEnvironment.application;
- mSettingsCache = SettingsCache.INSTANCE.get(targetContext);
- mSettingsCache.register(KEY_SYSTEM_URI_TEST1, mChangeListener);
- }
-
- @Test
- public void listenerCalledOnChange() {
- mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
- verify(mChangeListener, times(1)).onSettingsChanged(true);
- }
-
- @Test
- public void getValueRespectsDefaultValue() {
- // Case of key not found
- boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
- assertFalse(val);
- }
-
- @Test
- public void getValueHitsCache() {
- mSettingsCache.setKeyCache(Collections.singletonMap(KEY_SYSTEM_URI_TEST1, true));
- boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
- assertTrue(val);
- }
-
- @Test
- public void getValueUpdatedCache() {
- // First ensure there's nothing in cache
- boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
- assertFalse(val);
-
- mSettingsCache.setKeyCache(Collections.singletonMap(KEY_SYSTEM_URI_TEST1, true));
- val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
- assertTrue(val);
- }
-
- @Test
- public void multipleListenersSingleKey() {
- SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class);
- mSettingsCache.register(KEY_SYSTEM_URI_TEST1, secondListener);
-
- mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
- verify(mChangeListener, times(1)).onSettingsChanged(true);
- verify(secondListener, times(1)).onSettingsChanged(true);
- }
-
- @Test
- public void singleListenerMultipleKeys() {
- SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class);
- mSettingsCache.register(KEY_SYSTEM_URI_TEST2, secondListener);
-
- mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
- mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST2);
- verify(mChangeListener, times(1)).onSettingsChanged(true);
- verify(secondListener, times(1)).onSettingsChanged(true);
- }
-
- @Test
- public void sameListenerMultipleKeys() {
- SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class);
- mSettingsCache.register(KEY_SYSTEM_URI_TEST2, mChangeListener);
-
- mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
- mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST2);
- verify(mChangeListener, times(2)).onSettingsChanged(true);
- verify(secondListener, times(0)).onSettingsChanged(true);
- }
-}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 86217d7..10023b4 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -138,10 +138,11 @@
mContext = context;
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
- mIconProvider = new IconProvider(context, Themes.isThemedIconEnabled(context));
+ mIconProvider = new IconProvider(context, Themes.isThemedIconEnabled(context));
mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
iconCacheFileName, mIconProvider);
- mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
+ mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
+ iconCacheFileName != null);
mOnTerminateCallback.add(mIconCache::close);
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 9ebec0a..f38f662 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -126,10 +126,12 @@
}
};
- LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
+ LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter,
+ boolean isPrimaryInstance) {
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
- mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel);
+ mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
+ isPrimaryInstance);
}
public ModelDelegate getModelDelegate() {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 2f9b5af..108091c 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -108,6 +108,8 @@
// relative scroll position unchanged in updateCurrentPageScroll. Cleared when snapping to a
// page.
protected int mCurrentPageScrollDiff;
+ // The current page the PagedView is scrolling over on it's way to the destination page.
+ protected int mCurrentScrollOverPage;
@ViewDebug.ExportedProperty(category = "launcher")
protected int mNextPage = INVALID_PAGE;
@@ -180,6 +182,7 @@
mScroller = new OverScroller(context, SCROLL);
mCurrentPage = 0;
+ mCurrentScrollOverPage = 0;
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
@@ -437,6 +440,7 @@
}
int prevPage = overridePrevPage != INVALID_PAGE ? overridePrevPage : mCurrentPage;
mCurrentPage = validateNewPage(currentPage);
+ mCurrentScrollOverPage = mCurrentPage;
updateCurrentPageScroll();
notifyPageSwitchListener(prevPage);
invalidate();
@@ -557,9 +561,11 @@
if (newPos < mMinScroll && oldPos >= mMinScroll) {
mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
mScroller.abortAnimation();
+ onEdgeAbsorbingScroll();
} else if (newPos > mMaxScroll && oldPos <= mMaxScroll) {
mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
mScroller.abortAnimation();
+ onEdgeAbsorbingScroll();
}
}
@@ -577,6 +583,7 @@
sendScrollAccessibilityEvent();
int prevPage = mCurrentPage;
mCurrentPage = validateNewPage(mNextPage);
+ mCurrentScrollOverPage = mCurrentPage;
mNextPage = INVALID_PAGE;
notifyPageSwitchListener(prevPage);
@@ -766,7 +773,8 @@
childStart += primaryDimension + getChildGap();
// This makes sure that the space is added after the page, not after each panel
- if (i % panelCount == panelCount - 1) {
+ int lastPanel = mIsRtl ? 0 : panelCount - 1;
+ if (i % panelCount == lastPanel) {
childStart += mPageSpacing;
}
}
@@ -837,6 +845,7 @@
public void onViewRemoved(View child) {
super.onViewRemoved(child);
mCurrentPage = validateNewPage(mCurrentPage);
+ mCurrentScrollOverPage = mCurrentPage;
dispatchPageCountChanged();
}
@@ -1408,6 +1417,20 @@
protected void onNotSnappingToPageInFreeScroll() { }
+ /**
+ * Called when the view edges absorb part of the scroll. Subclasses can override this
+ * to provide custom behavior during animation.
+ */
+ protected void onEdgeAbsorbingScroll() {
+ }
+
+ /**
+ * Called when the current page closest to the center of the screen changes as part of the
+ * scroll. Subclasses can override this to provide custom behavior during scroll.
+ */
+ protected void onScrollOverPageChanged() {
+ }
+
protected boolean shouldFlingForVelocity(int velocity) {
float threshold = mAllowEasyFling ? mEasyFlingThresholdVelocity : mFlingThresholdVelocity;
return Math.abs(velocity) > threshold;
@@ -1553,7 +1576,7 @@
return getDisplacementFromScreenCenter(childIndex, screenCenter);
}
- private int getScreenCenter(int primaryScroll) {
+ protected int getScreenCenter(int primaryScroll) {
float primaryScale = mOrientationHandler.getPrimaryScale(this);
float primaryPivot = mOrientationHandler.getPrimaryValue(getPivotX(), getPivotY());
int pageOrientationSize = mOrientationHandler.getMeasuredSize(this);
@@ -1692,6 +1715,15 @@
}
@Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ int newDestinationPage = getDestinationPage();
+ if (newDestinationPage >= 0 && newDestinationPage != mCurrentScrollOverPage) {
+ mCurrentScrollOverPage = newDestinationPage;
+ onScrollOverPageChanged();
+ }
+ }
+
+ @Override
public CharSequence getAccessibilityClassName() {
// Some accessibility services have special logic for ScrollView. Since we provide same
// accessibility info as ScrollView, inform the service to handle use the same way.
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a21c8e3..54b2c96 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -338,15 +338,17 @@
int paddingBottom = grid.cellLayoutBottomPaddingPx;
int panelCount = getPanelCount();
+ int rightPanelModulus = mIsRtl ? 0 : panelCount - 1;
+ int leftPanelModulus = mIsRtl ? panelCount - 1 : 0;
int numberOfScreens = mScreenOrder.size();
for (int i = 0; i < numberOfScreens; i++) {
int paddingLeft = paddingLeftRight;
int paddingRight = paddingLeftRight;
if (panelCount > 1) {
- if (i % panelCount == 0) { // left side panel
+ if (i % panelCount == leftPanelModulus) {
paddingLeft = paddingLeftRight;
paddingRight = 0;
- } else if (i % panelCount == panelCount - 1) { // right side panel
+ } else if (i % panelCount == rightPanelModulus) {
paddingLeft = 0;
paddingRight = paddingLeftRight;
} else { // middle panel
@@ -857,18 +859,13 @@
mWorkspaceScreens.remove(emptyScreenId);
mScreenOrder.removeValue(emptyScreenId);
- int newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
-
-
- // When two panel home is enabled and the last page (the page on the right) doesn't
- // have any items, then Launcher database doesn't know about this page because it was added
- // by Launcher::bindAddScreens but wasn't inserted into the database. LauncherSettings's
- // generate new screen ID method will return the ID for the left page,
- // so we need to increment it.
- if (isTwoPanelEnabled() && emptyScreenId == EXTRA_EMPTY_SCREEN_ID && newScreenId % 2 == 1) {
- newScreenId++;
+ int newScreenId = -1;
+ // Launcher database isn't aware of empty pages that are already bound, so we need to
+ // skip those IDs manually.
+ while (newScreenId == -1 || mWorkspaceScreens.containsKey(newScreenId)) {
+ newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+ .getInt(LauncherSettings.Settings.EXTRA_VALUE);
}
mWorkspaceScreens.put(newScreenId, cl);
@@ -2453,21 +2450,32 @@
}
int nextPage = getNextPage();
- if (layout == null && !isPageInTransition()) {
- layout = verifyInsidePage(nextPage + (mIsRtl ? 1 : -1), Math.min(centerX, d.x), d.y);
+ IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1, nextPage + 1);
+ if (isTwoPanelEnabled()) {
+ // If two panel is enabled, users can also drag items to nextPage + 2
+ pageIndexesToVerify.add(nextPage + 2);
}
- if (layout == null && !isPageInTransition()) {
- layout = verifyInsidePage(nextPage + (mIsRtl ? -1 : 1), Math.max(centerX, d.x), d.y);
+ int touchX = (int) Math.min(centerX, d.x);
+ int touchY = d.y;
+
+ // Go through the pages and check if the dragged item is inside one of them
+ for (int pageIndex : pageIndexesToVerify) {
+ if (layout != null || isPageInTransition()) {
+ break;
+ }
+ layout = verifyInsidePage(pageIndex, touchX, touchY);
}
- // If two panel is enabled, users can also drag items to currentPage + 2
- if (isTwoPanelEnabled() && layout == null && !isPageInTransition()) {
- layout = verifyInsidePage(nextPage + (mIsRtl ? -2 : 2), Math.max(centerX, d.x), d.y);
- }
-
- // Always pick the current page.
+ // If the dragged item isn't located in one of the pages above, the icon will stay on the
+ // current screen. For two panel pick the closest panel on the current screen,
+ // on one panel just choose the current page.
if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
+ if (isTwoPanelEnabled()) {
+ nextPage = getScreenCenter(getScrollX()) > touchX
+ ? (mIsRtl ? nextPage + 1 : nextPage) // left side
+ : (mIsRtl ? nextPage : nextPage + 1); // right side
+ }
layout = (CellLayout) getChildAt(nextPage);
}
if (layout != mDragTargetLayout) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 57a3e1c..e779ee8 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -120,6 +120,7 @@
protected boolean mUsingTabs;
private boolean mIsSearching;
+ private boolean mHasWorkApps;
protected RecyclerViewFastScroller mTouchHandler;
protected final Point mFastScrollerOffset = new Point();
@@ -191,7 +192,7 @@
int currentPage = state.getInt(BUNDLE_KEY_CURRENT_PAGE, 0);
if (currentPage != 0 && mViewPager != null) {
mViewPager.setCurrentPage(currentPage);
- rebindAdapters(true);
+ rebindAdapters();
} else {
reset(true);
}
@@ -245,9 +246,10 @@
break;
}
}
+ mHasWorkApps = hasWorkApps;
if (!mAH[AdapterHolder.MAIN].appsList.hasFilter()) {
- rebindAdapters(hasWorkApps);
- if (hasWorkApps) {
+ rebindAdapters();
+ if (mHasWorkApps) {
resetWorkProfile();
}
}
@@ -324,6 +326,8 @@
mViewPager.getNextPage() == 0
? R.string.all_apps_button_personal_label
: R.string.all_apps_button_work_label;
+ } else if (mIsSearching) {
+ descriptionRes = R.string.all_apps_search_results;
} else {
descriptionRes = R.string.all_apps_button_label;
}
@@ -372,7 +376,7 @@
});
mHeader = findViewById(R.id.all_apps_header);
- rebindAdapters(mUsingTabs, true /* force */);
+ rebindAdapters(true /* force */);
mSearchContainer = findViewById(R.id.search_container_all_apps);
mSearchUiManager = (SearchUiManager) mSearchContainer;
@@ -441,11 +445,12 @@
}
}
- private void rebindAdapters(boolean showTabs) {
- rebindAdapters(showTabs, false /* force */);
+ private void rebindAdapters() {
+ rebindAdapters(false /* force */);
}
- protected void rebindAdapters(boolean showTabs, boolean force) {
+ protected void rebindAdapters(boolean force) {
+ boolean showTabs = mHasWorkApps && !mIsSearching;
if (showTabs == mUsingTabs && !force) {
return;
}
@@ -628,17 +633,13 @@
mAH[i].adapter.setLastSearchQuery(query);
}
mIsSearching = true;
- if (mUsingTabs) {
- rebindAdapters(false); // hide tabs
- }
+ rebindAdapters();
mHeader.setCollapsed(true);
}
public void onClearSearchResult() {
- if (mUsingTabs) {
- rebindAdapters(true); // show tabs
- }
mIsSearching = false;
+ rebindAdapters();
getActiveRecyclerView().scrollToTop();
}
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index f64b7cb..b6dcec6 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -22,7 +22,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.statemanager.StateManager.StateListener;
/**
* AllAppsContainerView with launcher specific callbacks
@@ -31,8 +30,6 @@
private final Launcher mLauncher;
- private StateListener<LauncherState> mWorkTabListener;
-
public LauncherAllAppsContainerView(Context context) {
this(context, null);
}
@@ -75,14 +72,6 @@
}
@Override
- public void setupHeader() {
- super.setupHeader();
- if (mWorkTabListener != null && !mUsingTabs) {
- mLauncher.getStateManager().removeStateListener(mWorkTabListener);
- }
- }
-
- @Override
public void onActivePageChanged(int currentActivePage) {
super.onActivePageChanged(currentActivePage);
}
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index 761053d..e2df16d 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -25,10 +25,15 @@
import android.content.SharedPreferences;
import android.text.TextUtils;
+import androidx.annotation.IntDef;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
+import com.android.launcher3.util.IntSet;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
import java.util.Objects;
@@ -41,13 +46,23 @@
public static final String KEY_HOTSEAT_COUNT = "migration_src_hotseat_count";
public static final String KEY_DEVICE_TYPE = "migration_src_device_type";
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_PHONE, TYPE_MULTI_DISPLAY, TYPE_TABLET})
+ public @interface DeviceType{}
public static final int TYPE_PHONE = 0;
public static final int TYPE_MULTI_DISPLAY = 1;
public static final int TYPE_TABLET = 2;
+ private static final IntSet COMPATIBLE_TYPES = IntSet.wrap(TYPE_PHONE, TYPE_MULTI_DISPLAY);
+
+ public static boolean deviceTypeCompatible(@DeviceType int typeA, @DeviceType int typeB) {
+ return typeA == typeB
+ || (COMPATIBLE_TYPES.contains(typeA) && COMPATIBLE_TYPES.contains(typeB));
+ }
+
private final String mGridSizeString;
private final int mNumHotseat;
- private final int mDeviceType;
+ private final @DeviceType int mDeviceType;
public DeviceGridState(InvariantDeviceProfile idp) {
mGridSizeString = String.format(Locale.ENGLISH, "%d,%d", idp.numColumns, idp.numRows);
@@ -69,7 +84,7 @@
/**
* Returns the device type for the grid
*/
- public int getDeviceType() {
+ public @DeviceType int getDeviceType() {
return mDeviceType;
}
@@ -109,7 +124,7 @@
if (o == null || getClass() != o.getClass()) return false;
DeviceGridState that = (DeviceGridState) o;
return mNumHotseat == that.mNumHotseat
- && mDeviceType == that.mDeviceType
+ && deviceTypeCompatible(mDeviceType, that.mDeviceType)
&& Objects.equals(mGridSizeString, that.mGridSizeString);
}
}
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
index 13ec1ec..765141a 100644
--- a/src/com/android/launcher3/model/ModelDelegate.java
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -40,19 +40,21 @@
* Creates and initializes a new instance of the delegate
*/
public static ModelDelegate newInstance(
- Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel) {
+ Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel,
+ boolean isPrimaryInstance) {
ModelDelegate delegate = Overrides.getObject(
ModelDelegate.class, context, R.string.model_delegate_class);
-
delegate.mApp = app;
delegate.mAppsList = appsList;
delegate.mDataModel = dataModel;
+ delegate.mIsPrimaryInstance = isPrimaryInstance;
return delegate;
}
protected LauncherAppState mApp;
protected AllAppsList mAppsList;
protected BgDataModel mDataModel;
+ protected boolean mIsPrimaryInstance;
public ModelDelegate() { }
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 8d02a4a..d59429d 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -84,9 +84,10 @@
}
private static boolean performRestore(Context context, DatabaseHelper helper) {
- if (new DeviceGridState(LauncherAppState.getIDP(context)).getDeviceType()
- != Utilities.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE)) {
- // DO not restore if the device types are different
+ if (!DeviceGridState.deviceTypeCompatible(
+ new DeviceGridState(LauncherAppState.getIDP(context)).getDeviceType(),
+ Utilities.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE))) {
+ // DO NOT restore if the device types are incompatible.
return false;
}
SQLiteDatabase db = helper.getWritableDatabase();
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index 4d63218..b06b8a1 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -20,6 +20,7 @@
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
@@ -29,6 +30,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
@@ -44,6 +46,7 @@
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -57,12 +60,15 @@
import androidx.preference.SwitchPreference;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.FlagTogglerPrefUi;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.OnboardingPrefs;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -104,6 +110,7 @@
initFlags();
loadPluginPrefs();
maybeAddSandboxCategory();
+ addOnboardingPrefsCatergory();
if (getActivity() != null) {
getActivity().setTitle("Developer Options");
@@ -153,6 +160,15 @@
}
});
+ if (getArguments() != null) {
+ String filter = getArguments().getString(EXTRA_FRAGMENT_ARG_KEY);
+ // Normally EXTRA_FRAGMENT_ARG_KEY is used to highlight the preference with the given
+ // key. This is a slight variation where we instead filter by the human-readable titles.
+ if (filter != null) {
+ filterBox.setText(filter);
+ }
+ }
+
View listView = getListView();
final int bottomPadding = listView.getPaddingBottom();
listView.setOnApplyWindowInsetsListener((v, insets) -> {
@@ -355,6 +371,28 @@
sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
}
+ private void addOnboardingPrefsCatergory() {
+ PreferenceCategory onboardingCategory = newCategory("Onboarding Flows");
+ onboardingCategory.setSummary("Reset these if you want to see the education again.");
+ for (Map.Entry<String, String[]> titleAndKeys : OnboardingPrefs.ALL_PREF_KEYS.entrySet()) {
+ String title = titleAndKeys.getKey();
+ String[] keys = titleAndKeys.getValue();
+ Preference onboardingPref = new Preference(getContext());
+ onboardingPref.setTitle(title);
+ onboardingPref.setSummary("Tap to reset");
+ onboardingPref.setOnPreferenceClickListener(preference -> {
+ SharedPreferences.Editor sharedPrefsEdit = Utilities.getPrefs(getContext()).edit();
+ for (String key : keys) {
+ sharedPrefsEdit.remove(key);
+ }
+ sharedPrefsEdit.apply();
+ Toast.makeText(getContext(), "Reset " + title, Toast.LENGTH_SHORT).show();
+ return true;
+ });
+ onboardingCategory.addPreference(onboardingPref);
+ }
+ }
+
private String toName(String action) {
String str = action.replace("com.android.systemui.action.PLUGIN_", "")
.replace("com.android.launcher3.action.PLUGIN_", "");
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 8d330d4..895ca08 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -388,11 +388,12 @@
public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
SplitConfigurationOptions.StagedSplitBounds splitInfo, int desiredStagePosition) {
float diff;
+ float horizontalDividerDiff = splitInfo.visualDividerBounds.width() / 2f;
if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
- diff = outRect.height() * (1f - splitInfo.leftTaskPercent);
+ diff = outRect.height() * (1f - splitInfo.leftTaskPercent) + horizontalDividerDiff;
outRect.bottom -= diff;
} else {
- diff = outRect.height() * splitInfo.leftTaskPercent;
+ diff = outRect.height() * splitInfo.leftTaskPercent + horizontalDividerDiff;
outRect.top += diff;
}
}
@@ -402,7 +403,7 @@
SplitConfigurationOptions.StagedSplitBounds splitInfo, int desiredStagePosition) {
if (desiredStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
// The preview set is for the bottom/right, inset by top/left task
- splitOffset.x = splitInfo.leftTopBounds.width() + splitInfo.dividerBounds.width() / 2;
+ splitOffset.x = splitInfo.leftTopBounds.width() + splitInfo.visualDividerBounds.width();
}
}
@@ -413,7 +414,7 @@
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = taskParent.getHeight() - spaceAboveSnapshot;
int totalThumbnailWidth = taskParent.getWidth();
- int dividerBar = splitBoundsConfig.dividerBounds.width() / 2;
+ int dividerBar = splitBoundsConfig.visualDividerBounds.width();
ViewGroup.LayoutParams primaryLp = mSnapshotView.getLayoutParams();
ViewGroup.LayoutParams secondaryLp = mSnapshotView2.getLayoutParams();
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 8284659..f1fd439 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -474,21 +474,23 @@
public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
SplitConfigurationOptions.StagedSplitBounds splitInfo, int desiredStagePosition) {
boolean isLandscape = dp.isLandscape;
+ float verticalDividerDiff = splitInfo.visualDividerBounds.height() / 2f;
+ float horizontalDividerDiff = splitInfo.visualDividerBounds.width() / 2f;
float diff;
if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
if (isLandscape) {
- diff = outRect.width() * (1f - splitInfo.leftTaskPercent);
+ diff = outRect.width() * (1f - splitInfo.leftTaskPercent) + horizontalDividerDiff;
outRect.right -= diff;
} else {
- diff = outRect.height() * (1f - splitInfo.topTaskPercent);
+ diff = outRect.height() * (1f - splitInfo.topTaskPercent) + verticalDividerDiff;
outRect.bottom -= diff;
}
} else {
if (isLandscape) {
- diff = outRect.width() * splitInfo.leftTaskPercent;
+ diff = outRect.width() * splitInfo.leftTaskPercent + horizontalDividerDiff;
outRect.left += diff;
} else {
- diff = outRect.height() * splitInfo.topTaskPercent;
+ diff = outRect.height() * splitInfo.topTaskPercent + verticalDividerDiff;
outRect.top += diff;
}
}
@@ -500,10 +502,12 @@
if (desiredStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
if (dp.isLandscape) {
splitOffset.x = splitInfo.leftTopBounds.width() +
- splitInfo.dividerBounds.width() / 2;
+ splitInfo.visualDividerBounds.width();
+ splitOffset.y = 0;
} else {
splitOffset.y = splitInfo.leftTopBounds.height() +
- splitInfo.dividerBounds.height() / 2;
+ splitInfo.visualDividerBounds.height();
+ splitOffset.x = 0;
}
}
}
@@ -516,9 +520,8 @@
int totalThumbnailHeight = taskParent.getHeight() - spaceAboveSnapshot;
int totalThumbnailWidth = taskParent.getWidth();
int dividerBar = (dp.isLandscape ?
- splitBoundsConfig.dividerBounds.width() :
- splitBoundsConfig.dividerBounds.height())
- / 2;
+ splitBoundsConfig.visualDividerBounds.width() :
+ splitBoundsConfig.visualDividerBounds.height());
ViewGroup.LayoutParams primaryLp = mSnapshotView.getLayoutParams();
ViewGroup.LayoutParams secondaryLp = mSnapshotView2.getLayoutParams();
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index cf1467a..5ba0d30 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -39,6 +39,14 @@
public static final String SEARCH_EDU_SEEN = "launcher.search_edu_seen";
public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count";
public static final String TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen";
+ // When adding a new key, add it here as well, to be able to reset it from Developer Options.
+ public static final Map<String, String[]> ALL_PREF_KEYS = Map.of(
+ "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT },
+ "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT,
+ HOTSEAT_LONGPRESS_TIP_SEEN },
+ "Search Education", new String[] { SEARCH_EDU_SEEN, SEARCH_SNACKBAR_COUNT },
+ "Taskbar Education", new String[] { TASKBAR_EDU_SEEN }
+ );
/**
* Events that either have happened or have not (booleans).
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 41693de..5093d85 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -88,25 +88,29 @@
public static class StagedSplitBounds {
public final Rect leftTopBounds;
public final Rect rightBottomBounds;
- public final Rect dividerBounds;
+ /** This rect represents the actual gap between the two apps */
+ public final Rect visualDividerBounds;
// This class is orientation-agnostic, so we compute both for later use
public final float topTaskPercent;
public final float leftTaskPercent;
- public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, Rect dividerBounds) {
+ public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds) {
this.leftTopBounds = leftTopBounds;
this.rightBottomBounds = rightBottomBounds;
- this.dividerBounds = dividerBounds;
- float totalHeight = this.leftTopBounds.height()
- + this.rightBottomBounds.height()
- + this.dividerBounds.height();
- float totalWidth = this.leftTopBounds.width()
- + this.rightBottomBounds.width()
- + this.dividerBounds.width();
- leftTaskPercent = this.leftTopBounds.width() / totalWidth;
- topTaskPercent = this.leftTopBounds.height() / totalHeight;
+ if (rightBottomBounds.top > leftTopBounds.top) {
+ // vertical apps, horizontal divider
+ this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
+ leftTopBounds.right, rightBottomBounds.top);
+ } else {
+ // horizontal apps, vertical divider
+ this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
+ rightBottomBounds.left, leftTopBounds.bottom);
+ }
+
+ leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right;
+ topTaskPercent = this.leftTopBounds.height() / (float) rightBottomBounds.bottom;
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java b/tests/src/com/android/launcher3/settings/SettingsActivityTest.java
similarity index 96%
rename from robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java
rename to tests/src/com/android/launcher3/settings/SettingsActivityTest.java
index 3271812..1c205f0 100644
--- a/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java
+++ b/tests/src/com/android/launcher3/settings/SettingsActivityTest.java
@@ -55,6 +55,7 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -75,6 +76,7 @@
}
@Test
+ @Ignore // b/199309785
public void testSettings_aboutTap_launchesActivity() {
ActivityScenario.launch(SettingsActivity.class);
onView(withId(R.id.recycler_view)).perform(
@@ -88,6 +90,7 @@
}
@Test
+ @Ignore // b/199309785
public void testSettings_developerOptionsTap_launchesActivityWithFragment() {
PluginPrefs.setHasPlugins(mApplicationContext);
ActivityScenario.launch(SettingsActivity.class);
@@ -100,6 +103,7 @@
}
@Test
+ @Ignore // b/199309785
public void testSettings_aboutScreenIntent() {
Bundle fragmentArgs = new Bundle();
fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen");
@@ -114,6 +118,7 @@
}
@Test
+ @Ignore // b/199309785
public void testSettings_developerOptionsFragmentIntent() {
Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
.putExtra(EXTRA_FRAGMENT, DeveloperOptionsFragment.class.getName());
@@ -125,6 +130,7 @@
}
@Test
+ @Ignore // b/199309785
public void testSettings_intentWithUnknownFragment() {
String fragmentClass = PreferenceFragmentCompat.class.getName();
Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
@@ -139,6 +145,7 @@
}
@Test
+ @Ignore // b/199309785
public void testSettings_backButtonFinishesActivity() {
Bundle fragmentArgs = new Bundle();
fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen");