Merge "Use event time of app side for latency metrics" into ub-launcher3-master
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 97e3786..a47a500 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -30,6 +30,7 @@
with some minor changed based on the derivative app.
-->
+ <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
diff --git a/gradle.properties b/gradle.properties
index 7a51375..7f4c609 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -10,4 +10,4 @@
PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7
BUILD_TOOLS_VERSION=28.0.3
-COMPILE_SDK=android-R
+COMPILE_SDK=android-S
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 5491daa..6c88e55 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -144,7 +144,7 @@
LauncherActivityInterface.INSTANCE);
tvs.setDp(mDeviceProfile);
- int launcherRotation = DisplayController.INSTANCE.get(mContext).getInfo().rotation;
+ int launcherRotation = DisplayController.getDefaultDisplay(mContext).getInfo().rotation;
if (mAppRotation < 0) {
mAppRotation = launcherRotation;
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index df1833d..54a753c 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -802,33 +802,31 @@
}
private void addCujInstrumentation(Animator anim, int cuj, String transition) {
- if (Trace.isEnabled()) {
- anim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- Trace.beginAsyncSection(transition, 0);
- InteractionJankMonitorWrapper.begin(cuj);
- super.onAnimationStart(animation);
- }
+ anim.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ Trace.beginAsyncSection(transition, 0);
+ InteractionJankMonitorWrapper.begin(cuj);
+ super.onAnimationStart(animation);
+ }
- @Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- InteractionJankMonitorWrapper.cancel(cuj);
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ InteractionJankMonitorWrapper.cancel(cuj);
+ }
- @Override
- public void onAnimationSuccess(Animator animator) {
- InteractionJankMonitorWrapper.end(cuj);
- }
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ InteractionJankMonitorWrapper.end(cuj);
+ }
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0);
- }
- });
- }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0);
+ }
+ });
}
/**
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index 58937c3..9944270 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -251,7 +251,7 @@
return "predictions";
}
case SHORTCUTS_CONTAINER: {
- return "shortcuts";
+ return "deep-shortcuts";
}
case FOLDER: {
FolderContainer fc = ci.getFolder();
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index be57dec..c3f5c00 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -15,11 +15,15 @@
*/
package com.android.launcher3.model;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.formatElapsedTime;
+
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
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 android.app.prediction.AppPredictionContext;
@@ -29,10 +33,12 @@
import android.app.prediction.AppTargetEvent;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
+import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -40,12 +46,16 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.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;
@@ -61,6 +71,10 @@
public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChangeListener {
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
+ private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
+
+ private static final boolean IS_DEBUG = false;
+ private static final String TAG = "QuickstepModelDelegate";
private final PredictorState mAllAppsState =
new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions");
@@ -81,6 +95,7 @@
}
@Override
+ @WorkerThread
public void loadItems(UserManagerState ums, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) {
// TODO: Implement caching and preloading
super.loadItems(ums, pinnedShortcuts);
@@ -106,6 +121,38 @@
}
@Override
+ @WorkerThread
+ public void modelLoadComplete() {
+ super.modelLoadComplete();
+
+ // Log snapshot of the model
+ SharedPreferences prefs = getDevicePrefs(mApp.getContext());
+ long lastSnapshotTimeMillis = prefs.getLong(LAST_SNAPSHOT_TIME_MILLIS, 0);
+ // Log snapshot only if previous snapshot was older than a day
+ long now = System.currentTimeMillis();
+ if (now - lastSnapshotTimeMillis < DAY_IN_MILLIS) {
+ if (IS_DEBUG) {
+ String elapsedTime = formatElapsedTime((now - lastSnapshotTimeMillis) / 1000);
+ Log.d(TAG, String.format(
+ "Skipped snapshot logging since previous snapshot was %s old.",
+ elapsedTime));
+ }
+ } else {
+ IntSparseArrayMap<ItemInfo> itemsIdMap;
+ synchronized (mDataModel) {
+ itemsIdMap = mDataModel.itemsIdMap.clone();
+ }
+ InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+ for (ItemInfo info : itemsIdMap) {
+ FolderInfo parent = info.container > 0
+ ? (FolderInfo) itemsIdMap.get(info.container) : null;
+ StatsLogCompatManager.writeSnapshot(info.buildProto(parent), instanceId);
+ }
+ prefs.edit().putLong(LAST_SNAPSHOT_TIME_MILLIS, now).apply();
+ }
+ }
+
+ @Override
public void validateData() {
super.validateData();
if (mAllAppsState.predictor != null) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 5d227be..6cf3c7a 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -41,10 +41,8 @@
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -95,8 +93,10 @@
import com.android.quickstep.util.InputConsumerProxy;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.ProtoTracer;
+import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.SwipePipToHomeAnimator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.views.LiveTileOverlay;
import com.android.quickstep.views.RecentsView;
@@ -233,6 +233,10 @@
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
+ private static final long SWIPE_PIP_TO_HOME_DURATION = 425;
+ private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
+ protected boolean mIsSwipingPipToHome;
+
public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
@@ -618,13 +622,6 @@
updateSysUiFlags(mCurrentShift.value);
applyWindowTransform();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (mRecentsAnimationTargets != null) {
- LiveTileOverlay.INSTANCE.update(
- mTaskViewSimulator.getCurrentRect(),
- mTaskViewSimulator.getCurrentCornerRadius());
- }
- }
updateLauncherTransitionProgress();
}
@@ -800,6 +797,8 @@
}
private void onSettledOnEndTarget() {
+ // Fast-finish the attaching animation if it's still running.
+ maybeUpdateRecentsAttachedState(false);
final GestureEndTarget endTarget = mGestureState.getEndTarget();
switch (endTarget) {
case HOME:
@@ -1053,25 +1052,46 @@
if (mGestureState.getEndTarget() == HOME) {
mTaskViewSimulator.setDrawsBelowRecents(false);
- HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration);
- RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
- windowAnim.addAnimatorListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- if (mRecentsAnimationController == null) {
- // If the recents animation is interrupted, we still end the running
- // animation (not canceled) so this is still called. In that case, we can
- // skip doing any future work here for the current gesture.
- return;
- }
- // Finalize the state and notify of the change
- mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
- }
- });
getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
- windowAnim.start(mContext, velocityPxPerMs);
+ final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
+ : null;
+ HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration);
+ mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome()
+ && runningTaskTarget != null
+ && runningTaskTarget.pictureInPictureParams != null
+ && TaskInfoCompat.isAutoEnterPipEnabled(
+ runningTaskTarget.pictureInPictureParams)
+ && TaskInfoCompat.getPipSourceRectHint(
+ runningTaskTarget.pictureInPictureParams) != null;
+ if (mIsSwipingPipToHome) {
+ mSwipePipToHomeAnimator = getSwipePipToHomeAnimator(
+ homeAnimFactory, runningTaskTarget);
+ mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION);
+ mSwipePipToHomeAnimator.setInterpolator(interpolator);
+ mSwipePipToHomeAnimator.setFloatValues(0f, 1f);
+ mSwipePipToHomeAnimator.start();
+ mRunningWindowAnim = RunningWindowAnim.wrap(mSwipePipToHomeAnimator);
+ } else {
+ mSwipePipToHomeAnimator = null;
+ RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
+ windowAnim.addAnimatorListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case,
+ // we can skip doing any future work here for the current gesture.
+ return;
+ }
+ // Finalize the state and notify of the change
+ mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+ }
+ });
+ windowAnim.start(mContext, velocityPxPerMs);
+ mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
+ }
homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
- mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
mLauncherTransitionController = null;
} else {
ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
@@ -1115,6 +1135,47 @@
}
}
+ private SwipePipToHomeAnimator getSwipePipToHomeAnimator(HomeAnimationFactory homeAnimFactory,
+ RemoteAnimationTargetCompat runningTaskTarget) {
+ // Directly animate the app to PiP (picture-in-picture) mode
+ final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
+ final RecentsOrientedState orientationState = mTaskViewSimulator.getOrientationState();
+ final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
+ .startSwipePipToHome(taskInfo.topActivity,
+ TaskInfoCompat.getTopActivityInfo(taskInfo),
+ runningTaskTarget.pictureInPictureParams,
+ orientationState.getRecentsActivityRotation(),
+ mDp.hotseatBarSizePx);
+ final SwipePipToHomeAnimator swipePipToHomeAnimator = new SwipePipToHomeAnimator(
+ runningTaskTarget.taskId,
+ taskInfo.topActivity,
+ runningTaskTarget.leash.getSurfaceControl(),
+ TaskInfoCompat.getPipSourceRectHint(runningTaskTarget.pictureInPictureParams),
+ TaskInfoCompat.getWindowConfigurationBounds(taskInfo),
+ destinationBounds);
+ swipePipToHomeAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Ensure Launcher ends in NORMAL state, we intentionally skip other callbacks
+ // since they are not relevant in this swipe-pip-to-home case.
+ homeAnimFactory.createActivityAnimationToHome().dispatchOnStart();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case, we can
+ // skip doing any future work here for the current gesture.
+ return;
+ }
+ // Finalize the state and notify of the change
+ mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+ }
+ });
+ return swipePipToHomeAnimator;
+ }
+
private void computeRecentsScrollIfInvisible() {
if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
// Views typically don't compute scroll when invisible as an optimization,
@@ -1372,6 +1433,7 @@
// If there are no targets or the animation not started, then there is nothing to finish
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
} else {
+ maybeFinishSwipePipToHome();
finishRecentsControllerToHome(
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
@@ -1379,6 +1441,22 @@
doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
}
+ /**
+ * Resets the {@link #mIsSwipingPipToHome} and notifies SysUI that transition is finished
+ * if applicable. This should happen before {@link #finishRecentsControllerToHome(Runnable)}.
+ */
+ private void maybeFinishSwipePipToHome() {
+ if (mIsSwipingPipToHome && mSwipePipToHomeAnimator != null) {
+ SystemUiProxy.INSTANCE.get(mContext).stopSwipePipToHome(
+ mSwipePipToHomeAnimator.getComponentName(),
+ mSwipePipToHomeAnimator.getDestinationBounds());
+ mRecentsAnimationController.setFinishTaskBounds(
+ mSwipePipToHomeAnimator.getTaskId(),
+ mSwipePipToHomeAnimator.getDestinationBounds());
+ mIsSwipingPipToHome = false;
+ }
+ }
+
protected abstract void finishRecentsControllerToHome(Runnable callback);
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
@@ -1405,13 +1483,11 @@
mRecentsAnimationTargets.apps,
mRecentsAnimationTargets.apps.length + 1);
apps[apps.length - 1] = appearedTaskTarget;
- boolean launcherClosing =
- taskIsATargetWithMode(apps, mActivity.getTaskId(), MODE_CLOSING);
AnimatorSet anim = new AnimatorSet();
TaskViewUtils.composeRecentsLaunchAnimator(
anim, taskView, apps,
- mRecentsAnimationTargets.wallpapers, launcherClosing,
+ mRecentsAnimationTargets.wallpapers, true /* launcherClosing */,
mActivity.getStateManager(), mRecentsView,
mActivityInterface.getDepthController());
anim.addListener(new AnimatorListenerAdapter(){
@@ -1611,6 +1687,11 @@
}
mTaskViewSimulator.apply(mTransformParams);
}
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mRecentsAnimationTargets != null) {
+ LiveTileOverlay.INSTANCE.update(
+ mTaskViewSimulator.getCurrentRect(),
+ mTaskViewSimulator.getCurrentCornerRadius());
+ }
ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 4411455..842fb84 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -56,6 +56,7 @@
final TaskView runningTaskView = mRecentsView.getRunningTaskView();
final View workspaceView;
if (runningTaskView != null
+ && !mIsSwipingPipToHome
&& runningTaskView.getTask().key.getComponent() != null) {
workspaceView = mActivity.getWorkspace().getFirstMatchForAppClose(
runningTaskView.getTask().key.getComponent().getPackageName(),
@@ -140,5 +141,10 @@
new StaggeredWorkspaceAnim(mActivity, velocity,
true /* animateOverviewScrim */).start();
}
+
+ @Override
+ public boolean supportSwipePipToHome() {
+ return true;
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 32268a4..80308a5 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -15,19 +15,25 @@
*/
package com.android.quickstep;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.UserManager;
import android.util.Log;
import com.android.launcher3.BuildConfig;
import com.android.launcher3.MainProcessInitializer;
+import com.android.launcher3.util.Executors;
+import com.android.quickstep.logging.SettingsChangeLogger;
import com.android.systemui.shared.system.ThreadedRendererCompat;
@SuppressWarnings("unused")
+@TargetApi(Build.VERSION_CODES.R)
public class QuickstepProcessInitializer extends MainProcessInitializer {
private static final String TAG = "QuickstepProcessInitializer";
+ private static final int SETUP_DELAY_MILLIS = 5000;
public QuickstepProcessInitializer(Context context) { }
@@ -51,5 +57,9 @@
// Elevate GPU priority for Quickstep and Remote animations.
ThreadedRendererCompat.setContextPriority(
ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
+
+ // Initialize settings logger after a default timeout
+ Executors.MAIN_EXECUTOR.getHandler()
+ .postDelayed(() -> new SettingsChangeLogger(context), SETUP_DELAY_MILLIS);
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 62bbf4d..7de658c 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.graphics.Rect;
+
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
@@ -140,6 +142,18 @@
}
/**
+ * Sets the final bounds on a Task. This is used by Launcher to notify the system that
+ * animating Activity to PiP has completed and the associated task surface should be updated
+ * accordingly. This should be called before `finish`
+ * @param taskId for which the leash should be updated
+ * @param destinationBounds bounds of the final PiP window
+ */
+ public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+ UI_HELPER_EXECUTOR.execute(
+ () -> mController.setFinishTaskBounds(taskId, destinationBounds));
+ }
+
+ /**
* Enables the input consumer to start intercepting touches in the app window.
*/
public void enableInputConsumer() {
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 7406dea..8f7ec3b 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -165,6 +165,14 @@
public void update(RectF currentRect, float progress, float radius) { }
public void onCancel() { }
+
+ /**
+ * @return {@code true} if this factory supports animating an Activity to PiP window on
+ * swiping up to home.
+ */
+ public boolean supportSwipePipToHome() {
+ return false;
+ }
}
/**
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 65e89cf..dd0ed8f 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -21,12 +21,14 @@
import android.app.ActivityManager.TaskDescription;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityManager;
@@ -34,6 +36,7 @@
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
@@ -42,7 +45,6 @@
import com.android.quickstep.util.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskDescriptionCompat;
@@ -163,9 +165,8 @@
key.getComponent(), key.userId);
}
if (activityInfo != null) {
- entry.contentDescription = ActivityManagerWrapper.getInstance()
- .getBadgedContentDescription(activityInfo, task.key.userId,
- task.taskDescription);
+ entry.contentDescription = getBadgedContentDescription(
+ activityInfo, task.key.userId, task.taskDescription);
}
}
@@ -173,6 +174,21 @@
return entry;
}
+ private String getBadgedContentDescription(ActivityInfo info, int userId, TaskDescription td) {
+ PackageManager pm = mContext.getPackageManager();
+ String taskLabel = td == null ? null : Utilities.trim(td.getLabel());
+ if (TextUtils.isEmpty(taskLabel)) {
+ taskLabel = Utilities.trim(info.loadLabel(pm));
+ }
+
+ String applicationLabel = Utilities.trim(info.applicationInfo.loadLabel(pm));
+ String badgedApplicationLabel = userId != UserHandle.myUserId()
+ ? pm.getUserBadgedLabel(applicationLabel, UserHandle.of(userId)).toString()
+ : applicationLabel;
+ return applicationLabel.equals(taskLabel)
+ ? badgedApplicationLabel : badgedApplicationLabel + " " + taskLabel;
+ }
+
@WorkerThread
private Drawable getDefaultIcon(int userId) {
synchronized (mDefaultIcons) {
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 3a47024..6677724 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -31,6 +31,7 @@
import android.view.View;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.android.launcher3.BaseActivity;
@@ -157,9 +158,8 @@
getActionsView().setCallbacks(new OverlayUICallbacks() {
@Override
public void onShare() {
- endLiveTileMode(isAllowedByPolicy);
if (isAllowedByPolicy) {
- mImageApi.startShareActivity();
+ endLiveTileMode(mImageApi::startShareActivity);
} else {
showBlockedByPolicyMessage();
}
@@ -168,8 +168,7 @@
@SuppressLint("NewApi")
@Override
public void onScreenshot() {
- endLiveTileMode(isAllowedByPolicy);
- saveScreenshot(task);
+ endLiveTileMode(() -> saveScreenshot(task));
}
});
}
@@ -178,17 +177,15 @@
/**
* End rendering live tile in Overview.
*
- * @param showScreenshot if it's true, we take a screenshot and switch to it.
+ * @param callback callback to run, after switching to screenshot
*/
- public void endLiveTileMode(boolean showScreenshot) {
+ public void endLiveTileMode(@NonNull Runnable callback) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
- if (showScreenshot) {
- recentsView.switchToScreenshot(
- () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
- } else {
- recentsView.finishRecentsAnimation(true /* toRecents */, null);
- }
+ recentsView.switchToScreenshot(
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */, callback));
+ } else {
+ callback.run();
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 5520ef7..ed761cf 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -41,6 +41,7 @@
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
@@ -129,6 +130,21 @@
return taskView;
}
+ public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
+ PendingAnimation out) {
+ boolean isRunningTask = v.isRunningTask();
+ TransformParams params = null;
+ TaskViewSimulator tsv = null;
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
+ params = v.getRecentsView().getLiveTileParams();
+ tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
+ }
+ createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets,
+ depthController, out, params, tsv);
+ }
+
/**
* Creates an animation that controls the window of the opening targets for the recents launch
* animation.
@@ -136,19 +152,25 @@
public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
- PendingAnimation out) {
+ PendingAnimation out, @Nullable TransformParams params,
+ @Nullable TaskViewSimulator tsv) {
boolean isQuickSwitch = v.isEndQuickswitchCuj();
v.setEndQuickswitchCuj(false);
- SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+ boolean inLiveTileMode =
+ ENABLE_QUICKSTEP_LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
final RemoteAnimationTargets targets =
new RemoteAnimationTargets(appTargets, wallpaperTargets,
- ENABLE_QUICKSTEP_LIVE_TILE.get() ? MODE_CLOSING : MODE_OPENING);
- targets.addReleaseCheck(applier);
+ inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
- TransformParams params = new TransformParams()
+ if (params == null) {
+ SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+ targets.addReleaseCheck(applier);
+
+ params = new TransformParams()
.setSyncTransactionApplier(applier)
.setTargetSet(targets);
+ }
final RecentsView recentsView = v.getRecentsView();
int taskIndex = recentsView.indexOfChild(v);
@@ -162,8 +184,9 @@
int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
TaskViewSimulator topMostSimulator = null;
- if (targets.apps.length > 0) {
- TaskViewSimulator tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
+
+ if (tsv == null && targets.apps.length > 0) {
+ tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
tsv.setDp(dp);
tsv.setLayoutRotation(displayRotation, displayRotation);
tsv.setPreview(targets.apps[targets.apps.length - 1]);
@@ -171,19 +194,24 @@
tsv.recentsViewScale.value = 1;
tsv.setScroll(startScroll);
+ // Fade in the task during the initial 20% of the animation
+ out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
+ clampToProgress(LINEAR, 0, 0.2f));
+ }
+
+ if (tsv != null) {
out.setFloat(tsv.fullScreenProgress,
AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
out.setFloat(tsv.recentsViewScale,
AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
- out.addOnFrameCallback(() -> tsv.apply(params));
+ TaskViewSimulator finalTsv = tsv;
+ TransformParams finalParams = params;
+ out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
topMostSimulator = tsv;
}
- // Fade in the task during the initial 20% of the animation
- out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, clampToProgress(LINEAR, 0, 0.2f));
-
if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
@@ -269,7 +297,6 @@
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
depthController, pa);
- anim.play(pa.buildAnim());
Animator childStateAnimation = null;
// Found a visible recents task that matches the opening app, lets launch the app from there
@@ -302,7 +329,11 @@
}
};
}
- anim.play(launcherAnim);
+ pa.add(launcherAnim);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1) {
+ pa.addOnFrameCallback(recentsView::redrawLiveTile);
+ }
+ anim.play(pa.buildAnim());
// Set the current animation first, before adding windowAnimEndListener. Setting current
// animation adds some listeners which need to be called before windowAnimEndListener
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
index 6862f07..5c81e5f 100644
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
@@ -104,7 +104,13 @@
hideFeedback();
hideHandCoachingAnimation();
showRippleEffect(
- () -> mTutorialFragment.changeController(ASSISTANT_COMPLETE));
+ () -> {
+ if (mTutorialFragment.isTutorialComplete()) {
+ mTutorialFragment.changeController(ASSISTANT_COMPLETE);
+ } else {
+ mTutorialFragment.continueTutorial();
+ }
+ });
break;
case ASSISTANT_NOT_STARTED_BAD_ANGLE:
showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 921e568..161e015 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -130,7 +130,13 @@
hideFeedback();
hideHandCoachingAnimation();
showRippleEffect(
- () -> mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE));
+ () -> {
+ if (mTutorialFragment.isTutorialComplete()) {
+ mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE);
+ } else {
+ mTutorialFragment.continueTutorial();
+ }
+ });
break;
case BACK_CANCELLED_FROM_LEFT:
showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index f8d9d8d..8b6777b 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.interaction;
-import static com.android.quickstep.interaction.TutorialFragment.KEY_TUTORIAL_TYPE;
-
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
@@ -25,11 +23,14 @@
import android.view.View;
import android.view.Window;
+import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.R;
import com.android.quickstep.interaction.TutorialController.TutorialType;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.List;
/** Shows the gesture interactive sandbox in full screen mode. */
@@ -37,6 +38,9 @@
private static final String LOG_TAG = "GestureSandboxActivity";
+ private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
+
+ private Deque<TutorialType> mTutorialSteps;
private TutorialFragment mFragment;
@Override
@@ -45,7 +49,9 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.gesture_tutorial_activity);
- mFragment = TutorialFragment.newInstance(getTutorialType(getIntent().getExtras()));
+ Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
+ mTutorialSteps = getTutorialSteps(args);
+ mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
getSupportFragmentManager().beginTransaction()
.add(R.id.gesture_tutorial_fragment_container, mFragment)
.commit();
@@ -72,17 +78,65 @@
}
}
- private TutorialType getTutorialType(Bundle extras) {
- TutorialType defaultType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
+ super.onSaveInstanceState(savedInstanceState);
+ }
- if (extras == null || !extras.containsKey(KEY_TUTORIAL_TYPE)) {
- return defaultType;
+ /** Returns true iff there aren't anymore tutorial types to display to the user. */
+ public boolean isTutorialComplete() {
+ return mTutorialSteps.isEmpty();
+ }
+
+ /**
+ * Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
+ *
+ * If there is no following step, the tutorial is closed.
+ */
+ public void continueTutorial() {
+ if (isTutorialComplete()) {
+ mFragment.closeTutorial();
+ return;
}
- try {
- return TutorialType.valueOf(extras.getString(KEY_TUTORIAL_TYPE, ""));
- } catch (IllegalArgumentException e) {
- return defaultType;
+ mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.gesture_tutorial_fragment_container, mFragment)
+ .runOnCommit(() -> mFragment.onAttachedToWindow())
+ .commit();
+ }
+
+ private String[] getTutorialStepNames() {
+ String[] tutorialStepNames = new String[mTutorialSteps.size()];
+
+ int i = 0;
+ for (TutorialType tutorialStep : mTutorialSteps) {
+ tutorialStepNames[i++] = tutorialStep.name();
}
+
+ return tutorialStepNames;
+ }
+
+ private Deque<TutorialType> getTutorialSteps(Bundle extras) {
+ Deque<TutorialType> defaultSteps = new ArrayDeque<>();
+ defaultSteps.push(TutorialType.RIGHT_EDGE_BACK_NAVIGATION);
+
+ if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
+ return defaultSteps;
+ }
+
+ String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS);
+
+ if (tutorialStepNames == null) {
+ return defaultSteps;
+ }
+
+ Deque<TutorialType> tutorialSteps = new ArrayDeque<>();
+ for (String tutorialStepName : tutorialStepNames) {
+ tutorialSteps.addLast(TutorialType.valueOf(tutorialStepName));
+ }
+
+ return tutorialSteps;
}
private void hideSystemUI() {
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 0edabd4..95352d1 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -94,8 +94,13 @@
case HOME_NAVIGATION:
switch (result) {
case HOME_GESTURE_COMPLETED: {
- animateFakeTaskViewHome(finalVelocity, () ->
- mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE));
+ animateFakeTaskViewHome(finalVelocity, () -> {
+ if (mTutorialFragment.isTutorialComplete()) {
+ mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE);
+ } else {
+ mTutorialFragment.continueTutorial();
+ }
+ });
break;
}
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index c636eba..45cbd0b 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -104,8 +104,13 @@
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
break;
case OVERVIEW_GESTURE_COMPLETED:
- fadeOutFakeTaskView(true, () ->
- mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE));
+ fadeOutFakeTaskView(true, () -> {
+ if (mTutorialFragment.isTutorialComplete()) {
+ mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE);
+ } else {
+ mTutorialFragment.continueTutorial();
+ }
+ });
break;
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
case HOME_OR_OVERVIEW_CANCELLED:
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index f297d5a..608fe72 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -54,6 +54,7 @@
fragment = new BackGestureTutorialFragment();
tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
}
+
Bundle args = new Bundle();
args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
fragment.setArguments(args);
@@ -197,6 +198,20 @@
return mHandCoachingAnimation;
}
+ void continueTutorial() {
+ if (!(getContext() instanceof GestureSandboxActivity)) {
+ closeTutorial();
+ return;
+ }
+ GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
+
+ if (gestureSandboxActivity == null) {
+ closeTutorial();
+ return;
+ }
+ gestureSandboxActivity.continueTutorial();
+ }
+
void closeTutorial() {
FragmentActivity activity = getActivity();
if (activity != null) {
@@ -207,4 +222,13 @@
void startSystemNavigationSetting() {
startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
}
+
+ boolean isTutorialComplete() {
+ if (!(getContext() instanceof GestureSandboxActivity)) {
+ return true;
+ }
+ GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
+
+ return gestureSandboxActivity == null || gestureSandboxActivity.isTutorialComplete();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
new file mode 100644
index 0000000..0bb0bbc
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.logging;
+
+import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_DISABLED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_ENABLED;
+import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE;
+import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.TypedArray;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.launcher3.AutoInstallsLayout;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.util.SecureSettingsObserver;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Utility class to log launcher settings changes
+ */
+public class SettingsChangeLogger implements
+ NavigationModeChangeListener, OnSharedPreferenceChangeListener {
+
+ private static final String TAG = "SettingsChangeLogger";
+ private static final String ROOT_TAG = "androidx.preference.PreferenceScreen";
+ private static final String BOOLEAN_PREF = "SwitchPreference";
+
+ private final Context mContext;
+ private final ArrayMap<String, LoggablePref> mLoggablePrefs;
+
+ private Mode mNavMode;
+ private boolean mNotificationDotsEnabled;
+
+ public SettingsChangeLogger(Context context) {
+ mContext = context;
+ mLoggablePrefs = loadPrefKeys(context);
+ mNavMode = SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
+
+ Utilities.getPrefs(context).registerOnSharedPreferenceChangeListener(this);
+ getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this);
+
+ SecureSettingsObserver dotsObserver =
+ newNotificationSettingsObserver(context, this::onNotificationDotsChanged);
+ mNotificationDotsEnabled = dotsObserver.getValue();
+ dispatchUserEvent();
+
+ }
+
+ private static ArrayMap<String, LoggablePref> loadPrefKeys(Context context) {
+ XmlPullParser parser = context.getResources().getXml(R.xml.launcher_preferences);
+ ArrayMap<String, LoggablePref> result = new ArrayMap<>();
+
+ try {
+ AutoInstallsLayout.beginDocument(parser, ROOT_TAG);
+ final int depth = parser.getDepth();
+ int type;
+ while (((type = parser.next()) != XmlPullParser.END_TAG
+ || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ if (BOOLEAN_PREF.equals(parser.getName())) {
+ TypedArray a = context.obtainStyledAttributes(
+ Xml.asAttributeSet(parser), R.styleable.LoggablePref);
+ String key = a.getString(R.styleable.LoggablePref_android_key);
+ LoggablePref pref = new LoggablePref();
+ pref.defaultValue =
+ a.getBoolean(R.styleable.LoggablePref_android_defaultValue, true);
+ pref.eventIdOn = a.getInt(R.styleable.LoggablePref_logIdOn, 0);
+ pref.eventIdOff = a.getInt(R.styleable.LoggablePref_logIdOff, 0);
+ if (pref.eventIdOff > 0 && pref.eventIdOn > 0) {
+ result.put(key, pref);
+ }
+ }
+ }
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Error parsing preference xml", e);
+ }
+ return result;
+ }
+
+ private void onNotificationDotsChanged(boolean isDotsEnabled) {
+ mNotificationDotsEnabled = isDotsEnabled;
+ dispatchUserEvent();
+ }
+
+ @Override
+ public void onNavigationModeChanged(Mode newMode) {
+ mNavMode = newMode;
+ dispatchUserEvent();
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if (LAST_PREDICTION_ENABLED_STATE.equals(key) || mLoggablePrefs.containsKey(key)) {
+ dispatchUserEvent();
+ }
+ }
+
+ private void dispatchUserEvent() {
+ StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
+ .withInstanceId(new InstanceIdSequence().newInstanceId());
+
+ logger.log(mNotificationDotsEnabled
+ ? LAUNCHER_NOTIFICATION_DOT_ENABLED
+ : LAUNCHER_NOTIFICATION_DOT_DISABLED);
+ logger.log(mNavMode.launcherEvent);
+ logger.log(getDevicePrefs(mContext).getBoolean(LAST_PREDICTION_ENABLED_STATE, true)
+ ? LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED
+ : LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED);
+
+ SharedPreferences prefs = Utilities.getPrefs(mContext);
+ mLoggablePrefs.forEach((key, lp) -> logger.log(() ->
+ prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff));
+ }
+
+ private static class LoggablePref {
+ public boolean defaultValue;
+ public int eventIdOn;
+ public int eventIdOff;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 059d158..d949126 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,10 +16,6 @@
package com.android.quickstep.logging;
-import static android.text.format.DateUtils.DAY_IN_MILLIS;
-import static android.text.format.DateUtils.formatElapsedTime;
-
-import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
@@ -28,8 +24,6 @@
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
-import static java.lang.System.currentTimeMillis;
-
import android.content.Context;
import android.util.Log;
@@ -44,22 +38,16 @@
import com.android.launcher3.logger.LauncherAtom.FromState;
import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.logging.InstanceId;
-import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.LogConfig;
import com.android.systemui.shared.system.SysUiStatsLog;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -78,7 +66,6 @@
private static final String TAG = "StatsLog";
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
- private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
// from nano to lite, bake constant to prevent robo test failure.
@@ -101,71 +88,10 @@
}
/**
- * Logs impression of the current workspace with additional launcher events.
+ * Synchronously writes an itemInfo to stats log
*/
- @Override
- public void logSnapshot(List<EventEnum> extraEvents) {
- LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
- new SnapshotWorker(extraEvents));
- }
-
- private class SnapshotWorker extends BaseModelUpdateTask {
- private final InstanceId mInstanceId;
- private final List<EventEnum> mExtraEvents;
-
- SnapshotWorker(List<EventEnum> extraEvents) {
- mInstanceId = new InstanceIdSequence(1 << 20 /*InstanceId.INSTANCE_ID_MAX*/)
- .newInstanceId();
- this.mExtraEvents = extraEvents;
- }
-
- @Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- long lastSnapshotTimeMillis = getDevicePrefs(mContext)
- .getLong(LAST_SNAPSHOT_TIME_MILLIS, 0);
- // Log snapshot only if previous snapshot was older than a day
- if (currentTimeMillis() - lastSnapshotTimeMillis < DAY_IN_MILLIS) {
- if (IS_VERBOSE) {
- String elapsedTime = formatElapsedTime(
- (currentTimeMillis() - lastSnapshotTimeMillis) / 1000);
- Log.d(TAG, String.format(
- "Skipped snapshot logging since previous snapshot was %s old.",
- elapsedTime));
- }
- return;
- }
-
- IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
- ArrayList<ItemInfo> workspaceItems = (ArrayList) dataModel.workspaceItems.clone();
- ArrayList<LauncherAppWidgetInfo> appWidgets = (ArrayList) dataModel.appWidgets.clone();
- for (ItemInfo info : workspaceItems) {
- LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
- writeSnapshot(atomInfo, mInstanceId);
- }
- for (FolderInfo fInfo : folders) {
- try {
- ArrayList<WorkspaceItemInfo> folderContents =
- (ArrayList) Executors.MAIN_EXECUTOR.submit(fInfo.contents::clone).get();
- for (ItemInfo info : folderContents) {
- LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
- writeSnapshot(atomInfo, mInstanceId);
- }
- } catch (Exception e) {
- }
- }
- for (ItemInfo info : appWidgets) {
- LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
- writeSnapshot(atomInfo, mInstanceId);
- }
- mExtraEvents
- .forEach(eventName -> logger().withInstanceId(mInstanceId).log(eventName));
-
- getDevicePrefs(mContext).edit()
- .putLong(LAST_SNAPSHOT_TIME_MILLIS, currentTimeMillis()).apply();
- }
- }
-
- private void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
+ @WorkerThread
+ public static void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
if (IS_VERBOSE) {
Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.getId(), info));
}
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index d0f6879..da5f59e 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -193,7 +193,10 @@
if (isFirstDetectedPause) {
mOnMotionPauseListener.onMotionPauseDetected();
}
- mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+ // Null check again as onMotionPauseDetected() maybe have called clear().
+ if (mOnMotionPauseListener != null) {
+ mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
new file mode 100644
index 0000000..87fee79
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.shared.pip.PipSurfaceTransactionHelper;
+
+/**
+ * An {@link Animator} that animates an Activity to PiP (picture-in-picture) window when
+ * swiping up (in gesture navigation mode). Note that this class is derived from
+ * {@link com.android.wm.shell.pip.PipAnimationController.PipTransitionAnimator}.
+ *
+ * TODO: consider sharing this class including the animator and leash operations between
+ * Launcher and SysUI. Also, there should be one source of truth for the corner radius of the
+ * PiP window, which would ideally be on SysUI side as well.
+ */
+public class SwipePipToHomeAnimator extends ValueAnimator implements
+ ValueAnimator.AnimatorUpdateListener {
+ private final int mTaskId;
+ private final ComponentName mComponentName;
+ private final SurfaceControl mLeash;
+ private final Rect mStartBounds = new Rect();
+ private final Rect mDestinationBounds = new Rect();
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+
+ /** for calculating the transform in {@link #onAnimationUpdate(ValueAnimator)} */
+ private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
+ private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
+ private final Rect mSourceHintRectInsets = new Rect();
+ private final Rect mSourceInsets = new Rect();
+
+ /**
+ * Flag to avoid the double-end problem since the leash would have been released
+ * after the first end call and any further operations upon it would lead to NPE.
+ */
+ private boolean mHasAnimationEnded;
+
+ public SwipePipToHomeAnimator(int taskId,
+ @NonNull ComponentName componentName,
+ @NonNull SurfaceControl leash,
+ @NonNull Rect sourceRectHint,
+ @NonNull Rect startBounds,
+ @NonNull Rect destinationBounds) {
+ mTaskId = taskId;
+ mComponentName = componentName;
+ mLeash = leash;
+ mStartBounds.set(startBounds);
+ mDestinationBounds.set(destinationBounds);
+ mSurfaceTransactionHelper = new PipSurfaceTransactionHelper();
+
+ mSourceHintRectInsets.set(sourceRectHint.left - startBounds.left,
+ sourceRectHint.top - startBounds.top,
+ startBounds.right - sourceRectHint.right,
+ startBounds.bottom - sourceRectHint.bottom);
+
+ addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ SwipePipToHomeAnimator.this.onAnimationEnd();
+ }
+ });
+ addUpdateListener(this);
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ if (mHasAnimationEnded) return;
+
+ final float fraction = animator.getAnimatedFraction();
+ final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds, mDestinationBounds);
+ final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets,
+ mSourceHintRectInsets);
+ final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mStartBounds, bounds, insets);
+ mSurfaceTransactionHelper.resetCornerRadius(tx, mLeash);
+ tx.apply();
+ }
+
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ public Rect getDestinationBounds() {
+ return mDestinationBounds;
+ }
+
+ private void onAnimationEnd() {
+ if (mHasAnimationEnded) return;
+
+ final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ mSurfaceTransactionHelper.reset(tx, mLeash, mDestinationBounds);
+ tx.apply();
+ mHasAnimationEnded = true;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 08fe48d..61ba411 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -52,16 +52,16 @@
public static final IntProperty<TaskViewSimulator> SCROLL =
new IntProperty<TaskViewSimulator>("scroll") {
- @Override
- public void setValue(TaskViewSimulator simulator, int i) {
- simulator.setScroll(i);
- }
+ @Override
+ public void setValue(TaskViewSimulator simulator, int scroll) {
+ simulator.setScroll(scroll);
+ }
- @Override
- public Integer get(TaskViewSimulator simulator) {
- return simulator.mScrollState.scroll;
- }
- };
+ @Override
+ public Integer get(TaskViewSimulator simulator) {
+ return simulator.mScrollState.scroll;
+ }
+ };
private final Rect mTmpCropRect = new Rect();
private final RectF mTempRectF = new RectF();
@@ -72,7 +72,6 @@
private final BaseActivityInterface mSizeStrategy;
private final Rect mTaskRect = new Rect();
- private float mOffsetY;
private boolean mDrawsBelowRecents;
private final PointF mPivot = new PointF();
private DeviceProfile mDp;
@@ -89,6 +88,8 @@
// TaskView properties
private final FullscreenDrawParams mCurrentFullscreenParams;
private float mCurveScale = 1;
+ public final AnimatedFloat taskPrimaryTranslation = new AnimatedFloat();
+ public final AnimatedFloat taskSecondaryTranslation = new AnimatedFloat();
// RecentsView properties
public final AnimatedFloat recentsViewScale = new AnimatedFloat();
@@ -178,10 +179,6 @@
}
}
- public void setOffsetY(float offsetY) {
- mOffsetY = offsetY;
- }
-
public void setDrawsBelowRecents(boolean drawsBelowRecents) {
mDrawsBelowRecents = drawsBelowRecents;
}
@@ -298,7 +295,11 @@
// Apply TaskView matrix: scale, translate, scroll
mMatrix.postScale(mCurveScale, mCurveScale, taskWidth / 2, taskHeight / 2);
- mMatrix.postTranslate(mTaskRect.left, mTaskRect.top + mOffsetY);
+ mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
+ mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+ taskPrimaryTranslation.value);
+ mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
+ taskSecondaryTranslation.value);
mOrientationState.getOrientationHandler().set(
mMatrix, MATRIX_POST_TRANSLATE, mScrollState.scroll);
diff --git a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
index c6c2d7e..f6eb0e2 100644
--- a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -65,6 +65,10 @@
invalidateSelf();
}
+ public void update(float left, float top, float right, float bottom) {
+ mCurrentRect.set(left, top, right, bottom);
+ }
+
public void setIcon(Drawable icon) {
mIcon = icon;
}
@@ -94,18 +98,16 @@
@Override
public void draw(Canvas canvas) {
- if (mCurrentRect != null) {
- canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
- if (mIcon != null && mIconAnimationProgress > 0f) {
- canvas.save();
- float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
- 1f).getInterpolation(mIconAnimationProgress);
- canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
- mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
- canvas.scale(scale, scale);
- mIcon.draw(canvas);
- canvas.restore();
- }
+ canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
+ if (mIcon != null && mIconAnimationProgress > 0f) {
+ canvas.save();
+ float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
+ 1f).getInterpolation(mIconAnimationProgress);
+ canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
+ mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
+ canvas.scale(scale, scale);
+ mIcon.draw(canvas);
+ canvas.restore();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2158e03..264a182 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -51,6 +51,7 @@
import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
@@ -99,7 +100,6 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PendingAnimation.EndState;
-import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -114,6 +114,7 @@
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
+import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsAnimationController;
@@ -230,6 +231,7 @@
view.setScaleX(scale);
view.setScaleY(scale);
view.mLastComputedTaskPushOutDistance = null;
+ view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
view.updatePageOffsets();
view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
}
@@ -873,9 +875,14 @@
// Since we reuse the same mLiveTileTaskViewSimulator in the RecentsView, we need
// to reset the params after it settles in Overview from swipe up so that we don't
// render with obsolete param values.
+ mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = 0;
+ mLiveTileTaskViewSimulator.taskSecondaryTranslation.value = 0;
mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
- mLiveTileTaskViewSimulator.setOffsetY(0);
+
+ // Reset the live tile rect
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ LiveTileOverlay.INSTANCE.update(0, 0, deviceProfile.widthPx, deviceProfile.heightPx);
}
if (mRunningTaskTileHidden) {
setRunningTaskHidden(mRunningTaskTileHidden);
@@ -1540,7 +1547,10 @@
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && getRunningTaskView() == taskView) {
anim.addOnFrameCallback(() -> {
- mLiveTileTaskViewSimulator.setOffsetY(taskView.getTranslationY());
+ mLiveTileTaskViewSimulator.taskSecondaryTranslation.value =
+ mOrientationHandler.getSecondaryValue(
+ taskView.getTranslationX(),
+ taskView.getTranslationY());
redrawLiveTile();
});
}
@@ -1986,6 +1996,7 @@
TaskView task = getTaskViewAt(i);
mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
}
+ mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
}
/**
@@ -2084,22 +2095,35 @@
boolean launchingCenterTask = taskIndex == centerTaskIndex;
float toScale = getMaxScaleForFullScreen();
+ RecentsView recentsView = tv.getRecentsView();
if (launchingCenterTask) {
- RecentsView recentsView = tv.getRecentsView();
anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale));
anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1));
} else {
// We are launching an adjacent task, so parallax the center and other adjacent task.
float displacementX = tv.getWidth() * (toScale - tv.getCurveScale());
- anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex), TRANSLATION_X,
- mIsRtl ? -displacementX : displacementX));
+ float primaryTranslation = mIsRtl ? -displacementX : displacementX;
+ anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex),
+ mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation));
+ int runningTaskIndex = recentsView.getRunningTaskIndex();
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && runningTaskIndex != -1
+ && runningTaskIndex != taskIndex) {
+ anim.play(ObjectAnimator.ofFloat(
+ recentsView.getLiveTileTaskViewSimulator().taskPrimaryTranslation,
+ AnimatedFloat.VALUE,
+ primaryTranslation));
+ }
int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
if (otherAdjacentTaskIndex >= 0 && otherAdjacentTaskIndex < getPageCount()) {
- anim.play(new PropertyListBuilder()
- .translationX(mIsRtl ? -displacementX : displacementX)
- .scale(1)
- .build(getPageAt(otherAdjacentTaskIndex)));
+ PropertyValuesHolder[] properties = new PropertyValuesHolder[3];
+ properties[0] = PropertyValuesHolder.ofFloat(
+ mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation);
+ properties[1] = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
+ properties[2] = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
+
+ anim.play(ObjectAnimator.ofPropertyValuesHolder(getPageAt(otherAdjacentTaskIndex),
+ properties));
}
}
return anim;
@@ -2264,6 +2288,10 @@
return mLiveTileTaskViewSimulator;
}
+ public TransformParams getLiveTileParams() {
+ return mLiveTileParams;
+ }
+
// TODO: To be removed in a follow up CL
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
RecentsAnimationTargets recentsAnimationTargets) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 54a793c..5154018 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -39,6 +39,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
@@ -65,7 +66,6 @@
import android.widget.FrameLayout;
import android.widget.Toast;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
@@ -75,6 +75,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
@@ -82,10 +83,12 @@
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
+import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.TaskCornerRadius;
@@ -175,7 +178,7 @@
private float mCurveScale;
private float mFullscreenProgress;
private final FullscreenDrawParams mCurrentFullscreenParams;
- private final BaseDraggingActivity mActivity;
+ private final StatefulActivity mActivity;
private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0;
@@ -212,18 +215,31 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mActivity = BaseDraggingActivity.fromContext(context);
+ mActivity = StatefulActivity.fromContext(context);
setOnClickListener((view) -> {
if (getTask() == null) {
return;
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (isRunningTask()) {
- // TODO: Replace this animation with createRecentsWindowAnimator
- createLaunchAnimationForRunningTask().start();
- } else {
- launchTask(true /* animate */);
- }
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
+ RecentsView recentsView = getRecentsView();
+ RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
+ recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
+
+ AnimatorSet anim = new AnimatorSet();
+ TaskViewUtils.composeRecentsLaunchAnimator(
+ anim, this, targets.apps,
+ targets.wallpapers, true /* launcherClosing */,
+ mActivity.getStateManager(), recentsView,
+ recentsView.getDepthController());
+ anim.addListener(new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
+ recentsView.finishRecentsAnimation(false, null);
+ }
+ });
+ anim.start();
} else {
launchTask(true /* animate */);
}
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index 79fb612..069954c 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2015 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,16 +12,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.BubbleTextView
- xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
- style="@style/BaseIcon"
+ style="@style/BaseIcon.AllApps"
android:id="@+id/icon"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
launcher:iconDisplay="all_apps"
- launcher:centerVertically="true"
- android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
- android:paddingRight="@dimen/dynamic_grid_cell_padding_x" />
+ launcher:centerVertically="true" />
diff --git a/res/layout/search_result_icon.xml b/res/layout/search_result_icon.xml
new file mode 100644
index 0000000..3c1dd49
--- /dev/null
+++ b/res/layout/search_result_icon.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher3.views.SearchResultIcon xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ style="@style/BaseIcon.AllApps"
+ launcher:iconDisplay="all_apps"
+ launcher:centerVertically="true" />
+
diff --git a/res/layout/switch_preference_with_settings.xml b/res/layout/switch_preference_with_settings.xml
index d319561..cd51833 100644
--- a/res/layout/switch_preference_with_settings.xml
+++ b/res/layout/switch_preference_with_settings.xml
@@ -26,7 +26,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_setting"
- android:tint="@android:color/black"
+ android:forceDarkAllowed="true"
android:padding="12dp"
android:background="?android:attr/selectableItemBackgroundBorderless" />
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 6b0f300..c3ad1eb 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -174,6 +174,14 @@
<attr name="canThumbDetach" format="boolean" />
</declare-styleable>
+ <declare-styleable name="LoggablePref">
+ <attr name="android:key" />
+ <attr name="android:defaultValue" />
+ <!-- Ground truth of this Pref integer can be found in StatsLogManager -->
+ <attr name="logIdOn" format="integer" />
+ <attr name="logIdOff" format="integer" />
+ </declare-styleable>
+
<declare-styleable name="PreviewFragment">
<attr name="android:name" />
<attr name="android:id" />
diff --git a/res/values/styles.xml b/res/values/styles.xml
index fd3d873..067cf7f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -223,6 +223,16 @@
<item name="android:lines">1</item>
</style>
+ <!-- Base theme for AllApps BubbleTextViews -->
+ <style name="BaseIcon.AllApps" parent="BaseIcon">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:stateListAnimator">@animator/all_apps_fastscroll_icon_anim</item>
+ <item name="android:paddingLeft">@dimen/dynamic_grid_cell_padding_x</item>
+ <item name="android:paddingRight">@dimen/dynamic_grid_cell_padding_x</item>
+ </style>
+
+
<!-- Icon displayed on the workspace -->
<style name="BaseIcon.Workspace" >
<item name="android:shadowRadius">2.0</item>
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 7e72208..e4bea50 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -15,7 +15,8 @@
-->
<androidx.preference.PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android">
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto">
<com.android.launcher3.settings.NotificationDotsPreference
android:key="pref_icon_badging"
@@ -30,19 +31,31 @@
</intent>
</com.android.launcher3.settings.NotificationDotsPreference>
+ <!--
+ LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED(613)
+ LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_DISABLED(614)
+ -->
<SwitchPreference
android:key="pref_add_icon_to_home"
android:title="@string/auto_add_shortcuts_label"
android:summary="@string/auto_add_shortcuts_description"
android:defaultValue="true"
- android:persistent="true" />
+ android:persistent="true"
+ launcher:logIdOn="613"
+ launcher:logIdOff="614" />
+ <!--
+ LAUNCHER_HOME_SCREEN_ROTATION_ENABLED(615)
+ LAUNCHER_HOME_SCREEN_ROTATION_DISABLED(616)
+ -->
<SwitchPreference
android:key="pref_allowRotation"
android:title="@string/allow_rotation_title"
android:summary="@string/allow_rotation_desc"
android:defaultValue="@bool/allow_rotation"
- android:persistent="true" />
+ android:persistent="true"
+ launcher:logIdOn="615"
+ launcher:logIdOff="616" />
<androidx.preference.PreferenceScreen
android:key="pref_developer_options"
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 817d028..1015a32 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.FastBitmapDrawable.newIcon;
+import static com.android.launcher3.graphics.IconShape.getShape;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
@@ -27,15 +28,18 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
+import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Process;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.Property;
@@ -50,6 +54,8 @@
import com.android.launcher3.Launcher.OnResumeCallback;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.allapps.AllAppsSectionDecorator;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.FolderIcon;
@@ -60,6 +66,7 @@
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconCache.IconLoadRequest;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -79,7 +86,7 @@
* too aggressive.
*/
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
- IconLabelDotView, DraggableView, Reorderable {
+ IconLabelDotView, DraggableView, Reorderable, AllAppsSectionDecorator.SelfDecoratingView {
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
@@ -87,6 +94,8 @@
private static final int DISPLAY_HERO_APP = 5;
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
+ private static final float HIGHLIGHT_SCALE = 1.16f;
+
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
@@ -95,6 +104,11 @@
private float mScaleForReorderBounce = 1f;
+ protected final Paint mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Path mHighlightPath = new Path();
+ protected int mHighlightColor = Color.TRANSPARENT;
+ private final BlurMaskFilter mHighlightShadowFilter;
+
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
@Override
@@ -208,6 +222,11 @@
setEllipsize(TruncateAt.END);
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
setTextAlpha(1f);
+
+ int shadowSize = context.getResources().getDimensionPixelSize(
+ R.dimen.blur_size_click_shadow);
+ mHighlightShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.INNER);
+
}
@Override
@@ -421,8 +440,38 @@
@Override
public void onDraw(Canvas canvas) {
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mHighlightColor != Color.TRANSPARENT) {
+ int count = canvas.save();
+ drawFocusHighlight(canvas);
+ canvas.restoreToCount(count);
+ }
super.onDraw(canvas);
- drawDotIfNecessary(canvas);
+ }
+
+ protected void drawFocusHighlight(Canvas canvas) {
+ boolean isBadged = getTag() instanceof ItemInfo && !Process.myUserHandle().equals(
+ ((ItemInfo) getTag()).user);
+ float insetScale = (HIGHLIGHT_SCALE - 1) / 2;
+ canvas.translate(-getIconSize() * insetScale, -insetScale * getIconSize());
+ float outlineSize = getIconSize() * HIGHLIGHT_SCALE;
+ mHighlightPath.reset();
+ mHighlightPaint.reset();
+ getIconBounds(mDotParams.iconBounds);
+ getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left, mDotParams.iconBounds.top,
+ outlineSize / 2);
+ if (isBadged) {
+ float borderSize = outlineSize - getIconSize();
+ float badgeSize = LauncherIcons.getBadgeSizeForIconSize(getIconSize()) + borderSize;
+ float badgeInset = outlineSize - badgeSize;
+ getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left + badgeInset,
+ mDotParams.iconBounds.top + badgeInset, badgeSize / 2);
+ }
+ mHighlightPaint.setMaskFilter(mHighlightShadowFilter);
+ mHighlightPaint.setColor(mDotParams.color);
+ canvas.drawPath(mHighlightPath, mHighlightPaint);
+ mHighlightPaint.setMaskFilter(null);
+ mHighlightPaint.setColor(mHighlightColor);
+ canvas.drawPath(mHighlightPath, mHighlightPaint);
}
/**
@@ -656,8 +705,13 @@
updateIcon(icon);
+ ItemInfo itemInfo = (ItemInfo) getTag();
+
// If the current icon is a placeholder color, animate its update.
- if (mIcon != null && mIcon instanceof PlaceHolderIconDrawable) {
+ if (mIcon != null
+ && mIcon instanceof PlaceHolderIconDrawable
+ && (itemInfo == null
+ || itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
animateIconUpdate((PlaceHolderIconDrawable) mIcon, icon);
}
@@ -787,10 +841,11 @@
@Override
public SafeCloseable prepareDrawDragView() {
+ int highlightColor = mHighlightColor;
+ mHighlightColor = Color.TRANSPARENT;
resetIconScale();
setForceHideDot(true);
- return () -> {
- };
+ return () -> mHighlightColor = highlightColor;
}
private void resetIconScale() {
@@ -827,4 +882,17 @@
});
iconUpdateAnimation.start();
}
+
+
+ @Override
+ public void decorate(int color) {
+ mHighlightColor = color;
+ invalidate();
+ }
+
+ @Override
+ public void removeDecoration() {
+ mHighlightColor = Color.TRANSPARENT;
+ invalidate();
+ }
}
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index fd4c30c..70d8476 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -79,9 +79,7 @@
public DraggableView originalView = null;
/** Used for matching DROP event with its corresponding DRAG event on the server side. */
- public final InstanceId logInstanceId =
- new InstanceIdSequence(1 << 20 /*InstanceId.INSTANCE_ID_MAX*/)
- .newInstanceId();
+ public final InstanceId logInstanceId = new InstanceIdSequence().newInstanceId();
public DragObject(Context context) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 370bd6f..ff53b5f 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -201,7 +201,7 @@
DisplayController.getDefaultDisplay(context).getInfo(),
getPredefinedDeviceProfiles(context, gridName));
- Info myInfo = new Info(context, display);
+ Info myInfo = new Info(display);
DisplayOption myDisplayOption = invDistWeightedInterpolate(
myInfo, getPredefinedDeviceProfiles(context, gridName));
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 2973cf7..aeed16a 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -188,6 +188,9 @@
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
+ final Bundle extra = new Bundle();
+ extra.putString(LauncherSettings.Settings.EXTRA_DB_NAME, mOpenHelper.getDatabaseName());
+ result.setExtras(extra);
result.setNotificationUri(getContext().getContentResolver(), uri);
return result;
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 58a418e..d2758f5 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -358,6 +358,8 @@
public static final String EXTRA_VALUE = "value";
+ public static final String EXTRA_DB_NAME = "db_name";
+
public static Bundle call(ContentResolver cr, String method) {
return call(cr, method, null /* arg */);
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 98328cf..777ea3c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -139,6 +139,10 @@
public static final int DEFAULT_PAGE = 0;
+ private static final int DEFAULT_SMARTSPACE_HEIGHT = 1;
+
+ private static final int EXPANDED_SMARTSPACE_HEIGHT = 2;
+
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
@@ -507,7 +511,10 @@
.inflate(R.layout.search_container_workspace, firstPage, false);
}
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
+ int cellVSpan = FeatureFlags.EXPANDED_SMARTSPACE.get()
+ ? EXPANDED_SMARTSPACE_HEIGHT : DEFAULT_SMARTSPACE_HEIGHT;
+ CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(),
+ cellVSpan);
lp.canReorder = false;
if (!firstPage.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true)) {
Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
@@ -2078,40 +2085,40 @@
mLastReorderY = -1;
}
- /*
- *
- * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
- * coordinate space. The argument xy is modified with the return result.
- */
- private void mapPointFromSelfToChild(View v, float[] xy) {
- xy[0] = xy[0] - v.getLeft();
- xy[1] = xy[1] - v.getTop();
- }
+ /*
+ *
+ * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
+ * coordinate space. The argument xy is modified with the return result.
+ */
+ private void mapPointFromSelfToChild(View v, float[] xy) {
+ xy[0] = xy[0] - v.getLeft();
+ xy[1] = xy[1] - v.getTop();
+ }
- boolean isPointInSelfOverHotseat(int x, int y) {
- mTempFXY[0] = x;
- mTempFXY[1] = y;
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
- View hotseat = mLauncher.getHotseat();
- return mTempFXY[0] >= hotseat.getLeft() &&
- mTempFXY[0] <= hotseat.getRight() &&
- mTempFXY[1] >= hotseat.getTop() &&
- mTempFXY[1] <= hotseat.getBottom();
- }
+ boolean isPointInSelfOverHotseat(int x, int y) {
+ mTempFXY[0] = x;
+ mTempFXY[1] = y;
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
+ View hotseat = mLauncher.getHotseat();
+ return mTempFXY[0] >= hotseat.getLeft()
+ && mTempFXY[0] <= hotseat.getRight()
+ && mTempFXY[1] >= hotseat.getTop()
+ && mTempFXY[1] <= hotseat.getBottom();
+ }
/**
* Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
* @param layout either hotseat of a page in workspace
* @param xy the point location in workspace co-ordinate space
*/
- private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
- if (mLauncher.isHotseatLayout(layout)) {
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
- mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
- } else {
- mapPointFromSelfToChild(layout, xy);
- }
- }
+ private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
+ if (mLauncher.isHotseatLayout(layout)) {
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
+ mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
+ } else {
+ mapPointFromSelfToChild(layout, xy);
+ }
+ }
private boolean isDragWidget(DragObject d) {
return (d.dragInfo instanceof LauncherAppWidgetInfo ||
@@ -2393,9 +2400,9 @@
spanY = mDragInfo.spanY;
}
- final int container = mLauncher.isHotseatLayout(cellLayout) ?
- LauncherSettings.Favorites.CONTAINER_HOTSEAT :
- LauncherSettings.Favorites.CONTAINER_DESKTOP;
+ final int container = mLauncher.isHotseatLayout(cellLayout)
+ ? LauncherSettings.Favorites.CONTAINER_HOTSEAT
+ : LauncherSettings.Favorites.CONTAINER_DESKTOP;
final int screenId = getIdForScreen(cellLayout);
if (!mLauncher.isHotseatLayout(cellLayout)
&& screenId != getScreenIdForPageIndex(mCurrentPage)
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 75ab00a..5d5e017 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -16,7 +16,7 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
-import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
+import static com.android.launcher3.allapps.AllAppsGridAdapter.SearchAdapterItem;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
@@ -57,6 +57,7 @@
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.model.data.AppInfo;
@@ -67,9 +68,7 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
-import com.android.systemui.plugins.shared.SearchTargetEvent;
-
-import java.util.function.IntConsumer;
+import com.android.systemui.plugins.shared.SearchTarget;
/**
* The all apps view container.
@@ -546,13 +545,9 @@
return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem);
}
AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild();
- if (focusedItem instanceof AdapterItemWithPayload) {
- IntConsumer onSelection =
- ((AdapterItemWithPayload) focusedItem).getSelectionHandler();
- if (onSelection != null) {
- onSelection.accept(SearchTargetEvent.QUICK_SELECT);
- return true;
- }
+ if (focusedItem instanceof SearchAdapterItem) {
+ SearchTarget searchTarget = ((SearchAdapterItem) focusedItem).getSearchTarget();
+ SearchEventTracker.INSTANCE.get(getContext()).quickSelect(searchTarget);
}
if (focusedItem.appInfo != null) {
ItemInfo itemInfo = focusedItem.appInfo;
@@ -585,6 +580,10 @@
int padding = mHeader.getMaxTranslation();
for (int i = 0; i < mAH.length; i++) {
mAH[i].padding.top = padding;
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mUsingTabs) {
+ //add extra space between tabs and recycler view
+ mAH[i].padding.top += mLauncher.getDeviceProfile().edgeMarginPx;
+ }
mAH[i].applyPadding();
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 8bc8e53..f773191 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -20,8 +20,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -37,29 +35,22 @@
import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityRecordCompat;
-import androidx.lifecycle.LiveData;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import androidx.slice.Slice;
-import androidx.slice.widget.SliceLiveData;
import androidx.slice.widget.SliceView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
import com.android.launcher3.allapps.search.SearchSectionInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.views.HeroSearchResultView;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.launcher3.views.SearchSliceWrapper;
import com.android.systemui.plugins.shared.SearchTarget;
-import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.util.List;
-import java.util.function.IntConsumer;
/**
* The grid view adapter of all the apps.
@@ -100,9 +91,11 @@
public static final int VIEW_TYPE_SEARCH_SUGGEST = 1 << 13;
+ public static final int VIEW_TYPE_SEARCH_ICON = 1 << 14;
+
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
- public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
+ public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON | VIEW_TYPE_SEARCH_ICON;
/**
* ViewHolder for each icon.
@@ -192,56 +185,25 @@
|| viewType == VIEW_TYPE_SEARCH_PEOPLE
|| viewType == VIEW_TYPE_SEARCH_THUMBNAIL
|| viewType == VIEW_TYPE_SEARCH_ICON_ROW
+ || viewType == VIEW_TYPE_SEARCH_ICON
|| viewType == VIEW_TYPE_SEARCH_SUGGEST;
}
}
/**
* Extension of AdapterItem that contains an extra payload specific to item
- *
- * @param <T> Play load Type
*/
- public static class AdapterItemWithPayload<T> extends AdapterItem {
- private T mPayload;
- private String mSearchSessionId;
- private AllAppsSearchPlugin mPlugin;
- private IntConsumer mSelectionHandler;
+ public static class SearchAdapterItem extends AdapterItem {
+ private SearchTarget mSearchTarget;
- public AllAppsSearchPlugin getPlugin() {
- return mPlugin;
- }
-
- public void setPlugin(AllAppsSearchPlugin plugin) {
- mPlugin = plugin;
- }
-
- public AdapterItemWithPayload(T payload, int type, AllAppsSearchPlugin plugin) {
- mPayload = payload;
+ public SearchAdapterItem(SearchTarget searchTarget, int type) {
+ mSearchTarget = searchTarget;
viewType = type;
- mPlugin = plugin;
}
- public void setSelectionHandler(IntConsumer runnable) {
- mSelectionHandler = runnable;
+ public SearchTarget getSearchTarget() {
+ return mSearchTarget;
}
-
- public void setSearchSessionId(String searchSessionId) {
- mSearchSessionId = searchSessionId;
- }
-
- public String getSearchSessionId() {
- return mSearchSessionId;
- }
-
- public IntConsumer getSelectionHandler() {
- return mSelectionHandler;
- }
-
- public T getPayload() {
- return mPayload;
- }
-
-
}
/**
@@ -426,11 +388,8 @@
R.layout.all_apps_icon, parent, false);
icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mIconFocusListener);
- if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- icon.setOnClickListener(mOnIconClickListener);
- icon.setOnLongClickListener(mOnIconLongClickListener);
- }
-
+ icon.setOnClickListener(mOnIconClickListener);
+ icon.setOnLongClickListener(mOnIconLongClickListener);
// Ensure the all apps icon height matches the workspace icons in portrait mode.
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
return new ViewHolder(icon);
@@ -446,6 +405,9 @@
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
+ case VIEW_TYPE_SEARCH_ICON:
+ return new ViewHolder(mLayoutInflater.inflate(
+ R.layout.search_result_icon, parent, false));
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
return new ViewHolder(
mLayoutInflater.inflate(R.layout.search_section_title, parent, false));
@@ -480,6 +442,10 @@
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()
+ && holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
+ ((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
+ }
switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON:
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
@@ -487,34 +453,6 @@
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset();
icon.applyFromApplicationInfo(info);
- if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- break;
- }
- //TODO: replace with custom TopHitBubbleTextView with support for both shortcut
- // and apps
- if (adapterItem instanceof AdapterItemWithPayload) {
- AdapterItemWithPayload item = (AdapterItemWithPayload) adapterItem;
- item.setSelectionHandler(type -> {
- SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP,
- type, item.position, item.getSearchSessionId());
- e.bundle = HeroSearchResultView.getAppBundle(info);
- if (item.getPlugin() != null) {
- item.getPlugin().notifySearchTargetEvent(e);
- }
- });
- icon.setOnClickListener(view -> {
- item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
- mOnIconClickListener.onClick(view);
- });
- icon.setOnLongClickListener(view -> {
- item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
- return mOnIconLongClickListener.onLongClick(view);
- });
- }
- else {
- icon.setOnClickListener(mOnIconClickListener);
- icon.setOnLongClickListener(mOnIconLongClickListener);
- }
break;
case VIEW_TYPE_EMPTY_SEARCH:
TextView emptyViewText = (TextView) holder.itemView;
@@ -532,39 +470,25 @@
break;
case VIEW_TYPE_SEARCH_SLICE:
SliceView sliceView = (SliceView) holder.itemView;
- AdapterItemWithPayload<Uri> slicePayload =
- (AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position);
- sliceView.setOnSliceActionListener((info1, s) -> {
- if (slicePayload.getPlugin() != null) {
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
- SearchTarget.ItemType.SETTINGS_SLICE,
- SearchTargetEvent.CHILD_SELECT, slicePayload.position,
- slicePayload.getSearchSessionId());
- searchTargetEvent.bundle = new Bundle();
- searchTargetEvent.bundle.putParcelable("uri", slicePayload.getPayload());
- slicePayload.getPlugin().notifySearchTargetEvent(searchTargetEvent);
- }
- });
- try {
- LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher,
- slicePayload.getPayload());
- liveData.observe((Launcher) mLauncher, sliceView);
- sliceView.setTag(liveData);
- } catch (Exception ignored) {
- }
+ SearchAdapterItem slicePayload = (SearchAdapterItem) mApps.getAdapterItems().get(
+ position);
+ SearchTarget searchTarget = slicePayload.getSearchTarget();
+ sliceView.setTag(new SearchSliceWrapper(mLauncher, sliceView, searchTarget));
+
break;
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
case VIEW_TYPE_SEARCH_HERO_APP:
case VIEW_TYPE_SEARCH_ROW:
+ case VIEW_TYPE_SEARCH_ICON:
case VIEW_TYPE_SEARCH_ICON_ROW:
case VIEW_TYPE_SEARCH_PEOPLE:
case VIEW_TYPE_SEARCH_THUMBNAIL:
case VIEW_TYPE_SEARCH_SUGGEST:
- AdapterItemWithPayload item =
- (AdapterItemWithPayload) mApps.getAdapterItems().get(position);
- PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
- payloadResultView.setup(item);
+ SearchAdapterItem item =
+ (SearchAdapterItem) mApps.getAdapterItems().get(position);
+ SearchTargetHandler payloadResultView = (SearchTargetHandler) holder.itemView;
+ payloadResultView.applySearchTarget(item.getSearchTarget());
break;
case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
@@ -576,17 +500,15 @@
public void onViewRecycled(@NonNull ViewHolder holder) {
super.onViewRecycled(holder);
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
- if (holder.itemView instanceof BubbleTextView) {
- BubbleTextView icon = (BubbleTextView) holder.itemView;
- icon.setOnClickListener(null);
- icon.setOnLongClickListener(null);
- } else if (holder.itemView instanceof SliceView) {
+ if (holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
+ ((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
+ }
+ if (holder.itemView instanceof SliceView) {
SliceView sliceView = (SliceView) holder.itemView;
- sliceView.setOnSliceActionListener(null);
- if (sliceView.getTag() instanceof LiveData) {
- LiveData sliceLiveData = (LiveData) sliceView.getTag();
- sliceLiveData.removeObservers((Launcher) mLauncher);
+ if (sliceView.getTag() instanceof SearchSliceWrapper) {
+ ((SearchSliceWrapper) sliceView.getTag()).destroy();
}
+ sliceView.setTag(null);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
index eae9c0a..e2550f5 100644
--- a/src/com/android/launcher3/allapps/AllAppsPagedView.java
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -25,11 +25,11 @@
public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
- final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
- final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
- final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
+ static final float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
+ static final float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
+ static final float TOUCH_SLOP_DAMPING_FACTOR = 4;
- public AllAppsPagedView(Context context) {
+ public AllAppsPagedView(Context context) {
this(context, null);
}
@@ -42,6 +42,7 @@
int topPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0
: context.getResources().getDimensionPixelOffset(
R.dimen.all_apps_header_top_padding);
+ setPadding(0, topPadding, 0, 0);
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 72b6d94..1fa43d0 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -24,6 +24,7 @@
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
@@ -45,6 +46,8 @@
* A RecyclerView with custom fast scroll support for the all apps view.
*/
public class AllAppsRecyclerView extends BaseRecyclerView {
+ private static final String TAG = "AllAppsContainerView";
+ private static final boolean DEBUG = true;
private AlphabeticalAppsList mApps;
private final int mNumAppsPerRow;
@@ -131,7 +134,9 @@
if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.draw(c);
}
-
+ if (DEBUG) {
+ Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
+ }
super.onDraw(c);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
index c131697..3c81811 100644
--- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
+++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
@@ -54,6 +54,9 @@
int i = 0;
while (i < itemCount) {
View view = parent.getChildAt(i);
+ if (view instanceof SelfDecoratingView) {
+ ((SelfDecoratingView) view).removeDecoration();
+ }
int position = parent.getChildAdapterPosition(view);
AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
if (adapterItem.searchSectionInfo != null) {
@@ -93,7 +96,7 @@
int index = mAppsView.getApps().getFocusedChildIndex();
AppsGridLayoutManager layoutManager = (AppsGridLayoutManager)
mAppsView.getActiveRecyclerView().getLayoutManager();
- if (layoutManager.findFirstVisibleItemPosition() == index
+ if (layoutManager.findFirstVisibleItemPosition() <= index
&& index < parent.getChildCount()) {
decorationHandler.onFocusDraw(c, parent.getChildAt(index));
}
@@ -156,6 +159,10 @@
if (view == null) {
return;
}
+ if (view instanceof SelfDecoratingView) {
+ ((SelfDecoratingView) view).decorate(mFocusColor);
+ return;
+ }
mPaint.setColor(mFocusColor);
canvas.drawRoundRect(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom(), mRadius, mRadius, mPaint);
@@ -169,4 +176,18 @@
}
}
+ /**
+ * An interface for a view to draw highlight indicator
+ */
+ public interface SelfDecoratingView {
+ /**
+ * Removes decorations drawing if focus is acquired by another view
+ */
+ void removeDecoration();
+
+ /**
+ * Draws highlight indicator on view.
+ */
+ void decorate(int focusColor);
+ }
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 8c059d5..2450787 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -178,16 +178,46 @@
/**
* Sets results list for search
*/
- public boolean setSearchResults(ArrayList<AdapterItem> f) {
- if (f == null || mSearchResults != f) {
- boolean same = mSearchResults != null && mSearchResults.equals(f);
- mSearchResults = f;
+ public boolean setSearchResults(ArrayList<AdapterItem> results) {
+ if (results == null || mSearchResults != results) {
+ boolean same = mSearchResults != null && mSearchResults.equals(results);
+ mSearchResults = results;
onAppsUpdated();
return !same;
}
return false;
}
+ public boolean appendSearchResults(ArrayList<AdapterItem> results) {
+ if (mSearchResults != null && results != null && results.size() > 0) {
+ updateSearchAdapterItems(results, mSearchResults.size());
+ refreshRecyclerView();
+ return true;
+ }
+ return false;
+ }
+
+ void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
+ SearchSectionInfo lastSection = null;
+ for (int i = 0; i < list.size(); i++) {
+ AdapterItem adapterItem = list.get(i);
+ adapterItem.position = offset + i;
+ mAdapterItems.add(adapterItem);
+ if (adapterItem.searchSectionInfo != lastSection) {
+ if (adapterItem.searchSectionInfo != null) {
+ adapterItem.searchSectionInfo.setPosStart(adapterItem.position);
+ }
+ if (lastSection != null) {
+ lastSection.setPosEnd(adapterItem.position - 1);
+ }
+ lastSection = adapterItem.searchSectionInfo;
+ }
+ if (adapterItem.isCountedForAccessibility()) {
+ mAccessibilityResultsCount++;
+ }
+ }
+ }
+
/**
* Updates internals when the set of apps are updated.
*/
@@ -294,28 +324,7 @@
}
appSection.setPosEnd(mApps.isEmpty() ? appSection.getPosStart() : position - 1);
} else {
- List<AppInfo> appInfos = new ArrayList<>();
- SearchSectionInfo lastSection = null;
- for (int i = 0; i < mSearchResults.size(); i++) {
- AdapterItem adapterItem = mSearchResults.get(i);
- adapterItem.position = i;
- mAdapterItems.add(adapterItem);
- if (adapterItem.searchSectionInfo != lastSection) {
- if (adapterItem.searchSectionInfo != null) {
- adapterItem.searchSectionInfo.setPosStart(i);
- }
- if (lastSection != null) {
- lastSection.setPosEnd(i - 1);
- }
- lastSection = adapterItem.searchSectionInfo;
- }
- if (AllAppsGridAdapter.isIconViewType(adapterItem.viewType)) {
- appInfos.add(adapterItem.appInfo);
- }
- if (adapterItem.isCountedForAccessibility()) {
- mAccessibilityResultsCount++;
- }
- }
+ updateSearchAdapterItems(mSearchResults, 0);
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
// Append the search market item
if (hasNoFilteredResults()) {
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 82c4db4..bc5a5f2 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -30,13 +30,11 @@
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.shared.SearchTarget;
-import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.util.ArrayList;
import java.util.List;
@@ -196,9 +194,16 @@
/**
* Called when the search from primary source is complete.
*
- * @param items sorted list of search result adapter items.
+ * @param items sorted list of search result adapter items
*/
- void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items);
+ void onSearchResult(String query, ArrayList<AdapterItem> items);
+
+ /**
+ * Called when the search from secondary source is complete.
+ *
+ * @param items sorted list of search result adapter items
+ */
+ void onAppendSearchResult(String query, ArrayList<AdapterItem> items);
/**
* Called when the search results should be cleared.
@@ -208,50 +213,20 @@
/**
* An interface for supporting dynamic search results
- *
- * @param <T> Type of payload
*/
- public interface PayloadResultHandler<T> {
- /**
- * Updates View using Adapter's payload
- */
+ public interface SearchTargetHandler {
- default void setup(AdapterItemWithPayload<T> adapterItemWithPayload) {
- Object[] targetInfo = getTargetInfo();
- if (targetInfo != null) {
- targetInfo[0] = adapterItemWithPayload.getSearchSessionId();
- targetInfo[1] = adapterItemWithPayload.position;
- }
- applyAdapterInfo(adapterItemWithPayload);
+ /**
+ * Update view using values from {@link SearchTarget}
+ */
+ void applySearchTarget(SearchTarget searchTarget);
+
+ /**
+ * Handles selection of SearchTarget
+ */
+ default void handleSelection(int eventType) {
}
- void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
-
- /**
- * Gets object created by {@link PayloadResultHandler#createTargetInfo()}
- */
- Object[] getTargetInfo();
-
- /**
- * Creates a wrapper object to hold searchSessionId and item position
- */
- default Object[] createTargetInfo() {
- return new Object[2];
- }
-
- /**
- * Generates a SearchTargetEvent object for a PayloadHandlerView
- */
- default SearchTargetEvent getSearchTargetEvent(SearchTarget.ItemType itemType,
- int eventType) {
- Object[] targetInfo = getTargetInfo();
- if (targetInfo == null) return null;
-
- String searchSessionId = (String) targetInfo[0];
- int position = (int) targetInfo[1];
- return new SearchTargetEvent(itemType, eventType,
- position, searchSessionId);
- }
}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 7518521..000ccbb 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -42,7 +42,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.SearchUiManager;
@@ -173,7 +173,7 @@
}
@Override
- public void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items) {
+ public void onSearchResult(String query, ArrayList<AdapterItem> items) {
if (items != null) {
mApps.setSearchResults(items);
notifyResultChanged();
@@ -182,6 +182,14 @@
}
@Override
+ public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
+ if (items != null) {
+ mApps.appendSearchResults(items);
+ notifyResultChanged();
+ }
+ }
+
+ @Override
public void clearSearchResult() {
if (mApps.setSearchResults(null)) {
notifyResultChanged();
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
index dc9c155..84688e1 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
@@ -16,6 +16,7 @@
package com.android.launcher3.allapps.search;
import android.content.Context;
+import android.os.CancellationSignal;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
@@ -47,11 +48,12 @@
}
@Override
- public void performSearch(String query, Consumer<ArrayList<AdapterItem>> callback) {
+ public void query(String input, Consumer<ArrayList<AdapterItem>> callback,
+ CancellationSignal cancellationSignal) {
mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- List<AppInfo> matchingResults = getTitleMatchResult(apps.data, query);
+ List<AppInfo> matchingResults = getTitleMatchResult(apps.data, input);
callback.accept(getAdapterItems(matchingResults));
}
});
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 5ed7de5..3bddace 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -46,8 +46,10 @@
@Override
public void doSearch(final String query,
final AllAppsSearchBarController.Callbacks callback) {
- mAppsSearchPipeline.performSearch(query,
- results -> mResultHandler.post(() -> callback.onSearchResult(query, results)));
+ mAppsSearchPipeline.query(query,
+ results -> mResultHandler.post(
+ () -> callback.onSearchResult(query, results)),
+ null);
}
public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
diff --git a/src/com/android/launcher3/allapps/search/SearchEventTracker.java b/src/com/android/launcher3/allapps/search/SearchEventTracker.java
new file mode 100644
index 0000000..c276434
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/SearchEventTracker.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 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.allapps.search;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+import java.util.WeakHashMap;
+
+/**
+ * A singleton class to track and report search events to search provider
+ */
+public class SearchEventTracker {
+ @Nullable
+ private AllAppsSearchPlugin mPlugin;
+ private final WeakHashMap<SearchTarget, SearchTargetHandler>
+ mCallbacks = new WeakHashMap<>();
+
+ public static final MainThreadInitializedObject<SearchEventTracker> INSTANCE =
+ new MainThreadInitializedObject<>(SearchEventTracker::new);
+
+ private SearchEventTracker(Context context) {
+ }
+
+ /**
+ * Returns instance of SearchEventTracker
+ */
+ public static SearchEventTracker getInstance(Context context) {
+ return SearchEventTracker.INSTANCE.get(context);
+ }
+
+ /**
+ * Sets current connected plugin for event reporting
+ */
+ public void setPlugin(@Nullable AllAppsSearchPlugin plugin) {
+ mPlugin = plugin;
+ }
+
+ /**
+ * Sends SearchTargetEvent to search provider
+ */
+ public void notifySearchTargetEvent(SearchTargetEvent searchTargetEvent) {
+ if (mPlugin != null) {
+ UI_HELPER_EXECUTOR.post(() -> mPlugin.notifySearchTargetEvent(searchTargetEvent));
+ }
+ }
+
+ /**
+ * Registers a {@link SearchTargetHandler} to handle quick launch for specified SearchTarget.
+ */
+ public void registerWeakHandler(SearchTarget searchTarget, SearchTargetHandler targetHandler) {
+ mCallbacks.put(searchTarget, targetHandler);
+ }
+
+ /**
+ * Handles quick select for SearchTarget
+ */
+ public void quickSelect(SearchTarget searchTarget) {
+ SearchTargetHandler searchTargetHandler = mCallbacks.get(searchTarget);
+ if (searchTargetHandler != null) {
+ searchTargetHandler.handleSelection(SearchTargetEvent.QUICK_SELECT);
+ }
+ }
+
+ /**
+ * flushes all registered quick select handlers
+ */
+ public void clearHandlers() {
+ mCallbacks.clear();
+ }
+}
diff --git a/src/com/android/launcher3/allapps/search/SearchPipeline.java b/src/com/android/launcher3/allapps/search/SearchPipeline.java
index 545f0e3..3516a41 100644
--- a/src/com/android/launcher3/allapps/search/SearchPipeline.java
+++ b/src/com/android/launcher3/allapps/search/SearchPipeline.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps.search;
+import android.os.CancellationSignal;
+
import com.android.launcher3.allapps.AllAppsGridAdapter;
import java.util.ArrayList;
@@ -23,10 +25,13 @@
/**
* An interface for handling search within pipeline
*/
+// Remove when System Service API is added.
public interface SearchPipeline {
/**
* Perform query
*/
- void performSearch(String query, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> cb);
+ void query(String input,
+ Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
+ CancellationSignal cancellationSignal);
}
diff --git a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java b/src/com/android/launcher3/allapps/search/SearchSectionInfo.java
index e026e84..464df68 100644
--- a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java
+++ b/src/com/android/launcher3/allapps/search/SearchSectionInfo.java
@@ -22,7 +22,7 @@
*/
public class SearchSectionInfo {
- private String mTitle;
+ private String mSectionId;
private SectionDecorationHandler mDecorationHandler;
public int getPosStart() {
@@ -48,8 +48,8 @@
this(null);
}
- public SearchSectionInfo(String title) {
- mTitle = title;
+ public SearchSectionInfo(String sectionId) {
+ mSectionId = sectionId;
}
public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) {
@@ -62,9 +62,9 @@
}
/**
- * Returns the section's title
+ * Returns the section's ID
*/
- public String getTitle() {
- return mTitle == null ? "" : mTitle;
+ public String getSectionId() {
+ return mSectionId == null ? "" : mSectionId;
}
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 58b0a87..8e6c2a7 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -182,9 +182,13 @@
"Uses a separate recents activity instead of using the integrated recents+Launcher UI");
public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
- "ENABLE_MINIMAL_DEVICE", true,
+ "ENABLE_MINIMAL_DEVICE", false,
"Allow user to toggle minimal device mode in launcher.");
+ public static final BooleanFlag EXPANDED_SMARTSPACE = new DeviceFlag(
+ "EXPANDED_SMARTSPACE", false, "Expands smartspace height to two rows. "
+ + "Any apps occupying the first row will be removed from workspace.");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2c5bf32..7e90769 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -29,8 +29,6 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ResourceBasedOverride;
-import java.util.List;
-
/**
* Handles the user event logging in R+.
*
@@ -463,10 +461,4 @@
context.getApplicationContext(), R.string.stats_log_manager_class);
return mgr;
}
-
- /**
- * Logs impression of the current workspace with additional launcher events.
- */
- public void logSnapshot(List<EventEnum> additionalEvents) {
- }
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index a27ac23..532834e 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -445,7 +445,8 @@
if (item.screenId == Workspace.FIRST_SCREEN_ID) {
// Mark the first row as occupied (if the feature is enabled)
// in order to account for the QSB.
- screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
+ int spanY = FeatureFlags.EXPANDED_SMARTSPACE.get() ? 2 : 1;
+ screen.markCells(0, 0, countX + 1, spanY, FeatureFlags.QSB_ON_FIRST_SCREEN);
}
occupied.put(item.screenId, screen);
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index c0d5882..b108788 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -41,6 +41,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -130,6 +131,7 @@
private final Set<PackageUserKey> mPendingPackages = new HashSet<>();
private boolean mItemsDeleted = false;
+ private String mDbName;
public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
ModelDelegate modelDelegate, LoaderResults results) {
@@ -274,12 +276,20 @@
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
loadFolderNames();
}
- sanitizeData();
+
+ // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
+ // sanitizeData should not be invoked if the workspace is loaded from a db different
+ // from the main db as defined in the invariant device profile.
+ // (e.g. both grid preview and minimal device mode uses a different db)
+ if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
+ sanitizeData();
+ }
verifyNotStopped();
updateHandler.finish();
logger.addSplit("finish icon update");
+ mModelDelegate.modelLoadComplete();
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
@@ -349,7 +359,9 @@
final LoaderCursor c = new LoaderCursor(
contentResolver.query(contentUri, null, selection, null, null), contentUri,
mApp, mUserManagerState);
-
+ final Bundle extras = c.getExtras();
+ mDbName = extras == null
+ ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME);
try {
final int appWidgetIdIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.APPWIDGET_ID);
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
index 3ed8809..92bea5b 100644
--- a/src/com/android/launcher3/model/ModelDelegate.java
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -78,6 +78,12 @@
public void workspaceLoadComplete() { }
/**
+ * Called at the end of model load task
+ */
+ @WorkerThread
+ public void modelLoadComplete() { }
+
+ /**
* Called when the delegate is no loner needed
*/
@WorkerThread
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index f4b059d..47d214d 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -275,7 +275,8 @@
launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra(
- "tutorial_type", "RIGHT_EDGE_BACK_NAVIGATION"));
+ "tutorial_steps",
+ new String[] {"RIGHT_EDGE_BACK_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchBackTutorialPreference);
@@ -284,7 +285,9 @@
launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent.putExtra("tutorial_type", "HOME_NAVIGATION"));
+ startActivity(launchSandboxIntent.putExtra(
+ "tutorial_steps",
+ new String[] {"HOME_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchHomeTutorialPreference);
@@ -293,7 +296,9 @@
launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION"));
+ startActivity(launchSandboxIntent.putExtra(
+ "tutorial_steps",
+ new String[] {"OVERVIEW_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchOverviewTutorialPreference);
@@ -302,7 +307,9 @@
launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent.putExtra("tutorial_type", "ASSISTANT"));
+ startActivity(launchSandboxIntent.putExtra(
+ "tutorial_steps",
+ new String[] {"ASSISTANT"}));
return true;
});
sandboxCategory.addPreference(launchAssistantTutorialPreference);
@@ -311,7 +318,9 @@
launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent.putExtra("tutorial_type", "SANDBOX_MODE"));
+ startActivity(launchSandboxIntent.putExtra(
+ "tutorial_steps",
+ new String[] {"SANDBOX_MODE"}));
return true;
});
sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 17f02be..ad6710f 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -42,13 +42,13 @@
public class LandscapePagedViewHandler implements PagedOrientationHandler {
@Override
- public int getPrimaryValue(int x, int y) {
- return y;
+ public <T> T getPrimaryValue(T x, T y) {
+ return x;
}
@Override
- public int getSecondaryValue(int x, int y) {
- return x;
+ public <T> T getSecondaryValue(T x, T y) {
+ return y;
}
@Override
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 114b75a..a9c50cd 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -84,8 +84,8 @@
boolean getRecentsRtlSetting(Resources resources);
float getDegreesRotated();
int getRotation();
- int getPrimaryValue(int x, int y);
- int getSecondaryValue(int x, int y);
+ <T> T getPrimaryValue(T x, T y);
+ <T> T getSecondaryValue(T x, T y);
void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll);
/** Uses {@params pagedView}.getScroll[X|Y]() method for the secondary amount*/
void delegateScrollTo(PagedView pagedView, int primaryScroll);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 5f5b2d1..587e35a 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -40,12 +40,12 @@
public class PortraitPagedViewHandler implements PagedOrientationHandler {
@Override
- public int getPrimaryValue(int x, int y) {
+ public <T> T getPrimaryValue(T x, T y) {
return x;
}
@Override
- public int getSecondaryValue(int x, int y) {
+ public <T> T getSecondaryValue(T x, T y) {
return y;
}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 355c949..3ab736a 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -31,6 +31,8 @@
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.Utilities;
+
import java.util.ArrayList;
/**
@@ -157,13 +159,13 @@
private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
private DisplayController.Info mInfo;
- private DisplayHolder(Context displayContext) {
+ private DisplayHolder(Context displayContext, Display display) {
mDisplayContext = displayContext;
// Note that the Display object must be obtained from DisplayManager which is
// associated to the display context, so the Display is isolated from Activity and
// Application to provide the actual state of device that excludes the additional
// adjustment and override.
- mInfo = new DisplayController.Info(mDisplayContext);
+ mInfo = new DisplayController.Info(display);
mId = mInfo.id;
}
@@ -180,22 +182,31 @@
}
protected void handleOnChange() {
+ Display display = Utilities.ATLEAST_R
+ ? mDisplayContext.getDisplay()
+ : mDisplayContext
+ .getSystemService(DisplayManager.class)
+ .getDisplay(mId);
+ if (display == null) {
+ return;
+ }
+
Info oldInfo = mInfo;
- Info info = new Info(mDisplayContext);
+ Info newInfo = new Info(display);
int change = 0;
- if (info.hasDifferentSize(oldInfo)) {
+ if (newInfo.hasDifferentSize(oldInfo)) {
change |= CHANGE_SIZE;
}
- if (oldInfo.rotation != info.rotation) {
+ if (newInfo.rotation != oldInfo.rotation) {
change |= CHANGE_ROTATION;
}
- if (info.singleFrameMs != oldInfo.singleFrameMs) {
+ if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
change |= CHANGE_FRAME_DELAY;
}
if (change != 0) {
- mInfo = info;
+ mInfo = newInfo;
final int flags = change;
MAIN_EXECUTOR.execute(() -> notifyChange(flags));
}
@@ -216,7 +227,7 @@
// Use application context to create display context so that it can have its own
// Resources.
Context displayContext = context.getApplicationContext().createDisplayContext(display);
- return new DisplayHolder(displayContext);
+ return new DisplayHolder(displayContext, display);
}
}
@@ -244,12 +255,7 @@
this.metrics = metrics;
}
- private Info(Context context) {
- this(context, context.getSystemService(DisplayManager.class)
- .getDisplay(DEFAULT_DISPLAY));
- }
-
- public Info(Context context, Display display) {
+ public Info(Display display) {
id = display.getDisplayId();
rotation = display.getRotation();
@@ -262,7 +268,8 @@
display.getRealSize(realSize);
display.getCurrentSizeRange(smallestSize, largestSize);
- metrics = context.getResources().getDisplayMetrics();
+ metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
}
private boolean hasDifferentSize(Info info) {
diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java
index 91337ba..a098df9 100644
--- a/src/com/android/launcher3/views/HeroSearchResultView.java
+++ b/src/com/android/launcher3/views/HeroSearchResultView.java
@@ -20,10 +20,8 @@
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.graphics.Point;
-import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.View;
@@ -37,8 +35,9 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
-import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
+import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider;
@@ -48,24 +47,28 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.launcher3.util.ComponentKey;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
+import java.util.ArrayList;
import java.util.List;
/**
* A view representing a high confidence app search result that includes shortcuts
+ * TODO (sfufa@) consolidate this with SearchResultIconRow
*/
-public class HeroSearchResultView extends LinearLayout implements DragSource,
- PayloadResultHandler<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> {
+public class HeroSearchResultView extends LinearLayout implements DragSource, SearchTargetHandler {
+
+ public static final String TARGET_TYPE_HERO_APP = "hero_app";
public static final int MAX_SHORTCUTS_COUNT = 2;
- private final Object[] mTargetInfo = createTargetInfo();
- BubbleTextView mBubbleTextView;
- View mIconView;
- BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
- AllAppsSearchPlugin mPlugin;
+
+ private SearchTarget mSearchTarget;
+ private BubbleTextView mBubbleTextView;
+ private View mIconView;
+ private BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
+
public HeroSearchResultView(Context context) {
super(context);
@@ -106,35 +109,39 @@
grid.allAppsIconSizePx));
bubbleTextView.setOnClickListener(view -> {
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag();
- SearchTargetEvent event = getSearchTargetEvent(
- SearchTarget.ItemType.APP_HERO,
- SearchTargetEvent.CHILD_SELECT);
- event.bundle = getAppBundle(itemInfo);
- event.bundle.putString("shortcut_id", itemInfo.getDeepShortcutId());
- if (mPlugin != null) {
- mPlugin.notifySearchTargetEvent(event);
- }
+ SearchTargetEvent event = new SearchTargetEvent.Builder(mSearchTarget,
+ SearchTargetEvent.CHILD_SELECT).setShortcutPosition(itemInfo.rank).build();
+ SearchEventTracker.getInstance(getContext()).notifySearchTargetEvent(event);
launcher.getItemOnClickListener().onClick(view);
});
}
}
- /**
- * Apply {@link ItemInfo} for appIcon and shortcut Icons
- */
@Override
- public void applyAdapterInfo(
- AdapterItemWithPayload<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> adapterItem) {
- mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
+ public void applySearchTarget(SearchTarget searchTarget) {
+ mSearchTarget = searchTarget;
+ AllAppsStore apps = Launcher.getLauncher(getContext()).getAppsView().getAppsStore();
+ AppInfo appInfo = apps.getApp(new ComponentKey(searchTarget.getComponentName(),
+ searchTarget.getUserHandle()));
+ List<ShortcutInfo> infos = mSearchTarget.getShortcutInfos();
+
+ ArrayList<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcuts = new ArrayList<>();
+ for (int i = 0; infos != null && i < infos.size() && i < MAX_SHORTCUTS_COUNT; i++) {
+ ShortcutInfo shortcutInfo = infos.get(i);
+ ItemInfoWithIcon si = new WorkspaceItemInfo(shortcutInfo, getContext());
+ si.rank = i;
+ shortcuts.add(new Pair<>(shortcutInfo, si));
+ }
+
+ mBubbleTextView.applyFromApplicationInfo(appInfo);
mIconView.setBackground(mBubbleTextView.getIcon());
- mIconView.setTag(adapterItem.appInfo);
- List<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcutDetails = adapterItem.getPayload();
+ mIconView.setTag(appInfo);
LauncherAppState appState = LauncherAppState.getInstance(getContext());
for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
BubbleTextView shortcutView = mDeepShortcutTextViews[i];
- mDeepShortcutTextViews[i].setVisibility(shortcutDetails.size() > i ? VISIBLE : GONE);
- if (i < shortcutDetails.size()) {
- Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcutDetails.get(i);
+ mDeepShortcutTextViews[i].setVisibility(shortcuts.size() > i ? VISIBLE : GONE);
+ if (i < shortcuts.size()) {
+ Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcuts.get(i);
//apply ItemInfo and prepare view
shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second);
MODEL_EXECUTOR.execute(() -> {
@@ -144,13 +151,7 @@
});
}
}
- mPlugin = adapterItem.getPlugin();
- adapterItem.setSelectionHandler(this::handleSelection);
- }
-
- @Override
- public Object[] getTargetInfo() {
- return mTargetInfo;
+ SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
}
@Override
@@ -188,38 +189,21 @@
mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView,
draggableView, mContainer, itemInfo, previewProvider, new DragOptions());
- SearchTargetEvent event = mContainer.getSearchTargetEvent(
- SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS);
- event.bundle = getAppBundle(itemInfo);
- if (mContainer.mPlugin != null) {
- mContainer.mPlugin.notifySearchTargetEvent(event);
- }
-
+ SearchTargetEvent event = new SearchTargetEvent.Builder(mContainer.mSearchTarget,
+ SearchTargetEvent.LONG_PRESS).build();
+ SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(event);
return false;
}
}
- private void handleSelection(int eventType) {
+ @Override
+ public void handleSelection(int eventType) {
ItemInfo itemInfo = (ItemInfo) mBubbleTextView.getTag();
if (itemInfo == null) return;
Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo);
- SearchTargetEvent event = getSearchTargetEvent(
- SearchTarget.ItemType.APP_HERO, eventType);
- event.bundle = getAppBundle(itemInfo);
- if (mPlugin != null) {
- mPlugin.notifySearchTargetEvent(event);
- }
- }
-
- /**
- * Helper method to generate {@link SearchTargetEvent} bundle from {@link ItemInfo}
- */
- public static Bundle getAppBundle(ItemInfo itemInfo) {
- Bundle b = new Bundle();
- b.putParcelable(Intent.EXTRA_COMPONENT_NAME, itemInfo.getTargetComponent());
- b.putParcelable(Intent.EXTRA_USER, itemInfo.user);
- return b;
+ SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
}
}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 15ff2f5..77cec80 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -42,7 +42,7 @@
*/
public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener {
- private static final float SCRIM_ALPHA = .8f;
+ private static final float SCRIM_ALPHA = .75f;
protected final T mLauncher;
private final WallpaperColorInfo mWallpaperColorInfo;
protected final int mEndScrim;
diff --git a/src/com/android/launcher3/views/SearchResultIcon.java b/src/com/android/launcher3/views/SearchResultIcon.java
new file mode 100644
index 0000000..ea06d5b
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchResultIcon.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.allapps.search.SearchEventTracker;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.util.ComponentKey;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+/**
+ * A {@link BubbleTextView} representing a single cell result in AllApps
+ */
+public class SearchResultIcon extends BubbleTextView implements
+ AllAppsSearchBarController.SearchTargetHandler, View.OnClickListener,
+ View.OnLongClickListener {
+
+
+ public static final String TARGET_TYPE_APP = "app";
+
+ private final Launcher mLauncher;
+
+ private SearchTarget mSearchTarget;
+
+ public SearchResultIcon(Context context) {
+ this(context, null, 0);
+ }
+
+ public SearchResultIcon(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SearchResultIcon(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mLauncher = Launcher.getLauncher(getContext());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ setLongPressTimeoutFactor(1f);
+ setOnFocusChangeListener(mLauncher.getFocusHandler());
+ setOnClickListener(this);
+ setOnLongClickListener(this);
+ getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
+ }
+
+ @Override
+ public void applySearchTarget(SearchTarget searchTarget) {
+ mSearchTarget = searchTarget;
+ AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
+ SearchEventTracker.getInstance(getContext()).registerWeakHandler(mSearchTarget, this);
+ if (searchTarget.getItemType().equals(TARGET_TYPE_APP)) {
+ AppInfo appInfo = appsStore.getApp(new ComponentKey(searchTarget.getComponentName(),
+ searchTarget.getUserHandle()));
+ applyFromApplicationInfo(appInfo);
+ }
+ }
+
+ @Override
+ public void handleSelection(int eventType) {
+ mLauncher.getItemOnClickListener().onClick(this);
+ SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
+ }
+
+ @Override
+ public void onClick(View view) {
+ handleSelection(SearchTargetEvent.SELECT);
+ }
+
+ @Override
+ public boolean onLongClick(View view) {
+ SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget, SearchTargetEvent.LONG_PRESS).build());
+ return ItemLongClickListener.INSTANCE_ALL_APPS.onLongClick(view);
+ }
+}
diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java
index 6d9c86a..bdbe890 100644
--- a/src/com/android/launcher3/views/SearchResultIconRow.java
+++ b/src/com/android/launcher3/views/SearchResultIconRow.java
@@ -23,9 +23,9 @@
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.content.res.TypedArray;
+import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Build;
-import android.os.Bundle;
import android.util.AttributeSet;
import android.widget.EditText;
@@ -35,8 +35,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
@@ -44,25 +44,30 @@
import com.android.launcher3.model.data.RemoteActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.launcher3.util.Themes;
import com.android.systemui.plugins.shared.SearchTarget;
-import com.android.systemui.plugins.shared.SearchTarget.ItemType;
import com.android.systemui.plugins.shared.SearchTargetEvent;
/**
* A view representing a stand alone shortcut search result
*/
public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
- AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
+ AllAppsSearchBarController.SearchTargetHandler {
- private final Object[] mTargetInfo = createTargetInfo();
- private final int mCustomIconResId;
+
+ public static final String TARGET_TYPE_REMOTE_ACTION = "remote_action";
+ public static final String TARGET_TYPE_SUGGEST = "suggest";
+ public static final String TARGET_TYPE_SHORTCUT = "shortcut";
+
+
+ public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result";
+ public static final String REMOTE_ACTION_TOKEN = "action_token";
+
private final boolean mMatchesInset;
- private ShortcutInfo mShortcutInfo;
- private AllAppsSearchPlugin mPlugin;
- private AdapterItemWithPayload<SearchTarget> mAdapterItem;
+ private SearchTarget mSearchTarget;
+ @Nullable private Drawable mCustomIcon;
public SearchResultIconRow(@NonNull Context context) {
this(context, null, 0);
@@ -78,10 +83,15 @@
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SearchResultIconRow, defStyleAttr, 0);
- mCustomIconResId = a.getResourceId(R.styleable.SearchResultIconRow_customIcon, 0);
mMatchesInset = a.getBoolean(R.styleable.SearchResultIconRow_matchTextInsetWithQuery,
false);
+ int customIconResId = a.getResourceId(R.styleable.SearchResultIconRow_customIcon, 0);
+
+ if (customIconResId != 0) {
+ mCustomIcon = Launcher.getLauncher(context).getDrawable(customIconResId);
+ }
+
a.recycle();
}
@@ -100,36 +110,38 @@
}
}
+ @Override
+ protected void drawFocusHighlight(Canvas canvas) {
+ mHighlightPaint.setColor(mHighlightColor);
+ float r = Themes.getDialogCornerRadius(getContext());
+ canvas.drawRoundRect(0, 0, getWidth(), getHeight(), r, r, mHighlightPaint);
+ }
+
@Override
- public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
- if (mAdapterItem != null) {
- mAdapterItem.setSelectionHandler(null);
- }
- mAdapterItem = adapterItemWithPayload;
- SearchTarget payload = adapterItemWithPayload.getPayload();
- mPlugin = adapterItemWithPayload.getPlugin();
+ public void applySearchTarget(SearchTarget searchTarget) {
+ mSearchTarget = searchTarget;
+ String type = searchTarget.getItemType();
+ if (type.equals(TARGET_TYPE_REMOTE_ACTION) || type.equals(TARGET_TYPE_SUGGEST)) {
+ prepareUsingRemoteAction(searchTarget.getRemoteAction(),
+ searchTarget.getExtras().getString(REMOTE_ACTION_TOKEN),
+ searchTarget.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START),
+ type.equals(TARGET_TYPE_REMOTE_ACTION));
- if (payload.mRemoteAction != null) {
- prepareUsingRemoteAction(payload.mRemoteAction,
- payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
- payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START),
- payload.type == ItemType.ACTION);
- } else {
- prepareUsingShortcutInfo(payload.shortcuts.get(0));
+ } else if (type.equals(TARGET_TYPE_SHORTCUT)) {
+ prepareUsingShortcutInfo(searchTarget.getShortcutInfos().get(0));
}
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
- adapterItemWithPayload.setSelectionHandler(this::handleSelection);
+ SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
}
private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
- mShortcutInfo = shortcutInfo;
- WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
+ WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext());
applyFromWorkspaceItem(workspaceItemInfo);
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
if (!loadIconFromResource()) {
MODEL_EXECUTOR.execute(() -> {
- launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
+ launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, shortcutInfo);
reapplyItemInfoAsync(workspaceItemInfo);
});
}
@@ -165,43 +177,28 @@
}
private boolean loadIconFromResource() {
- if (mCustomIconResId == 0) return false;
- setIcon(Launcher.getLauncher(getContext()).getDrawable(mCustomIconResId));
+ if (mCustomIcon == null) return false;
+ setIcon(mCustomIcon);
return true;
}
void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
- MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon));
+ MAIN_EXECUTOR.post(() -> {
+ reapplyItemInfo(itemInfoWithIcon);
+ mCustomIcon = getIcon();
+ });
}
@Override
- public Object[] getTargetInfo() {
- return mTargetInfo;
- }
-
- private void handleSelection(int eventType) {
+ public void handleSelection(int eventType) {
ItemInfo itemInfo = (ItemInfo) getTag();
Launcher launcher = Launcher.getLauncher(getContext());
- final SearchTargetEvent searchTargetEvent;
if (itemInfo instanceof WorkspaceItemInfo) {
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
- searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.SHORTCUT,
- eventType);
- searchTargetEvent.shortcut = mShortcutInfo;
} else {
- RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
- ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
- searchTargetEvent = getSearchTargetEvent(ItemType.ACTION,
- eventType);
- searchTargetEvent.bundle = new Bundle();
- searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
- searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
- remoteItemInfo.shouldStartInLauncher());
- searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
- remoteItemInfo.getToken());
+ ItemClickHandler.onClickRemoteAction(launcher, (RemoteActionItemInfo) itemInfo);
}
- if (mPlugin != null) {
- mPlugin.notifySearchTargetEvent(searchTargetEvent);
- }
+ SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
}
}
diff --git a/src/com/android/launcher3/views/SearchResultPeopleView.java b/src/com/android/launcher3/views/SearchResultPeopleView.java
index f20b080..e499bd5 100644
--- a/src/com/android/launcher3/views/SearchResultPeopleView.java
+++ b/src/com/android/launcher3/views/SearchResultPeopleView.java
@@ -42,11 +42,10 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
@@ -56,7 +55,9 @@
* A view representing a single people search result in all apps
*/
public class SearchResultPeopleView extends LinearLayout implements
- AllAppsSearchBarController.PayloadResultHandler<Bundle> {
+ AllAppsSearchBarController.SearchTargetHandler {
+
+ public static final String TARGET_TYPE_PEOPLE = "people";
private final int mIconSize;
private final int mButtonSize;
@@ -64,9 +65,10 @@
private View mIconView;
private TextView mTitleView;
private ImageButton[] mProviderButtons = new ImageButton[3];
- private AllAppsSearchPlugin mPlugin;
private Intent mIntent;
- private final Object[] mTargetInfo = createTargetInfo();
+
+
+ private SearchTarget mSearchTarget;
public SearchResultPeopleView(Context context) {
this(context, null, 0);
@@ -103,10 +105,9 @@
}
@Override
- public void applyAdapterInfo(
- AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
- Bundle payload = adapterItemWithPayload.getPayload();
- mPlugin = adapterItemWithPayload.getPlugin();
+ public void applySearchTarget(SearchTarget searchTarget) {
+ mSearchTarget = searchTarget;
+ Bundle payload = searchTarget.getExtras();
mTitleView.setText(payload.getString("title"));
mIntent = payload.getParcelable("intent");
Bitmap contactIcon = payload.getParcelable("icon");
@@ -125,7 +126,7 @@
if (providers != null && i < providers.size()) {
Bundle provider = providers.get(i);
Intent intent = provider.getParcelable("intent");
- setupProviderButton(button, provider, intent, adapterItemWithPayload);
+ setupProviderButton(button, provider, intent);
UI_HELPER_EXECUTOR.post(() -> {
String pkg = provider.getString("package_name");
Drawable appIcon = getAppIcon(pkg);
@@ -138,13 +139,13 @@
button.setVisibility(GONE);
}
}
- adapterItemWithPayload.setSelectionHandler(this::handleSelection);
+ SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
}
/**
- * Normalizes the bitmap to look like rounded App Icon
- * TODO(b/170234747) to support styling, generate adaptive icon drawable and generate
- * bitmap from it.
+ * Normalizes the bitmap to look like rounded App Icon
+ * TODO(b/170234747) to support styling, generate adaptive icon drawable and generate
+ * bitmap from it.
*/
private Bitmap roundBitmap(Bitmap icon) {
final RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
@@ -180,42 +181,25 @@
}
}
- @Override
- public Object[] getTargetInfo() {
- return mTargetInfo;
- }
-
- private void setupProviderButton(ImageButton button, Bundle provider, Intent intent,
- AllAppsGridAdapter.AdapterItem adapterItem) {
+ private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) {
Launcher launcher = Launcher.getLauncher(getContext());
button.setOnClickListener(b -> {
launcher.startActivitySafely(b, intent, null);
- SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
- SearchTarget.ItemType.PEOPLE,
- SearchTargetEvent.CHILD_SELECT);
- searchTargetEvent.bundle = new Bundle();
- searchTargetEvent.bundle.putParcelable("intent", intent);
- searchTargetEvent.bundle.putString("title", mTitleView.getText().toString());
- searchTargetEvent.bundle.putBundle("provider", provider);
- if (mPlugin != null) {
- mPlugin.notifySearchTargetEvent(searchTargetEvent);
- }
+ Bundle bundle = new Bundle();
+ bundle.putBundle("provider", provider);
+ SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget,
+ SearchTargetEvent.CHILD_SELECT).setExtras(bundle).build());
});
}
-
- private void handleSelection(int eventType) {
+ @Override
+ public void handleSelection(int eventType) {
if (mIntent != null) {
Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivitySafely(this, mIntent, null);
- SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE,
- eventType);
- searchTargetEvent.bundle = new Bundle();
- searchTargetEvent.bundle.putParcelable("intent", mIntent);
- searchTargetEvent.bundle.putString("title", mTitleView.getText().toString());
- if (mPlugin != null) {
- mPlugin.notifySearchTargetEvent(searchTargetEvent);
- }
+ SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
}
}
}
diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java
index c7133fd..86ed436 100644
--- a/src/com/android/launcher3/views/SearchResultPlayItem.java
+++ b/src/com/android/launcher3/views/SearchResultPlayItem.java
@@ -41,11 +41,10 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.util.Themes;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
@@ -57,10 +56,13 @@
* A View representing a PlayStore item.
*/
public class SearchResultPlayItem extends LinearLayout implements
- AllAppsSearchBarController.PayloadResultHandler<Bundle> {
+ AllAppsSearchBarController.SearchTargetHandler {
+
+ public static final String TARGET_TYPE_PLAY = "play";
private static final int BITMAP_CROP_MASK_COLOR = 0xff424242;
-
+ final Paint mIconPaint = new Paint();
+ final Rect mTempRect = new Rect();
private final DeviceProfile mDeviceProfile;
private View mIconView;
private TextView mTitleView;
@@ -68,11 +70,8 @@
private Button mPreviewButton;
private String mPackageName;
private boolean mIsInstantGame;
- private AllAppsSearchPlugin mPlugin;
- private final Object[] mTargetInfo = createTargetInfo();
- final Paint mIconPaint = new Paint();
- final Rect mTempRect = new Rect();
+ private SearchTarget mSearchTarget;
public SearchResultPlayItem(Context context) {
@@ -105,14 +104,35 @@
iconParams.height = mDeviceProfile.allAppsIconSizePx;
iconParams.width = mDeviceProfile.allAppsIconSizePx;
setOnClickListener(view -> handleSelection(SearchTargetEvent.SELECT));
-
}
+
+ private Bitmap getRoundedBitmap(Bitmap bitmap) {
+ final int iconSize = bitmap.getWidth();
+ final float radius = Themes.getDialogCornerRadius(getContext());
+
+ Bitmap output = BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> {
+ mTempRect.set(0, 0, iconSize, iconSize);
+ final RectF rectF = new RectF(mTempRect);
+
+ mIconPaint.setAntiAlias(true);
+ mIconPaint.reset();
+ canvas.drawARGB(0, 0, 0, 0);
+ mIconPaint.setColor(BITMAP_CROP_MASK_COLOR);
+ canvas.drawRoundRect(rectF, radius, radius, mIconPaint);
+
+ mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint);
+ });
+ return output;
+ }
+
+
@Override
- public void applyAdapterInfo(AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
- Bundle bundle = adapterItemWithPayload.getPayload();
- mPlugin = adapterItemWithPayload.getPlugin();
- adapterItemWithPayload.setSelectionHandler(this::handleSelection);
+ public void applySearchTarget(SearchTarget searchTarget) {
+ mSearchTarget = searchTarget;
+ Bundle bundle = searchTarget.getExtras();
+ SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
if (bundle.getString("package", "").equals(mPackageName)) {
return;
}
@@ -143,33 +163,6 @@
});
}
-
- private Bitmap getRoundedBitmap(Bitmap bitmap) {
- final int iconSize = bitmap.getWidth();
- final float radius = Themes.getDialogCornerRadius(getContext());
-
- Bitmap output = BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> {
- mTempRect.set(0, 0, iconSize, iconSize);
- final RectF rectF = new RectF(mTempRect);
-
- mIconPaint.setAntiAlias(true);
- mIconPaint.reset();
- canvas.drawARGB(0, 0, 0, 0);
- mIconPaint.setColor(BITMAP_CROP_MASK_COLOR);
- canvas.drawRoundRect(rectF, radius, radius, mIconPaint);
-
- mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint);
- });
- return output;
- }
-
-
- @Override
- public Object[] getTargetInfo() {
- return mTargetInfo;
- }
-
private void showIfNecessary(TextView textView, @Nullable String string) {
if (string == null || string.isEmpty()) {
textView.setVisibility(GONE);
@@ -179,7 +172,8 @@
}
}
- private void handleSelection(int eventType) {
+ @Override
+ public void handleSelection(int eventType) {
if (mPackageName == null) return;
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(
"https://play.google.com/store/apps/details?id="
@@ -205,12 +199,7 @@
}
private void logSearchEvent(int eventType) {
- SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
- SearchTarget.ItemType.PLAY_RESULTS, eventType);
- searchTargetEvent.bundle = new Bundle();
- searchTargetEvent.bundle.putString("package_name", mPackageName);
- if (mPlugin != null) {
- mPlugin.notifySearchTargetEvent(searchTargetEvent);
- }
+ SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
}
}
diff --git a/src/com/android/launcher3/views/SearchSectionHeaderView.java b/src/com/android/launcher3/views/SearchSectionHeaderView.java
index 0fe0a43..326c23d 100644
--- a/src/com/android/launcher3/views/SearchSectionHeaderView.java
+++ b/src/com/android/launcher3/views/SearchSectionHeaderView.java
@@ -21,14 +21,16 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.systemui.plugins.shared.SearchTarget;
/**
* Header text view that shows a title for a given section in All apps search
*/
public class SearchSectionHeaderView extends TextView implements
- AllAppsSearchBarController.PayloadResultHandler<String> {
+ AllAppsSearchBarController.SearchTargetHandler {
+ public static final String TARGET_TYPE_SECTION_HEADER = "section_header";
+
public SearchSectionHeaderView(Context context) {
super(context);
}
@@ -43,8 +45,8 @@
}
@Override
- public void applyAdapterInfo(AllAppsGridAdapter.AdapterItemWithPayload<String> adapterItem) {
- String title = adapterItem.getPayload();
+ public void applySearchTarget(SearchTarget searchTarget) {
+ String title = searchTarget.getExtras().getString("title");
if (title == null || !title.isEmpty()) {
setText(title);
setVisibility(VISIBLE);
@@ -52,9 +54,4 @@
setVisibility(INVISIBLE);
}
}
-
- @Override
- public Object[] getTargetInfo() {
- return null;
- }
}
diff --git a/src/com/android/launcher3/views/SearchSettingsRowView.java b/src/com/android/launcher3/views/SearchSettingsRowView.java
index a1a0172..30f686c 100644
--- a/src/com/android/launcher3/views/SearchSettingsRowView.java
+++ b/src/com/android/launcher3/views/SearchSettingsRowView.java
@@ -29,9 +29,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
@@ -41,14 +40,16 @@
* A row of tappable TextViews with a breadcrumb for settings search.
*/
public class SearchSettingsRowView extends LinearLayout implements
- View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler<Bundle> {
+ View.OnClickListener, AllAppsSearchBarController.SearchTargetHandler {
+
+ public static final String TARGET_TYPE_SETTINGS_ROW = "settings_row";
+
private TextView mTitleView;
private TextView mDescriptionView;
private TextView mBreadcrumbsView;
private Intent mIntent;
- private AllAppsSearchPlugin mPlugin;
- private final Object[] mTargetInfo = createTargetInfo();
+ private SearchTarget mSearchTarget;
public SearchSettingsRowView(@NonNull Context context) {
@@ -75,10 +76,9 @@
}
@Override
- public void applyAdapterInfo(
- AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
- Bundle bundle = adapterItemWithPayload.getPayload();
- mPlugin = adapterItemWithPayload.getPlugin();
+ public void applySearchTarget(SearchTarget searchTarget) {
+ mSearchTarget = searchTarget;
+ Bundle bundle = searchTarget.getExtras();
mIntent = bundle.getParcelable("intent");
showIfAvailable(mTitleView, bundle.getString("title"));
showIfAvailable(mDescriptionView, bundle.getString("description"));
@@ -86,12 +86,7 @@
//TODO: implement RTL friendly breadcrumbs view
showIfAvailable(mBreadcrumbsView, breadcrumbs != null
? String.join(" > ", breadcrumbs) : null);
- adapterItemWithPayload.setSelectionHandler(this::handleSelection);
- }
-
- @Override
- public Object[] getTargetInfo() {
- return mTargetInfo;
+ SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
}
private void showIfAvailable(TextView view, @Nullable String string) {
@@ -108,19 +103,15 @@
handleSelection(SearchTargetEvent.SELECT);
}
- private void handleSelection(int eventType) {
+ @Override
+ public void handleSelection(int eventType) {
if (mIntent == null) return;
// TODO: create ItemInfo object and then use it to call startActivityForResult for proper
// WW logging
Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivityForResult(mIntent, 0);
- SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
- SearchTarget.ItemType.SETTINGS_ROW, eventType);
- searchTargetEvent.bundle = new Bundle();
- searchTargetEvent.bundle.putParcelable("intent", mIntent);
- if (mPlugin != null) {
- mPlugin.notifySearchTargetEvent(searchTargetEvent);
- }
+ SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
}
}
diff --git a/src/com/android/launcher3/views/SearchSliceWrapper.java b/src/com/android/launcher3/views/SearchSliceWrapper.java
new file mode 100644
index 0000000..f8a7dc0
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchSliceWrapper.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.widget.EventInfo;
+import androidx.slice.widget.SliceLiveData;
+import androidx.slice.widget.SliceView;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.allapps.search.SearchEventTracker;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+/**
+ * A Wrapper class for {@link SliceView} search results
+ */
+public class SearchSliceWrapper implements SliceView.OnSliceActionListener {
+
+ public static final String TARGET_TYPE_SLICE = "settings_slice";
+
+ private static final String TAG = "SearchSliceController";
+ private static final String URI_EXTRA_KEY = "slice_uri";
+
+
+ private final Launcher mLauncher;
+ private final SearchTarget mSearchTarget;
+ private final SliceView mSliceView;
+ private LiveData<Slice> mSliceLiveData;
+
+ public SearchSliceWrapper(Context context, SliceView sliceView, SearchTarget searchTarget) {
+ mLauncher = Launcher.getLauncher(context);
+ mSearchTarget = searchTarget;
+ mSliceView = sliceView;
+ sliceView.setOnSliceActionListener(this);
+ try {
+ mSliceLiveData = SliceLiveData.fromUri(mLauncher, getSliceUri());
+ mSliceLiveData.observe((Launcher) mLauncher, sliceView);
+ } catch (Exception ex) {
+ Log.e(TAG, "unable to bind slice", ex);
+ }
+ }
+
+ /**
+ * Unregisters event handlers and removes lifecycle observer
+ */
+ public void destroy() {
+ mSliceView.setOnSliceActionListener(null);
+ mSliceLiveData.removeObservers(mLauncher);
+ }
+
+ @Override
+ public void onSliceAction(@NonNull EventInfo info, @NonNull SliceItem item) {
+ SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget,
+ SearchTargetEvent.CHILD_SELECT).build());
+ }
+
+ private Uri getSliceUri() {
+ return mSearchTarget.getExtras().getParcelable(URI_EXTRA_KEY);
+ }
+}
diff --git a/src/com/android/launcher3/views/ThumbnailSearchResultView.java b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
index 81bcad9..e929d7f 100644
--- a/src/com/android/launcher3/views/ThumbnailSearchResultView.java
+++ b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.views;
+import static com.android.launcher3.views.SearchResultIconRow.REMOTE_ACTION_SHOULD_START;
+import static com.android.launcher3.views.SearchResultIconRow.REMOTE_ACTION_TOKEN;
+
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -26,14 +29,13 @@
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.android.launcher3.Launcher;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.RemoteActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Themes;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
@@ -41,11 +43,12 @@
* A view representing a high confidence app search result that includes shortcuts
*/
public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView
- implements AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
+ implements AllAppsSearchBarController.SearchTargetHandler {
- private final Object[] mTargetInfo = createTargetInfo();
- AllAppsSearchPlugin mPlugin;
- int mPosition;
+ public static final String TARGET_TYPE_SCREENSHOT = "screenshot";
+ public static final String TARGET_TYPE_SCREENSHOT_LEGACY = "screenshot_legacy";
+
+ private SearchTarget mSearchTarget;
public ThumbnailSearchResultView(Context context) {
super(context);
@@ -59,7 +62,8 @@
super(context, attrs, defStyleAttr);
}
- private void handleSelection(int eventType) {
+ @Override
+ public void handleSelection(int eventType) {
Launcher launcher = Launcher.getLauncher(getContext());
ItemInfo itemInfo = (ItemInfo) getTag();
if (itemInfo instanceof RemoteActionItemInfo) {
@@ -68,36 +72,30 @@
} else {
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
}
- if (mPlugin != null) {
- SearchTargetEvent event = getSearchTargetEvent(
- SearchTarget.ItemType.SCREENSHOT, eventType);
- mPlugin.notifySearchTargetEvent(event);
- }
+ SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
}
@Override
- public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItem) {
- Launcher launcher = Launcher.getLauncher(getContext());
- mPosition = adapterItem.position;
-
- SearchTarget target = adapterItem.getPayload();
+ public void applySearchTarget(SearchTarget target) {
+ mSearchTarget = target;
Bitmap bitmap;
- if (target.mRemoteAction != null) {
- RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.mRemoteAction,
- target.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
- target.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
- bitmap = ((BitmapDrawable) target.mRemoteAction.getIcon()
- .loadDrawable(getContext())).getBitmap();
- Bitmap crop = Bitmap.createBitmap(bitmap, 0,
+ if (target.getRemoteAction() != null) {
+ RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.getRemoteAction(),
+ target.getExtras().getString(REMOTE_ACTION_TOKEN),
+ target.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START));
+ bitmap = ((BitmapDrawable) target.getRemoteAction().getIcon()
+ .loadDrawable(getContext())).getBitmap();
+ // crop
+ bitmap = Bitmap.createBitmap(bitmap, 0,
bitmap.getHeight() / 2 - bitmap.getWidth() / 2,
bitmap.getWidth(), bitmap.getWidth());
- bitmap = crop;
setTag(itemInfo);
} else {
- bitmap = (Bitmap) target.bundle.getParcelable("bitmap");
+ bitmap = (Bitmap) target.getExtras().getParcelable("bitmap");
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
itemInfo.intent = new Intent(Intent.ACTION_VIEW)
- .setData(Uri.parse(target.bundle.getString("uri")))
+ .setData(Uri.parse(target.getExtras().getString("uri")))
.setType("image/*")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
setTag(itemInfo);
@@ -106,12 +104,6 @@
drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
setImageDrawable(drawable);
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
- mPlugin = adapterItem.getPlugin();
- adapterItem.setSelectionHandler(this::handleSelection);
- }
-
- @Override
- public Object[] getTargetInfo() {
- return mTargetInfo;
+ SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(target, this);
}
}
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
index aa3ab8f..5cc238d 100644
--- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
+++ b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
@@ -17,6 +17,8 @@
package com.android.systemui.plugins;
import android.app.Activity;
+import android.os.Bundle;
+import android.os.CancellationSignal;
import android.view.View;
import com.android.systemui.plugins.annotations.ProvidesInterface;
@@ -32,7 +34,7 @@
@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
public interface AllAppsSearchPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
- int VERSION = 7;
+ int VERSION = 8;
void setup(Activity activity, View view);
@@ -49,10 +51,21 @@
void onWindowVisibilityChanged(int visibility);
/**
- * Send signal when user starts typing, perform search, when search ends
+ * Send signal when user starts typing, perform search, notify search target
+ * event when search ends.
*/
void startedSearchSession();
- void performSearch(String query, Consumer<List<SearchTarget>> results);
+
+ /**
+ * Main function that triggers search.
+ *
+ * @param input string that has been typed by a user
+ * @param inputArgs extra info that may be relevant for the input query
+ * @param results contains the result that will be rendered in all apps search surface
+ * @param cancellationSignal {@link CancellationSignal} can be used to share status of current
+ */
+ void query(String input, Bundle inputArgs, Consumer<List<SearchTarget>> results,
+ CancellationSignal cancellationSignal);
/**
* Send over search target interaction events to Plugin
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java b/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java
index 3f0dc39..2c7972e 100644
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java
+++ b/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java
@@ -16,8 +16,10 @@
package com.android.systemui.plugins.shared;
import android.app.RemoteAction;
+import android.content.ComponentName;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
+import android.os.UserHandle;
import java.util.List;
@@ -26,139 +28,145 @@
*/
public class SearchTarget implements Comparable<SearchTarget> {
+ private final String mItemId;
+ private final String mItemType;
+ private final float mScore;
- /**
- * A bundle key for boolean value of whether remote action should be started in launcher or not
- */
- public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result";
- public static final String REMOTE_ACTION_TOKEN = "action_token";
+ private final ComponentName mComponentName;
+ private final UserHandle mUserHandle;
+ private final List<ShortcutInfo> mShortcutInfos;
+ //TODO: (sfufa) replace with a list of a custom type
+ private final RemoteAction mRemoteAction;
+ private final Bundle mExtras;
-
- public enum ViewType {
-
- /**
- * Consists of N number of icons. (N: launcher column count)
- */
- TOP_HIT(0),
-
- /**
- * Consists of 1 icon and two subsidiary icons.
- */
- HERO(1),
-
- /**
- * Main/sub/breadcrumb texts are rendered.
- */
- DETAIL(2),
-
- /**
- * Consists of an icon, three detail strings.
- */
- ROW(3),
-
- /**
- * Consists of an icon, three detail strings and a button.
- */
- ROW_WITH_BUTTON(4),
-
- /**
- * Consists of a single slice view
- */
- SLICE(5),
-
- /**
- * Similar to hero section.
- */
- SHORTCUT(6),
-
- /**
- * Person icon and handling app icons are rendered.
- */
- PEOPLE(7),
-
- /**
- * N number of 1x1 ratio thumbnail is rendered.
- * (current N = 3)
- */
- THUMBNAIL(8),
-
- /**
- * Fallback search icon and relevant text is rendered.
- */
- SUGGEST(9);
-
- private final int mId;
-
- ViewType(int id) {
- mId = id;
- }
-
- public int get() {
- return mId;
- }
+ private SearchTarget(String itemId, String itemType, float score,
+ ComponentName componentName, UserHandle userHandle, List<ShortcutInfo> shortcutInfos,
+ RemoteAction remoteAction, Bundle extras) {
+ mItemId = itemId;
+ mItemType = itemType;
+ mScore = score;
+ mComponentName = componentName;
+ mUserHandle = userHandle;
+ mShortcutInfos = shortcutInfos;
+ mExtras = extras;
+ mRemoteAction = remoteAction;
}
- public enum ItemType {
- PLAY_RESULTS(0, "Play Store", ViewType.DETAIL),
- SETTINGS_ROW(1, "Settings", ViewType.ROW),
- SETTINGS_SLICE(2, "Settings", ViewType.SLICE),
- APP(3, "", ViewType.TOP_HIT),
- APP_HERO(4, "", ViewType.HERO),
- SHORTCUT(5, "Shortcuts", ViewType.SHORTCUT),
- PEOPLE(6, "People", ViewType.PEOPLE),
- SCREENSHOT(7, "Screenshots", ViewType.THUMBNAIL),
- ACTION(8, "Actions", ViewType.SHORTCUT),
- SUGGEST(9, "Fallback Search", ViewType.SUGGEST),
- CHROME_TAB(10, "Chrome Tab", ViewType.SHORTCUT);
-
- private final int mId;
-
- /** Used to render section title. */
- private final String mTitle;
- private final ViewType mViewType;
-
- ItemType(int id, String title, ViewType type) {
- mId = id;
- mTitle = title;
- mViewType = type;
- }
-
- public ViewType getViewType() {
- return mViewType;
- }
-
- public String getTitle() {
- return mTitle;
- }
-
- public int getId() {
- return mId;
- }
+ public String getItemId() {
+ return mItemId;
}
- public ItemType type;
- public List<ShortcutInfo> shortcuts;
- public Bundle bundle;
- public float score;
- public String mSessionId;
- public RemoteAction mRemoteAction;
+ public String getItemType() {
+ return mItemType;
+ }
- /**
- * Constructor to create the search target. Bundle is currently temporary to hold
- * search target primitives that cannot be expressed as java primitive objects
- * or AOSP native objects.
- */
- public SearchTarget(ItemType itemType, List<ShortcutInfo> shortcuts,
- Bundle bundle, float score, String sessionId) {
- this.type = itemType;
- this.shortcuts = shortcuts;
- this.bundle = bundle;
- this.score = score;
- this.mSessionId = sessionId;
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ public UserHandle getUserHandle() {
+ return mUserHandle;
+ }
+
+ public float getScore() {
+ return mScore;
+ }
+
+ public List<ShortcutInfo> getShortcutInfos() {
+ return mShortcutInfos;
+ }
+
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ public RemoteAction getRemoteAction() {
+ return mRemoteAction;
}
@Override
public int compareTo(SearchTarget o) {
- return Float.compare(o.score, score);
+ return Float.compare(o.mScore, mScore);
+ }
+
+ /**
+ * A builder for {@link SearchTarget}
+ */
+ public static final class Builder {
+
+
+ private String mItemId;
+
+ private final String mItemType;
+ private final float mScore;
+
+
+ private ComponentName mComponentName;
+ private UserHandle mUserHandle;
+ private List<ShortcutInfo> mShortcutInfos;
+ private Bundle mExtras;
+ private RemoteAction mRemoteAction;
+
+ public Builder(String itemType, float score) {
+ this(itemType, score, null, null);
+ }
+
+ public Builder(String itemType, float score, ComponentName cn,
+ UserHandle user) {
+ mItemType = itemType;
+ mScore = score;
+ mComponentName = cn;
+ mUserHandle = user;
+ }
+
+ public String getItemId() {
+ return mItemId;
+ }
+
+ public float getScore() {
+ return mScore;
+ }
+
+ public Builder setItemId(String itemId) {
+ mItemId = itemId;
+ return this;
+ }
+
+ public Builder setComponentName(ComponentName componentName) {
+ mComponentName = componentName;
+ return this;
+ }
+
+ public Builder setUserHandle(UserHandle userHandle) {
+ mUserHandle = userHandle;
+ return this;
+ }
+
+ public Builder setShortcutInfos(List<ShortcutInfo> shortcutInfos) {
+ mShortcutInfos = shortcutInfos;
+ return this;
+ }
+
+ public Builder setExtras(Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ public Builder setRemoteAction(RemoteAction remoteAction) {
+ mRemoteAction = remoteAction;
+ return this;
+ }
+
+ /**
+ * Builds a {@link SearchTarget}
+ */
+ public SearchTarget build() {
+ if (mItemId == null) {
+ throw new IllegalStateException("Item ID is required for building SearchTarget");
+ }
+ return new SearchTarget(mItemId, mItemType, mScore, mComponentName, mUserHandle,
+ mShortcutInfos,
+ mRemoteAction, mExtras);
+ }
}
}
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java
index 5016abc..290fe54 100644
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java
+++ b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java
@@ -15,32 +15,76 @@
*/
package com.android.systemui.plugins.shared;
-import android.app.RemoteAction;
-import android.content.pm.ShortcutInfo;
import android.os.Bundle;
/**
* Event used for the feedback loop to the plugin. (and future aiai)
*/
public class SearchTargetEvent {
+ public static final int POSITION_NONE = -1;
+
public static final int SELECT = 0;
public static final int QUICK_SELECT = 1;
public static final int LONG_PRESS = 2;
public static final int CHILD_SELECT = 3;
- public SearchTarget.ItemType type;
- public ShortcutInfo shortcut;
- public RemoteAction remoteAction;
- public int eventType;
- public Bundle bundle;
- public int index;
- public String sessionIdentifier;
+ private final SearchTarget mSearchTarget;
+ private final int mEventType;
+ private final int mShortcutPosition;
+ private final Bundle mExtras;
- public SearchTargetEvent(SearchTarget.ItemType itemType, int eventType, int index,
- String sessionId) {
- this.type = itemType;
- this.eventType = eventType;
- this.index = index;
- this.sessionIdentifier = sessionId;
+ public SearchTargetEvent(SearchTarget searchTarget, int eventType, int shortcutPosition,
+ Bundle extras) {
+ mSearchTarget = searchTarget;
+ mEventType = eventType;
+ mShortcutPosition = shortcutPosition;
+ mExtras = extras;
}
+
+
+ public SearchTarget getSearchTarget() {
+ return mSearchTarget;
+ }
+
+ public int getShortcutPosition() {
+ return mShortcutPosition;
+ }
+
+ public int getEventType() {
+ return mEventType;
+ }
+
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * A builder for {@link SearchTarget}
+ */
+ public static final class Builder {
+ private final SearchTarget mSearchTarget;
+ private final int mEventType;
+ private int mShortcutPosition = POSITION_NONE;
+ private Bundle mExtras;
+
+ public Builder(SearchTarget searchTarget, int eventType) {
+ mSearchTarget = searchTarget;
+ mEventType = eventType;
+ }
+
+ public Builder setShortcutPosition(int shortcutPosition) {
+ mShortcutPosition = shortcutPosition;
+ return this;
+ }
+
+ public Builder setExtras(Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ public SearchTargetEvent build() {
+ return new SearchTargetEvent(mSearchTarget, mEventType, mShortcutPosition, mExtras);
+ }
+ }
+
}