Snap for 6013487 from c5027ce20d5e86c62cb1d0a5cb9778af108e0334 to rvc-release
Change-Id: Idcfc6074032a1874a876ffec88816a4cf95f4eb0
diff --git a/Android.mk b/Android.mk
index 5def65f..3d1d996 100644
--- a/Android.mk
+++ b/Android.mk
@@ -200,7 +200,7 @@
$(LOCAL_PATH)/quickstep/recents_ui_overrides/res
LOCAL_FULL_LIBS_MANIFEST_FILES := \
- $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
+ $(LOCAL_PATH)/AndroidManifest.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
@@ -247,7 +247,7 @@
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/go/AndroidManifest.xml \
- $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
+ $(LOCAL_PATH)/AndroidManifest.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
@@ -293,7 +293,7 @@
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/go/AndroidManifest.xml \
- $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
+ $(LOCAL_PATH)/AndroidManifest.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
diff --git a/OWNERS b/OWNERS
index 7340e84..538ca33 100644
--- a/OWNERS
+++ b/OWNERS
@@ -10,7 +10,6 @@
sunnygoyal@google.com
twickham@google.com
winsonc@google.com
-zakcohen@google.com
per-file FeatureFlags.java = sunnygoyal@google.com, adamcohen@google.com
per-file BaseFlags.java = sunnygoyal@google.com, adamcohen@google.com
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index f84a82e..fae1eff 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -22,7 +22,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3" >
- <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="25"/>
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/go/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
deleted file mode 100644
index 0c60468..0000000
--- a/go/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
-import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
-import com.android.launcher3.util.TouchController;
-import com.android.quickstep.SysUINavigationMode;
-
-import java.util.ArrayList;
-
-public class QuickstepLauncher extends BaseQuickstepLauncher {
-
- public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
-
- @Override
- public TouchController[] createTouchControllers() {
- ArrayList<TouchController> list = new ArrayList<>();
- list.add(getDragController());
-
- if (getDeviceProfile().isVerticalBarLayout()) {
- list.add(new LandscapeStatesTouchController(this));
- list.add(new LandscapeEdgeSwipeController(this));
- } else {
- boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(this)
- .getMode().hasGestures;
- list.add(new PortraitStatesTouchController(this, allowDragToOverview));
- }
- return list.toArray(new TouchController[list.size()]);
- }
-}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
new file mode 100644
index 0000000..f2aa842
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.views.IconRecentsView;
+
+import java.util.ArrayList;
+
+/**
+ * Provides recents-related {@link UiFactory} logic and classes.
+ */
+public abstract class RecentsUiFactory {
+
+ public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
+
+ public static TouchController[] createTouchControllers(Launcher launcher) {
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(launcher.getDragController());
+
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new LandscapeStatesTouchController(launcher));
+ list.add(new LandscapeEdgeSwipeController(launcher));
+ } else {
+ boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(launcher)
+ .getMode().hasGestures;
+ list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));
+ }
+ if (Utilities.IS_DEBUG_DEVICE
+ && !launcher.getDeviceProfile().isMultiWindowMode
+ && !launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new StatusBarTouchController(launcher));
+ }
+ return list.toArray(new TouchController[list.size()]);
+ }
+
+ /**
+ * Creates and returns the controller responsible for recents view state transitions.
+ *
+ * @param launcher the launcher activity
+ * @return state handler for recents
+ */
+ public static StateHandler createRecentsViewStateController(Launcher launcher) {
+ return new RecentsViewStateController(launcher);
+ }
+
+ /**
+ * Clean-up logic that occurs when recents is no longer in use/visible.
+ *
+ * @param launcher the launcher activity
+ */
+ public static void resetOverview(Launcher launcher) {
+ IconRecentsView recentsView = launcher.getOverviewPanel();
+ recentsView.setTransitionedFromApp(false);
+ }
+
+ /**
+ * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
+ *
+ * @param launcher the launcher activity
+ */
+ public static void onLauncherStateOrResumeChanged(Launcher launcher) {}
+
+ public static RotationMode getRotationMode(DeviceProfile dp) {
+ return RotationMode.NORMAL;
+ }
+
+ public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) { }
+}
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 04753d2..6b50088 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -46,13 +46,13 @@
RemoteAnimationProvider {
private static final String TAG = "AppToOverviewAnimationProvider";
- private final BaseActivityInterface<T> mActivityInterface;
+ private final BaseActivityInterface<T> mHelper;
private final int mTargetTaskId;
private IconRecentsView mRecentsView;
private AppToOverviewAnimationListener mAnimationReadyListener;
- AppToOverviewAnimationProvider(BaseActivityInterface<T> activityInterface, int targetTaskId) {
- mActivityInterface = activityInterface;
+ AppToOverviewAnimationProvider(BaseActivityInterface<T> helper, int targetTaskId) {
+ mHelper = helper;
mTargetTaskId = targetTaskId;
}
@@ -68,15 +68,15 @@
/**
* Callback for when the activity is ready/initialized.
*
+ * @param activity the activity that is ready
* @param wasVisible true if it was visible before
*/
- boolean onActivityReady(Boolean wasVisible) {
- T activity = mActivityInterface.getCreatedActivity();
+ boolean onActivityReady(T activity, Boolean wasVisible) {
if (mAnimationReadyListener != null) {
mAnimationReadyListener.onActivityReady(activity);
}
BaseActivityInterface.AnimationFactory factory =
- mActivityInterface.prepareRecentsUI(wasVisible,
+ mHelper.prepareRecentsUI(activity, wasVisible,
false /* animate activity */, (controller) -> {
controller.dispatchOnStart();
ValueAnimator anim = controller.getAnimationPlayer()
diff --git a/go/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/go/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index ecb9472..2af8441 100644
--- a/go/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/go/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -29,8 +29,8 @@
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.views.IconRecentsView;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
-import java.util.function.Predicate;
/**
* {@link BaseActivityInterface} for recents when the default launcher is different than the
@@ -43,13 +43,12 @@
public FallbackActivityInterface() { }
@Override
- public AnimationFactory prepareRecentsUI(boolean activityVisible,
+ public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
if (activityVisible) {
return (transitionLength) -> { };
}
- RecentsActivity activity = getCreatedActivity();
IconRecentsView rv = activity.getOverviewPanel();
rv.setUsingRemoteAnimation(true);
rv.setAlpha(0);
@@ -85,9 +84,8 @@
@Override
public ActivityInitListener createActivityInitListener(
- Predicate<Boolean> onInitListener) {
- return new ActivityInitListener<>((activity, alreadyOnHome) ->
- onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
+ BiPredicate<RecentsActivity, Boolean> onInitListener) {
+ return new ActivityInitListener(onInitListener, RecentsActivity.ACTIVITY_TRACKER);
}
@Nullable
@@ -117,5 +115,5 @@
}
@Override
- public void onLaunchTaskSuccess() { }
+ public void onLaunchTaskSuccess(RecentsActivity activity) { }
}
diff --git a/go/quickstep/src/com/android/quickstep/GoActivityInterface.java b/go/quickstep/src/com/android/quickstep/GoActivityInterface.java
index b62d17c..5ce0f4c 100644
--- a/go/quickstep/src/com/android/quickstep/GoActivityInterface.java
+++ b/go/quickstep/src/com/android/quickstep/GoActivityInterface.java
@@ -17,7 +17,7 @@
BaseActivityInterface<T> {
@Override
- public void onTransitionCancelled(boolean activityVisible) {
+ public void onTransitionCancelled(T activity, boolean activityVisible) {
// Go transitions to overview are all atomic.
}
@@ -29,7 +29,7 @@
}
@Override
- public void onSwipeUpToRecentsComplete() {
+ public void onSwipeUpToRecentsComplete(T activity) {
// Go does not support swipe up gesture.
}
@@ -39,7 +39,7 @@
}
@Override
- public HomeAnimationFactory prepareHomeUI() {
+ public HomeAnimationFactory prepareHomeUI(T activity) {
// Go does not support gestures from app to home.
return null;
}
@@ -63,7 +63,7 @@
}
@Override
- public void onLaunchTaskFailed() {
+ public void onLaunchTaskFailed(T activity) {
// Go does not support gestures from one task to another.
}
}
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 3e93480..5bff8e8 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -26,8 +26,8 @@
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.views.IconRecentsView;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
-import java.util.function.Predicate;
/**
* {@link BaseActivityInterface} for the in-launcher recents.
@@ -36,15 +36,15 @@
public final class LauncherActivityInterface extends GoActivityInterface<Launcher> {
@Override
- public AnimationFactory prepareRecentsUI(boolean activityVisible, boolean animateActivity,
+ public AnimationFactory prepareRecentsUI(Launcher activity,
+ boolean activityVisible, boolean animateActivity,
Consumer<AnimatorPlaybackController> callback) {
- Launcher launcher = getCreatedActivity();
- LauncherState fromState = launcher.getStateManager().getState();
- launcher.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(true);
+ LauncherState fromState = activity.getStateManager().getState();
+ activity.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(true);
//TODO: Implement this based off where the recents view needs to be for app => recents anim.
return new AnimationFactory() {
public void createActivityInterface(long transitionLength) {
- callback.accept(launcher.getStateManager().createAnimationToNewWorkspace(
+ callback.accept(activity.getStateManager().createAnimationToNewWorkspace(
fromState, OVERVIEW, transitionLength));
}
@@ -54,9 +54,9 @@
}
@Override
- public LauncherInitListener createActivityInitListener(Predicate<Boolean> onInitListener) {
- return new LauncherInitListener((activity, alreadyOnHome) ->
- onInitListener.test(alreadyOnHome));
+ public LauncherInitListener createActivityInitListener(
+ BiPredicate<Launcher, Boolean> onInitListener) {
+ return new LauncherInitListener(onInitListener);
}
@Override
@@ -105,8 +105,7 @@
}
@Override
- public void onLaunchTaskSuccess() {
- Launcher launcher = getCreatedActivity();
+ public void onLaunchTaskSuccess(Launcher launcher) {
launcher.getStateManager().moveToRestState();
}
}
diff --git a/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java b/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java
deleted file mode 100644
index e7099ec..0000000
--- a/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import com.android.launcher3.Launcher;
-
-/** Empty class, only exists so that lowRamWithQuickstepIconRecentsDebug compiles. */
-public class ShelfPeekAnim {
- public ShelfPeekAnim(Launcher launcher) {
- }
-
- public enum ShelfAnimState {
- }
-}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index e380698..87b4d4e 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -40,7 +40,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -67,7 +66,6 @@
import com.android.launcher3.util.Themes;
import com.android.quickstep.ContentFillItemAnimator;
import com.android.quickstep.RecentsModel;
-import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
import com.android.quickstep.RecentsToActivityHelper;
import com.android.quickstep.TaskActionController;
import com.android.quickstep.TaskAdapter;
@@ -76,7 +74,6 @@
import com.android.quickstep.TaskSwipeCallback;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@@ -90,8 +87,7 @@
* Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
* base.
*/
-public final class IconRecentsView extends FrameLayout
- implements Insettable, TaskVisualsChangeListener {
+public final class IconRecentsView extends FrameLayout implements Insettable {
public static final FloatProperty<IconRecentsView> CONTENT_ALPHA =
new FloatProperty<IconRecentsView>("contentAlpha") {
@@ -163,24 +159,7 @@
private AnimatorSet mLayoutAnimation;
private final ArraySet<View> mLayingOutViews = new ArraySet<>();
private Rect mInsets;
-
- public IconRecentsView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mActivity = BaseActivity.fromContext(context);
- mContext = context;
- mStatusBarForegroundScrim =
- Themes.getAttrDrawable(mContext, R.attr.workspaceStatusBarScrim);
- mTaskLoader = new TaskListLoader(mContext);
- mTaskAdapter = new TaskAdapter(mTaskLoader);
- mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks());
- mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter,
- mActivity.getStatsLogManager());
- mTaskAdapter.setActionController(mTaskActionController);
- mTaskLayoutManager = new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */);
- }
-
- @Override
- public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
+ private final RecentsModel.TaskThumbnailChangeListener listener = (taskId, thumbnailData) -> {
ArrayList<TaskItemView> itemViews = getTaskViews();
for (int i = 0, size = itemViews.size(); i < size; i++) {
TaskItemView taskView = itemViews.get(i);
@@ -195,10 +174,23 @@
}
}
return null;
- }
+ };
- @Override
- public void onTaskIconChanged(String pkg, UserHandle user) { }
+ public IconRecentsView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mActivity = BaseActivity.fromContext(context);
+ mContext = context;
+ mStatusBarForegroundScrim =
+ Themes.getAttrDrawable(mContext, R.attr.workspaceStatusBarScrim);
+ mTaskLoader = new TaskListLoader(mContext);
+ mTaskAdapter = new TaskAdapter(mTaskLoader);
+ mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks());
+ mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter,
+ mActivity.getStatsLogManager());
+ mTaskAdapter.setActionController(mTaskActionController);
+ mTaskLayoutManager = new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */);
+ RecentsModel.INSTANCE.get(context).addThumbnailChangeListener(listener);
+ }
@Override
protected void onFinishInflate() {
@@ -283,18 +275,6 @@
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
- }
-
- @Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
int childCount = mTaskRecyclerView.getChildCount();
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index a3c7c07..5c4f37c 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -116,7 +116,7 @@
icon = createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f);
}
- return BitmapInfo.of(icon, extractColor(icon));
+ return BitmapInfo.fromBitmap(icon, mDisableColorExtractor ? null : mColorExtractor);
}
public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
@@ -183,10 +183,7 @@
bitmap = createIconBitmap(badged, 1f);
}
}
- int color = extractColor(result);
- return icon instanceof BitmapInfo.Extender
- ? ((BitmapInfo.Extender) icon).getExtendedInfo(result, color, this)
- : BitmapInfo.of(result, color);
+ return BitmapInfo.fromBitmap(bitmap, mDisableColorExtractor ? null : mColorExtractor);
}
public Bitmap createScaledBitmapWithoutShadow(Drawable icon, boolean shrinkNonAdaptiveIcons) {
@@ -337,10 +334,6 @@
iconDpi);
}
- private int extractColor(Bitmap bitmap) {
- return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap);
- }
-
/**
* Returns the correct badge size given an icon size
*/
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
index d33f9b1..245561e 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
@@ -18,55 +18,32 @@
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
-import androidx.annotation.NonNull;
-
public class BitmapInfo {
public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
- public static final BitmapInfo LOW_RES_INFO = fromBitmap(LOW_RES_ICON);
- public final Bitmap icon;
- public final int color;
+ public Bitmap icon;
+ public int color;
- public BitmapInfo(Bitmap icon, int color) {
- this.icon = icon;
- this.color = color;
- }
-
- /**
- * Ideally icon should not be null, except in cases when generating hardware bitmap failed
- */
- public final boolean isNullOrLowRes() {
- return icon == null || icon == LOW_RES_ICON;
+ public void applyTo(BitmapInfo info) {
+ info.icon = icon;
+ info.color = color;
}
public final boolean isLowRes() {
return LOW_RES_ICON == icon;
}
- public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap) {
- return of(bitmap, 0);
+ public static BitmapInfo fromBitmap(Bitmap bitmap) {
+ return fromBitmap(bitmap, null);
}
- public static BitmapInfo of(@NonNull Bitmap bitmap, int color) {
- return new BitmapInfo(bitmap, color);
- }
-
- /**
- * Interface to be implemented by drawables to provide a custom BitmapInfo
- */
- public interface Extender {
-
- /**
- * Called for creating a custom BitmapInfo
- */
- default BitmapInfo getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory) {
- return BitmapInfo.of(bitmap, color);
- }
-
- /**
- * Notifies the drawable that it will be drawn directly in the UI, without any preprocessing
- */
- default void prepareToDrawOnUi() { }
+ public static BitmapInfo fromBitmap(Bitmap bitmap, ColorExtractor dominantColorExtractor) {
+ BitmapInfo info = new BitmapInfo();
+ info.icon = bitmap;
+ info.color = dominantColorExtractor != null
+ ? dominantColorExtractor.findDominantColorByHue(bitmap)
+ : 0;
+ return info;
}
}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index 6f63d88..93f0538 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -71,10 +71,7 @@
// Empty class name is used for storing package default entry.
public static final String EMPTY_CLASS_NAME = ".";
- public static class CacheEntry {
-
- @NonNull
- public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO;
+ public static class CacheEntry extends BitmapInfo {
public CharSequence title = "";
public CharSequence contentDescription = "";
}
@@ -262,23 +259,23 @@
if (!replaceExisting) {
entry = mCache.get(key);
// We can't reuse the entry if the high-res icon is not present.
- if (entry == null || entry.bitmap.isNullOrLowRes()) {
+ if (entry == null || entry.icon == null || entry.isLowRes()) {
entry = null;
}
}
if (entry == null) {
entry = new CacheEntry();
- entry.bitmap = cachingLogic.loadIcon(mContext, object);
+ cachingLogic.loadIcon(mContext, object, entry);
}
// Icon can't be loaded from cachingLogic, which implies alternative icon was loaded
// (e.g. fallback icon, default icon). So we drop here since there's no point in caching
// an empty entry.
- if (entry.bitmap.isNullOrLowRes()) return;
+ if (entry.icon == null) return;
entry.title = cachingLogic.getLabel(object);
entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
if (cachingLogic.addToMemCache()) mCache.put(key, entry);
- ContentValues values = newContentValues(entry.bitmap, entry.title.toString(),
+ ContentValues values = newContentValues(entry, entry.title.toString(),
componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
addIconToDB(values, componentName, info, userSerial);
}
@@ -303,8 +300,8 @@
return mDefaultIcons.get(user);
}
- public boolean isDefaultIcon(BitmapInfo icon, UserHandle user) {
- return getDefaultIcon(user).icon == icon.icon;
+ public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
+ return getDefaultIcon(user).icon == icon;
}
/**
@@ -318,7 +315,7 @@
assertWorkerThread();
ComponentKey cacheKey = new ComponentKey(componentName, user);
CacheEntry entry = mCache.get(cacheKey);
- if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) {
+ if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
if (cachingLogic.addToMemCache()) {
mCache.put(cacheKey, entry);
@@ -333,7 +330,7 @@
providerFetchedOnce = true;
if (object != null) {
- entry.bitmap = cachingLogic.loadIcon(mContext, object);
+ cachingLogic.loadIcon(mContext, object, entry);
} else {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
@@ -341,15 +338,15 @@
if (packageEntry != null) {
if (DEBUG) Log.d(TAG, "using package default icon for " +
componentName.toShortString());
- entry.bitmap = packageEntry.bitmap;
+ packageEntry.applyTo(entry);
entry.title = packageEntry.title;
entry.contentDescription = packageEntry.contentDescription;
}
}
- if (entry.bitmap == null) {
+ if (entry.icon == null) {
if (DEBUG) Log.d(TAG, "using default icon for " +
componentName.toShortString());
- entry.bitmap = getDefaultIcon(user);
+ getDefaultIcon(user).applyTo(entry);
}
}
}
@@ -393,10 +390,10 @@
}
if (icon != null) {
BaseIconFactory li = getIconFactory();
- entry.bitmap = li.createIconBitmap(icon);
+ li.createIconBitmap(icon).applyTo(entry);
li.close();
}
- if (!TextUtils.isEmpty(title) && entry.bitmap.icon != null) {
+ if (!TextUtils.isEmpty(title) && entry.icon != null) {
mCache.put(cacheKey, entry);
}
}
@@ -416,7 +413,7 @@
ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
- if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) {
+ if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
boolean entryUpdated = true;
@@ -441,8 +438,8 @@
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
- entry.bitmap = BitmapInfo.of(
- useLowResIcon ? LOW_RES_ICON : iconInfo.icon, iconInfo.color);
+ entry.icon = useLowResIcon ? LOW_RES_ICON : iconInfo.icon;
+ entry.color = iconInfo.color;
// Add the icon in the DB here, since these do not get written during
// package updates.
@@ -464,7 +461,7 @@
return entry;
}
- protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
+ private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
Cursor c = null;
try {
c = mIconDb.query(
@@ -475,7 +472,7 @@
Long.toString(getSerialNumberForUser(cacheKey.user))});
if (c.moveToNext()) {
// Set the alpha to be 255, so that we never have a wrong color
- entry.bitmap = BitmapInfo.of(LOW_RES_ICON, setColorAlphaBound(c.getInt(0), 255));
+ entry.color = setColorAlphaBound(c.getInt(0), 255);
entry.title = c.getString(1);
if (entry.title == null) {
entry.title = "";
@@ -485,12 +482,13 @@
entry.title, cacheKey.user);
}
- if (!lowRes) {
+ if (lowRes) {
+ entry.icon = LOW_RES_ICON;
+ } else {
byte[] data = c.getBlob(2);
try {
- entry.bitmap = BitmapInfo.of(
- BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions),
- entry.bitmap.color);
+ entry.icon = BitmapFactory.decodeByteArray(data, 0, data.length,
+ mDecodeOptions);
} catch (Exception e) { }
}
return true;
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
index a89ede7..3aa783a 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
@@ -21,7 +21,6 @@
import android.os.LocaleList;
import android.os.UserHandle;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.icons.BitmapInfo;
@@ -34,8 +33,7 @@
CharSequence getLabel(T object);
- @NonNull
- BitmapInfo loadIcon(Context context, T object);
+ void loadIcon(Context context, T object, BitmapInfo target);
/**
* Provides a option list of keywords to associate with this object
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
index bcdbce5..d0db157 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
@@ -171,8 +171,7 @@
long updateTime = c.getLong(indexLastUpdate);
int version = c.getInt(indexVersion);
T app = componentMap.remove(component);
- if (version == info.versionCode
- && updateTime == cachingLogic.getLastUpdatedTime(app, info)
+ if (version == info.versionCode && updateTime == info.lastUpdateTime
&& TextUtils.equals(c.getString(systemStateIndex),
mIconCache.getIconSystemState(info.packageName))) {
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
deleted file mode 100644
index 60afddb..0000000
--- a/quickstep/AndroidManifest-launcher.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2019, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.launcher3">
- <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
- <!--
- Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
- Refer comments around specific entries on how to extend individual components.
- -->
-
- <application
- android:backupAgent="com.android.launcher3.LauncherBackupAgent"
- android:fullBackupOnly="true"
- android:fullBackupContent="@xml/backupscheme"
- android:hardwareAccelerated="true"
- android:icon="@drawable/ic_launcher_home"
- android:label="@string/derived_app_name"
- android:theme="@style/AppTheme"
- android:largeHeap="@bool/config_largeHeap"
- android:restoreAnyVersion="true"
- android:supportsRtl="true" >
-
- <!--
- Main launcher activity. When extending only change the name, and keep all the
- attributes and intent filters the same
- -->
- <activity
- android:name="com.android.launcher3.uioverrides.QuickstepLauncher"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:windowSoftInputMode="adjustPan"
- android:screenOrientation="unspecified"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
- android:resizeableActivity="true"
- android:resumeWhilePausing="true"
- android:taskAffinity=""
- android:enabled="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY"/>
- <category android:name="android.intent.category.LAUNCHER_APP" />
- </intent-filter>
- <meta-data
- android:name="com.android.launcher3.grid.control"
- android:value="${packageName}.grid_control" />
- </activity>
-
- </application>
-</manifest>
diff --git a/quickstep/recents_ui_overrides/res/drawable/predicted_icon_background.xml b/quickstep/recents_ui_overrides/res/drawable/predicted_icon_background.xml
deleted file mode 100644
index cfc6d48..0000000
--- a/quickstep/recents_ui_overrides/res/drawable/predicted_icon_background.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:inset="@dimen/predicted_icon_background_inset">
- <shape>
- <solid android:color="?attr/folderFillColor" />
- <corners android:radius="@dimen/predicted_icon_background_corner_radius" />
- </shape>
-</inset>
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
index ee672d4..863a8ba 100644
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -28,9 +28,4 @@
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
<dimen name="swipe_up_y_overshoot">10dp</dimen>
<dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
-
- <!-- Predicted icon related -->
- <dimen name="predicted_icon_background_corner_radius">15dp</dimen>
- <dimen name="predicted_icon_background_inset">8dp</dimen>
-
</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
deleted file mode 100644
index 424333c..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.content.ComponentName;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.appprediction.ComponentKeyMapper;
-import com.android.launcher3.appprediction.DynamicItemCache;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.ComponentKey;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Provides prediction ability for the hotseat. Fills gaps in hotseat with predicted items, allows
- * pinning of predicted apps and manages replacement of predicted apps with user drag.
- */
-public class HotseatPredictionController implements DragController.DragListener,
- View.OnAttachStateChangeListener, SystemShortcut.Factory<QuickstepLauncher>,
- InvariantDeviceProfile.OnIDPChangeListener, AllAppsStore.OnUpdateListener,
- IconCache.ItemInfoUpdateReceiver {
-
- private static final String TAG = "PredictiveHotseat";
- private static final boolean DEBUG = false;
-
- private static final String PREDICTION_CLIENT = "hotseat";
-
- private boolean mDragStarted = false;
- private int mHotSeatItemsCount;
-
- private Launcher mLauncher;
- private Hotseat mHotseat;
-
- private List<ComponentKeyMapper> mComponentKeyMappers = new ArrayList<>();
-
- private DynamicItemCache mDynamicItemCache;
-
- private AppPredictor mAppPredictor;
- private AllAppsStore mAllAppsStore;
-
- public HotseatPredictionController(Launcher launcher) {
- mLauncher = launcher;
- mHotseat = launcher.getHotseat();
- mAllAppsStore = mLauncher.getAppsView().getAppsStore();
- mAllAppsStore.addUpdateListener(this);
- mDynamicItemCache = new DynamicItemCache(mLauncher, () -> fillGapsWithPrediction(false));
- mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
- launcher.getDeviceProfile().inv.addOnChangeListener(this);
- mHotseat.addOnAttachStateChangeListener(this);
- createPredictor();
- }
-
- @Override
- public void onViewAttachedToWindow(View view) {
- mLauncher.getDragController().addDragListener(this);
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- mLauncher.getDragController().removeDragListener(this);
- }
-
- /**
- * Fills gaps in the hotseat with predictions
- */
- public void fillGapsWithPrediction(boolean animate) {
- if (mDragStarted) {
- return;
- }
- List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
- int predictionIndex = 0;
- ArrayList<ItemInfo> newItemsToAdd = new ArrayList<>();
- for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
- View child = mHotseat.getChildAt(
- mHotseat.getCellXFromOrder(rank),
- mHotseat.getCellYFromOrder(rank));
-
- if (child != null && !isPredictedIcon(child)) {
- continue;
- }
- if (predictedApps.size() <= predictionIndex) {
- // Remove predicted apps from the past
- if (isPredictedIcon(child)) {
- mHotseat.removeView(child);
- }
- continue;
- }
-
- WorkspaceItemInfo predictedItem = predictedApps.get(predictionIndex++);
- if (isPredictedIcon(child)) {
- BubbleTextView icon = (BubbleTextView) child;
- icon.applyFromWorkspaceItem(predictedItem);
- } else {
- newItemsToAdd.add(predictedItem);
- }
- preparePredictionInfo(predictedItem, rank);
- }
- mLauncher.bindItems(newItemsToAdd, animate);
- for (BubbleTextView icon : getPredictedIcons()) {
- icon.verifyHighRes();
- icon.setOnLongClickListener((v) -> {
- PopupContainerWithArrow.showForIcon((BubbleTextView) v);
- return true;
- });
- icon.setBackgroundResource(R.drawable.predicted_icon_background);
- }
- }
-
- /**
- * Unregisters callbacks and frees resources
- */
- public void destroy() {
- mAllAppsStore.removeUpdateListener(this);
- mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
- mHotseat.removeOnAttachStateChangeListener(this);
- if (mAppPredictor != null) {
- mAppPredictor.destroy();
- }
- }
-
- private void createPredictor() {
- AppPredictionManager apm = mLauncher.getSystemService(AppPredictionManager.class);
- if (apm == null) {
- return;
- }
- if (mAppPredictor != null) {
- mAppPredictor.destroy();
- }
- mAppPredictor = apm.createAppPredictionSession(
- new AppPredictionContext.Builder(mLauncher)
- .setUiSurface(PREDICTION_CLIENT)
- .setPredictedTargetCount(mHotSeatItemsCount)
- .build());
- mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(),
- this::setPredictedApps);
- mAppPredictor.requestPredictionUpdate();
- }
-
- private void setPredictedApps(List<AppTarget> appTargets) {
- mComponentKeyMappers.clear();
- for (AppTarget appTarget : appTargets) {
- ComponentKey key;
- if (appTarget.getShortcutInfo() != null) {
- key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
- } else {
- key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
- appTarget.getClassName()), appTarget.getUser());
- }
- mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
- }
- updateDependencies();
- fillGapsWithPrediction(false);
- }
-
- private void updateDependencies() {
- mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this,
- mHotSeatItemsCount);
- }
-
- private void pinPrediction(ItemInfo info) {
- BubbleTextView icon = (BubbleTextView) mHotseat.getChildAt(
- mHotseat.getCellXFromOrder(info.rank),
- mHotseat.getCellYFromOrder(info.rank));
- if (icon == null) {
- return;
- }
- WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo((WorkspaceItemInfo) info);
- mLauncher.getModelWriter().addItemToDatabase(workspaceItemInfo,
- LauncherSettings.Favorites.CONTAINER_HOTSEAT, workspaceItemInfo.screenId,
- workspaceItemInfo.cellX, workspaceItemInfo.cellY);
- ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
- icon.reset();
- icon.applyFromWorkspaceItem(workspaceItemInfo);
- icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
- }
-
- private List<WorkspaceItemInfo> mapToWorkspaceItemInfo(
- List<ComponentKeyMapper> components) {
- AllAppsStore allAppsStore = mLauncher.getAppsView().getAppsStore();
- if (allAppsStore.getApps().length == 0) {
- return Collections.emptyList();
- }
-
- List<WorkspaceItemInfo> predictedApps = new ArrayList<>();
- for (ComponentKeyMapper mapper : components) {
- ItemInfoWithIcon info = mapper.getApp(allAppsStore);
- if (info instanceof AppInfo) {
- WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((AppInfo) info);
- predictedApps.add(predictedApp);
- } else if (info instanceof WorkspaceItemInfo) {
- predictedApps.add(new WorkspaceItemInfo((WorkspaceItemInfo) info));
- } else {
- if (DEBUG) {
- Log.e(TAG, "Predicted app not found: " + mapper);
- }
- }
- // Stop at the number of hotseat items
- if (predictedApps.size() == mHotSeatItemsCount) {
- break;
- }
- }
- return predictedApps;
- }
-
- private List<BubbleTextView> getPredictedIcons() {
- List<BubbleTextView> icons = new ArrayList<>();
- ViewGroup vg = mHotseat.getShortcutsAndWidgets();
- for (int i = 0; i < vg.getChildCount(); i++) {
- View child = vg.getChildAt(i);
- if (isPredictedIcon(child)) {
- icons.add((BubbleTextView) child);
- }
- }
- return icons;
- }
-
- private void removePredictedApps(boolean animate) {
- for (BubbleTextView icon : getPredictedIcons()) {
- if (animate) {
- icon.animate().scaleY(0).scaleX(0).setListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- if (icon.getParent() != null) {
- mHotseat.removeView(icon);
- }
- }
- });
- } else {
- if (icon.getParent() != null) {
- mHotseat.removeView(icon);
- }
- }
- }
- }
-
- @Override
- public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- removePredictedApps(true);
- mDragStarted = true;
- }
-
- @Override
- public void onDragEnd() {
- if (!mDragStarted) {
- return;
- }
- mDragStarted = false;
- fillGapsWithPrediction(true);
- }
-
- @Nullable
- @Override
- public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
- ItemInfo itemInfo) {
- if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
- return null;
- }
- return new PinPrediction(activity, itemInfo);
- }
-
- private void preparePredictionInfo(WorkspaceItemInfo itemInfo, int rank) {
- itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
- itemInfo.rank = rank;
- itemInfo.cellX = rank;
- itemInfo.cellY = mHotSeatItemsCount - rank - 1;
- itemInfo.screenId = rank;
- }
-
- @Override
- public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
- this.mHotSeatItemsCount = profile.numHotseatIcons;
- createPredictor();
- }
-
- @Override
- public void onAppsUpdated() {
- updateDependencies();
- fillGapsWithPrediction(false);
- }
-
- @Override
- public void reapplyItemInfo(ItemInfoWithIcon info) {
-
- }
-
- private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
-
- private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
- super(R.drawable.ic_pin, R.string.pin_prediction, target,
- itemInfo);
- }
-
- @Override
- public void onClick(View view) {
- dismissTaskMenuView(mTarget);
- pinPrediction(mItemInfo);
- }
- }
-
- private static boolean isPredictedIcon(View view) {
- return view instanceof BubbleTextView && view.getTag() instanceof WorkspaceItemInfo
- && ((WorkspaceItemInfo) view.getTag()).container
- == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 6946508..d842484 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -17,10 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
@@ -30,15 +27,12 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationBuilder;
@@ -151,37 +145,8 @@
@Override
public Animator createStateElementAnimation(int index, float... values) {
switch (index) {
- case INDEX_SHELF_ANIM: {
- AllAppsTransitionController aatc = mLauncher.getAllAppsController();
- Animator springAnim = aatc.createSpringAnimation(values);
-
- if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
- // Translate hotseat with the shelf until reaching overview.
- float overviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
- ScaleAndTranslation sat = OVERVIEW.getHotseatScaleAndTranslation(mLauncher);
- float shiftRange = aatc.getShiftRange();
- if (values.length == 1) {
- values = new float[] {aatc.getProgress(), values[0]};
- }
- ValueAnimator hotseatAnim = ValueAnimator.ofFloat(values);
- hotseatAnim.addUpdateListener(anim -> {
- float progress = (Float) anim.getAnimatedValue();
- if (progress >= overviewProgress || mLauncher.isInState(BACKGROUND_APP)) {
- float hotseatShift = (progress - overviewProgress) * shiftRange;
- mLauncher.getHotseat().setTranslationY(hotseatShift + sat.translationY);
- }
- });
- hotseatAnim.setInterpolator(LINEAR);
- hotseatAnim.setDuration(springAnim.getDuration());
-
- AnimatorSet anim = new AnimatorSet();
- anim.play(hotseatAnim);
- anim.play(springAnim);
- return anim;
- }
-
- return springAnim;
- }
+ case INDEX_SHELF_ANIM:
+ return mLauncher.getAllAppsController().createSpringAnimation(values);
case INDEX_RECENTS_FADE_ANIM:
return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
RecentsView.CONTENT_ALPHA, values);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
new file mode 100644
index 0000000..76050d5
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3;
+
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+
+import java.util.function.BiPredicate;
+
+public class LauncherInitListenerEx extends LauncherInitListener {
+
+ public LauncherInitListenerEx(BiPredicate<Launcher, Boolean> onInitListener) {
+ super(onInitListener);
+ }
+
+ @Override
+ public boolean init(Launcher launcher, boolean alreadyOnHome) {
+ PredictionUiStateManager.INSTANCE.get(launcher).switchClient(Client.OVERVIEW);
+ return super.init(launcher, alreadyOnHome);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
index 0712285..b9f4147 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
@@ -18,6 +18,8 @@
import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+import android.content.Context;
+
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.allapps.AllAppsStore;
@@ -27,9 +29,11 @@
public class ComponentKeyMapper {
protected final ComponentKey componentKey;
+ private final Context mContext;
private final DynamicItemCache mCache;
- public ComponentKeyMapper(ComponentKey key, DynamicItemCache cache) {
+ public ComponentKeyMapper(Context context, ComponentKey key, DynamicItemCache cache) {
+ mContext = context;
componentKey = key;
mCache = cache;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
index 38bb180..65e69b6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
@@ -18,7 +18,6 @@
import static android.content.pm.PackageManager.MATCH_INSTANT;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
import android.content.Context;
import android.content.Intent;
@@ -38,10 +37,8 @@
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
-import com.android.launcher3.AppInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -75,7 +72,6 @@
private final Handler mUiHandler;
private final InstantAppResolver mInstantAppResolver;
private final Runnable mOnUpdateCallback;
- private final IconCache mIconCache;
private final Map<ShortcutKey, WorkspaceItemInfo> mShortcuts;
private final Map<String, InstantAppItemInfo> mInstantApps;
@@ -86,7 +82,6 @@
mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
mInstantAppResolver = InstantAppResolver.newInstance(context);
mOnUpdateCallback = onUpdateCallback;
- mIconCache = LauncherAppState.getInstance(mContext).getIconCache();
mShortcuts = new HashMap<>();
mInstantApps = new HashMap<>();
@@ -175,7 +170,7 @@
if (!details.isEmpty()) {
WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
- si.bitmap = li.createShortcutIcon(details.get(0), true /* badged */, null);
+ si.applyFrom(li.createShortcutIcon(details.get(0), true /* badged */, null));
} catch (Exception e) {
if (DEBUG) {
Log.e(TAG, "Error loading shortcut icon for " + shortcutKey.toString());
@@ -214,7 +209,7 @@
InstantAppItemInfo info = new InstantAppItemInfo(intent, pkgName);
IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
iconCache.getTitleAndIcon(info, false);
- if (info.bitmap.icon == null || iconCache.isDefaultIcon(info.bitmap, info.user)) {
+ if (info.iconBitmap == null || iconCache.isDefaultIcon(info.iconBitmap, info.user)) {
return null;
}
return info;
@@ -245,35 +240,4 @@
public WorkspaceItemInfo getShortcutInfo(ShortcutKey key) {
return mShortcuts.get(key);
}
-
- /**
- * requests and caches icons for app targets
- */
- public void updateDependencies(List<ComponentKeyMapper> componentKeyMappers,
- AllAppsStore appsStore, IconCache.ItemInfoUpdateReceiver callback, int itemCount) {
- List<String> instantAppsToLoad = new ArrayList<>();
- List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
- int total = componentKeyMappers.size();
- for (int i = 0, count = 0; i < total && count < itemCount; i++) {
- ComponentKeyMapper mapper = componentKeyMappers.get(i);
- // Update instant apps
- if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) {
- instantAppsToLoad.add(mapper.getPackage());
- count++;
- } else if (mapper.getComponentKey() instanceof ShortcutKey) {
- shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey());
- count++;
- } else {
- // Reload high res icon
- AppInfo info = (AppInfo) mapper.getApp(appsStore);
- if (info != null) {
- if (info.usingLowResIcon()) {
- mIconCache.updateIconInBackground(callback, info);
- }
- count++;
- }
- }
- }
- cacheItems(shortcutsToLoad, instantAppsToLoad);
- }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index 8338c2e..1a59770 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,12 +18,14 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.content.ComponentName;
import android.content.Context;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
import com.android.launcher3.ItemInfoWithIcon;
@@ -34,6 +36,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
@@ -236,7 +239,7 @@
key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
appTarget.getClassName()), appTarget.getUser());
}
- state.apps.add(new ComponentKeyMapper(key, mDynamicItemCache));
+ state.apps.add(new ComponentKeyMapper(mContext, key, mDynamicItemCache));
}
}
updateDependencies(state);
@@ -247,8 +250,33 @@
if (!state.isEnabled || mAppsView == null) {
return;
}
- mDynamicItemCache.updateDependencies(state.apps, mAppsView.getAppsStore(), this,
- mMaxIconsPerRow);
+
+ IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
+ List<String> instantAppsToLoad = new ArrayList<>();
+ List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
+ int total = state.apps.size();
+ for (int i = 0, count = 0; i < total && count < mMaxIconsPerRow; i++) {
+ ComponentKeyMapper mapper = state.apps.get(i);
+ // Update instant apps
+ if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) {
+ instantAppsToLoad.add(mapper.getPackage());
+ count++;
+ } else if (mapper.getComponentKey() instanceof ShortcutKey) {
+ shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey());
+ count++;
+ } else {
+ // Reload high res icon
+ AppInfo info = (AppInfo) mapper.getApp(mAppsView.getAppsStore());
+ if (info != null) {
+ if (info.usingLowResIcon()) {
+ // TODO: Update icon cache to support null callbacks.
+ iconCache.updateIconInBackground(this, info);
+ }
+ count++;
+ }
+ }
+ }
+ mDynamicItemCache.cacheItems(shortcutsToLoad, instantAppsToLoad);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
similarity index 67%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 7dc5616..4c04b29 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -13,27 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.Gravity;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.HotseatPredictionController;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
-import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
@@ -49,20 +48,28 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.views.RecentsView;
import java.util.ArrayList;
-import java.util.stream.Stream;
-public class QuickstepLauncher extends BaseQuickstepLauncher {
+/**
+ * Provides recents-related {@link UiFactory} logic and classes.
+ */
+public abstract class RecentsUiFactory {
+
+ private static final String TAG = RecentsUiFactory.class.getSimpleName();
public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
+
/**
* Reusable command for applying the shelf height on the background thread.
*/
- public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
- SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
+ public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) -> {
+ SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
+ };
+
public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
@Override
public void mapRect(int left, int top, int right, int bottom, Rect out) {
@@ -89,6 +96,7 @@
}
}
};
+
public static RotationMode ROTATION_SEASCAPE = new RotationMode(90) {
@Override
public void mapRect(int left, int top, int right, int bottom, Rect out) {
@@ -134,114 +142,83 @@
| horizontalGravity | verticalGravity;
}
};
- private HotseatPredictionController mHotseatPredictionController;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
- mHotseatPredictionController = new HotseatPredictionController(this);
- }
- }
-
- @Override
- protected RotationMode getFakeRotationMode(DeviceProfile dp) {
+ public static RotationMode getRotationMode(DeviceProfile dp) {
return !dp.isVerticalBarLayout() ? RotationMode.NORMAL
: (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE);
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- onStateOrResumeChanged();
- }
+ public static TouchController[] createTouchControllers(Launcher launcher) {
+ Mode mode = SysUINavigationMode.getMode(launcher);
- @Override
- protected void onActivityFlagsChanged(int changeBits) {
- super.onActivityFlagsChanged(changeBits);
-
- if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
- | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0
- && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0) {
- onStateOrResumeChanged();
- }
- }
-
- @Override
- public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
- if (mHotseatPredictionController != null) {
- return Stream.concat(super.getSupportedShortcuts(),
- Stream.of(mHotseatPredictionController));
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(launcher.getDragController());
+ if (mode == NO_BUTTON) {
+ list.add(new QuickSwitchTouchController(launcher));
+ list.add(new NavBarToHomeTouchController(launcher));
+ list.add(new FlingAndHoldTouchController(launcher));
} else {
- return super.getSupportedShortcuts();
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new OverviewToAllAppsTouchController(launcher));
+ list.add(new LandscapeEdgeSwipeController(launcher));
+ if (mode.hasGestures) {
+ list.add(new TransposedQuickSwitchTouchController(launcher));
+ }
+ } else {
+ list.add(new PortraitStatesTouchController(launcher,
+ mode.hasGestures /* allowDragToOverview */));
+ if (mode.hasGestures) {
+ list.add(new QuickSwitchTouchController(launcher));
+ }
+ }
+ }
+
+ if (!launcher.getDeviceProfile().isMultiWindowMode) {
+ list.add(new StatusBarTouchController(launcher));
+ }
+
+ list.add(new LauncherTaskViewController(launcher));
+ return list.toArray(new TouchController[list.size()]);
+ }
+
+ /**
+ * Creates and returns the controller responsible for recents view state transitions.
+ *
+ * @param launcher the launcher activity
+ * @return state handler for recents
+ */
+ public static StateHandler createRecentsViewStateController(Launcher launcher) {
+ return new RecentsViewStateController(launcher);
+ }
+
+ /** Clears the swipe shared state for the current swipe gesture. */
+ public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ launcher.<RecentsView>getOverviewPanel().switchToScreenshot(
+ () -> TouchInteractionService.getSwipeSharedState().clearAllState(
+ finishAnimation));
+ } else {
+ TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation);
}
}
/**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
+ *
+ * @param launcher the launcher activity
*/
- private void onStateOrResumeChanged() {
- LauncherState state = getStateManager().getState();
- DeviceProfile profile = getDeviceProfile();
- boolean visible = (state == NORMAL || state == OVERVIEW) && isUserActive()
+ public static void onLauncherStateOrResumeChanged(Launcher launcher) {
+ LauncherState state = launcher.getStateManager().getState();
+ DeviceProfile profile = launcher.getDeviceProfile();
+ boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
&& !profile.isVerticalBarLayout();
- UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
+ UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT, visible ? 1 : 0,
profile.hotseatBarSizePx);
if (state == NORMAL) {
- ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
+ launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
}
}
- @Override
- public void finishBindingItems(int pageBoundFirst) {
- super.finishBindingItems(pageBoundFirst);
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.fillGapsWithPrediction(false);
- }
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.destroy();
- }
- }
-
- @Override
- public TouchController[] createTouchControllers() {
- Mode mode = SysUINavigationMode.getMode(this);
-
- ArrayList<TouchController> list = new ArrayList<>();
- list.add(getDragController());
- if (mode == NO_BUTTON) {
- list.add(new QuickSwitchTouchController(this));
- list.add(new NavBarToHomeTouchController(this));
- list.add(new FlingAndHoldTouchController(this));
- } else {
- if (getDeviceProfile().isVerticalBarLayout()) {
- list.add(new OverviewToAllAppsTouchController(this));
- list.add(new LandscapeEdgeSwipeController(this));
- if (mode.hasGestures) {
- list.add(new TransposedQuickSwitchTouchController(this));
- }
- } else {
- list.add(new PortraitStatesTouchController(this,
- mode.hasGestures /* allowDragToOverview */));
- if (mode.hasGestures) {
- list.add(new QuickSwitchTouchController(this));
- }
- }
- }
-
- if (!getDeviceProfile().isMultiWindowMode) {
- list.add(new StatusBarTouchController(this));
- }
-
- list.add(new LauncherTaskViewController(this));
- return list.toArray(new TouchController[list.size()]);
- }
-
private static final class LauncherTaskViewController extends
TaskViewTouchController<Launcher> {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index bb66ae1..e4e60a0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -69,7 +69,7 @@
return super.getOverviewScaleAndTranslation(launcher);
}
TaskView dummyTask;
- if (recentsView.getCurrentPage() >= recentsView.getTaskViewStartIndex()) {
+ if (recentsView.getCurrentPage() >= 0) {
if (recentsView.getCurrentPage() <= taskCount - 1) {
dummyTask = recentsView.getCurrentPageTaskView();
} else {
@@ -98,7 +98,7 @@
if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
// Translate hotseat offscreen if we show it in overview.
ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher);
- scaleAndTranslation.translationY += LayoutUtils.getShelfTrackingDistance(launcher,
+ scaleAndTranslation.translationY = LayoutUtils.getShelfTrackingDistance(launcher,
launcher.getDeviceProfile());
return scaleAndTranslation;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 25eaab1..93d4de1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -32,6 +32,7 @@
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
+import android.content.Context;
import android.graphics.Rect;
import android.view.View;
@@ -46,7 +47,6 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -91,19 +91,8 @@
@Override
public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
- DeviceProfile dp = launcher.getDeviceProfile();
- if (dp.allAppsIconSizePx >= dp.iconSizePx) {
- return new ScaleAndTranslation(1, 0, 0);
- } else {
- float scale = ((float) dp.allAppsIconSizePx) / dp.iconSizePx;
- // Distance between the screen center (which is the pivotY for hotseat) and the
- // bottom of the hotseat (which we want to preserve)
- float distanceFromBottom = dp.heightPx / 2 - dp.hotseatBarBottomPaddingPx;
- // On scaling, the bottom edge is moved closer to the pivotY. We move the
- // hotseat back down so that the bottom edge's position is preserved.
- float translationY = distanceFromBottom * (1 - scale);
- return new ScaleAndTranslation(scale, 0, translationY);
- }
+ // If the hotseat icons are visible in overview, keep them in their normal position.
+ return super.getWorkspaceScaleAndTranslation(launcher);
}
return getWorkspaceScaleAndTranslation(launcher);
}
@@ -171,7 +160,15 @@
}
public static float getDefaultSwipeHeight(Launcher launcher) {
- return LayoutUtils.getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
+ return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
+ }
+
+ public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
+ float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
+ if (SysUINavigationMode.getMode(context) == SysUINavigationMode.Mode.NO_BUTTON) {
+ swipeHeight -= dp.getInsets().bottom;
+ }
+ return swipeHeight;
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 7b4bb02..6c9f46f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -20,14 +20,13 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.GestureState;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
/**
* State to indicate we are about to launch a recent task. Note that this state is only used when
- * quick switching from launcher; quick switching from an app uses LauncherSwipeHandler.
- * @see GestureState.GestureEndTarget#NEW_TASK
+ * quick switching from launcher; quick switching from an app uses WindowTransformSwipeHelper.
+ * @see com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget#NEW_TASK
*/
public class QuickSwitchState extends BackgroundAppState {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index 626292e..ee2e951 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -35,12 +35,12 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -51,7 +51,6 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.quickstep.SystemUiProxy;
-import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.views.RecentsView;
@@ -107,7 +106,8 @@
}
});
mPeekAnim.start();
- VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
+ recentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1,
peekDuration, 0);
@@ -173,7 +173,7 @@
}
@Override
- public void onDragEnd(float velocity) {
+ public void onDragEnd(float velocity, boolean fling) {
if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
if (mPeekAnim != null) {
mPeekAnim.cancel();
@@ -196,7 +196,7 @@
});
overviewAnim.start();
} else {
- super.onDragEnd(velocity);
+ super.onDragEnd(velocity, fling);
}
View searchView = mLauncher.getAppsView().getSearchView();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index ad4a343..d66af1a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -22,9 +22,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -45,25 +43,21 @@
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.TouchController;
-import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
/**
* Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.
*/
-public class NavBarToHomeTouchController implements TouchController,
- SingleAxisSwipeDetector.Listener {
+public class NavBarToHomeTouchController implements TouchController, SwipeDetector.Listener {
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
private final Launcher mLauncher;
- private final SingleAxisSwipeDetector mSwipeDetector;
+ private final SwipeDetector mSwipeDetector;
private final float mPullbackDistance;
private boolean mNoIntercept;
@@ -73,8 +67,7 @@
public NavBarToHomeTouchController(Launcher launcher) {
mLauncher = launcher;
- mSwipeDetector = new SingleAxisSwipeDetector(mLauncher, this,
- SingleAxisSwipeDetector.VERTICAL);
+ mSwipeDetector = new SwipeDetector(mLauncher, this, SwipeDetector.VERTICAL);
mPullbackDistance = mLauncher.getResources().getDimension(R.dimen.home_pullback_distance);
}
@@ -86,8 +79,7 @@
if (mNoIntercept) {
return false;
}
- mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_POSITIVE,
- false /* ignoreSlop */);
+ mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
}
if (mNoIntercept) {
@@ -109,10 +101,6 @@
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
return true;
}
- if (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
- && AssistantUtilities.isExcludedAssistantRunning()) {
- return true;
- }
return false;
}
@@ -139,13 +127,8 @@
if (!recentsView.isRtl()) {
pullbackDist = -pullbackDist;
}
- ObjectAnimator pullback = ObjectAnimator.ofFloat(recentsView, TRANSLATION_X,
- pullbackDist);
+ Animator pullback = ObjectAnimator.ofFloat(recentsView, TRANSLATION_X, pullbackDist);
pullback.setInterpolator(PULLBACK_INTERPOLATOR);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- pullback.addUpdateListener(
- valueAnimator -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
- }
anim.play(pullback);
} else if (mStartState == ALL_APPS) {
AnimatorSetBuilder builder = new AnimatorSetBuilder();
@@ -190,19 +173,13 @@
}
@Override
- public void onDragEnd(float velocity) {
- boolean fling = mSwipeDetector.isFling(velocity);
+ public void onDragEnd(float velocity, boolean fling) {
final int logAction = fling ? Touch.FLING : Touch.SWIPE;
float progress = mCurrentAnimation.getProgressFraction();
float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(progress);
boolean success = interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS
|| (velocity < 0 && fling);
if (success) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = mLauncher.getOverviewPanel();
- recentsView.switchToScreenshot(null,
- () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
- }
mLauncher.getStateManager().goToState(mEndState, true,
() -> onSwipeInteractionCompleted(mEndState));
if (mStartState != mEndState) {
@@ -213,8 +190,6 @@
AbstractFloatingView.closeAllOpenViews(mLauncher);
logStateChange(topOpenView.getLogContainerType(), logAction);
}
- ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
} else {
// Quickly return to the state we came from (we didn't move far).
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 912be98..5c3b55d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -30,7 +30,6 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.view.MotionEvent;
@@ -43,7 +42,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.quickstep.SysUINavigationMode;
@@ -51,7 +50,6 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
/**
* Handles quick switching to a recent task from the home screen.
@@ -61,10 +59,10 @@
private @Nullable TaskView mTaskToLaunch;
public QuickSwitchTouchController(Launcher launcher) {
- this(launcher, SingleAxisSwipeDetector.HORIZONTAL);
+ this(launcher, SwipeDetector.HORIZONTAL);
}
- protected QuickSwitchTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
+ protected QuickSwitchTouchController(Launcher l, SwipeDetector.Direction dir) {
super(l, dir);
}
@@ -96,8 +94,6 @@
super.onDragStart(start);
mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
mTaskToLaunch = mLauncher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
- ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index ad02de1..00e4f58 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -19,9 +19,6 @@
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator;
@@ -35,8 +32,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.touch.BaseSwipeDetector;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.launcher3.util.PendingAnimation;
@@ -50,14 +46,15 @@
* Touch controller for handling task view card swipes
*/
public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
- extends AnimatorListenerAdapter implements TouchController,
- SingleAxisSwipeDetector.Listener {
+ extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener {
+
+ private static final String TAG = "OverviewSwipeController";
// Progress after which the transition is assumed to be a success in case user does not fling
public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
protected final T mActivity;
- private final SingleAxisSwipeDetector mDetector;
+ private final SwipeDetector mDetector;
private final RecentsView mRecentsView;
private final int[] mTempCords = new int[2];
@@ -77,7 +74,7 @@
public TaskViewTouchController(T activity) {
mActivity = activity;
mRecentsView = activity.getOverviewPanel();
- mDetector = new SingleAxisSwipeDetector(activity, this, SingleAxisSwipeDetector.VERTICAL);
+ mDetector = new SwipeDetector(activity, this, SwipeDetector.VERTICAL);
}
private boolean canInterceptTouch() {
@@ -116,7 +113,7 @@
int directionsToDetectScroll = 0;
boolean ignoreSlopWhenSettling = false;
if (mCurrentAnimation != null) {
- directionsToDetectScroll = DIRECTION_BOTH;
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
ignoreSlopWhenSettling = true;
} else {
mTaskBeingDragged = null;
@@ -129,12 +126,12 @@
if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
// Don't allow swipe down to open if we don't support swipe up
// to enter overview.
- directionsToDetectScroll = DIRECTION_POSITIVE;
+ directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
} else {
// The task can be dragged up to dismiss it,
// and down to open if it's the current page.
directionsToDetectScroll = i == mRecentsView.getCurrentPage()
- ? DIRECTION_BOTH : DIRECTION_POSITIVE;
+ ? SwipeDetector.DIRECTION_BOTH : SwipeDetector.DIRECTION_POSITIVE;
}
break;
}
@@ -168,8 +165,8 @@
return;
}
int scrollDirections = mDetector.getScrollDirections();
- if (goingUp && ((scrollDirections & DIRECTION_POSITIVE) == 0)
- || !goingUp && ((scrollDirections & DIRECTION_NEGATIVE) == 0)) {
+ if (goingUp && ((scrollDirections & SwipeDetector.DIRECTION_POSITIVE) == 0)
+ || !goingUp && ((scrollDirections & SwipeDetector.DIRECTION_NEGATIVE) == 0)) {
// Trying to re-init in an unsupported direction.
return;
}
@@ -246,8 +243,7 @@
}
@Override
- public void onDragEnd(float velocity) {
- boolean fling = mDetector.isFling(velocity);
+ public void onDragEnd(float velocity, boolean fling) {
final boolean goingToEnd;
final int logAction;
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
@@ -264,7 +260,7 @@
logAction = Touch.SWIPE;
goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
}
- long animationDuration = BaseSwipeDetector.calculateDuration(
+ long animationDuration = SwipeDetector.calculateDuration(
velocity, goingToEnd ? (1 - progress) : progress);
if (blockedFling && !goingToEnd) {
animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
index 0ed5291..f1e4041 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
@@ -17,12 +17,12 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.touch.SwipeDetector;
public class TransposedQuickSwitchTouchController extends QuickSwitchTouchController {
public TransposedQuickSwitchTouchController(Launcher launcher) {
- super(launcher, SingleAxisSwipeDetector.VERTICAL);
+ super(launcher, SwipeDetector.VERTICAL);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 59b117f..8a11ac8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -70,7 +70,7 @@
activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
BaseActivityInterface.AnimationFactory factory =
- mHelper.prepareRecentsUI(wasVisible,
+ mHelper.prepareRecentsUI(activity, wasVisible,
false /* animate activity */, (controller) -> {
controller.dispatchOnStart();
ValueAnimator anim = controller.getAnimationPlayer()
@@ -102,7 +102,7 @@
anim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
- mHelper.onSwipeUpToRecentsComplete();
+ mHelper.onSwipeUpToRecentsComplete(mActivity);
if (mRecentsView != null) {
mRecentsView.animateUpRunningTaskIconScale();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 4f50e33..e1e994c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,15 +15,20 @@
*/
package com.android.quickstep;
+import static android.os.VibrationEffect.EFFECT_CLICK;
+import static android.os.VibrationEffect.createPredefined;
+
+import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.Animator;
import android.annotation.TargetApi;
+import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
@@ -31,6 +36,11 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -45,9 +55,9 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.graphics.RotationMode;
-import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
+import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AppWindowAnimationHelper;
@@ -86,14 +96,17 @@
protected float mDragLengthFactor = 1;
protected final Context mContext;
- protected final RecentsAnimationDeviceState mDeviceState;
- protected final GestureState mGestureState;
+ protected final OverviewComponentObserver mOverviewComponentObserver;
protected final BaseActivityInterface<T> mActivityInterface;
- protected final InputConsumerController mInputConsumer;
+ protected final RecentsModel mRecentsModel;
+ protected final int mRunningTaskId;
protected final AppWindowAnimationHelper mAppWindowAnimationHelper;
protected final TransformParams mTransformParams = new TransformParams();
+ private final Vibrator mVibrator;
+ protected final Mode mMode;
+
// Shift in the range of [0, 1].
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
@@ -101,6 +114,7 @@
protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
protected final ActivityInitListener mActivityInitListener;
+ protected final InputConsumerController mInputConsumer;
protected RecentsAnimationController mRecentsAnimationController;
protected RecentsAnimationTargets mRecentsAnimationTargets;
@@ -115,30 +129,54 @@
protected Runnable mGestureEndCallback;
+ protected final Handler mMainThreadHandler = MAIN_EXECUTOR.getHandler();
protected MultiStateCallback mStateCallback;
protected boolean mCanceled;
protected int mFinishingRecentsAnimationForNewTaskId = -1;
- protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, InputConsumerController inputConsumer) {
+ protected BaseSwipeUpHandler(Context context, GestureState gestureState,
+ OverviewComponentObserver overviewComponentObserver,
+ RecentsModel recentsModel, InputConsumerController inputConsumer, int runningTaskId) {
mContext = context;
- mDeviceState = deviceState;
- mGestureState = gestureState;
+ mOverviewComponentObserver = overviewComponentObserver;
mActivityInterface = gestureState.getActivityInterface();
+ mRecentsModel = recentsModel;
mActivityInitListener =
mActivityInterface.createActivityInitListener(this::onActivityInit);
+ mRunningTaskId = runningTaskId;
mInputConsumer = inputConsumer;
+ mMode = SysUINavigationMode.getMode(context);
mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
-
+ mVibrator = context.getSystemService(Vibrator.class);
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext));
}
+ protected void setStateOnUiThread(int stateFlag) {
+ if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
+ mStateCallback.setState(stateFlag);
+ } else {
+ postAsyncCallback(mMainThreadHandler, () -> mStateCallback.setState(stateFlag));
+ }
+ }
+
protected void performHapticFeedback() {
- VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
+ if (!mVibrator.hasVibrator()) {
+ return;
+ }
+ if (Settings.System.getInt(
+ mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 0) {
+ return;
+ }
+
+ VibrationEffect effect = createPredefined(EFFECT_CLICK);
+ if (effect == null) {
+ return;
+ }
+ UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(effect));
}
public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode rotationMode) {
@@ -208,14 +246,14 @@
success -> {
resultCallback.accept(success);
if (!success) {
- mActivityInterface.onLaunchTaskFailed();
+ mActivityInterface.onLaunchTaskFailed(mActivity);
nextTask.notifyTaskLaunchFailed(TAG);
} else {
- mActivityInterface.onLaunchTaskSuccess();
+ mActivityInterface.onLaunchTaskSuccess(mActivity);
}
- }, MAIN_EXECUTOR.getHandler());
+ }, mMainThreadHandler);
}
- mStateCallback.setStateOnUiThread(successStateFlag);
+ setStateOnUiThread(successStateFlag);
}
mCanceled = false;
mFinishingRecentsAnimationForNewTaskId = -1;
@@ -250,8 +288,7 @@
mRecentsAnimationTargets = targets;
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
final Rect overviewStackBounds;
- RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
- mGestureState.getRunningTaskId());
+ RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mRunningTaskId);
if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
overviewStackBounds = mActivityInterface
@@ -318,7 +355,7 @@
mAppWindowAnimationHelper.updateHomeBounds(getStackBounds(dp));
}
mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
- if (mDeviceState.isFullyGesturalNavMode()) {
+ if (mMode == Mode.NO_BUTTON) {
// We can drag all the way to the top of the screen.
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
}
@@ -329,7 +366,7 @@
*/
protected abstract boolean moveWindowWithRecentsScroll();
- protected abstract boolean onActivityInit(Boolean alreadyOnHome);
+ protected abstract boolean onActivityInit(final T activity, Boolean alreadyOnHome);
/**
* Called to create a input proxy for the running task
@@ -357,13 +394,13 @@
@UiThread
public abstract void onGestureEnded(float endVelocity, PointF velocity, PointF downPos);
- public abstract void onConsumerAboutToBeSwitched();
+ public abstract void onConsumerAboutToBeSwitched(SwipeSharedState sharedState);
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { }
public void initWhenReady() {
// Preload the plan
- RecentsModel.INSTANCE.get(mContext).getTasks(null);
+ mRecentsModel.getTasks(null);
mActivityInitListener.register();
}
@@ -480,8 +517,8 @@
public interface Factory {
- BaseSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs,
- boolean continuingLastGesture, boolean isLikelyToStartNewTask);
+ BaseSwipeUpHandler newHandler(GestureState gestureState, RunningTaskInfo runningTask,
+ long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask);
}
protected interface RunningWindowAnim {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index f889bc1..8deb835 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -30,7 +30,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -41,8 +40,8 @@
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
-import java.util.function.Predicate;
/**
* {@link BaseActivityInterface} for recents when the default launcher is different than the
@@ -55,7 +54,7 @@
public FallbackActivityInterface() { }
@Override
- public void onTransitionCancelled(boolean activityVisible) {
+ public void onTransitionCancelled(RecentsActivity activity, boolean activityVisible) {
// TODO:
}
@@ -73,11 +72,7 @@
}
@Override
- public void onSwipeUpToRecentsComplete() {
- RecentsActivity activity = getCreatedActivity();
- if (activity == null) {
- return;
- }
+ public void onSwipeUpToRecentsComplete(RecentsActivity activity) {
RecentsView recentsView = activity.getOverviewPanel();
recentsView.getClearAllButton().setVisibilityAlpha(1);
recentsView.setDisallowScrollToClearAll(false);
@@ -92,8 +87,7 @@
@NonNull
@Override
- public HomeAnimationFactory prepareHomeUI() {
- RecentsActivity activity = getCreatedActivity();
+ public HomeAnimationFactory prepareHomeUI(RecentsActivity activity) {
RecentsView recentsView = activity.getOverviewPanel();
return new HomeAnimationFactory() {
@@ -124,9 +118,8 @@
}
@Override
- public AnimationFactory prepareRecentsUI(boolean activityVisible,
+ public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
- RecentsActivity activity = getCreatedActivity();
if (activityVisible) {
return (transitionLength) -> { };
}
@@ -183,9 +176,8 @@
@Override
public ActivityInitListener createActivityInitListener(
- Predicate<Boolean> onInitListener) {
- return new ActivityInitListener<>((activity, alreadyOnHome) ->
- onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
+ BiPredicate<RecentsActivity, Boolean> onInitListener) {
+ return new ActivityInitListener(onInitListener, RecentsActivity.ACTIVITY_TRACKER);
}
@Nullable
@@ -236,21 +228,13 @@
}
@Override
- public void onLaunchTaskFailed() {
+ public void onLaunchTaskFailed(RecentsActivity activity) {
// TODO: probably go back to overview instead.
- RecentsActivity activity = getCreatedActivity();
- if (activity == null) {
- return;
- }
activity.<RecentsView>getOverviewPanel().startHome();
}
@Override
- public void onLaunchTaskSuccess() {
- RecentsActivity activity = getCreatedActivity();
- if (activity == null) {
- return;
- }
+ public void onLaunchTaskSuccess(RecentsActivity activity) {
activity.onTaskLaunched();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 844152b..f6b3654 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -29,7 +29,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.LauncherSwipeHandler.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.WindowTransformSwipeHandler.RECENTS_ATTACH_DURATION;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -47,23 +47,20 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherInitListenerEx;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.ShelfPeekAnim;
-import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
@@ -72,8 +69,8 @@
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
-import java.util.function.Predicate;
/**
* {@link BaseActivityInterface} for the in-launcher recents.
@@ -95,65 +92,50 @@
}
@Override
- public void onTransitionCancelled(boolean activityVisible) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- LauncherState startState = launcher.getStateManager().getRestState();
- launcher.getStateManager().goToState(startState, activityVisible);
+ public void onTransitionCancelled(Launcher activity, boolean activityVisible) {
+ LauncherState startState = activity.getStateManager().getRestState();
+ activity.getStateManager().goToState(startState, activityVisible);
}
@Override
- public void onSwipeUpToRecentsComplete() {
+ public void onSwipeUpToRecentsComplete(Launcher activity) {
// Re apply state in case we did something funky during the transition.
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- launcher.getStateManager().reapplyState();
- DiscoveryBounce.showForOverviewIfNeeded(launcher);
+ activity.getStateManager().reapplyState();
+ DiscoveryBounce.showForOverviewIfNeeded(activity);
}
@Override
- public void onSwipeUpToHomeComplete() {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
+ public void onSwipeUpToHomeComplete(Launcher activity) {
// Ensure recents is at the correct position for NORMAL state. For example, when we detach
// recents, we assume the first task is invisible, making translation off by one task.
- launcher.getStateManager().reapplyState();
+ activity.getStateManager().reapplyState();
setLauncherHideBackArrow(false);
}
private void setLauncherHideBackArrow(boolean hideBackArrow) {
Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
+ if (launcher != null) {
+ launcher.getRootView().setForceHideBackArrow(hideBackArrow);
}
- launcher.getRootView().setForceHideBackArrow(hideBackArrow);
}
@Override
public void onAssistantVisibilityChanged(float visibility) {
Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
+ if (launcher != null) {
+ launcher.onAssistantVisibilityChanged(visibility);
}
- launcher.onAssistantVisibilityChanged(visibility);
}
@NonNull
@Override
- public HomeAnimationFactory prepareHomeUI() {
- Launcher launcher = getCreatedActivity();
- final DeviceProfile dp = launcher.getDeviceProfile();
- final RecentsView recentsView = launcher.getOverviewPanel();
+ public HomeAnimationFactory prepareHomeUI(Launcher activity) {
+ final DeviceProfile dp = activity.getDeviceProfile();
+ final RecentsView recentsView = activity.getOverviewPanel();
final TaskView runningTaskView = recentsView.getRunningTaskView();
final View workspaceView;
if (runningTaskView != null && runningTaskView.getTask().key.getComponent() != null) {
- workspaceView = launcher.getWorkspace().getFirstMatchForAppClose(
+ workspaceView = activity.getWorkspace().getFirstMatchForAppClose(
runningTaskView.getTask().key.getComponent().getPackageName(),
UserHandle.of(runningTaskView.getTask().key.userId));
} else {
@@ -162,7 +144,7 @@
final RectF iconLocation = new RectF();
boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
FloatingIconView floatingIconView = canUseWorkspaceView
- ? FloatingIconView.getFloatingIconView(launcher, workspaceView,
+ ? FloatingIconView.getFloatingIconView(activity, workspaceView,
true /* hideOriginal */, iconLocation, false /* isOpening */)
: null;
setLauncherHideBackArrow(true);
@@ -188,14 +170,14 @@
public AnimatorPlaybackController createActivityAnimationToHome() {
// Return an empty APC here since we have an non-user controlled animation to home.
long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
- return launcher.getStateManager().createAnimationToNewWorkspace(NORMAL, accuracy,
+ return activity.getStateManager().createAnimationToNewWorkspace(NORMAL, accuracy,
0 /* animComponents */);
}
@Override
public void playAtomicAnimation(float velocity) {
// Setup workspace with 0 duration to prepare for our staggered animation.
- LauncherStateManager stateManager = launcher.getStateManager();
+ LauncherStateManager stateManager = activity.getStateManager();
AnimatorSetBuilder builder = new AnimatorSetBuilder();
// setRecentsAttachedToAppWindow() will animate recents out.
builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW);
@@ -205,40 +187,39 @@
// Stop scrolling so that it doesn't interfere with the translation offscreen.
recentsView.getScroller().forceFinished(true);
- new StaggeredWorkspaceAnim(launcher, workspaceView, velocity).start();
+ new StaggeredWorkspaceAnim(activity, workspaceView, velocity).start();
}
};
}
@Override
- public AnimationFactory prepareRecentsUI(boolean activityVisible,
+ public AnimationFactory prepareRecentsUI(Launcher activity, boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
- BaseQuickstepLauncher launcher = getCreatedActivity();
- final LauncherState startState = launcher.getStateManager().getState();
+ final LauncherState startState = activity.getStateManager().getState();
LauncherState resetState = startState;
if (startState.disableRestore) {
- resetState = launcher.getStateManager().getRestState();
+ resetState = activity.getStateManager().getRestState();
}
- launcher.getStateManager().setRestState(resetState);
+ activity.getStateManager().setRestState(resetState);
final LauncherState fromState = animateActivity ? BACKGROUND_APP : OVERVIEW;
- launcher.getStateManager().goToState(fromState, false);
+ activity.getStateManager().goToState(fromState, false);
// Since all apps is not visible, we can safely reset the scroll position.
// This ensures then the next swipe up to all-apps starts from scroll 0.
- launcher.getAppsView().reset(false /* animate */);
+ activity.getAppsView().reset(false /* animate */);
return new AnimationFactory() {
- private final ShelfPeekAnim mShelfAnim = launcher.getShelfPeekAnim();
+ private ShelfAnimState mShelfState;
private boolean mIsAttachedToWindow;
@Override
public void createActivityInterface(long transitionLength) {
- createActivityInterfaceInternal(launcher, fromState, transitionLength, callback);
+ createActivityInterfaceInternal(activity, fromState, transitionLength, callback);
// Creating the activity controller animation sometimes reapplies the launcher state
// (because we set the animation as the current state animation), so we reapply the
// attached state here as well to ensure recents is shown/hidden appropriately.
- if (SysUINavigationMode.getMode(launcher) == Mode.NO_BUTTON) {
+ if (SysUINavigationMode.getMode(activity) == Mode.NO_BUTTON) {
setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
}
}
@@ -252,13 +233,36 @@
@Override
public void onTransitionCancelled() {
- launcher.getStateManager().goToState(startState, false /* animate */);
+ activity.getStateManager().goToState(startState, false /* animate */);
}
@Override
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
long duration) {
- mShelfAnim.setShelfState(shelfState, interpolator, duration);
+ if (mShelfState == shelfState) {
+ return;
+ }
+ mShelfState = shelfState;
+ activity.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM);
+ if (mShelfState == ShelfAnimState.CANCEL) {
+ return;
+ }
+ float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(activity);
+ float shelfOverviewProgress = OVERVIEW.getVerticalProgress(activity);
+ // Peek based on default overview progress so we can see hotseat if we're showing
+ // that instead of predictions in overview.
+ float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(activity);
+ float shelfPeekingProgress = shelfHiddenProgress
+ - (shelfHiddenProgress - defaultOverviewProgress) * 0.25f;
+ float toProgress = mShelfState == ShelfAnimState.HIDE
+ ? shelfHiddenProgress
+ : mShelfState == ShelfAnimState.PEEK
+ ? shelfPeekingProgress
+ : shelfOverviewProgress;
+ Animator shelfAnim = activity.getStateManager()
+ .createStateElementAnimation(INDEX_SHELF_ANIM, toProgress);
+ shelfAnim.setInterpolator(interpolator);
+ shelfAnim.setDuration(duration).start();
}
@Override
@@ -267,8 +271,8 @@
return;
}
mIsAttachedToWindow = attached;
- LauncherRecentsView recentsView = launcher.getOverviewPanel();
- Animator fadeAnim = launcher.getStateManager()
+ LauncherRecentsView recentsView = activity.getOverviewPanel();
+ Animator fadeAnim = activity.getStateManager()
.createStateElementAnimation(
INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
@@ -282,7 +286,7 @@
float fromTranslationX = attached ? offscreenX - scrollOffsetX : 0;
float toTranslationX = attached ? 0 : offscreenX - scrollOffsetX;
- launcher.getStateManager()
+ activity.getStateManager()
.cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
if (!recentsView.isShown() && animate) {
@@ -294,7 +298,7 @@
if (!animate) {
recentsView.setTranslationX(toTranslationX);
} else {
- launcher.getStateManager().createStateElementAnimation(
+ activity.getStateManager().createStateElementAnimation(
INDEX_RECENTS_TRANSLATE_X_ANIM,
fromTranslationX, toTranslationX).start();
}
@@ -392,15 +396,15 @@
}
@Override
- public ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener) {
- return new LauncherInitListener((activity, alreadyOnHome) ->
- onInitListener.test(alreadyOnHome));
+ public ActivityInitListener createActivityInitListener(
+ BiPredicate<Launcher, Boolean> onInitListener) {
+ return new LauncherInitListenerEx(onInitListener);
}
@Nullable
@Override
- public BaseQuickstepLauncher getCreatedActivity() {
- return BaseQuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+ public Launcher getCreatedActivity() {
+ return Launcher.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@@ -465,20 +469,12 @@
}
@Override
- public void onLaunchTaskFailed() {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
+ public void onLaunchTaskFailed(Launcher launcher) {
launcher.getStateManager().goToState(OVERVIEW);
}
@Override
- public void onLaunchTaskSuccess() {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
+ public void onLaunchTaskSuccess(Launcher launcher) {
launcher.getStateManager().moveToRestState();
}
@@ -497,38 +493,22 @@
}
@Override
- public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
- Runnable onFinishRunnable) {
+ public void switchToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {
Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
RecentsView recentsView = launcher.getOverviewPanel();
if (recentsView == null) {
- if (onFinishRunnable != null) {
- onFinishRunnable.run();
+ if (runnable != null) {
+ runnable.run();
}
return;
}
- recentsView.switchToScreenshot(thumbnailData, onFinishRunnable);
- }
-
- @Override
- public void setOnDeferredActivityLaunchCallback(Runnable r) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
+ TaskView taskView = recentsView.getRunningTaskView();
+ if (taskView != null) {
+ taskView.setShowScreenshot(true);
+ taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
+ ViewUtils.postDraw(taskView, runnable);
+ } else if (runnable != null) {
+ runnable.run();
}
- launcher.setOnDeferredActivityLaunchCallback(r);
- }
-
- @Override
- public void updateOverviewPredictionState() {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
- PredictionUiStateManager.Client.OVERVIEW);
}
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
new file mode 100644
index 0000000..357c9fc
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
@@ -0,0 +1,137 @@
+/*
+ * 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.quickstep;
+
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.launcher3.config.FeatureFlags;
+
+import java.util.StringJoiner;
+import java.util.function.Consumer;
+
+/**
+ * Utility class to help manage multiple callbacks based on different states.
+ */
+public class MultiStateCallback {
+
+ private static final String TAG = "MultiStateCallback";
+ public static final boolean DEBUG_STATES = false;
+
+ private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
+ private final SparseArray<Consumer<Boolean>> mStateChangeHandlers = new SparseArray<>();
+
+ private final String[] mStateNames;
+
+ public MultiStateCallback(String[] stateNames) {
+ mStateNames = DEBUG_STATES ? stateNames : null;
+ }
+
+ private int mState = 0;
+
+ /**
+ * Adds the provided state flags to the global state and executes any callbacks as a result.
+ */
+ public void setState(int stateFlag) {
+ if (DEBUG_STATES) {
+ Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
+ + convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
+ }
+
+ int oldState = mState;
+ mState = mState | stateFlag;
+
+ int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ int state = mCallbacks.keyAt(i);
+
+ if ((mState & state) == state) {
+ Runnable callback = mCallbacks.valueAt(i);
+ if (callback != null) {
+ // Set the callback to null, so that it does not run again.
+ mCallbacks.setValueAt(i, null);
+ callback.run();
+ }
+ }
+ }
+ notifyStateChangeHandlers(oldState);
+ }
+
+ /**
+ * Adds the provided state flags to the global state and executes any change handlers
+ * as a result.
+ */
+ public void clearState(int stateFlag) {
+ if (DEBUG_STATES) {
+ Log.d(TAG, "[" + System.identityHashCode(this) + "] Removing "
+ + convertToFlagNames(stateFlag) + " from " + convertToFlagNames(mState));
+ }
+
+ int oldState = mState;
+ mState = mState & ~stateFlag;
+ notifyStateChangeHandlers(oldState);
+ }
+
+ private void notifyStateChangeHandlers(int oldState) {
+ int count = mStateChangeHandlers.size();
+ for (int i = 0; i < count; i++) {
+ int state = mStateChangeHandlers.keyAt(i);
+ boolean wasOn = (state & oldState) == state;
+ boolean isOn = (state & mState) == state;
+
+ if (wasOn != isOn) {
+ mStateChangeHandlers.valueAt(i).accept(isOn);
+ }
+ }
+ }
+
+ /**
+ * Sets the callbacks to be run when the provided states are enabled.
+ * The callback is only run once.
+ */
+ public void addCallback(int stateMask, Runnable callback) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && mCallbacks.get(stateMask) != null) {
+ throw new IllegalStateException("Multiple callbacks on same state");
+ }
+ mCallbacks.put(stateMask, callback);
+ }
+
+ /**
+ * Sets the handler to be called when the provided states are enabled or disabled.
+ */
+ public void addChangeHandler(int stateMask, Consumer<Boolean> handler) {
+ mStateChangeHandlers.put(stateMask, handler);
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ public boolean hasStates(int stateMask) {
+ return (mState & stateMask) == stateMask;
+ }
+
+ private String convertToFlagNames(int flags) {
+ StringJoiner joiner = new StringJoiner(", ", "[", " (" + flags + ")]");
+ for (int i = 0; i < mStateNames.length; i++) {
+ if ((flags & (1 << i)) != 0) {
+ joiner.add(mStateNames[i]);
+ }
+ }
+ return joiner.toString();
+ }
+
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index c4733bd..150c44d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -29,7 +29,6 @@
import androidx.annotation.BinderThread;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.ActivityInitListener;
@@ -204,8 +203,7 @@
return false;
}
- private boolean onActivityReady(Boolean wasVisible) {
- final T activity = mActivityInterface.getCreatedActivity();
+ private boolean onActivityReady(T activity, Boolean wasVisible) {
if (!mUserEventLogged) {
activity.getUserEventDispatcher().logActionCommand(
LauncherLogProto.Action.Command.RECENTS_BUTTON,
@@ -213,10 +211,6 @@
LauncherLogProto.ContainerType.TASKSWITCHER);
mUserEventLogged = true;
}
-
- // Switch prediction client to overview
- PredictionUiStateManager.INSTANCE.get(activity).switchClient(
- PredictionUiStateManager.Client.OVERVIEW);
return mAnimationProvider.onActivityReady(activity, wasVisible);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 92c55da..ce533a6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -10,6 +10,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.testing.TestInformationHandler;
import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -33,7 +34,7 @@
switch (method) {
case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
final float swipeHeight =
- LayoutUtils.getDefaultSwipeHeight(mContext, mDeviceProfile);
+ OverviewState.getDefaultSwipeHeight(mContext, mDeviceProfile);
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
return response;
}
@@ -111,6 +112,11 @@
@Override
protected boolean isLauncherInitialized() {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "isLauncherInitialized.TouchInteractionService.isInitialized=" +
+ TouchInteractionService.isInitialized());
+ }
return super.isLauncherInitialized() && TouchInteractionService.isInitialized();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
new file mode 100644
index 0000000..cd8e1a4
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.util.Log;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
+
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.io.PrintWriter;
+
+/**
+ * Utility class used to store state information shared across multiple transitions.
+ */
+public class SwipeSharedState implements RecentsAnimationListener {
+
+ private OverviewComponentObserver mOverviewComponentObserver;
+
+ private RecentsAnimationCallbacks mRecentsAnimationListener;
+ private RecentsAnimationController mLastRecentsAnimationController;
+ private RecentsAnimationTargets mLastAnimationTarget;
+
+ private boolean mLastAnimationCancelled = false;
+ private boolean mLastAnimationRunning = false;
+
+ public boolean canGestureBeContinued;
+ public boolean goingToLauncher;
+ public boolean recentsAnimationFinishInterrupted;
+ public int nextRunningTaskId = -1;
+ private int mLogId;
+
+ public void setOverviewComponentObserver(OverviewComponentObserver observer) {
+ mOverviewComponentObserver = observer;
+ }
+
+ @Override
+ public final void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ mLastRecentsAnimationController = controller;
+ mLastAnimationTarget = targets;
+
+ mLastAnimationCancelled = false;
+ mLastAnimationRunning = true;
+ }
+
+ @Override
+ public final void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ if (thumbnailData != null) {
+ mOverviewComponentObserver.getActivityInterface().switchToScreenshot(thumbnailData,
+ () -> {
+ mLastRecentsAnimationController.cleanupScreenshot();
+ clearAnimationState();
+ });
+ } else {
+ clearAnimationState();
+ }
+ }
+
+ @Override
+ public final void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ if (mLastRecentsAnimationController == controller) {
+ mLastAnimationRunning = false;
+ }
+ }
+
+ private void clearAnimationTarget() {
+ if (mLastAnimationTarget != null) {
+ mLastAnimationTarget.release();
+ mLastAnimationTarget = null;
+ }
+ }
+
+ private void clearAnimationState() {
+ clearAnimationTarget();
+
+ mLastAnimationCancelled = true;
+ mLastAnimationRunning = false;
+ }
+
+ private void clearListenerState(boolean finishAnimation) {
+ if (mRecentsAnimationListener != null) {
+ mRecentsAnimationListener.removeListener(this);
+ mRecentsAnimationListener.notifyAnimationCanceled();
+ if (mLastAnimationRunning && mLastRecentsAnimationController != null) {
+ Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
+ finishAnimation
+ ? mLastRecentsAnimationController::finishAnimationToHome
+ : mLastRecentsAnimationController::finishAnimationToApp);
+ mLastRecentsAnimationController = null;
+ mLastAnimationTarget = null;
+ }
+ }
+ mRecentsAnimationListener = null;
+ clearAnimationTarget();
+ mLastAnimationCancelled = false;
+ mLastAnimationRunning = false;
+ }
+
+ public RecentsAnimationCallbacks newRecentsAnimationCallbacks() {
+ Preconditions.assertUIThread();
+
+ if (mLastAnimationRunning) {
+ String msg = "New animation started before completing old animation";
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ throw new IllegalArgumentException(msg);
+ } else {
+ Log.e("SwipeSharedState", msg, new Exception());
+ }
+ }
+
+ clearListenerState(false /* finishAnimation */);
+ boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
+ : mOverviewComponentObserver.getActivityInterface().shouldMinimizeSplitScreen();
+ mRecentsAnimationListener = new RecentsAnimationCallbacks(shouldMinimiseSplitScreen);
+ mRecentsAnimationListener.addListener(this);
+ return mRecentsAnimationListener;
+ }
+
+ public RecentsAnimationCallbacks getActiveListener() {
+ return mRecentsAnimationListener;
+ }
+
+ public void applyActiveRecentsAnimationState(RecentsAnimationListener listener) {
+ if (mLastRecentsAnimationController != null) {
+ listener.onRecentsAnimationStart(mLastRecentsAnimationController,
+ mLastAnimationTarget);
+ } else if (mLastAnimationCancelled) {
+ listener.onRecentsAnimationCanceled(null);
+ }
+ }
+
+ /**
+ * Called when a recents animation has finished, but was interrupted before the next task was
+ * launched. The given {@param runningTaskId} should be used as the running task for the
+ * continuing input consumer.
+ */
+ public void setRecentsAnimationFinishInterrupted(int runningTaskId) {
+ recentsAnimationFinishInterrupted = true;
+ nextRunningTaskId = runningTaskId;
+ mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
+ }
+
+ public void clearAllState(boolean finishAnimation) {
+ clearListenerState(finishAnimation);
+ canGestureBeContinued = false;
+ recentsAnimationFinishInterrupted = false;
+ nextRunningTaskId = -1;
+ goingToLauncher = false;
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "goingToLauncher=" + goingToLauncher);
+ pw.println(prefix + "canGestureBeContinued=" + canGestureBeContinued);
+ pw.println(prefix + "recentsAnimationFinishInterrupted=" + recentsAnimationFinishInterrupted);
+ pw.println(prefix + "nextRunningTaskId=" + nextRunningTaskId);
+ pw.println(prefix + "lastAnimationCancelled=" + mLastAnimationCancelled);
+ pw.println(prefix + "lastAnimationRunning=" + mLastAnimationRunning);
+ pw.println(prefix + "logTraceId=" + mLogId);
+ }
+
+ public void setLogTraceId(int logId) {
+ this.mLogId = logId;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index b5441df..17457aa 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -19,11 +19,11 @@
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.graphics.Matrix;
+import android.view.View;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
-import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.views.TaskThumbnailView;
@@ -40,30 +40,30 @@
public class TaskOverlayFactory implements ResourceBasedOverride {
/** Note that these will be shown in order from top to bottom, if available for the task. */
- private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
- TaskShortcutFactory.APP_INFO,
- TaskShortcutFactory.SPLIT_SCREEN,
- TaskShortcutFactory.PIN,
- TaskShortcutFactory.INSTALL,
- TaskShortcutFactory.FREE_FORM,
- TaskShortcutFactory.WELLBEING
+ private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
+ new TaskSystemShortcut.AppInfo(),
+ new TaskSystemShortcut.SplitScreen(),
+ new TaskSystemShortcut.Pin(),
+ new TaskSystemShortcut.Install(),
+ new TaskSystemShortcut.Freeform()
};
- public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
- final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
+ public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
+ forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
+
+ public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
+ final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
- for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
- SystemShortcut shortcut = menuOption.getShortcut(activity, taskView);
- if (shortcut != null) {
- shortcuts.add(shortcut);
+ for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
+ View.OnClickListener onClickListener =
+ menuOption.getOnClickListener(activity, taskView);
+ if (onClickListener != null) {
+ shortcuts.add(menuOption);
}
}
return shortcuts;
}
- public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
- forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
-
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
return new TaskOverlay();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
deleted file mode 100644
index 9ba2e5a..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.view.View;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.model.WellbeingModel;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.popup.SystemShortcut.AppInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.InstantAppResolver;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskThumbnailView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Represents a system shortcut that can be shown for a recent task.
- */
-public interface TaskShortcutFactory {
-
- SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
-
- static WorkspaceItemInfo dummyInfo(TaskView view) {
- Task task = view.getTask();
-
- WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
- dummyInfo.intent = new Intent();
- ComponentName component = task.getTopComponent();
- dummyInfo.intent.setComponent(component);
- dummyInfo.user = UserHandle.of(task.key.userId);
- dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
- return dummyInfo;
- }
-
- TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, dummyInfo(view));
-
- abstract class MultiWindowFactory implements TaskShortcutFactory {
-
- private final int mIconRes;
- private final int mTextRes;
-
- MultiWindowFactory(int iconRes, int textRes) {
- mIconRes = iconRes;
- mTextRes = textRes;
- }
-
- protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
- protected abstract ActivityOptions makeLaunchOptions(Activity activity);
- protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
-
- @Override
- public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
- final Task task = taskView.getTask();
- if (!task.isDockable) {
- return null;
- }
- if (!isAvailable(activity, task.key.displayId)) {
- return null;
- }
- return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this);
- }
- }
-
- class MultiWindowSystemShortcut extends SystemShortcut {
-
- private Handler mHandler;
-
- private final RecentsView mRecentsView;
- private final TaskThumbnailView mThumbnailView;
- private final TaskView mTaskView;
- private final MultiWindowFactory mFactory;
-
- public MultiWindowSystemShortcut(int iconRes, int textRes,
- BaseDraggingActivity activity, TaskView taskView, MultiWindowFactory factory) {
- super(iconRes, textRes, activity, dummyInfo(taskView));
-
- mHandler = new Handler(Looper.getMainLooper());
- mTaskView = taskView;
- mRecentsView = activity.getOverviewPanel();
- mThumbnailView = taskView.getThumbnail();
- mFactory = factory;
- }
-
- @Override
- public void onClick(View view) {
- Task.TaskKey taskKey = mTaskView.getTask().key;
- final int taskId = taskKey.id;
-
- final View.OnLayoutChangeListener onLayoutChangeListener =
- new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int l, int t, int r, int b,
- int oldL, int oldT, int oldR, int oldB) {
- mTaskView.getRootView().removeOnLayoutChangeListener(this);
- mRecentsView.clearIgnoreResetTask(taskId);
-
- // Start animating in the side pages once launcher has been resized
- mRecentsView.dismissTask(mTaskView, false, false);
- }
- };
-
- final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
- new DeviceProfile.OnDeviceProfileChangeListener() {
- @Override
- public void onDeviceProfileChanged(DeviceProfile dp) {
- mTarget.removeOnDeviceProfileChangeListener(this);
- if (dp.isMultiWindowMode) {
- mTaskView.getRootView().addOnLayoutChangeListener(
- onLayoutChangeListener);
- }
- }
- };
-
- dismissTaskMenuView(mTarget);
-
- ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
- if (options != null
- && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
- options)) {
- if (!mFactory.onActivityStarted(mTarget)) {
- return;
- }
- // Add a device profile change listener to kick off animating the side tasks
- // once we enter multiwindow mode and relayout
- mTarget.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
-
- final Runnable animStartedListener = () -> {
- // Hide the task view and wait for the window to be resized
- // TODO: Consider animating in launcher and do an in-place start activity
- // afterwards
- mRecentsView.setIgnoreResetTask(taskId);
- mTaskView.setAlpha(0f);
- };
-
- final int[] position = new int[2];
- mThumbnailView.getLocationOnScreen(position);
- final int width = (int) (mThumbnailView.getWidth() * mTaskView.getScaleX());
- final int height = (int) (mThumbnailView.getHeight() * mTaskView.getScaleY());
- final Rect taskBounds = new Rect(position[0], position[1],
- position[0] + width, position[1] + height);
-
- // Take the thumbnail of the task without a scrim and apply it back after
- float alpha = mThumbnailView.getDimAlpha();
- mThumbnailView.setDimAlpha(0);
- Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
- taskBounds.width(), taskBounds.height(), mThumbnailView, 1f,
- Color.BLACK);
- mThumbnailView.setDimAlpha(alpha);
-
- AppTransitionAnimationSpecsFuture future =
- new AppTransitionAnimationSpecsFuture(mHandler) {
- @Override
- public List<AppTransitionAnimationSpecCompat> composeSpecs() {
- return Collections.singletonList(new AppTransitionAnimationSpecCompat(
- taskId, thumbnail, taskBounds));
- }
- };
- WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
- future, animStartedListener, mHandler, true /* scaleUp */,
- taskKey.displayId);
- }
- }
- }
-
- TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(
- R.drawable.ic_split_screen, R.string.recent_task_option_split_screen) {
-
- @Override
- protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
- // Don't show menu-item if already in multi-window and the task is from
- // the secondary display.
- // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
- // implementation is enabled
- return !activity.getDeviceProfile().isMultiWindowMode
- && (displayId == -1 || displayId == DEFAULT_DISPLAY);
- }
-
- @Override
- protected ActivityOptions makeLaunchOptions(Activity activity) {
- final ActivityCompat act = new ActivityCompat(activity);
- final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
- act.getDisplayId());
- if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
- return null;
- }
- boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
- return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
- }
-
- @Override
- protected boolean onActivityStarted(BaseDraggingActivity activity) {
- SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
- activity.getUserEventDispatcher().logActionOnControl(TAP,
- LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
- return true;
- }
- };
-
- TaskShortcutFactory FREE_FORM = new MultiWindowFactory(
- R.drawable.ic_split_screen, R.string.recent_task_option_freeform) {
-
- @Override
- protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
- return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
- }
-
- @Override
- protected ActivityOptions makeLaunchOptions(Activity activity) {
- ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
- // Arbitrary bounds only because freeform is in dev mode right now
- Rect r = new Rect(50, 50, 200, 200);
- activityOptions.setLaunchBounds(r);
- return activityOptions;
- }
-
- @Override
- protected boolean onActivityStarted(BaseDraggingActivity activity) {
- activity.returnToHomescreen();
- return true;
- }
- };
-
- TaskShortcutFactory PIN = (activity, tv) -> {
- if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
- return null;
- }
- if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
- return null;
- }
- if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
- // We shouldn't be able to pin while an app is locked.
- return null;
- }
- return new PinSystemShortcut(activity, tv);
- };
-
- class PinSystemShortcut extends SystemShortcut {
-
- private static final String TAG = "PinSystemShortcut";
-
- private final TaskView mTaskView;
-
- public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
- super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, dummyInfo(tv));
- mTaskView = tv;
- }
-
- @Override
- public void onClick(View view) {
- Consumer<Boolean> resultCallback = success -> {
- if (success) {
- SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(
- mTaskView.getTask().key.id);
- } else {
- mTaskView.notifyTaskLaunchFailed(TAG);
- }
- };
- mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
- dismissTaskMenuView(mTarget);
- }
- }
-
- TaskShortcutFactory INSTALL = (activity, view) ->
- InstantAppResolver.newInstance(activity).isInstantApp(activity,
- view.getTask().getTopComponent().getPackageName())
- ? new SystemShortcut.Install(activity, dummyInfo(view)) : null;
-
- TaskShortcutFactory WELLBEING = (activity, view) ->
- WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, dummyInfo(view));
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
new file mode 100644
index 0000000..5a2e3ff
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.view.View;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.InstantAppResolver;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.ActivityCompat;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Represents a system shortcut that can be shown for a recent task.
+ */
+public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
+
+ private static final String TAG = "TaskSystemShortcut";
+
+ protected T mSystemShortcut;
+
+ public TaskSystemShortcut(T systemShortcut) {
+ super(systemShortcut);
+ mSystemShortcut = systemShortcut;
+ }
+
+ protected TaskSystemShortcut(int iconResId, int labelResId) {
+ super(iconResId, labelResId);
+ }
+
+ @Override
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
+ return null;
+ }
+
+ public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
+ Task task = view.getTask();
+
+ WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
+ dummyInfo.intent = new Intent();
+ ComponentName component = task.getTopComponent();
+ dummyInfo.intent.setComponent(component);
+ dummyInfo.user = UserHandle.of(task.key.userId);
+ dummyInfo.title = TaskUtils.getTitle(activity, task);
+
+ return getOnClickListenerForTask(activity, task, dummyInfo);
+ }
+
+ protected View.OnClickListener getOnClickListenerForTask(
+ BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) {
+ return mSystemShortcut.getOnClickListener(activity, dummyInfo);
+ }
+
+ public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
+ public AppInfo() {
+ super(new SystemShortcut.AppInfo());
+ }
+ }
+
+ public static abstract class MultiWindow extends TaskSystemShortcut {
+
+ private Handler mHandler;
+
+ public MultiWindow(int iconRes, int textRes) {
+ super(iconRes, textRes);
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
+ protected abstract ActivityOptions makeLaunchOptions(Activity activity);
+ protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
+
+ @Override
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, TaskView taskView) {
+ final Task task = taskView.getTask();
+ final int taskId = task.key.id;
+ final int displayId = task.key.displayId;
+ if (!task.isDockable) {
+ return null;
+ }
+ if (!isAvailable(activity, displayId)) {
+ return null;
+ }
+ final RecentsView recentsView = activity.getOverviewPanel();
+
+ final TaskThumbnailView thumbnailView = taskView.getThumbnail();
+ return (v -> {
+ final View.OnLayoutChangeListener onLayoutChangeListener =
+ new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int l, int t, int r, int b,
+ int oldL, int oldT, int oldR, int oldB) {
+ taskView.getRootView().removeOnLayoutChangeListener(this);
+ recentsView.clearIgnoreResetTask(taskId);
+
+ // Start animating in the side pages once launcher has been resized
+ recentsView.dismissTask(taskView, false, false);
+ }
+ };
+
+ final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
+ new DeviceProfile.OnDeviceProfileChangeListener() {
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ activity.removeOnDeviceProfileChangeListener(this);
+ if (dp.isMultiWindowMode) {
+ taskView.getRootView().addOnLayoutChangeListener(
+ onLayoutChangeListener);
+ }
+ }
+ };
+
+ dismissTaskMenuView(activity);
+
+ ActivityOptions options = makeLaunchOptions(activity);
+ if (options != null
+ && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
+ options)) {
+ if (!onActivityStarted(activity)) {
+ return;
+ }
+ // Add a device profile change listener to kick off animating the side tasks
+ // once we enter multiwindow mode and relayout
+ activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
+
+ final Runnable animStartedListener = () -> {
+ // Hide the task view and wait for the window to be resized
+ // TODO: Consider animating in launcher and do an in-place start activity
+ // afterwards
+ recentsView.setIgnoreResetTask(taskId);
+ taskView.setAlpha(0f);
+ };
+
+ final int[] position = new int[2];
+ thumbnailView.getLocationOnScreen(position);
+ final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
+ final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
+ final Rect taskBounds = new Rect(position[0], position[1],
+ position[0] + width, position[1] + height);
+
+ // Take the thumbnail of the task without a scrim and apply it back after
+ float alpha = thumbnailView.getDimAlpha();
+ thumbnailView.setDimAlpha(0);
+ Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
+ taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
+ Color.BLACK);
+ thumbnailView.setDimAlpha(alpha);
+
+ AppTransitionAnimationSpecsFuture future =
+ new AppTransitionAnimationSpecsFuture(mHandler) {
+ @Override
+ public List<AppTransitionAnimationSpecCompat> composeSpecs() {
+ return Collections.singletonList(new AppTransitionAnimationSpecCompat(
+ taskId, thumbnail, taskBounds));
+ }
+ };
+ WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
+ future, animStartedListener, mHandler, true /* scaleUp */, displayId);
+ }
+ });
+ }
+ }
+
+ public static class SplitScreen extends MultiWindow {
+ public SplitScreen() {
+ super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
+ }
+
+ @Override
+ protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
+ // Don't show menu-item if already in multi-window and the task is from
+ // the secondary display.
+ // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
+ // implementation is enabled
+ return !activity.getDeviceProfile().isMultiWindowMode
+ && (displayId == -1 || displayId == DEFAULT_DISPLAY);
+ }
+
+ @Override
+ protected ActivityOptions makeLaunchOptions(Activity activity) {
+ final ActivityCompat act = new ActivityCompat(activity);
+ final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
+ act.getDisplayId());
+ if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
+ return null;
+ }
+ boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
+ return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
+ }
+
+ @Override
+ protected boolean onActivityStarted(BaseDraggingActivity activity) {
+ SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
+ activity.getUserEventDispatcher().logActionOnControl(TAP,
+ LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
+ return true;
+ }
+ }
+
+ public static class Freeform extends MultiWindow {
+ public Freeform() {
+ super(R.drawable.ic_split_screen, R.string.recent_task_option_freeform);
+ }
+
+ @Override
+ protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
+ return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
+ }
+
+ @Override
+ protected ActivityOptions makeLaunchOptions(Activity activity) {
+ ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
+ // Arbitrary bounds only because freeform is in dev mode right now
+ Rect r = new Rect(50, 50, 200, 200);
+ activityOptions.setLaunchBounds(r);
+ return activityOptions;
+ }
+
+ @Override
+ protected boolean onActivityStarted(BaseDraggingActivity activity) {
+ activity.returnToHomescreen();
+ return true;
+ }
+ }
+
+ public static class Pin extends TaskSystemShortcut {
+
+ private static final String TAG = Pin.class.getSimpleName();
+
+ private Handler mHandler;
+
+ public Pin() {
+ super(R.drawable.ic_pin, R.string.recent_task_option_pin);
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ @Override
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, TaskView taskView) {
+ if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
+ return null;
+ }
+ if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
+ return null;
+ }
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+ // We shouldn't be able to pin while an app is locked.
+ return null;
+ }
+ return view -> {
+ Consumer<Boolean> resultCallback = success -> {
+ if (success) {
+ SystemUiProxy.INSTANCE.get(activity).startScreenPinning(
+ taskView.getTask().key.id);
+ } else {
+ taskView.notifyTaskLaunchFailed(TAG);
+ }
+ };
+ taskView.launchTask(true, resultCallback, mHandler);
+ dismissTaskMenuView(activity);
+ };
+ }
+ }
+
+ public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
+ public Install() {
+ super(new SystemShortcut.Install());
+ }
+
+ @Override
+ protected View.OnClickListener getOnClickListenerForTask(
+ BaseDraggingActivity activity, Task task, ItemInfo itemInfo) {
+ if (InstantAppResolver.newInstance(activity).isInstantApp(activity,
+ task.getTopComponent().getPackageName())) {
+ return mSystemShortcut.createOnClickListener(activity, itemInfo);
+ }
+ return null;
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 52ae115..5591e40 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -33,8 +33,8 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.Service;
+import android.app.TaskInfo;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
@@ -49,11 +49,12 @@
import android.view.MotionEvent;
import androidx.annotation.BinderThread;
-import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.config.FeatureFlags;
@@ -61,21 +62,20 @@
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
+import com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer;
import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
-import com.android.quickstep.inputconsumers.OverscrollInputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
+import com.android.quickstep.inputconsumers.QuickCaptureInputConsumer;
import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.AssistantUtilities;
-import com.android.systemui.plugins.OverscrollPlugin;
-import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -83,6 +83,7 @@
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.system.TaskInfoCompat;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -111,7 +112,8 @@
* Service connected by system-UI for handling touch interaction.
*/
@TargetApi(Build.VERSION_CODES.Q)
-public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin> {
+public class TouchInteractionService extends Service implements
+ NavigationModeChangeListener {
private static final String TAG = "TouchInteractionService";
@@ -120,8 +122,6 @@
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
private int mBackGestureNotificationCounter = -1;
- @Nullable
- private OverscrollPlugin mOverscrollPlugin;
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@@ -129,11 +129,13 @@
public void onInitialize(Bundle bundle) {
ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
- MAIN_EXECUTOR.execute(() -> {
- SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy);
- TouchInteractionService.this.initInputMonitor();
- preloadOverview(true /* fromInit */);
- });
+ MAIN_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(TouchInteractionService.this)
+ .setProxy(proxy));
+ MAIN_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor);
+ MAIN_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */));
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "TIS initialized");
+ }
sIsInitialized = true;
}
@@ -167,19 +169,15 @@
@BinderThread
@Override
public void onAssistantAvailable(boolean available) {
- MAIN_EXECUTOR.execute(() -> {
- mDeviceState.setAssistantAvailable(available);
- TouchInteractionService.this.onAssistantVisibilityChanged();
- });
+ MAIN_EXECUTOR.execute(() -> mDeviceState.setAssistantAvailable(available));
+ MAIN_EXECUTOR.execute(TouchInteractionService.this::onAssistantVisibilityChanged);
}
@BinderThread
@Override
public void onAssistantVisibilityChanged(float visibility) {
- MAIN_EXECUTOR.execute(() -> {
- mDeviceState.setAssistantVisibility(visibility);
- TouchInteractionService.this.onAssistantVisibilityChanged();
- });
+ MAIN_EXECUTOR.execute(() -> mDeviceState.setAssistantVisibility(visibility));
+ MAIN_EXECUTOR.execute(TouchInteractionService.this::onAssistantVisibilityChanged);
}
@BinderThread
@@ -201,10 +199,8 @@
@BinderThread
public void onSystemUiStateChanged(int stateFlags) {
- MAIN_EXECUTOR.execute(() -> {
- mDeviceState.setSystemUiFlags(stateFlags);
- TouchInteractionService.this.onSystemUiFlagsChanged();
- });
+ MAIN_EXECUTOR.execute(() -> mDeviceState.setSystemUiFlags(stateFlags));
+ MAIN_EXECUTOR.execute(TouchInteractionService.this::onSystemUiFlagsChanged);
}
@BinderThread
@@ -232,6 +228,8 @@
private static boolean sConnected = false;
private static boolean sIsInitialized = false;
+ private static final SwipeSharedState sSwipeSharedState = new SwipeSharedState();
+ private int mLogId;
public static boolean isConnected() {
return sConnected;
@@ -241,38 +239,45 @@
return sIsInitialized;
}
- private final BaseSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
- this::createLauncherSwipeHandler;
- private final BaseSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
- this::createFallbackSwipeHandler;
+ public static SwipeSharedState getSwipeSharedState() {
+ return sSwipeSharedState;
+ }
+
+ private final InputConsumer mResetGestureInputConsumer =
+ new ResetGestureInputConsumer(sSwipeSharedState);
+
+ private final BaseSwipeUpHandler.Factory mWindowTreansformFactory =
+ this::createWindowTransformSwipeHandler;
+ private final BaseSwipeUpHandler.Factory mFallbackNoButtonFactory =
+ this::createFallbackNoButtonSwipeHandler;
private ActivityManagerWrapper mAM;
+ private RecentsModel mRecentsModel;
private OverviewCommandHelper mOverviewCommandHelper;
private OverviewComponentObserver mOverviewComponentObserver;
private InputConsumerController mInputConsumer;
private RecentsAnimationDeviceState mDeviceState;
- private TaskAnimationManager mTaskAnimationManager;
private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
private InputConsumer mConsumer = InputConsumer.NO_OP;
private Choreographer mMainChoreographer;
- private InputConsumer mResetGestureInputConsumer;
- private GestureState mGestureState = new GestureState();
private InputMonitorCompat mInputMonitorCompat;
private InputEventReceiver mInputEventReceiver;
+ private Mode mMode = Mode.THREE_BUTTONS;
@Override
public void onCreate() {
super.onCreate();
+ mDeviceState = new RecentsAnimationDeviceState(this);
+ mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
+
// Initialize anything here that is needed in direct boot mode.
// Everything else should be initialized in onUserUnlocked() below.
mMainChoreographer = Choreographer.getInstance();
mAM = ActivityManagerWrapper.getInstance();
- mDeviceState = new RecentsAnimationDeviceState(this);
- mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
- mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
+ onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
sConnected = true;
}
@@ -295,7 +300,7 @@
Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 1");
}
disposeEventHandlers();
- if (mDeviceState.isButtonNavMode() || !SystemUiProxy.INSTANCE.get(this).isActive()) {
+ if (!mMode.hasGestures || !SystemUiProxy.INSTANCE.get(this).isActive()) {
return;
}
if (TestProtocol.sDebugTracing) {
@@ -314,22 +319,25 @@
mDeviceState.updateGestureTouchRegions();
}
- /**
- * Called when the navigation mode changes, guaranteed to be after the device state has updated.
- */
- private void onNavigationModeChanged(SysUINavigationMode.Mode mode) {
+ @Override
+ public void onNavigationModeChanged(Mode newMode) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onNavigationModeChanged " + newMode);
+ }
+ mMode = newMode;
initInputMonitor();
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
}
@UiThread
public void onUserUnlocked() {
- mTaskAnimationManager = new TaskAnimationManager();
+ mRecentsModel = RecentsModel.INSTANCE.get(this);
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
mOverviewComponentObserver);
- mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
+
+ sSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver);
mInputConsumer.registerInputConsumer();
onSystemUiFlagsChanged();
onAssistantVisibilityChanged();
@@ -339,24 +347,10 @@
mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
.getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
-
- PluginManagerWrapper.INSTANCE.get(getBaseContext()).addPluginListener(this,
- OverscrollPlugin.class, false /* allowMultiple */);
- }
-
- private void onDeferredActivityLaunch() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mOverviewComponentObserver.getActivityInterface().switchRunningTaskViewToScreenshot(
- null, () -> {
- mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
- });
- } else {
- mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
- }
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
- if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
+ if (!mDeviceState.isUserUnlocked() || !mMode.hasGestures) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
// mode doesn't have gestures
return;
@@ -391,8 +385,9 @@
@Override
public void onDestroy() {
- PluginManagerWrapper.INSTANCE.get(getBaseContext()).removePluginListener(this);
-
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "TIS destroyed");
+ }
sIsInitialized = false;
if (mDeviceState.isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
@@ -400,6 +395,7 @@
}
disposeEventHandlers();
mDeviceState.destroy();
+ SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
SystemUiProxy.INSTANCE.get(this).setProxy(null);
sConnected = false;
@@ -428,19 +424,19 @@
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
MotionEvent event = (MotionEvent) ev;
if (event.getAction() == ACTION_DOWN) {
- GestureState newGestureState = new GestureState(mOverviewComponentObserver,
- ActiveGestureLog.INSTANCE.generateAndSetLogId());
- newGestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
- () -> mAM.getRunningTask(0)));
+ GestureState newGestureState = new GestureState(
+ mOverviewComponentObserver.getActivityInterface());
+
+ mLogId = ActiveGestureLog.INSTANCE.generateAndSetLogId();
+ sSwipeSharedState.setLogTraceId(mLogId);
if (mDeviceState.isInSwipeUpTouchRegion(event)) {
+ boolean useSharedState = mConsumer.useSharedSwipeState();
mConsumer.onConsumerAboutToBeSwitched();
- mConsumer = newConsumer(mGestureState, newGestureState, event);
-
+ mConsumer = newConsumer(newGestureState, useSharedState, event);
ActiveGestureLog.INSTANCE.addLog("setInputConsumer", mConsumer.getType());
mUncheckedConsumer = mConsumer;
- } else if (mDeviceState.isUserUnlocked()
- && mDeviceState.isFullyGesturalNavMode()
+ } else if (mDeviceState.isUserUnlocked() && mMode == Mode.NO_BUTTON
&& mDeviceState.canTriggerAssistantAction(event)) {
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we should
// not interrupt it. QuickSwitch assumes that interruption can only happen if the
@@ -450,9 +446,6 @@
} else {
mUncheckedConsumer = InputConsumer.NO_OP;
}
-
- // Save the current gesture state
- mGestureState = newGestureState;
}
ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
@@ -460,41 +453,39 @@
TraceHelper.INSTANCE.endFlagsOverride(traceToken);
}
- private InputConsumer newConsumer(GestureState previousGestureState,
- GestureState newGestureState, MotionEvent event) {
+ private InputConsumer newConsumer(GestureState gestureState, boolean useSharedState,
+ MotionEvent event) {
boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
if (!mDeviceState.isUserUnlocked()) {
if (canStartSystemGesture) {
// This handles apps launched in direct boot mode (e.g. dialer) as well as apps
// launched while device is locked even after exiting direct boot mode (e.g. camera).
- return createDeviceLockedInputConsumer(newGestureState);
+ return createDeviceLockedInputConsumer(gestureState,
+ mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
} else {
return mResetGestureInputConsumer;
}
}
- // When there is an existing recents animation running, bypass systemState check as this is
- // a followup gesture and the first gesture started in a valid system state.
- InputConsumer base = canStartSystemGesture
- || previousGestureState.isRecentsAnimationRunning()
- ? newBaseConsumer(previousGestureState, newGestureState, event)
- : mResetGestureInputConsumer;
- if (mDeviceState.isFullyGesturalNavMode()) {
+ // When using sharedState, bypass systemState check as this is a followup gesture and the
+ // first gesture started in a valid system state.
+ InputConsumer base = canStartSystemGesture || useSharedState
+ ? newBaseConsumer(gestureState, useSharedState, event) : mResetGestureInputConsumer;
+ if (mMode == Mode.NO_BUTTON) {
if (mDeviceState.canTriggerAssistantAction(event)) {
- base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat);
+ base = new AssistantInputConsumer(this, gestureState, base, mInputMonitorCompat);
}
- if (mOverscrollPlugin != null) {
- // Put the overscroll gesture as higher priority than the Assistant or base gestures
- base = new OverscrollInputConsumer(this, newGestureState, base, mInputMonitorCompat,
- mOverscrollPlugin);
+ if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
+ // Put the Compose gesture as higher priority than the Assistant or base gestures
+ base = new QuickCaptureInputConsumer(this, gestureState, base, mInputMonitorCompat);
}
if (mDeviceState.isScreenPinningActive()) {
// Note: we only allow accessibility to wrap this, and it replaces the previous
// base input consumer (which should be NO_OP anyway since topTaskLocked == true).
- base = new ScreenPinnedInputConsumer(this, newGestureState);
+ base = new ScreenPinnedInputConsumer(this, gestureState);
}
if (mDeviceState.isAccessibilityMenuAvailable()) {
@@ -509,7 +500,6 @@
return base;
}
-<<<<<<< HEAD
private InputConsumer newBaseConsumer(GestureState gestureState, boolean useSharedState,
MotionEvent event) {
RunningTaskInfo runningTaskInfo = TraceHelper.whitelistIpcs("getRunningTask.0",
@@ -517,20 +507,15 @@
if (!useSharedState) {
sSwipeSharedState.clearAllState(false /* finishAnimation */);
}
-=======
- private InputConsumer newBaseConsumer(GestureState previousGestureState,
- GestureState gestureState, MotionEvent event) {
->>>>>>> ub-launcher3-master
if (mDeviceState.isKeyguardShowingOccluded()) {
// This handles apps showing over the lockscreen (e.g. camera)
- return createDeviceLockedInputConsumer(gestureState);
+ return createDeviceLockedInputConsumer(gestureState, runningTaskInfo);
}
boolean forceOverviewInputConsumer = false;
- if (AssistantUtilities.isExcludedAssistant(gestureState.getRunningTask())) {
+ if (isExcludedAssistant(runningTaskInfo)) {
// In the case where we are in the excluded assistant state, ignore it and treat the
// running activity as the task behind the assistant
-<<<<<<< HEAD
runningTaskInfo = TraceHelper.whitelistIpcs("getRunningTask.assistant",
() -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
@@ -540,90 +525,80 @@
forceOverviewInputConsumer =
runningTaskInfo.baseIntent.getComponent(). equals(homeComponent);
}
-=======
- gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.assistant",
- () -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT /* ignoreActivityType */)));
- ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent();
- ComponentName runningComponent =
- gestureState.getRunningTask().baseIntent.getComponent();
- forceOverviewInputConsumer =
- runningComponent != null && runningComponent.equals(homeComponent);
->>>>>>> ub-launcher3-master
}
- if (previousGestureState.getFinishingRecentsAnimationTaskId() > 0) {
+ if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher
+ && !sSwipeSharedState.recentsAnimationFinishInterrupted) {
+ return mResetGestureInputConsumer;
+ } else if (sSwipeSharedState.recentsAnimationFinishInterrupted) {
// If the finish animation was interrupted, then continue using the other activity input
// consumer but with the next task as the running task
RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
- info.id = previousGestureState.getFinishingRecentsAnimationTaskId();
- gestureState.updateRunningTask(info);
- return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
- } else if (gestureState.getRunningTask() == null) {
- return mResetGestureInputConsumer;
- } else if (previousGestureState.isRunningAnimationToLauncher()
+ info.id = sSwipeSharedState.nextRunningTaskId;
+ return createOtherActivityInputConsumer(gestureState, event, info);
+ } else if (sSwipeSharedState.goingToLauncher
|| gestureState.getActivityInterface().isResumed()
|| forceOverviewInputConsumer) {
- return createOverviewInputConsumer(
- previousGestureState, gestureState, event, forceOverviewInputConsumer);
+ return createOverviewInputConsumer(gestureState, event);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get()
&& gestureState.getActivityInterface().isInLiveTileMode()) {
- return createOverviewInputConsumer(
- previousGestureState, gestureState, event, forceOverviewInputConsumer);
- } else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
+ return createOverviewInputConsumer(gestureState, event);
+ } else if (mDeviceState.isGestureBlockedActivity(runningTaskInfo)) {
return mResetGestureInputConsumer;
} else {
- return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
+ return createOtherActivityInputConsumer(gestureState, event, runningTaskInfo);
}
}
- private InputConsumer createOtherActivityInputConsumer(GestureState previousGestureState,
- GestureState gestureState, MotionEvent event) {
+ private boolean isExcludedAssistant(TaskInfo info) {
+ return info != null
+ && TaskInfoCompat.getActivityType(info) == ACTIVITY_TYPE_ASSISTANT
+ && (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+ }
+
+ private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
+ MotionEvent event, RunningTaskInfo runningTaskInfo) {
final boolean shouldDefer;
final BaseSwipeUpHandler.Factory factory;
- if (mDeviceState.isFullyGesturalNavMode()
- && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
- shouldDefer = previousGestureState.getFinishingRecentsAnimationTaskId() < 0;
- factory = mFallbackSwipeHandlerFactory;
+ if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+ shouldDefer = !sSwipeSharedState.recentsAnimationFinishInterrupted;
+ factory = mFallbackNoButtonFactory;
} else {
shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
event);
- factory = mLauncherSwipeHandlerFactory;
+ factory = mWindowTreansformFactory;
}
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
- return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
- gestureState, shouldDefer, this::onConsumerInactive,
- mInputMonitorCompat, disableHorizontalSwipe, factory);
+ return new OtherActivityInputConsumer(this, mDeviceState, gestureState, runningTaskInfo,
+ shouldDefer, this::onConsumerInactive, sSwipeSharedState, mInputMonitorCompat,
+ disableHorizontalSwipe, factory, mLogId);
}
- private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState) {
- if (mDeviceState.isFullyGesturalNavMode() && gestureState.getRunningTask() != null) {
- return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
- gestureState, mInputMonitorCompat);
+ private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState,
+ RunningTaskInfo taskInfo) {
+ if (mMode == Mode.NO_BUTTON && taskInfo != null) {
+ return new DeviceLockedInputConsumer(this, mDeviceState, gestureState,
+ sSwipeSharedState, mInputMonitorCompat, taskInfo.taskId, mLogId);
} else {
return mResetGestureInputConsumer;
}
}
- public InputConsumer createOverviewInputConsumer(GestureState previousGestureState,
- GestureState gestureState, MotionEvent event,
- boolean forceOverviewInputConsumer) {
+ public InputConsumer createOverviewInputConsumer(GestureState gestureState, MotionEvent event) {
BaseDraggingActivity activity = gestureState.getActivityInterface().getCreatedActivity();
if (activity == null) {
return mResetGestureInputConsumer;
}
- if (activity.getRootView().hasWindowFocus()
- || previousGestureState.isRunningAnimationToLauncher()
- || (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
- && forceOverviewInputConsumer)) {
+ if (activity.getRootView().hasWindowFocus() || sSwipeSharedState.goingToLauncher) {
return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat,
false /* startingInActivityBounds */);
} else {
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
- return new OverviewWithoutFocusInputConsumer(activity, mDeviceState, gestureState,
+ return new OverviewWithoutFocusInputConsumer(activity, gestureState,
mInputMonitorCompat, disableHorizontalSwipe);
}
}
@@ -642,7 +617,7 @@
if (!mDeviceState.isUserUnlocked()) {
return;
}
- if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+ if (!mMode.hasGestures && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
// Prevent the overview from being started before the real home on first boot.
return;
}
@@ -657,8 +632,8 @@
mOverviewComponentObserver.getActivityInterface();
if (activityInterface.getCreatedActivity() == null) {
// Make sure that UI states will be initialized.
- activityInterface.createActivityInitListener((wasVisible) -> {
- AppLaunchTracker.INSTANCE.get(TouchInteractionService.this);
+ activityInterface.createActivityInitListener((activity, wasVisible) -> {
+ AppLaunchTracker.INSTANCE.get(activity);
return false;
}).register();
} else if (fromInit) {
@@ -668,8 +643,9 @@
return;
}
- mTaskAnimationManager.preloadRecentsAnimation(
- mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
+ // Pass null animation handler to indicate this start is preload.
+ startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(),
+ null);
}
@Override
@@ -709,9 +685,14 @@
// Dump everything
mDeviceState.dump(pw);
pw.println("TouchState:");
+ pw.println(" navMode=" + mMode);
boolean resumed = mOverviewComponentObserver != null
&& mOverviewComponentObserver.getActivityInterface().isResumed();
pw.println(" resumed=" + resumed);
+ pw.println(" useSharedState=" + mConsumer.useSharedSwipeState());
+ if (mConsumer.useSharedSwipeState()) {
+ sSwipeSharedState.dump(" ", pw);
+ }
pw.println(" mConsumer=" + mConsumer.getName());
pw.println("FeatureFlags:");
pw.println(" APPLY_CONFIG_AT_RUNTIME=" + APPLY_CONFIG_AT_RUNTIME.get());
@@ -737,16 +718,20 @@
}
}
- private BaseSwipeUpHandler createLauncherSwipeHandler(GestureState gestureState,
- long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
- return new LauncherSwipeHandler(this, mDeviceState, mTaskAnimationManager,
- gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
+ private BaseSwipeUpHandler createWindowTransformSwipeHandler(GestureState gestureState,
+ RunningTaskInfo runningTask, long touchTimeMs, boolean continuingLastGesture,
+ boolean isLikelyToStartNewTask) {
+ return new WindowTransformSwipeHandler(this, mDeviceState, gestureState, runningTask,
+ touchTimeMs, mOverviewComponentObserver, continuingLastGesture, mInputConsumer,
+ mRecentsModel);
}
- private BaseSwipeUpHandler createFallbackSwipeHandler(GestureState gestureState,
- long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
- return new FallbackSwipeHandler(this, mDeviceState, gestureState,
- mInputConsumer, isLikelyToStartNewTask, continuingLastGesture);
+ private BaseSwipeUpHandler createFallbackNoButtonSwipeHandler(GestureState gestureState,
+ RunningTaskInfo runningTask, long touchTimeMs, boolean continuingLastGesture,
+ boolean isLikelyToStartNewTask) {
+ return new FallbackNoButtonInputConsumer(this, gestureState, mOverviewComponentObserver,
+ runningTask, mRecentsModel, mInputConsumer, isLikelyToStartNewTask,
+ continuingLastGesture);
}
protected boolean shouldNotifyBackGesture() {
@@ -769,14 +754,4 @@
UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
.startRecentsActivity(intent, null, listener, null, null));
}
-
- @Override
- public void onPluginConnected(OverscrollPlugin overscrollPlugin, Context context) {
- mOverscrollPlugin = overscrollPlugin;
- }
-
- @Override
- public void onPluginDisconnected(OverscrollPlugin overscrollPlugin) {
- mOverscrollPlugin = null;
- }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
similarity index 78%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 3d664be..1168758 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -24,15 +24,13 @@
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
-import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
-import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
-import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
-import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
-import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
+import static com.android.quickstep.BaseActivityInterface.AnimationFactory.ShelfAnimState.HIDE;
+import static com.android.quickstep.BaseActivityInterface.AnimationFactory.ShelfAnimState.PEEK;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.NEW_TASK;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.RECENTS;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
@@ -40,6 +38,7 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
+import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
@@ -69,15 +68,13 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.BaseActivityInterface.AnimationFactory;
+import com.android.quickstep.BaseActivityInterface.AnimationFactory.ShelfAnimState;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
-import com.android.quickstep.GestureState.GestureEndTarget;
+import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AppWindowAnimationHelper.TargetAlphaProvider;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SharedApiCompat;
-import com.android.quickstep.util.ShelfPeekAnim;
-import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
import com.android.quickstep.views.LiveTileOverlay;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -86,13 +83,10 @@
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-/**
- * Handles the navigation gestures when Launcher is the default home activity.
- */
@TargetApi(Build.VERSION_CODES.O)
-public class LauncherSwipeHandler<T extends BaseDraggingActivity>
+public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
extends BaseSwipeUpHandler<T, RecentsView> implements OnApplyWindowInsetsListener {
- private static final String TAG = LauncherSwipeHandler.class.getSimpleName();
+ private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
@@ -144,6 +138,42 @@
private static final int LAUNCHER_UI_STATES =
STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
+ public enum GestureEndTarget {
+ HOME(1, STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT, true, false,
+ ContainerType.WORKSPACE, false),
+
+ RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
+ | STATE_SCREENSHOT_VIEW_SHOWN, true, false, ContainerType.TASKSWITCHER, true),
+
+ NEW_TASK(0, STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT, false, true,
+ ContainerType.APP, true),
+
+ LAST_TASK(0, STATE_RESUME_LAST_TASK, false, true, ContainerType.APP, false);
+
+ GestureEndTarget(float endShift, int endState, boolean isLauncher, boolean canBeContinued,
+ int containerType, boolean recentsAttachedToAppWindow) {
+ this.endShift = endShift;
+ this.endState = endState;
+ this.isLauncher = isLauncher;
+ this.canBeContinued = canBeContinued;
+ this.containerType = containerType;
+ this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
+ }
+
+ /** 0 is app, 1 is overview */
+ public final float endShift;
+ /** The state to apply when we reach this final target */
+ public final int endState;
+ /** Whether the target is in the launcher activity */
+ public final boolean isLauncher;
+ /** Whether the user can start a new gesture while this one is finishing */
+ public final boolean canBeContinued;
+ /** Used to log where the user ended up after the gesture ends */
+ public final int containerType;
+ /** Whether RecentsView should be attached to the window as we animate to this target */
+ public final boolean recentsAttachedToAppWindow;
+ }
+
public static final long MAX_SWIPE_DURATION = 350;
public static final long MIN_SWIPE_DURATION = 80;
public static final long MIN_OVERSHOOT_DURATION = 120;
@@ -153,6 +183,7 @@
Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured";
+ private static final long SHELF_ANIM_DURATION = 240;
public static final long RECENTS_ATTACH_DURATION = 300;
/**
@@ -160,8 +191,10 @@
*/
private static final int LOG_NO_OP_PAGE_INDEX = -1;
- private final TaskAnimationManager mTaskAnimationManager;
+ private final RecentsAnimationDeviceState mDeviceState;
+ private final GestureState mGestureState;
+ private GestureEndTarget mGestureEndTarget;
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim mRunningWindowAnim;
private boolean mIsShelfPeeking;
@@ -177,6 +210,8 @@
private boolean mHasLauncherTransitionControllerStarted;
private AnimationFactory mAnimationFactory = (t) -> { };
+ private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
+ private boolean mLiveTileOverlayAttached = false;
private boolean mWasLauncherAlreadyVisible;
@@ -190,14 +225,13 @@
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
- private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
-
- public LauncherSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
- TaskAnimationManager taskAnimationManager, GestureState gestureState,
- long touchTimeMs, boolean continuingLastGesture,
- InputConsumerController inputConsumer) {
- super(context, deviceState, gestureState, inputConsumer);
- mTaskAnimationManager = taskAnimationManager;
+ public WindowTransformSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
+ GestureState gestureState, RunningTaskInfo runningTaskInfo, long touchTimeMs,
+ OverviewComponentObserver overviewComponentObserver, boolean continuingLastGesture,
+ InputConsumerController inputConsumer, RecentsModel recentsModel) {
+ super(context, gestureState, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id);
+ mDeviceState = deviceState;
+ mGestureState = gestureState;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
initStateCallbacks();
@@ -206,65 +240,62 @@
private void initStateCallbacks() {
mStateCallback = new MultiStateCallback(STATE_NAMES);
- mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
this::onLauncherPresentAndGestureStarted);
- mStateCallback.runOnceAtState(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,
+ mStateCallback.addCallback(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,
this::initializeLauncherAnimationController);
- mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
this::launcherFrameDrawn);
- mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
| STATE_GESTURE_CANCELLED,
this::resetStateForAnimationCancel);
- mStateCallback.runOnceAtState(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
this::sendRemoteAnimationsToAnimationFactory);
- mStateCallback.runOnceAtState(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
this::resumeLastTask);
- mStateCallback.runOnceAtState(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED,
+ mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED,
this::startNewTask);
- mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
| STATE_LAUNCHER_DRAWN | STATE_CAPTURE_SCREENSHOT,
this::switchToScreenshot);
- mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
+ mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
| STATE_SCALED_CONTROLLER_RECENTS,
this::finishCurrentTransitionToRecents);
- mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
+ mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
| STATE_SCALED_CONTROLLER_HOME,
this::finishCurrentTransitionToHome);
- mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
+ mStateCallback.addCallback(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
this::reset);
- mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
| STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS
| STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
| STATE_GESTURE_STARTED,
this::setupLauncherUiAfterSwipeUpToRecentsAnimation);
- mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED, this::onEndTargetSet);
-
- mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
- mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+ mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
this::invalidateHandlerWithLauncher);
- mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
+ mStateCallback.addCallback(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
this::notifyTransitionCancelled);
if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
+ mStateCallback.addChangeHandler(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
| STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
(b) -> mRecentsView.setRunningTaskHidden(!b));
}
}
@Override
- protected boolean onActivityInit(Boolean alreadyOnHome) {
- final T activity = mActivityInterface.getCreatedActivity();
+ protected boolean onActivityInit(final T activity, Boolean alreadyOnHome) {
if (mActivity == activity) {
return true;
}
@@ -290,28 +321,21 @@
mStateCallback.setState(STATE_LAUNCHER_PRESENT);
if (alreadyOnHome) {
- onLauncherStart();
+ onLauncherStart(activity);
} else {
- activity.runOnceOnStart(this::onLauncherStart);
+ activity.setOnStartCallback(this::onLauncherStart);
}
setupRecentsViewUi();
-
- if (mDeviceState.getNavMode() == TWO_BUTTONS) {
- // If the device is in two button mode, swiping up will show overview with predictions
- // so we need to kick off switching to the overview predictions as soon as possible
- mActivityInterface.updateOverviewPredictionState();
- }
return true;
}
@Override
protected boolean moveWindowWithRecentsScroll() {
- return mGestureState.getEndTarget() != HOME;
+ return mGestureEndTarget != HOME;
}
- private void onLauncherStart() {
- final T activity = mActivityInterface.getCreatedActivity();
+ private void onLauncherStart(final T activity) {
if (mActivity != activity) {
return;
}
@@ -321,9 +345,9 @@
// If we've already ended the gesture and are going home, don't prepare recents UI,
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
- if (mGestureState.getEndTarget() != HOME) {
+ if (mGestureEndTarget != HOME) {
Runnable initAnimFactory = () -> {
- mAnimationFactory = mActivityInterface.prepareRecentsUI(
+ mAnimationFactory = mActivityInterface.prepareRecentsUI(mActivity,
mWasLauncherAlreadyVisible, true,
this::onAnimatorPlaybackControllerCreated);
maybeUpdateRecentsAttachedState(false /* animate */);
@@ -332,7 +356,7 @@
// Launcher is visible, but might be about to stop. Thus, if we prepare recents
// now, it might get overridden by moveToRestState() in onStop(). To avoid this,
// wait until the next gesture (and possibly launcher) starts.
- mStateCallback.runOnceAtState(STATE_GESTURE_STARTED, initAnimFactory);
+ mStateCallback.addCallback(STATE_GESTURE_STARTED, initAnimFactory);
} else {
initAnimFactory.run();
}
@@ -376,31 +400,15 @@
// that time by a previous window transition.
setupRecentsViewUi();
- // For the duration of the gesture, in cases where an activity is launched while the
- // activity is not yet resumed, finish the animation to ensure we get resumed
- mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback(
- mOnDeferredActivityLaunch);
-
notifyGestureStartedAsync();
}
- private void onDeferredActivityLaunch() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mActivityInterface.switchRunningTaskViewToScreenshot(
- null, () -> {
- mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
- });
- } else {
- mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
- }
- }
-
private void setupRecentsViewUi() {
if (mContinuingLastGesture) {
updateSysUiFlags(mCurrentShift.value);
return;
}
- mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId());
+ mRecentsView.onGestureAnimationStart(mRunningTaskId);
}
private void launcherFrameDrawn() {
@@ -429,15 +437,16 @@
.getHighResLoadingState().setVisible(true);
}
+ private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
+ float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 +
+ mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
+ float interpolation = Math.min(1, offsetX / distanceToReachEdge);
+ return TaskView.getCurveScaleForInterpolation(interpolation);
+ }
+
@Override
public void onMotionPauseChanged(boolean isPaused) {
- setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
-
- if (mDeviceState.isFullyGesturalNavMode() && isPaused) {
- // In fully gestural nav mode, switch to overview predictions once the user has paused
- // (this is a no-op if the predictions are already in that state)
- mActivityInterface.updateOverviewPredictionState();
- }
+ setShelfState(isPaused ? PEEK : HIDE, OVERSHOOT_1_2, SHELF_ANIM_DURATION);
}
public void maybeUpdateRecentsAttachedState() {
@@ -452,15 +461,16 @@
* Note this method has no effect unless the navigation mode is NO_BUTTON.
*/
private void maybeUpdateRecentsAttachedState(boolean animate) {
- if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
+ if (mMode != Mode.NO_BUTTON || mRecentsView == null) {
return;
}
- RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
- ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
- : null;
+ RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets == null
+ ? null
+ : mRecentsAnimationTargets.findTask(mRunningTaskId);
final boolean recentsAttachedToAppWindow;
- if (mGestureState.getEndTarget() != null) {
- recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow;
+ int runningTaskIndex = mRecentsView.getRunningTaskIndex();
+ if (mGestureEndTarget != null) {
+ recentsAttachedToAppWindow = mGestureEndTarget.recentsAttachedToAppWindow;
} else if (mContinuingLastGesture
&& mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
recentsAttachedToAppWindow = true;
@@ -507,10 +517,9 @@
}
private void buildAnimationController() {
- if (mGestureState.getEndTarget() == HOME || mHasLauncherTransitionControllerStarted) {
- // We don't want a new mLauncherTransitionController if
- // mGestureState.getEndTarget() == HOME (it has its own animation) or if we're already
- // animating the current controller.
+ if (mGestureEndTarget == HOME || mHasLauncherTransitionControllerStarted) {
+ // We don't want a new mLauncherTransitionController if mGestureEndTarget == HOME (it
+ // has its own animation) or if we're already animating the current controller.
return;
}
initTransitionEndpoints(mActivity.getDeviceProfile());
@@ -534,7 +543,7 @@
@Override
public Intent getLaunchIntent() {
- return mGestureState.getOverviewIntent();
+ return mOverviewComponentObserver.getOverviewIntent();
}
@Override
@@ -546,8 +555,7 @@
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationTargets != null) {
- LiveTileOverlay.getInstance().update(
- mAppWindowAnimationHelper.getCurrentRectWithInsets(),
+ mLiveTileOverlay.update(mAppWindowAnimationHelper.getCurrentRectWithInsets(),
mAppWindowAnimationHelper.getCurrentCornerRadius());
}
}
@@ -555,7 +563,7 @@
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed;
- if (!mDeviceState.isFullyGesturalNavMode()) {
+ if (mMode != Mode.NO_BUTTON) {
performHapticFeedback();
}
}
@@ -568,7 +576,7 @@
}
private void updateLauncherTransitionProgress() {
- if (mGestureState.getEndTarget() == HOME) {
+ if (mGestureEndTarget == HOME) {
return;
}
// Normalize the progress to 0 to 1, as the animation controller will clamp it to that
@@ -605,9 +613,9 @@
super.onRecentsAnimationStart(controller, targets);
// Only add the callback to enable the input consumer after we actually have the controller
- mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
+ mStateCallback.addCallback(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
mRecentsAnimationController::enableInputConsumer);
- mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
+ setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
mPassedOverviewThreshold = false;
}
@@ -615,11 +623,9 @@
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
super.onRecentsAnimationCanceled(thumbnailData);
- if (mRecentsView != null) {
- mRecentsView.setRecentsAnimationTargets(null, null);
- }
+ mRecentsView.setRecentsAnimationTargets(null, null);
mActivityInitListener.unregister();
- mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
+ setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
}
@@ -627,7 +633,7 @@
public void onGestureStarted() {
notifyGestureStartedAsync();
mShiftAtGestureStart = mCurrentShift.value;
- mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
+ setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
}
@@ -650,7 +656,7 @@
@Override
public void onGestureCancelled() {
updateDisplacement(0);
- mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
+ setStateOnUiThread(STATE_GESTURE_COMPLETED);
mLogAction = Touch.SWIPE_NOOP;
handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */);
}
@@ -665,7 +671,7 @@
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
- mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
+ setStateOnUiThread(STATE_GESTURE_COMPLETED);
mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
boolean isVelocityVertical = Math.abs(velocity.y) > Math.abs(velocity.x);
@@ -680,11 +686,11 @@
@Override
protected InputConsumer createNewInputProxyHandler() {
- endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
+ endRunningWindowAnim(mGestureEndTarget == HOME /* cancel */);
endLauncherTransitionController();
if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// Hide the task view, if not already hidden
- setTargetAlphaProvider(LauncherSwipeHandler::getHiddenTargetAlpha);
+ setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
}
BaseDraggingActivity activity = mActivityInterface.getCreatedActivity();
@@ -702,24 +708,6 @@
}
}
- private void onEndTargetSet() {
- switch (mGestureState.getEndTarget()) {
- case HOME:
- mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
- break;
- case RECENTS:
- mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
- | STATE_SCREENSHOT_VIEW_SHOWN);
- break;
- case NEW_TASK:
- mStateCallback.setState(STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT);
- break;
- case LAST_TASK:
- mStateCallback.setState(STATE_RESUME_LAST_TASK);
- break;
- }
- }
-
private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
boolean isCancel) {
final GestureEndTarget endTarget;
@@ -741,7 +729,7 @@
if (!isFling) {
if (isCancel) {
endTarget = LAST_TASK;
- } else if (mDeviceState.isFullyGesturalNavMode()) {
+ } else if (mMode == Mode.NO_BUTTON) {
if (mIsShelfPeeking) {
endTarget = RECENTS;
} else if (goingToNewTask) {
@@ -762,9 +750,9 @@
boolean willGoToNewTaskOnSwipeUp =
goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
- if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
+ if (mMode == Mode.NO_BUTTON && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
endTarget = HOME;
- } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !mIsShelfPeeking) {
+ } else if (mMode == Mode.NO_BUTTON && isSwipeUp && !mIsShelfPeeking) {
// If swiping at a diagonal, base end target on the faster velocity.
endTarget = NEW_TASK;
} else if (isSwipeUp) {
@@ -789,7 +777,7 @@
float currentShift = mCurrentShift.value;
final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
isFling, isCancel);
- float endShift = endTarget.isLauncher ? 1 : 0;
+ float endShift = endTarget.endShift;
final float startShift;
Interpolator interpolator = DEACCEL;
if (!isFling) {
@@ -804,7 +792,7 @@
float minFlingVelocity = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_min_velocity);
if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
- if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) {
+ if (endTarget == RECENTS && mMode != Mode.NO_BUTTON) {
Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
startShift, endShift, endShift, endVelocity / 1000,
mTransitionDragLength, mContext);
@@ -837,7 +825,7 @@
setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
} else if (endTarget == RECENTS) {
- LiveTileOverlay.getInstance().startIconAnimation();
+ mLiveTileOverlay.startIconAnimation();
if (mRecentsView != null) {
int nearestPage = mRecentsView.getPageNearestToCenterOfScreen();
if (mRecentsView.getNextPage() != nearestPage) {
@@ -850,7 +838,7 @@
}
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
- if (mDeviceState.isFullyGesturalNavMode()) {
+ if (mMode == Mode.NO_BUTTON) {
setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
}
} else if (endTarget == NEW_TASK || endTarget == LAST_TASK) {
@@ -892,15 +880,14 @@
@UiThread
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
- // Set the state, but don't notify until the animation completes
- mGestureState.setEndTarget(target, false /* isAtomic */);
+ mGestureEndTarget = target;
maybeUpdateRecentsAttachedState();
- if (mGestureState.getEndTarget() == HOME) {
+ if (mGestureEndTarget == HOME) {
HomeAnimationFactory homeAnimFactory;
if (mActivity != null) {
- homeAnimFactory = mActivityInterface.prepareHomeUI();
+ homeAnimFactory = mActivityInterface.prepareHomeUI(mActivity);
} else {
homeAnimFactory = new HomeAnimationFactory() {
@NonNull
@@ -917,15 +904,14 @@
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
}
};
- mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+ mStateCallback.addChangeHandler(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
isPresent -> mRecentsView.startHome());
}
RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
windowAnim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
- // Finalize the state and notify of the change
- mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+ setStateOnUiThread(target.endState);
}
});
windowAnim.start(velocityPxPerMs);
@@ -950,9 +936,10 @@
// We are about to launch the current running task, so use LAST_TASK state
// instead of NEW_TASK. This could happen, for example, if our scroll is
// aborted after we determined the target to be NEW_TASK.
- mGestureState.setEndTarget(LAST_TASK);
+ setStateOnUiThread(LAST_TASK.endState);
+ } else {
+ setStateOnUiThread(target.endState);
}
- mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
});
windowAnim.start();
@@ -960,7 +947,7 @@
}
// Always play the entire launcher animation when going home, since it is separate from
// the animation that has been controlled thus far.
- if (mGestureState.getEndTarget() == HOME) {
+ if (mGestureEndTarget == HOME) {
start = 0;
}
@@ -1012,22 +999,21 @@
}
// Make sure recents is in its final state
maybeUpdateRecentsAttachedState(false);
- mActivityInterface.onSwipeUpToHomeComplete();
+ mActivityInterface.onSwipeUpToHomeComplete(mActivity);
}
});
return anim;
}
@Override
- public void onConsumerAboutToBeSwitched() {
- if (mActivity != null) {
- // In the off chance that the gesture ends before Launcher is started, we should clear
- // the callback here so that it doesn't update with the wrong state
- mActivity.clearRunOnceOnStartCallback();
- resetLauncherListenersAndOverlays();
+ public void onConsumerAboutToBeSwitched(SwipeSharedState sharedState) {
+ if (mGestureEndTarget != null) {
+ sharedState.canGestureBeContinued = mGestureEndTarget.canBeContinued;
+ sharedState.goingToLauncher = mGestureEndTarget.isLauncher;
}
- if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) {
- cancelCurrentAnimation();
+
+ if (sharedState.canGestureBeContinued) {
+ cancelCurrentAnimation(sharedState);
} else {
reset();
}
@@ -1047,15 +1033,6 @@
@UiThread
private void startNewTask() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mRecentsAnimationController.finish(true /* toRecents */, this::startNewTaskInternal);
- } else {
- startNewTaskInternal();
- }
- }
-
- @UiThread
- private void startNewTaskInternal() {
startNewTask(STATE_HANDLER_INVALIDATED, success -> {
if (!success) {
// We couldn't launch the task, so take user to overview so they can
@@ -1068,14 +1045,14 @@
}
private void reset() {
- mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
/**
* Cancels any running animation so that the active target can be overriden by a new swipe
* handle (in case of quick switch).
*/
- private void cancelCurrentAnimation() {
+ private void cancelCurrentAnimation(SwipeSharedState sharedState) {
mCanceled = true;
mCurrentShift.cancelAnimation();
if (mLauncherTransitionController != null && mLauncherTransitionController
@@ -1093,7 +1070,7 @@
? newRunningTaskView.getTask().key.id
: -1;
mRecentsView.setCurrentTask(newRunningTaskId);
- mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
+ sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId);
}
}
@@ -1112,7 +1089,9 @@
endLauncherTransitionController();
mRecentsView.onGestureAnimationEnd();
- resetLauncherListenersAndOverlays();
+
+ mActivity.getRootView().setOnApplyWindowInsetsListener(null);
+ removeLiveTileOverlay();
}
private void endLauncherTransitionController() {
@@ -1123,71 +1102,58 @@
}
}
- private void resetLauncherListenersAndOverlays() {
- // Reset the callback for deferred activity launches
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mActivityInterface.setOnDeferredActivityLaunchCallback(null);
- }
- mActivity.getRootView().setOnApplyWindowInsetsListener(null);
- removeLiveTileOverlay();
- }
-
private void notifyTransitionCancelled() {
mAnimationFactory.onTransitionCancelled();
}
private void resetStateForAnimationCancel() {
boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
- mActivityInterface.onTransitionCancelled(wasVisible);
+ mActivityInterface.onTransitionCancelled(mActivity, wasVisible);
// Leave the pending invisible flag, as it may be used by wallpaper open animation.
mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
}
private void switchToScreenshot() {
- final int runningTaskId = mGestureState.getRunningTaskId();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationController != null) {
- SharedApiCompat.setWillFinishToHome(mRecentsAnimationController.getController(),
- true /* willFinishToHome */);
// Update the screenshot of the task
if (mTaskSnapshot == null) {
- mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
+ mTaskSnapshot = mRecentsAnimationController.screenshotTask(mRunningTaskId);
}
- mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot, false /* refreshNow */);
+ mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot, false /* refreshNow */);
}
- mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
} else if (!hasTargets()) {
// If there are no targets, then we don't need to capture anything
- mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
} else {
boolean finishTransitionPosted = false;
if (mRecentsAnimationController != null) {
// Update the screenshot of the task
if (mTaskSnapshot == null) {
- mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
+ mTaskSnapshot = mRecentsAnimationController.screenshotTask(mRunningTaskId);
}
final TaskView taskView;
- if (mGestureState.getEndTarget() == HOME) {
+ if (mGestureEndTarget == HOME) {
// Capture the screenshot before finishing the transition to home to ensure it's
// taken in the correct orientation, but no need to update the thumbnail.
taskView = null;
} else {
- taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot);
+ taskView = mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot);
}
if (taskView != null && !mCanceled) {
// Defer finishing the animation until the next launcher frame with the
// new thumbnail
finishTransitionPosted = ViewUtils.postDraw(taskView,
- () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
- this::isCanceled);
+ () -> setStateOnUiThread(STATE_SCREENSHOT_CAPTURED), this::isCanceled);
}
}
if (!finishTransitionPosted) {
// If we haven't posted a draw callback, set the state immediately.
Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
- mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
TraceHelper.INSTANCE.endSection(traceToken);
}
}
@@ -1195,24 +1161,23 @@
private void finishCurrentTransitionToRecents() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
- } else if (!hasTargets() || mRecentsAnimationController == null) {
- // If there are no targets or the animation not started, then there is nothing to finish
- mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+ setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+ } else if (!hasTargets()) {
+ // If there are no targets, then there is nothing to finish
+ setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
} else {
- mRecentsAnimationController.finish(true /* toRecents */,
- () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+ synchronized (mRecentsAnimationController) {
+ mRecentsAnimationController.finish(true /* toRecents */,
+ () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+ }
}
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
}
private void finishCurrentTransitionToHome() {
- if (!hasTargets() || mRecentsAnimationController == null) {
- // If there are no targets or the animation not started, then there is nothing to finish
- mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
- } else {
+ synchronized (mRecentsAnimationController) {
mRecentsAnimationController.finish(true /* toRecents */,
- () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED),
+ () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED),
true /* sendUserLeaveHint */);
}
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
@@ -1221,14 +1186,15 @@
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
endLauncherTransitionController();
- mActivityInterface.onSwipeUpToRecentsComplete();
+ mActivityInterface.onSwipeUpToRecentsComplete(mActivity);
if (mRecentsAnimationController != null) {
mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
true /* screenshot */);
}
mRecentsView.onSwipeUpAnimationSuccess();
- SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
+ RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
+
doLogGesture(RECENTS);
reset();
}
@@ -1238,15 +1204,20 @@
updateFinalShift();
}
- private void addLiveTileOverlay() {
- if (LiveTileOverlay.getInstance().attach(mActivity.getRootView().getOverlay())) {
- mRecentsView.setLiveTileOverlayAttached(true);
+ private synchronized void addLiveTileOverlay() {
+ if (!mLiveTileOverlayAttached) {
+ mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
+ mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
+ mLiveTileOverlayAttached = true;
}
}
- private void removeLiveTileOverlay() {
- LiveTileOverlay.getInstance().detach(mActivity.getRootView().getOverlay());
- mRecentsView.setLiveTileOverlayAttached(false);
+ private synchronized void removeLiveTileOverlay() {
+ if (mLiveTileOverlayAttached) {
+ mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
+ mRecentsView.setLiveTileOverlay(null);
+ mLiveTileOverlayAttached = false;
+ }
}
public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, float expectedAlpha) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 2f73fc1..0b5129c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -23,6 +23,11 @@
}
@Override
+ public boolean useSharedSwipeState() {
+ return mDelegate.useSharedSwipeState();
+ }
+
+ @Override
public boolean allowInterceptByParent() {
return mDelegate.allowInterceptByParent() && mState != STATE_ACTIVE;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 5a34520..12b7c26 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -22,7 +22,8 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
+import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import android.content.ComponentName;
@@ -44,9 +45,9 @@
import com.android.quickstep.MultiStateCallback;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationTargets;
-import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -75,16 +76,18 @@
private final Context mContext;
private final RecentsAnimationDeviceState mDeviceState;
- private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private final float mTouchSlopSquared;
+ private final SwipeSharedState mSwipeSharedState;
private final InputMonitorCompat mInputMonitorCompat;
private final PointF mTouchDown = new PointF();
private final AppWindowAnimationHelper mAppWindowAnimationHelper;
+ private int mLogId;
private final AppWindowAnimationHelper.TransformParams mTransformParams;
private final Point mDisplaySize;
private final MultiStateCallback mStateCallback;
+ public final int mRunningTaskId;
private VelocityTracker mVelocityTracker;
private float mProgress;
@@ -95,23 +98,25 @@
private RecentsAnimationTargets mRecentsAnimationTargets;
public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
- TaskAnimationManager taskAnimationManager, GestureState gestureState,
- InputMonitorCompat inputMonitorCompat) {
+ GestureState gestureState, SwipeSharedState swipeSharedState,
+ InputMonitorCompat inputMonitorCompat, int runningTaskId, int logId) {
mContext = context;
mDeviceState = deviceState;
- mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mTouchSlopSquared = squaredTouchSlop(context);
+ mSwipeSharedState = swipeSharedState;
mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
+ mLogId = logId;
mTransformParams = new AppWindowAnimationHelper.TransformParams();
mInputMonitorCompat = inputMonitorCompat;
+ mRunningTaskId = runningTaskId;
// Do not use DeviceProfile as the user data might be locked
mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize;
// Init states
mStateCallback = new MultiStateCallback(STATE_NAMES);
- mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
+ mStateCallback.addCallback(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
this::endRemoteAnimation);
mVelocityTracker = VelocityTracker.obtain();
@@ -202,14 +207,16 @@
private void startRecentsTransition() {
mThresholdCrossed = true;
- mInputMonitorCompat.pilferPointers();
-
+ RecentsAnimationCallbacks callbacks = mSwipeSharedState.newRecentsAnimationCallbacks();
+ callbacks.addListener(this);
Intent intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_DEFAULT)
.setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
- .putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
- mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
+ .putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
+
+ mInputMonitorCompat.pilferPointers();
+ startRecentsActivityAsync(intent, callbacks);
}
@Override
@@ -219,8 +226,7 @@
mRecentsAnimationTargets = targets;
Rect displaySize = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
- RemoteAnimationTargetCompat targetCompat = targets.findTask(
- mGestureState.getRunningTaskId());
+ RemoteAnimationTargetCompat targetCompat = targets.findTask(mRunningTaskId);
if (targetCompat != null) {
mAppWindowAnimationHelper.updateSource(displaySize, targetCompat);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
similarity index 72%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
index 24f247b..370b487 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
@@ -13,20 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep;
+package com.android.quickstep.inputconsumers;
-import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
-import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
-import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
-import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
-import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.HOME;
+import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.LAST_TASK;
+import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.NEW_TASK;
+import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.RECENTS;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
@@ -34,25 +35,32 @@
import android.graphics.RectF;
import android.os.Bundle;
-import android.util.ArrayMap;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.util.ObjectWrapper;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
-import com.android.quickstep.GestureState.GestureEndTarget;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.BaseSwipeUpHandler;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.MultiStateCallback;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.InputConsumerController;
-/**
- * Handles the navigation gestures when a 3rd party launcher is the default home activity.
- */
-public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
+public class FallbackNoButtonInputConsumer extends
+ BaseSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[5] : null;
@@ -75,41 +83,51 @@
private static final int STATE_APP_CONTROLLER_RECEIVED =
getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
- public static class EndTargetAnimationParams {
+ public enum GestureEndTarget {
+ HOME(3, 100, 1),
+ RECENTS(1, 300, 0),
+ LAST_TASK(0, 150, 1),
+ NEW_TASK(0, 150, 1);
+
private final float mEndProgress;
private final long mDurationMultiplier;
private final float mLauncherAlpha;
- EndTargetAnimationParams(float endProgress, long durationMultiplier, float launcherAlpha) {
+ GestureEndTarget(float endProgress, long durationMultiplier, float launcherAlpha) {
mEndProgress = endProgress;
mDurationMultiplier = durationMultiplier;
mLauncherAlpha = launcherAlpha;
}
}
- private static ArrayMap<GestureEndTarget, EndTargetAnimationParams>
- mEndTargetAnimationParams = new ArrayMap();
private final AnimatedFloat mLauncherAlpha = new AnimatedFloat(this::onLauncherAlphaChanged);
private boolean mIsMotionPaused = false;
+ private GestureEndTarget mEndTarget;
private final boolean mInQuickSwitchMode;
private final boolean mContinuingLastGesture;
private final boolean mRunningOverHome;
private final boolean mSwipeUpOverHome;
+ private final RunningTaskInfo mRunningTaskInfo;
+
private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
private RunningWindowAnim mFinishAnimation;
- public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, InputConsumerController inputConsumer,
+ public FallbackNoButtonInputConsumer(Context context, GestureState gestureState,
+ OverviewComponentObserver overviewComponentObserver,
+ RunningTaskInfo runningTaskInfo, RecentsModel recentsModel,
+ InputConsumerController inputConsumer,
boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
- super(context, deviceState, gestureState, inputConsumer);
+ super(context, gestureState, overviewComponentObserver, recentsModel, inputConsumer,
+ runningTaskInfo.id);
mLauncherAlpha.value = 1;
+ mRunningTaskInfo = runningTaskInfo;
mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
mContinuingLastGesture = continuingLastGesture;
- mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
+ mRunningOverHome = ActivityManagerWrapper.isHomeTask(runningTaskInfo);
mSwipeUpOverHome = mRunningOverHome && !mInQuickSwitchMode;
if (mSwipeUpOverHome) {
@@ -118,46 +136,40 @@
mAppWindowAnimationHelper.setBaseAlphaCallback((t, a) -> mLauncherAlpha.value);
}
- // Going home has an extra long progress to ensure that it animates into the screen
- mEndTargetAnimationParams.put(HOME, new EndTargetAnimationParams(3, 100, 1));
- mEndTargetAnimationParams.put(RECENTS, new EndTargetAnimationParams(1, 300, 0));
- mEndTargetAnimationParams.put(LAST_TASK, new EndTargetAnimationParams(0, 150, 1));
- mEndTargetAnimationParams.put(NEW_TASK, new EndTargetAnimationParams(0, 150, 1));
-
initStateCallbacks();
}
private void initStateCallbacks() {
mStateCallback = new MultiStateCallback(STATE_NAMES);
- mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
+ mStateCallback.addCallback(STATE_HANDLER_INVALIDATED,
this::onHandlerInvalidated);
- mStateCallback.runOnceAtState(STATE_RECENTS_PRESENT | STATE_HANDLER_INVALIDATED,
+ mStateCallback.addCallback(STATE_RECENTS_PRESENT | STATE_HANDLER_INVALIDATED,
this::onHandlerInvalidatedWithRecents);
- mStateCallback.runOnceAtState(STATE_GESTURE_CANCELLED | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.addCallback(STATE_GESTURE_CANCELLED | STATE_APP_CONTROLLER_RECEIVED,
this::finishAnimationTargetSetAnimationComplete);
if (mInQuickSwitchMode) {
- mStateCallback.runOnceAtState(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED
+ mStateCallback.addCallback(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED
| STATE_RECENTS_PRESENT,
this::finishAnimationTargetSet);
} else {
- mStateCallback.runOnceAtState(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.addCallback(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED,
this::finishAnimationTargetSet);
}
}
private void onLauncherAlphaChanged() {
- if (mRecentsAnimationTargets != null && mGestureState.getEndTarget() == null) {
+ if (mRecentsAnimationTargets != null && mEndTarget == null) {
applyTransformUnchecked();
}
}
@Override
- protected boolean onActivityInit(Boolean alreadyOnHome) {
- mActivity = mActivityInterface.getCreatedActivity();
- mRecentsView = mActivity.getOverviewPanel();
+ protected boolean onActivityInit(final RecentsActivity activity, Boolean alreadyOnHome) {
+ mActivity = activity;
+ mRecentsView = activity.getOverviewPanel();
linkRecentsViewScroll();
mRecentsView.setDisallowScrollToClearAll(true);
mRecentsView.getClearAllButton().setVisibilityAlpha(0);
@@ -166,12 +178,12 @@
if (!mContinuingLastGesture) {
if (mRunningOverHome) {
- mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
+ mRecentsView.onGestureAnimationStart(mRunningTaskInfo);
} else {
- mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId());
+ mRecentsView.onGestureAnimationStart(mRunningTaskId);
}
}
- mStateCallback.setStateOnUiThread(STATE_RECENTS_PRESENT);
+ setStateOnUiThread(STATE_RECENTS_PRESENT);
return true;
}
@@ -214,9 +226,9 @@
@Override
public Intent getLaunchIntent() {
if (mInQuickSwitchMode || mSwipeUpOverHome) {
- return mGestureState.getOverviewIntent();
+ return mOverviewComponentObserver.getOverviewIntent();
} else {
- return mGestureState.getHomeIntent();
+ return mOverviewComponentObserver.getHomeIntent();
}
}
@@ -235,8 +247,8 @@
@Override
public void onGestureCancelled() {
updateDisplacement(0);
- mGestureState.setEndTarget(LAST_TASK);
- mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED);
+ mEndTarget = LAST_TASK;
+ setStateOnUiThread(STATE_GESTURE_CANCELLED);
}
@Override
@@ -244,29 +256,28 @@
mEndVelocityPxPerMs.set(0, velocity.y / 1000);
if (mInQuickSwitchMode) {
// For now set it to non-null, it will be reset before starting the animation
- mGestureState.setEndTarget(LAST_TASK);
+ mEndTarget = LAST_TASK;
} else {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
boolean isFling = Math.abs(endVelocity) > flingThreshold;
if (isFling) {
- mGestureState.setEndTarget(endVelocity < 0 ? HOME : LAST_TASK);
+ mEndTarget = endVelocity < 0 ? HOME : LAST_TASK;
} else if (mIsMotionPaused) {
- mGestureState.setEndTarget(RECENTS);
+ mEndTarget = RECENTS;
} else {
- mGestureState.setEndTarget(mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW
- ? HOME
- : LAST_TASK);
+ mEndTarget = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? HOME : LAST_TASK;
}
}
- mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
+ setStateOnUiThread(STATE_GESTURE_COMPLETED);
}
@Override
- public void onConsumerAboutToBeSwitched() {
- if (mInQuickSwitchMode && mGestureState.getEndTarget() != null) {
- mGestureState.setEndTarget(HOME);
+ public void onConsumerAboutToBeSwitched(SwipeSharedState sharedState) {
+ if (mInQuickSwitchMode && mEndTarget != null) {
+ sharedState.canGestureBeContinued = true;
+ sharedState.goingToLauncher = false;
mCanceled = true;
mCurrentShift.cancelAnimation();
@@ -282,12 +293,12 @@
? newRunningTaskView.getTask().key.id
: -1;
mRecentsView.setCurrentTask(newRunningTaskId);
- mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
+ sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId);
}
mRecentsView.setOnScrollChangeListener(null);
}
} else {
- mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
}
@@ -308,12 +319,12 @@
}
private void finishAnimationTargetSetAnimationComplete() {
- switch (mGestureState.getEndTarget()) {
+ switch (mEndTarget) {
case HOME: {
if (mSwipeUpOverHome) {
mRecentsAnimationController.finish(false, null, false);
// Send a home intent to clear the task stack
- mContext.startActivity(mGestureState.getHomeIntent());
+ mContext.startActivity(mOverviewComponentObserver.getHomeIntent());
} else {
mRecentsAnimationController.finish(true, null, true);
}
@@ -328,8 +339,7 @@
break;
}
- final int runningTaskId = mGestureState.getRunningTaskId();
- ThumbnailData thumbnail = mRecentsAnimationController.screenshotTask(runningTaskId);
+ ThumbnailData thumbnail = mRecentsAnimationController.screenshotTask(mRunningTaskId);
mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
false /* screenshot */);
@@ -338,9 +348,9 @@
Bundle extras = new Bundle();
extras.putBinder(EXTRA_THUMBNAIL, new ObjectWrapper<>(thumbnail));
- extras.putInt(EXTRA_TASK_ID, runningTaskId);
+ extras.putInt(EXTRA_TASK_ID, mRunningTaskId);
- Intent intent = new Intent(mGestureState.getOverviewIntent())
+ Intent intent = new Intent(mOverviewComponentObserver.getOverviewIntent())
.putExtras(extras);
mContext.startActivity(intent, options.toBundle());
mRecentsAnimationController.cleanupScreenshot();
@@ -352,7 +362,7 @@
}
}
- mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
private void finishAnimationTargetSet() {
@@ -360,20 +370,17 @@
// Recalculate the end target, some views might have been initialized after
// gesture has ended.
if (mRecentsView == null || !hasTargets()) {
- mGestureState.setEndTarget(LAST_TASK);
+ mEndTarget = LAST_TASK;
} else {
final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
final int taskToLaunch = mRecentsView.getNextPage();
- mGestureState.setEndTarget(
- (runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex)
- ? NEW_TASK
- : LAST_TASK);
+ mEndTarget = (runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex)
+ ? NEW_TASK : LAST_TASK;
}
}
- EndTargetAnimationParams params = mEndTargetAnimationParams.get(mGestureState.getEndTarget());
- float endProgress = params.mEndProgress;
- long duration = (long) (params.mDurationMultiplier *
+ float endProgress = mEndTarget.mEndProgress;
+ long duration = (long) (mEndTarget.mDurationMultiplier *
Math.abs(endProgress - mCurrentShift.value));
if (mRecentsView != null) {
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
@@ -388,7 +395,7 @@
}
};
- if (mGestureState.getEndTarget() == HOME && !mRunningOverHome) {
+ if (mEndTarget == HOME && !mRunningOverHome) {
RectFSpringAnim anim = createWindowAnimationToHome(mCurrentShift.value, duration);
anim.addAnimatorListener(endListener);
anim.start(mEndVelocityPxPerMs);
@@ -397,7 +404,7 @@
AnimatorSet anim = new AnimatorSet();
anim.play(mLauncherAlpha.animateToValue(
- mLauncherAlpha.value, params.mLauncherAlpha));
+ mLauncherAlpha.value, mEndTarget.mLauncherAlpha));
anim.play(mCurrentShift.animateToValue(mCurrentShift.value, endProgress));
anim.setDuration(duration);
@@ -422,14 +429,13 @@
}
applyTransformUnchecked();
- mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
+ setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
}
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
- super.onRecentsAnimationCanceled(thumbnailData);
mRecentsView.setRecentsAnimationTargets(null, null);
- mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index bf2128d..02f4c40 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -26,10 +26,12 @@
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
+import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.annotation.TargetApi;
+import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
@@ -53,10 +55,13 @@
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationDeviceState;
-import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.SwipeSharedState;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -75,19 +80,21 @@
public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
private final RecentsAnimationDeviceState mDeviceState;
- private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
- private RecentsAnimationCallbacks mActiveCallbacks;
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
+ private final RunningTaskInfo mRunningTask;
+ private final SwipeSharedState mSwipeSharedState;
private final InputMonitorCompat mInputMonitorCompat;
+ private final SysUINavigationMode.Mode mMode;
private final BaseActivityInterface mActivityInterface;
private final BaseSwipeUpHandler.Factory mHandlerFactory;
+ private final NavBarPosition mNavBarPosition;
+
private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
private final MotionPauseDetector mMotionPauseDetector;
private final float mMotionPauseMinDisplacement;
-
private VelocityTracker mVelocityTracker;
private BaseSwipeUpHandler mInteractionHandler;
@@ -116,17 +123,20 @@
ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
true /* restoreHomeStackPosition */);
};
+ private int mLogId;
public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
- TaskAnimationManager taskAnimationManager, GestureState gestureState,
+ GestureState gestureState, RunningTaskInfo runningTaskInfo,
boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
- InputMonitorCompat inputMonitorCompat, boolean disableHorizontalSwipe,
- Factory handlerFactory) {
+ SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
+ boolean disableHorizontalSwipe, Factory handlerFactory, int logId) {
super(base);
+ mLogId = logId;
mDeviceState = deviceState;
- mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mMainThreadHandler = new Handler(Looper.getMainLooper());
+ mRunningTask = runningTaskInfo;
+ mMode = SysUINavigationMode.getMode(base);
mHandlerFactory = handlerFactory;
mActivityInterface = mGestureState.getActivityInterface();
@@ -137,8 +147,11 @@
mVelocityTracker = VelocityTracker.obtain();
mInputMonitorCompat = inputMonitorCompat;
- boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
+ boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null;
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
+ mSwipeSharedState = swipeSharedState;
+
+ mNavBarPosition = new NavBarPosition(base);
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
float slop = QUICKSTEP_TOUCH_SLOP_RATIO * mTouchSlop;
@@ -170,7 +183,7 @@
if (mPassedWindowMoveSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
- mDeviceState.getNavBarPosition().getRotationMode()));
+ mNavBarPosition.getRotationMode()));
}
int edgeFlags = ev.getEdgeFlags();
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -280,7 +293,7 @@
mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
}
- if (mDeviceState.isFullyGesturalNavMode()) {
+ if (mMode == Mode.NO_BUTTON) {
mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
@@ -316,22 +329,25 @@
long touchTimeMs, boolean isLikelyToStartNewTask) {
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
- mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs,
- mTaskAnimationManager.isRecentsAnimationRunning(), isLikelyToStartNewTask);
- mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
- mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler::onMotionPauseChanged);
- mInteractionHandler.initWhenReady();
+ RecentsAnimationCallbacks listenerSet = mSwipeSharedState.getActiveListener();
+ final BaseSwipeUpHandler handler = mHandlerFactory.newHandler(mGestureState, mRunningTask,
+ touchTimeMs, listenerSet != null, isLikelyToStartNewTask);
- if (mTaskAnimationManager.isRecentsAnimationRunning()) {
- mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
- mActiveCallbacks.addListener(mInteractionHandler);
- mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
+ mInteractionHandler = handler;
+ handler.setGestureEndCallback(this::onInteractionGestureFinished);
+ mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
+ handler.initWhenReady();
+
+ if (listenerSet != null) {
+ listenerSet.addListener(handler);
+ mSwipeSharedState.applyActiveRecentsAnimationState(handler);
notifyGestureStarted();
} else {
- Intent intent = mInteractionHandler.getLaunchIntent();
- intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
- mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
- mInteractionHandler);
+ RecentsAnimationCallbacks callbacks = mSwipeSharedState.newRecentsAnimationCallbacks();
+ callbacks.addListener(handler);
+ Intent intent = handler.getLaunchIntent();
+ intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
+ startRecentsActivityAsync(intent, callbacks);
}
}
@@ -351,10 +367,8 @@
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
- float velocity = mDeviceState.getNavBarPosition().isRightEdge()
- ? velocityX
- : mDeviceState.getNavBarPosition().isLeftEdge()
- ? -velocityX
+ float velocity = mNavBarPosition.isRightEdge() ? velocityX
+ : mNavBarPosition.isLeftEdge() ? -velocityX
: velocityY;
mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
@@ -388,7 +402,7 @@
// The consumer is being switched while we are active. Set up the shared state to be
// used by the next animation
removeListener();
- mInteractionHandler.onConsumerAboutToBeSwitched();
+ mInteractionHandler.onConsumerAboutToBeSwitched(mSwipeSharedState);
}
}
@@ -401,15 +415,16 @@
}
private void removeListener() {
- if (mActiveCallbacks != null) {
- mActiveCallbacks.removeListener(mInteractionHandler);
+ RecentsAnimationCallbacks listenerSet = mSwipeSharedState.getActiveListener();
+ if (listenerSet != null) {
+ listenerSet.removeListener(mInteractionHandler);
}
}
private float getDisplacement(MotionEvent ev) {
- if (mDeviceState.getNavBarPosition().isRightEdge()) {
+ if (mNavBarPosition.isRightEdge()) {
return ev.getX() - mDownPos.x;
- } else if (mDeviceState.getNavBarPosition().isLeftEdge()) {
+ } else if (mNavBarPosition.isLeftEdge()) {
return mDownPos.x - ev.getX();
} else {
return ev.getY() - mDownPos.y;
@@ -417,6 +432,11 @@
}
@Override
+ public boolean useSharedSwipeState() {
+ return mInteractionHandler != null;
+ }
+
+ @Override
public boolean allowInterceptByParent() {
return !mPassedPilferInputSlop;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 875ec29..50069ea 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -21,9 +21,9 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.content.Context;
-import android.content.Intent;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -35,34 +35,36 @@
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.GestureState;
+import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.InputConsumer;
-import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.GestureState;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.NavBarPosition;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
public class OverviewWithoutFocusInputConsumer implements InputConsumer {
- private final Context mContext;
- private final RecentsAnimationDeviceState mDeviceState;
- private final GestureState mGestureState;
private final InputMonitorCompat mInputMonitor;
private final boolean mDisableHorizontalSwipe;
private final PointF mDownPos = new PointF();
private final float mSquaredTouchSlop;
+ private final Context mContext;
+ private final NavBarPosition mNavBarPosition;
+ private final BaseActivityInterface mActivityInterface;
private boolean mInterceptedTouch;
private VelocityTracker mVelocityTracker;
- public OverviewWithoutFocusInputConsumer(Context context,
- RecentsAnimationDeviceState deviceState, GestureState gestureState,
+ public OverviewWithoutFocusInputConsumer(Context context, GestureState gestureState,
InputMonitorCompat inputMonitor, boolean disableHorizontalSwipe) {
- mContext = context;
- mDeviceState = deviceState;
- mGestureState = gestureState;
mInputMonitor = inputMonitor;
mDisableHorizontalSwipe = disableHorizontalSwipe;
+ mContext = context;
+ mActivityInterface = gestureState.getActivityInterface();
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
+ mNavBarPosition = new NavBarPosition(context);
+
mVelocityTracker = VelocityTracker.obtain();
}
@@ -133,11 +135,8 @@
mVelocityTracker.computeCurrentVelocity(100);
float velocityX = mVelocityTracker.getXVelocity();
float velocityY = mVelocityTracker.getYVelocity();
- float velocity = mDeviceState.getNavBarPosition().isRightEdge()
- ? -velocityX
- : mDeviceState.getNavBarPosition().isLeftEdge()
- ? velocityX
- : -velocityY;
+ float velocity = mNavBarPosition.isRightEdge()
+ ? -velocityX : (mNavBarPosition.isLeftEdge() ? velocityX : -velocityY);
final boolean triggerQuickstep;
int touch = Touch.FLING;
@@ -151,9 +150,9 @@
}
if (triggerQuickstep) {
- mContext.startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ mActivityInterface.closeOverlay();
+ ActivityManagerWrapper.getInstance()
+ .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
int pageIndex = -1; // This number doesn't reflect workspace page index.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/QuickCaptureInputConsumer.java
similarity index 71%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/QuickCaptureInputConsumer.java
index e3da98b..97ca730 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/QuickCaptureInputConsumer.java
@@ -24,28 +24,39 @@
import static com.android.launcher3.Utilities.squaredHypot;
+import android.app.ActivityOptions;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.PointF;
+import android.os.Bundle;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.R;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.plugins.OverscrollPlugin;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
- * Input consumer for handling events to pass to an {@code OverscrollPlugin}.
- *
+ * Input consumer for handling events to launch quick capture from launcher
* @param <T> Draggable activity subclass used by RecentsView
*/
-public class OverscrollInputConsumer<T extends BaseDraggingActivity> extends DelegateInputConsumer {
+public class QuickCaptureInputConsumer<T extends BaseDraggingActivity>
+ extends DelegateInputConsumer {
- private static final String TAG = "OverscrollInputConsumer";
+ private static final String TAG = "QuickCaptureInputConsumer";
+
+ private static final String QUICK_CAPTURE_PACKAGE = "com.google.auxe.compose";
+ private static final String QUICK_CAPTURE_PACKAGE_DEV = "com.google.auxe.compose.debug";
+
+ private static final String EXTRA_DEVICE_STATE = "deviceState";
+ private static final String DEVICE_STATE_LOCKED = "Locked";
+ private static final String DEVICE_STATE_LAUNCHER = "Launcher";
+ private static final String DEVICE_STATE_APP = "App";
+ private static final String DEVICE_STATE_UNKNOWN = "Unknown";
private static final int ANGLE_THRESHOLD = 35; // Degrees
@@ -58,18 +69,14 @@
private final float mSquaredSlop;
- private final Context mContext;
- private final GestureState mGestureState;
- @Nullable private final OverscrollPlugin mPlugin;
+ private Context mContext;
private RecentsView mRecentsView;
- public OverscrollInputConsumer(Context context, GestureState gestureState,
- InputConsumer delegate, InputMonitorCompat inputMonitor, OverscrollPlugin plugin) {
+ public QuickCaptureInputConsumer(Context context, GestureState gestureState,
+ InputConsumer delegate, InputMonitorCompat inputMonitor) {
super(delegate, inputMonitor);
mContext = context;
- mGestureState = gestureState;
- mPlugin = plugin;
float slop = ViewConfiguration.get(context).getScaledTouchSlop();
mSquaredSlop = slop * slop;
@@ -80,11 +87,11 @@
@Override
public int getType() {
- return TYPE_OVERSCROLL | mDelegate.getType();
+ return TYPE_QUICK_CAPTURE | mDelegate.getType();
}
- private boolean onActivityInit(Boolean alreadyOnHome) {
- mRecentsView = mGestureState.getActivityInterface().getCreatedActivity().getOverviewPanel();
+ private boolean onActivityInit(final BaseDraggingActivity activity, Boolean alreadyOnHome) {
+ mRecentsView = activity.getOverviewPanel();
return true;
}
@@ -140,7 +147,7 @@
mPassedSlop = true;
mStartDragPos.set(mLastPos.x, mLastPos.y);
- if (isOverscrolled()) {
+ if (isValidQuickCaptureGesture()) {
setActive(ev);
} else {
mState = STATE_DELEGATE_ACTIVE;
@@ -152,8 +159,8 @@
}
case ACTION_CANCEL:
case ACTION_UP:
- if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop && mPlugin != null) {
- mPlugin.onOverscroll(getDeviceState());
+ if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop) {
+ startQuickCapture();
}
mPassedSlop = false;
@@ -166,7 +173,7 @@
}
}
- private boolean isOverscrolled() {
+ private boolean isValidQuickCaptureGesture() {
// Make sure there isn't an app to quick switch to on our right
boolean atRightMostApp = (mRecentsView == null || mRecentsView.getRunningTaskIndex() <= 0);
@@ -178,19 +185,37 @@
return atRightMostApp && angleInBounds;
}
- private String getDeviceState() {
- String deviceState = OverscrollPlugin.DEVICE_STATE_UNKNOWN;
+ private void startQuickCapture() {
+ // Inspect our delegate's type to figure out where the user invoked Compose
+ String deviceState = DEVICE_STATE_UNKNOWN;
int consumerType = mDelegate.getType();
if (((consumerType & InputConsumer.TYPE_OVERVIEW) > 0)
|| ((consumerType & InputConsumer.TYPE_OVERVIEW_WITHOUT_FOCUS)) > 0) {
- deviceState = OverscrollPlugin.DEVICE_STATE_LAUNCHER;
+ deviceState = DEVICE_STATE_LAUNCHER;
} else if ((consumerType & InputConsumer.TYPE_OTHER_ACTIVITY) > 0) {
- deviceState = OverscrollPlugin.DEVICE_STATE_APP;
+ deviceState = DEVICE_STATE_APP;
} else if (((consumerType & InputConsumer.TYPE_RESET_GESTURE) > 0)
|| ((consumerType & InputConsumer.TYPE_DEVICE_LOCKED) > 0)) {
- deviceState = OverscrollPlugin.DEVICE_STATE_LOCKED;
+ deviceState = DEVICE_STATE_LOCKED;
}
- return deviceState;
+ // Then launch the app
+ PackageManager pm = mContext.getPackageManager();
+
+ Intent qcIntent = pm.getLaunchIntentForPackage(QUICK_CAPTURE_PACKAGE);
+
+ if (qcIntent == null) {
+ // If we couldn't find the regular app, try the dev version
+ qcIntent = pm.getLaunchIntentForPackage(QUICK_CAPTURE_PACKAGE_DEV);
+ }
+
+ if (qcIntent != null) {
+ qcIntent.putExtra(EXTRA_DEVICE_STATE, deviceState);
+
+ Bundle options = ActivityOptions.makeCustomAnimation(mContext, R.anim.slide_in_right,
+ 0).toBundle();
+
+ mContext.startActivity(qcIntent, options);
+ }
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
index d34b40b..e04c0c7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
@@ -18,17 +18,17 @@
import android.view.MotionEvent;
import com.android.quickstep.InputConsumer;
-import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.SwipeSharedState;
/**
* A NO_OP input consumer which also resets any pending gesture
*/
public class ResetGestureInputConsumer implements InputConsumer {
- private final TaskAnimationManager mTaskAnimationManager;
+ private final SwipeSharedState mSwipeSharedState;
- public ResetGestureInputConsumer(TaskAnimationManager taskAnimationManager) {
- mTaskAnimationManager = taskAnimationManager;
+ public ResetGestureInputConsumer(SwipeSharedState swipeSharedState) {
+ mSwipeSharedState = swipeSharedState;
}
@Override
@@ -39,8 +39,8 @@
@Override
public void onMotionEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN
- && mTaskAnimationManager.isRecentsAnimationRunning()) {
- mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
+ && mSwipeSharedState.getActiveListener() != null) {
+ mSwipeSharedState.clearAllState(false /* finishAnimation */);
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ActiveGestureLog.java
index fabfc4b..9a3bb76 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -33,7 +33,7 @@
*/
public static final String INTENT_EXTRA_LOG_TRACE_ID = "INTENT_EXTRA_LOG_TRACE_ID";
- private ActiveGestureLog() {
+ public ActiveGestureLog() {
super("touch_interaction_log", 40);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 4a39e73..24e7f0e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -169,7 +169,7 @@
return null;
}
- float progress = Utilities.boundToRange(params.progress, 0, 1);
+ float progress = params.progress;
updateCurrentRect(params);
SurfaceParams[] surfaceParams = new SurfaceParams[params.targetSet.unfilteredApps.length];
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java
deleted file mode 100644
index 552db1f..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT;
-
-import android.annotation.TargetApi;
-import android.app.TaskInfo;
-import android.content.Intent;
-import android.os.Build;
-
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskInfoCompat;
-
-/**
- * Utility class for interacting with the Assistant.
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public final class AssistantUtilities {
-
- /** Returns true if an Assistant activity that is excluded from recents is running. */
- public static boolean isExcludedAssistantRunning() {
- return isExcludedAssistant(ActivityManagerWrapper.getInstance().getRunningTask());
- }
-
- /** Returns true if the given task holds an Assistant activity that is excluded from recents. */
- public static boolean isExcludedAssistant(TaskInfo info) {
- return info != null
- && TaskInfoCompat.getActivityType(info) == ACTIVITY_TYPE_ASSISTANT
- && (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
- }
-
- private AssistantUtilities() {}
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
new file mode 100644
index 0000000..bbb318a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_LANDSCAPE;
+import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_SEASCAPE;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+
+import android.content.Context;
+import android.view.Surface;
+
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.SysUINavigationMode;
+
+/**
+ * Utility class to check nav bar position
+ */
+public class NavBarPosition {
+
+ private final SysUINavigationMode.Mode mMode;
+ private final int mDisplayRotation;
+
+ public NavBarPosition(Context context) {
+ mMode = SysUINavigationMode.getMode(context);
+ mDisplayRotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
+ }
+
+ public boolean isRightEdge() {
+ return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90;
+ }
+
+ public boolean isLeftEdge() {
+ return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
+ }
+
+ public RotationMode getRotationMode() {
+ return isLeftEdge() ? ROTATION_SEASCAPE
+ : (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
deleted file mode 100644
index 41be683..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF_ANIM;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.uioverrides.states.OverviewState;
-
-/**
- * Animates the shelf between states HIDE, PEEK, and OVERVIEW.
- */
-public class ShelfPeekAnim {
-
- public static final Interpolator INTERPOLATOR = OVERSHOOT_1_2;
- public static final long DURATION = 240;
-
- private final Launcher mLauncher;
-
- private ShelfAnimState mShelfState;
- private boolean mIsPeeking;
-
- public ShelfPeekAnim(Launcher launcher) {
- mLauncher = launcher;
- }
-
- /**
- * Animates to the given state, canceling the previous animation if it was still running.
- */
- public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
- if (mShelfState == shelfState) {
- return;
- }
- mLauncher.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM);
- mShelfState = shelfState;
- mIsPeeking = mShelfState == ShelfAnimState.PEEK || mShelfState == ShelfAnimState.HIDE;
- if (mShelfState == ShelfAnimState.CANCEL) {
- return;
- }
- float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(mLauncher);
- float shelfOverviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
- // Peek based on default overview progress so we can see hotseat if we're showing
- // that instead of predictions in overview.
- float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
- float shelfPeekingProgress = shelfHiddenProgress
- - (shelfHiddenProgress - defaultOverviewProgress) * 0.25f;
- float toProgress = mShelfState == ShelfAnimState.HIDE
- ? shelfHiddenProgress
- : mShelfState == ShelfAnimState.PEEK
- ? shelfPeekingProgress
- : shelfOverviewProgress;
- Animator shelfAnim = mLauncher.getStateManager()
- .createStateElementAnimation(INDEX_SHELF_ANIM, toProgress);
- shelfAnim.setInterpolator(interpolator);
- shelfAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationCancel(Animator animation) {
- mShelfState = ShelfAnimState.CANCEL;
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- mIsPeeking = mShelfState == ShelfAnimState.PEEK;
- }
- });
- shelfAnim.setDuration(duration).start();
- }
-
- /** @return Whether the shelf is currently peeking or animating to or from peeking. */
- public boolean isPeeking() {
- return mIsPeeking;
- }
-
- /** The various shelf states we can animate to. */
- public enum ShelfAnimState {
- HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
-
- ShelfAnimState(boolean shouldPreformHaptic) {
- this.shouldPreformHaptic = shouldPreformHaptic;
- }
-
- public final boolean shouldPreformHaptic;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 82fbbc6..0655c73 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -102,9 +102,8 @@
@Override
public void startHome() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- switchToScreenshot(null,
- () -> finishRecentsAnimation(true /* toRecents */,
- () -> mActivity.getStateManager().goToState(NORMAL)));
+ switchToScreenshot(() -> finishRecentsAnimation(true /* toRecents */,
+ () -> mActivity.getStateManager().goToState(NORMAL)));
} else {
mActivity.getStateManager().goToState(NORMAL);
}
@@ -326,8 +325,8 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- PluginManagerWrapper.INSTANCE.get(getContext()).addPluginListener(
- mRecentsExtraCardPluginListener, RecentsExtraCard.class);
+ PluginManagerWrapper.INSTANCE.get(getContext())
+ .addPluginListener(mRecentsExtraCardPluginListener, RecentsExtraCard.class);
}
@Override
@@ -378,10 +377,10 @@
}
@Override
- public void setContentAlpha(float alpha) {
- super.setContentAlpha(alpha);
+ public void resetTaskVisuals() {
+ super.resetTaskVisuals();
if (mRecentsExtraViewContainer != null) {
- mRecentsExtraViewContainer.setAlpha(alpha);
+ mRecentsExtraViewContainer.setAlpha(mContentAlpha);
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
index 18eda60..a838797 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -16,7 +16,6 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
-import android.view.ViewOverlay;
import com.android.launcher3.anim.Interpolators;
@@ -37,15 +36,6 @@
}
};
- private static LiveTileOverlay sInstance;
-
- public static LiveTileOverlay getInstance() {
- if (sInstance == null) {
- sInstance = new LiveTileOverlay();
- }
- return sInstance;
- }
-
private final Paint mPaint = new Paint();
private Rect mBoundsRect = new Rect();
@@ -56,9 +46,8 @@
private boolean mDrawEnabled = true;
private float mIconAnimationProgress = 0f;
- private boolean mIsAttached;
- private LiveTileOverlay() {
+ public LiveTileOverlay() {
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
@@ -135,23 +124,6 @@
return PixelFormat.TRANSLUCENT;
}
- public boolean attach(ViewOverlay overlay) {
- if (overlay != null && !mIsAttached) {
- overlay.add(this);
- mIsAttached = true;
- return true;
- }
-
- return false;
- }
-
- public void detach(ViewOverlay overlay) {
- if (overlay != null) {
- overlay.remove(this);
- mIsAttached = false;
- }
- }
-
private void setIconAnimationProgress(float progress) {
mIconAnimationProgress = progress;
invalidateSelf();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index eaa23a6..434a0c2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -59,7 +59,6 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
-import android.os.UserHandle;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
@@ -105,7 +104,7 @@
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
-import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
+import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.ViewUtils;
@@ -127,7 +126,7 @@
@TargetApi(Build.VERSION_CODES.P)
public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
- InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener {
+ InvariantDeviceProfile.OnIDPChangeListener, TaskThumbnailChangeListener {
private static final String TAG = RecentsView.class.getSimpleName();
@@ -307,7 +306,7 @@
private final int mEmptyMessagePadding;
private boolean mShowEmptyMessage;
private Layout mEmptyTextLayout;
- private boolean mLiveTileOverlayAttached;
+ private LiveTileOverlay mLiveTileOverlay;
// Keeps track of the index where the first TaskView should be
private int mTaskViewStartIndex = 0;
@@ -383,21 +382,6 @@
return null;
}
- @Override
- public void onTaskIconChanged(String pkg, UserHandle user) {
- for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView tv = getTaskViewAt(i);
- Task task = tv.getTask();
- if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
- && task.key.userId == user.getIdentifier()) {
- task.icon = null;
- if (tv.getIconView().getDrawable() != null) {
- tv.onTaskListVisibilityChanged(true /* visible */);
- }
- }
- }
- }
-
/**
* Update the thumbnail of the task.
* @param refreshNow Refresh immediately if it's true.
@@ -875,8 +859,8 @@
*/
public void onSwipeUpAnimationSuccess() {
if (getRunningTaskView() != null) {
- float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlayAttached
- ? LiveTileOverlay.getInstance().cancelIconAnimation()
+ float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlay != null
+ ? mLiveTileOverlay.cancelIconAnimation()
: 0f;
animateUpRunningTaskIconScale(startProgress);
}
@@ -1688,8 +1672,8 @@
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
final int[] visibleTasks = getVisibleChildrenRange();
- event.setFromIndex(taskViewCount - visibleTasks[1]);
- event.setToIndex(taskViewCount - visibleTasks[0]);
+ event.setFromIndex(taskViewCount - visibleTasks[1] - 1);
+ event.setToIndex(taskViewCount - visibleTasks[0] - 1);
event.setItemCount(taskViewCount);
}
}
@@ -1723,13 +1707,13 @@
mAppWindowAnimationHelper = appWindowAnimationHelper;
}
- public void setLiveTileOverlayAttached(boolean liveTileOverlayAttached) {
- mLiveTileOverlayAttached = liveTileOverlayAttached;
+ public void setLiveTileOverlay(LiveTileOverlay liveTileOverlay) {
+ mLiveTileOverlay = liveTileOverlay;
}
public void updateLiveTileIcon(Drawable icon) {
- if (mLiveTileOverlayAttached) {
- LiveTileOverlay.getInstance().setIcon(icon);
+ if (mLiveTileOverlay != null) {
+ mLiveTileOverlay.setIcon(icon);
}
}
@@ -1741,17 +1725,7 @@
return;
}
- mRecentsAnimationController.finish(toRecents, () -> {
- if (onFinishComplete != null) {
- onFinishComplete.run();
- // After we finish the recents animation, the current task id should be correctly
- // reset so that when the task is launched from Overview later, it goes through the
- // flow of starting a new task instead of finishing recents animation to app. A
- // typical example of this is (1) user swipes up from app to Overview (2) user
- // taps on QSB (3) user goes back to Overview and launch the most recent task.
- setCurrentTask(-1);
- }
- });
+ mRecentsAnimationController.finish(toRecents, onFinishComplete);
}
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
@@ -1854,8 +1828,8 @@
private void updateEnabledOverlays() {
int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
int taskCount = getTaskViewCount();
- for (int i = mTaskViewStartIndex; i < mTaskViewStartIndex + taskCount; i++) {
- getTaskViewAtByAbsoluteIndex(i).setOverlayEnabled(i == overlayEnabledPage);
+ for (int i = 0; i < taskCount; i++) {
+ getTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
}
}
@@ -1876,20 +1850,20 @@
return Math.max(insets.getSystemGestureInsets().right, insets.getSystemWindowInsetRight());
}
+
/** If it's in the live tile mode, switch the running task into screenshot mode. */
- public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
+ public void switchToScreenshot(Runnable onFinishRunnable) {
TaskView taskView = getRunningTaskView();
- if (taskView != null) {
- taskView.setShowScreenshot(true);
- if (thumbnailData != null) {
- taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
- } else {
- taskView.getThumbnail().refresh();
+ if (taskView == null) {
+ if (onFinishRunnable != null) {
+ onFinishRunnable.run();
}
- ViewUtils.postDraw(taskView, onFinishRunnable);
- } else {
- onFinishRunnable.run();
+ return;
}
+
+ taskView.setShowScreenshot(true);
+ taskView.getThumbnail().refresh();
+ ViewUtils.postDraw(taskView, onFinishRunnable);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
index 80022b4..07d0796 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -25,6 +26,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
@@ -39,13 +41,16 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.TaskOverlayFactory;
+import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.views.IconView.OnScaleUpdateListener;
+import java.util.List;
+
/**
* Contains options for a recent task when long-pressing its icon.
*/
@@ -192,15 +197,22 @@
params.topMargin = (int) -mThumbnailTopMargin;
mTaskIcon.setLayoutParams(params);
- TaskOverlayFactory.getEnabledShortcuts(taskView).forEach(this::addMenuOption);
+ final BaseDraggingActivity activity = BaseDraggingActivity.fromContext(getContext());
+ final List<TaskSystemShortcut> shortcuts =
+ TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(taskView);
+ final int count = shortcuts.size();
+ for (int i = 0; i < count; ++i) {
+ final TaskSystemShortcut menuOption = shortcuts.get(i);
+ addMenuOption(menuOption, menuOption.getOnClickListener(activity, taskView));
+ }
}
- private void addMenuOption(SystemShortcut menuOption) {
+ private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
ViewGroup menuOptionView = (ViewGroup) mActivity.getLayoutInflater().inflate(
R.layout.task_view_menu_option, this, false);
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
- menuOptionView.setOnClickListener(menuOption);
+ menuOptionView.setOnClickListener(onClickListener);
mOptionLayout.addView(menuOptionView);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index a1775f4..bfb9613 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -53,7 +53,6 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -62,6 +61,7 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
+import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.TaskCornerRadius;
@@ -287,19 +287,11 @@
public void launchTask(boolean animate, boolean freezeTaskList, Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = getRecentsView();
if (isRunningTask()) {
- recentsView.finishRecentsAnimation(false /* toRecents */,
+ getRecentsView().finishRecentsAnimation(false /* toRecents */,
() -> resultCallbackHandler.post(() -> resultCallback.accept(true)));
} else {
- // This is a workaround against the WM issue that app open is not correctly animated
- // when recents animation is being cleaned up (b/143774568). When that's possible,
- // we should rely on the framework side to cancel the recents animation, and we will
- // clean up the screenshot on the launcher side while we launch the next task.
- recentsView.switchToScreenshot(null,
- () -> recentsView.finishRecentsAnimation(true /* toRecents */,
- () -> launchTaskInternal(animate, freezeTaskList, resultCallback,
- resultCallbackHandler)));
+ launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
}
} else {
launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
@@ -721,8 +713,15 @@
getContext().getText(R.string.accessibility_close_task)));
final Context context = getContext();
- for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
- info.addAction(s.createAccessibilityAction(context));
+ final List<TaskSystemShortcut> shortcuts =
+ TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
+ final int count = shortcuts.size();
+ for (int i = 0; i < count; ++i) {
+ final TaskSystemShortcut menuOption = shortcuts.get(i);
+ OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
+ if (onClickListener != null) {
+ info.addAction(menuOption.createAccessibilityAction(context));
+ }
}
if (mDigitalWellBeingToast.hasLimit()) {
@@ -735,8 +734,8 @@
final RecentsView recentsView = getRecentsView();
final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
AccessibilityNodeInfo.CollectionItemInfo.obtain(
- 0, 1, recentsView.getTaskViewCount() - recentsView.indexOfChild(this) - 1,
- 1, false);
+ 0, 1, recentsView.getChildCount() - recentsView.indexOfChild(this) - 1, 1,
+ false);
info.setCollectionItemInfo(itemInfo);
}
@@ -753,9 +752,16 @@
return true;
}
- for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
- if (s.hasHandlerForAction(action)) {
- s.onClick(this);
+ final List<TaskSystemShortcut> shortcuts =
+ TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
+ final int count = shortcuts.size();
+ for (int i = 0; i < count; ++i) {
+ final TaskSystemShortcut menuOption = shortcuts.get(i);
+ if (menuOption.hasHandlerForAction(action)) {
+ OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
+ if (onClickListener != null) {
+ onClickListener.onClick(this);
+ }
return true;
}
}
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 5d9a009..98aaceb 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -33,6 +33,4 @@
<!-- Assistant Gesture -->
<integer name="assistant_gesture_min_time_threshold">200</integer>
<integer name="assistant_gesture_corner_deg_threshold">20</integer>
-
- <string name="wellbeing_provider_pkg" translatable="false"></string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
deleted file mode 100644
index 9ea13c6..0000000
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
-
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.model.WellbeingModel;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.proxy.ProxyActivityStarter;
-import com.android.launcher3.proxy.StartActivityParams;
-import com.android.launcher3.uioverrides.BackButtonAlphaHandler;
-import com.android.launcher3.uioverrides.RecentsViewStateController;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
-import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.RemoteFadeOutAnimationListener;
-import com.android.quickstep.util.ShelfPeekAnim;
-
-import java.util.stream.Stream;
-
-/**
- * Extension of Launcher activity to provide quickstep specific functionality
- */
-public abstract class BaseQuickstepLauncher extends Launcher
- implements NavigationModeChangeListener {
-
- /**
- * Reusable command for applying the back button alpha on the background thread.
- */
- public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
- (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(
- Float.intBitsToFloat(arg1), arg2 != 0);
-
- private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- SysUINavigationMode.Mode mode = SysUINavigationMode.INSTANCE.get(this)
- .addModeChangeListener(this);
- getRotationHelper().setRotationHadDifferentUI(mode != Mode.NO_BUTTON);
-
- if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
- getStateManager().addStateListener(new LauncherStateManager.StateListener() {
- @Override
- public void onStateTransitionStart(LauncherState toState) { }
-
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
- .get(BaseQuickstepLauncher.this).getMode().hasGestures;
- LauncherState prevState = getStateManager().getLastState();
-
- if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
- && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT
- <= getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
- getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
- getStateManager().removeStateListener(this);
- }
- }
- });
- }
-
- if (!getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
- getStateManager().addStateListener(new LauncherStateManager.StateListener() {
- @Override
- public void onStateTransitionStart(LauncherState toState) { }
-
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- LauncherState prevState = getStateManager().getLastState();
-
- if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT
- <= getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
- getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
- getStateManager().removeStateListener(this);
- }
- }
- });
- }
- }
-
- @Override
- public void onDestroy() {
- SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
- super.onDestroy();
- }
-
- @Override
- public void onNavigationModeChanged(Mode newMode) {
- getDragLayer().recreateControllers();
- getRotationHelper().setRotationHadDifferentUI(newMode != Mode.NO_BUTTON);
- }
-
- @Override
- public void onEnterAnimationComplete() {
- super.onEnterAnimationComplete();
- // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
- // as a part of quickstep, so that high-res thumbnails can load the next time we enter
- // overview
- RecentsModel.INSTANCE.get(this).getThumbnailCache()
- .getHighResLoadingState().setVisible(true);
- }
-
- @Override
- public void onTrimMemory(int level) {
- super.onTrimMemory(level);
- RecentsModel.INSTANCE.get(this).onTrimMemory(level);
- }
-
- @Override
- public void startIntentSenderForResult(IntentSender intent, int requestCode,
- Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
- if (requestCode != -1) {
- mPendingActivityRequestCode = requestCode;
- StartActivityParams params = new StartActivityParams(this, requestCode);
- params.intentSender = intent;
- params.fillInIntent = fillInIntent;
- params.flagsMask = flagsMask;
- params.flagsValues = flagsValues;
- params.extraFlags = extraFlags;
- params.options = options;
- startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
- } else {
- super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
- flagsValues, extraFlags, options);
- }
- }
-
- @Override
- public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
- if (requestCode != -1) {
- mPendingActivityRequestCode = -1;
- StartActivityParams params = new StartActivityParams(this, requestCode);
- params.intent = intent;
- params.options = options;
- startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
- } else {
- super.startActivityForResult(intent, requestCode, options);
- }
- }
-
- @Override
- protected void onDeferredResumed() {
- if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
- // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
- onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
- // ProxyActivityStarter is started with clear task to reset the task after which it
- // removes the task itself.
- startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
- }
- }
-
- @Override
- protected StateHandler[] createStateHandlers() {
- return new StateHandler[] {
- getAllAppsController(),
- getWorkspace(),
- new RecentsViewStateController(this),
- new BackButtonAlphaHandler(this)};
- }
-
- @Override
- protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
- if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) {
- float offscreenTranslationX = getDeviceProfile().widthPx
- - getOverviewPanel().getPaddingStart();
- return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
- }
- return super.getOverviewScaleAndTranslationForNormalState();
- }
-
- @Override
- public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
- QuickstepAppTransitionManagerImpl appTransitionManager =
- (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
- appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
-
- // On the first call clear the reference.
- signal.cancel();
-
- ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
- fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
- wallpaperTargets));
- AnimatorSet anim = new AnimatorSet();
- anim.play(fadeAnimation);
- return anim;
- }, signal);
- }
-
- @Override
- public void onDragLayerHierarchyChanged() {
- onLauncherStateOrFocusChanged();
- }
-
- @Override
- protected void onActivityFlagsChanged(int changeBits) {
- if ((changeBits
- & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
- onLauncherStateOrFocusChanged();
- }
-
- super.onActivityFlagsChanged(changeBits);
- }
-
- /**
- * Sets the back button visibility based on the current state/window focus.
- */
- private void onLauncherStateOrFocusChanged() {
- Mode mode = SysUINavigationMode.getMode(this);
- boolean shouldBackButtonBeHidden = mode.hasGestures
- && getStateManager().getState().hideBackButton
- && hasWindowFocus()
- && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
- if (shouldBackButtonBeHidden) {
- // Show the back button if there is a floating view visible.
- shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
- TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
- }
- UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
- shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
- if (getDragLayer() != null) {
- getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
- }
- }
-
- @Override
- public void finishBindingItems(int pageBoundFirst) {
- super.finishBindingItems(pageBoundFirst);
- // Instantiate and initialize WellbeingModel now that its loading won't interfere with
- // populating workspace.
- // TODO: Find a better place for this
- WellbeingModel.get(this);
- }
-
- @Override
- public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
- return Stream.concat(super.getSupportedShortcuts(),
- Stream.of(WellbeingModel.SHORTCUT_FACTORY));
- }
-
- public ShelfPeekAnim getShelfPeekAnim() {
- return mShelfPeekAnim;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 96340b2..663b125 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -22,7 +22,6 @@
import android.os.CancellationSignal;
import android.os.Handler;
-import com.android.launcher3.util.ActivityTracker;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
@@ -33,11 +32,6 @@
private RemoteAnimationProvider mRemoteAnimationProvider;
- /**
- * @param onInitListener a callback made when the activity is initialized. The callback should
- * return true to continue receiving callbacks (ie. for if the activity is
- * recreated).
- */
public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
super(onInitListener, Launcher.ACTIVITY_TRACKER);
}
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
deleted file mode 100644
index 5aa4388..0000000
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.model;
-
-import static android.content.ContentResolver.SCHEME_CONTENT;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-
-import android.annotation.TargetApi;
-import android.app.RemoteAction;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.LauncherApps;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Process;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.R;
-import com.android.launcher3.popup.RemoteActionShortcut;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.SimpleBroadcastReceiver;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Data model for digital wellbeing status of apps.
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public final class WellbeingModel {
- private static final String TAG = "WellbeingModel";
- private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
- private static final boolean DEBUG = false;
-
- private static final int MSG_PACKAGE_ADDED = 1;
- private static final int MSG_PACKAGE_REMOVED = 2;
- private static final int MSG_FULL_REFRESH = 3;
-
- // Welbeing contract
- private static final String METHOD_GET_ACTIONS = "get_actions";
- private static final String EXTRA_ACTIONS = "actions";
- private static final String EXTRA_ACTION = "action";
- private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown";
- private static final String EXTRA_PACKAGES = "packages";
-
- private static WellbeingModel sInstance;
-
- private final Context mContext;
- private final String mWellbeingProviderPkg;
- private final Handler mWorkerHandler;
-
- private final ContentObserver mContentObserver;
-
- private final Object mModelLock = new Object();
- // Maps the action Id to the corresponding RemoteAction
- private final Map<String, RemoteAction> mActionIdMap = new ArrayMap<>();
- private final Map<String, String> mPackageToActionId = new HashMap<>();
-
- private boolean mIsInTest;
-
- private WellbeingModel(final Context context) {
- mContext = context;
- mWorkerHandler =
- new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
-
- mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
- mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- // Wellbeing reports that app actions have changed.
- if (DEBUG || mIsInTest) {
- Log.d(TAG, "ContentObserver.onChange() called with: selfChange = [" + selfChange
- + "], uri = [" + uri + "]");
- }
- Preconditions.assertUIThread();
- updateWellbeingData();
- }
- };
-
- if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
- context.registerReceiver(
- new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
- PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
- Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
- Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
- Intent.ACTION_PACKAGE_RESTARTED));
-
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addDataScheme("package");
- context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
- filter);
-
- restartObserver();
- }
- }
-
- public void setInTest(boolean inTest) {
- mIsInTest = inTest;
- }
-
- protected void onWellbeingProviderChanged(Intent intent) {
- if (DEBUG || mIsInTest) {
- Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
- }
- restartObserver();
- }
-
- private void restartObserver() {
- final ContentResolver resolver = mContext.getContentResolver();
- resolver.unregisterContentObserver(mContentObserver);
- Uri actionsUri = apiBuilder().path("actions").build();
- try {
- resolver.registerContentObserver(
- actionsUri, true /* notifyForDescendants */, mContentObserver);
- } catch (Exception e) {
- Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
- if (mIsInTest) throw new RuntimeException(e);
- }
- updateWellbeingData();
- }
-
- @MainThread
- public static WellbeingModel get(@NonNull Context context) {
- Preconditions.assertUIThread();
- if (sInstance == null) {
- sInstance = new WellbeingModel(context.getApplicationContext());
- }
- return sInstance;
- }
-
- @MainThread
- private SystemShortcut getShortcutForApp(String packageName, int userId,
- BaseDraggingActivity activity, ItemInfo info) {
- Preconditions.assertUIThread();
- // Work profile apps are not recognized by digital wellbeing.
- if (userId != UserHandle.myUserId()) {
- if (DEBUG || mIsInTest) {
- Log.d(TAG, "getShortcutForApp [" + packageName + "]: not current user");
- }
- return null;
- }
-
- synchronized (mModelLock) {
- String actionId = mPackageToActionId.get(packageName);
- final RemoteAction action = actionId != null ? mActionIdMap.get(actionId) : null;
- if (action == null) {
- if (DEBUG || mIsInTest) {
- Log.d(TAG, "getShortcutForApp [" + packageName + "]: no action");
- }
- return null;
- }
- if (DEBUG || mIsInTest) {
- Log.d(TAG,
- "getShortcutForApp [" + packageName + "]: action: '" + action.getTitle()
- + "'");
- }
- return new RemoteActionShortcut(action, activity, info);
- }
- }
-
- private void updateWellbeingData() {
- mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
- }
-
- private Uri.Builder apiBuilder() {
- return new Uri.Builder()
- .scheme(SCHEME_CONTENT)
- .authority(mWellbeingProviderPkg + ".api");
- }
-
- private boolean updateActions(String... packageNames) {
- if (packageNames.length == 0) {
- return true;
- }
- if (DEBUG || mIsInTest) {
- Log.d(TAG, "retrieveActions() called with: packageNames = [" + String.join(", ",
- packageNames) + "]");
- }
- Preconditions.assertNonUiThread();
-
- Uri contentUri = apiBuilder().build();
- final Bundle remoteActionBundle;
- try (ContentProviderClient client = mContext.getContentResolver()
- .acquireUnstableContentProviderClient(contentUri)) {
- if (client == null) {
- if (DEBUG || mIsInTest) Log.i(TAG, "retrieveActions(): null provider");
- return false;
- }
-
- // Prepare wellbeing call parameters.
- final Bundle params = new Bundle();
- params.putStringArray(EXTRA_PACKAGES, packageNames);
- params.putInt(EXTRA_MAX_NUM_ACTIONS_SHOWN, 1);
- // Perform wellbeing call .
- remoteActionBundle = client.call(METHOD_GET_ACTIONS, null, params);
- } catch (DeadObjectException e) {
- Log.i(TAG, "retrieveActions(): DeadObjectException");
- return false;
- } catch (Exception e) {
- Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
- if (mIsInTest) throw new RuntimeException(e);
- return true;
- }
-
- synchronized (mModelLock) {
- // Remove the entries for requested packages, and then update the fist with what we
- // got from service
- Arrays.stream(packageNames).forEach(mPackageToActionId::remove);
-
- // The result consists of sub-bundles, each one is per a remote action. Each sub-bundle
- // has a RemoteAction and a list of packages to which the action applies.
- for (String actionId :
- remoteActionBundle.getStringArray(EXTRA_ACTIONS)) {
- final Bundle actionBundle = remoteActionBundle.getBundle(actionId);
- mActionIdMap.put(actionId,
- actionBundle.getParcelable(EXTRA_ACTION));
-
- final String[] packagesForAction =
- actionBundle.getStringArray(EXTRA_PACKAGES);
- if (DEBUG || mIsInTest) {
- Log.d(TAG, "....actionId: " + actionId + ", packages: " + String.join(", ",
- packagesForAction));
- }
- for (String packageName : packagesForAction) {
- mPackageToActionId.put(packageName, actionId);
- }
- }
- }
- if (DEBUG || mIsInTest) Log.i(TAG, "retrieveActions(): finished");
- return true;
- }
-
- private boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_PACKAGE_REMOVED: {
- String packageName = (String) msg.obj;
- mWorkerHandler.removeCallbacksAndMessages(packageName);
- synchronized (mModelLock) {
- mPackageToActionId.remove(packageName);
- }
- return true;
- }
- case MSG_PACKAGE_ADDED: {
- String packageName = (String) msg.obj;
- mWorkerHandler.removeCallbacksAndMessages(packageName);
- if (!updateActions(packageName)) {
- scheduleRefreshRetry(msg);
- }
- return true;
- }
-
- case MSG_FULL_REFRESH: {
- // Remove all existing messages
- mWorkerHandler.removeCallbacksAndMessages(null);
- final String[] packageNames = mContext.getSystemService(LauncherApps.class)
- .getActivityList(null, Process.myUserHandle()).stream()
- .map(li -> li.getApplicationInfo().packageName).distinct()
- .toArray(String[]::new);
- if (!updateActions(packageNames)) {
- scheduleRefreshRetry(msg);
- }
- return true;
- }
- }
- return false;
- }
-
- private void scheduleRefreshRetry(Message originalMsg) {
- int retryCount = originalMsg.arg1;
- if (retryCount >= RETRY_TIMES_MS.length) {
- // To many retries, skip
- return;
- }
-
- Message msg = Message.obtain(originalMsg);
- msg.arg1 = retryCount + 1;
- mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
- }
-
- private void onAppPackageChanged(Intent intent) {
- if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
- Preconditions.assertUIThread();
-
- final String packageName = intent.getData().getSchemeSpecificPart();
- if (packageName == null || packageName.length() == 0) {
- // they sent us a bad intent
- return;
- }
-
- final String action = intent.getAction();
- if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget();
- } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
- }
- }
-
- /**
- * Shortcut factory for generating wellbeing action
- */
- public static final SystemShortcut.Factory SHORTCUT_FACTORY = (activity, info) ->
- (info.getTargetComponent() == null) ? null : WellbeingModel.get(activity)
- .getShortcutForApp(
- info.getTargetComponent().getPackageName(), info.user.getIdentifier(),
- activity, info);
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
deleted file mode 100644
index 965b5f0..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.uioverrides;
-
-import android.app.Activity;
-import android.app.Person;
-import android.content.pm.ShortcutInfo;
-import android.util.Base64;
-
-import com.android.launcher3.Utilities;
-import com.android.systemui.shared.system.ActivityCompat;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
-import java.util.zip.Deflater;
-
-public class ApiWrapper {
-
- public static boolean dumpActivity(Activity activity, PrintWriter writer) {
- if (!Utilities.IS_DEBUG_DEVICE) {
- return false;
- }
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
- return false;
- }
-
- Deflater deflater = new Deflater();
- deflater.setInput(out.toByteArray());
- deflater.finish();
-
- out.reset();
- byte[] buffer = new byte[1024];
- while (!deflater.finished()) {
- int count = deflater.deflate(buffer); // returns the generated code... index
- out.write(buffer, 0, count);
- }
-
- writer.println("--encoded-view-dump-v0--");
- writer.println(Base64.encodeToString(
- out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
- return true;
- }
-
- public static Person[] getPersons(ShortcutInfo si) {
- Person[] persons = si.getPersons();
- return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
index 43dc882..aa0dfc3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
@@ -16,9 +16,11 @@
package com.android.launcher3.uioverrides;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.anim.AnimatorSetBuilder;
@@ -28,14 +30,18 @@
public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
- private final BaseQuickstepLauncher mLauncher;
+ private static final String TAG = "BackButtonAlphaHandler";
- public BackButtonAlphaHandler(BaseQuickstepLauncher launcher) {
+ private final Launcher mLauncher;
+
+ public BackButtonAlphaHandler(Launcher launcher) {
mLauncher = launcher;
}
@Override
- public void setState(LauncherState state) { }
+ public void setState(LauncherState state) {
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+ }
@Override
public void setStateWithAnimation(LauncherState toState,
@@ -46,8 +52,8 @@
if (!SysUINavigationMode.getMode(mLauncher).hasGestures) {
// If the nav mode is not gestural, then force back button alpha to be 1
- UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
- BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, 1f, true /* animate */);
+ UiThreadHelper.setBackButtonAlphaAsync(mLauncher, UiFactory.SET_BACK_BUTTON_ALPHA, 1f,
+ true /* animate */);
return;
}
@@ -58,8 +64,15 @@
anim.setDuration(config.duration);
anim.addUpdateListener(valueAnimator -> {
final float alpha = (float) valueAnimator.getAnimatedValue();
- UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
- BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, alpha, false /* animate */);
+ UiThreadHelper.setBackButtonAlphaAsync(mLauncher, UiFactory.SET_BACK_BUTTON_ALPHA,
+ alpha, false /* animate */);
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Reapply the final alpha in case some state (e.g. window focus) changed.
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+ }
});
builder.play(anim);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
new file mode 100644
index 0000000..d8aa235
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
@@ -0,0 +1,159 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static android.os.IBinder.FLAG_ONEWAY;
+
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.annotation.MainThread;
+
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+
+/**
+ * A binder proxy transaction listener for tracking non-whitelisted binder calls.
+ */
+public class DejankBinderTracker implements Binder.ProxyTransactListener {
+ private static final String TAG = "DejankBinderTracker";
+
+ private static final Object sLock = new Object();
+ private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>();
+ static {
+ // Common IPCs that are ok to block the main thread.
+ sWhitelistedFrameworkClasses.add("android.view.IWindowSession");
+ sWhitelistedFrameworkClasses.add("android.os.IPowerManager");
+ }
+ private static boolean sTemporarilyIgnoreTracking = false;
+
+ // Used by the client to limit binder tracking to specific regions
+ private static boolean sTrackingAllowed = false;
+
+ private BiConsumer<String, Integer> mUnexpectedTransactionCallback;
+ private boolean mIsTracking = false;
+
+ /**
+ * Temporarily ignore blocking binder calls for the duration of this {@link Runnable}.
+ */
+ @MainThread
+ public static void whitelistIpcs(Runnable runnable) {
+ sTemporarilyIgnoreTracking = true;
+ runnable.run();
+ sTemporarilyIgnoreTracking = false;
+ }
+
+ /**
+ * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
+ */
+ @MainThread
+ public static <T> T whitelistIpcs(Supplier<T> supplier) {
+ sTemporarilyIgnoreTracking = true;
+ T value = supplier.get();
+ sTemporarilyIgnoreTracking = false;
+ return value;
+ }
+
+ /**
+ * Enables binder tracking during a test.
+ */
+ @MainThread
+ public static void allowBinderTrackingInTests() {
+ sTrackingAllowed = true;
+ }
+
+ /**
+ * Disables binder tracking during a test.
+ */
+ @MainThread
+ public static void disallowBinderTrackingInTests() {
+ sTrackingAllowed = false;
+ }
+
+ public DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback) {
+ mUnexpectedTransactionCallback = unexpectedTransactionCallback;
+ }
+
+ @MainThread
+ public void startTracking() {
+ if (!Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
+ && !Build.TYPE.toLowerCase(Locale.ROOT).equals("eng")) {
+ Log.wtf(TAG, "Unexpected use of binder tracker in non-debug build", new Exception());
+ return;
+ }
+ if (mIsTracking) {
+ return;
+ }
+ mIsTracking = true;
+ Binder.setProxyTransactListener(this);
+ }
+
+ @MainThread
+ public void stopTracking() {
+ if (!mIsTracking) {
+ return;
+ }
+ mIsTracking = false;
+ Binder.setProxyTransactListener(null);
+ }
+
+ // Override the hidden Binder#onTransactStarted method
+ public synchronized Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
+ if (!mIsTracking
+ || !sTrackingAllowed
+ || sTemporarilyIgnoreTracking
+ || (flags & FLAG_ONEWAY) == FLAG_ONEWAY
+ || !isMainThread()) {
+ return null;
+ }
+
+ String descriptor;
+ try {
+ descriptor = binder.getInterfaceDescriptor();
+ if (sWhitelistedFrameworkClasses.contains(descriptor)) {
+ return null;
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ descriptor = binder.getClass().getSimpleName();
+ }
+
+ mUnexpectedTransactionCallback.accept(descriptor, transactionCode);
+ return null;
+ }
+
+ @Override
+ public Object onTransactStarted(IBinder binder, int transactionCode) {
+ // Do nothing
+ return null;
+ }
+
+ @Override
+ public void onTransactEnded(Object session) {
+ // Do nothing
+ }
+
+ public static boolean isMainThread() {
+ return Thread.currentThread() == Looper.getMainLooper().getThread();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
new file mode 100644
index 0000000..17c681b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -0,0 +1,267 @@
+/*
+ * 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.uioverrides;
+
+import static android.app.Activity.RESULT_CANCELED;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
+
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.app.Person;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.util.Base64;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.QuickstepAppTransitionManagerImpl;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.proxy.ProxyActivityStarter;
+import com.android.launcher3.proxy.StartActivityParams;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.RemoteFadeOutAnimationListener;
+import com.android.systemui.shared.system.ActivityCompat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.zip.Deflater;
+
+public class UiFactory extends RecentsUiFactory {
+
+ /**
+ * Reusable command for applying the back button alpha on the background thread.
+ */
+ public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
+ (context, arg1, arg2) -> {
+ SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(Float.intBitsToFloat(arg1),
+ arg2 != 0);
+ };
+
+ public static Runnable enableLiveUIChanges(Launcher launcher) {
+ NavigationModeChangeListener listener = m -> {
+ launcher.getDragLayer().recreateControllers();
+ launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
+ };
+ SysUINavigationMode mode = SysUINavigationMode.INSTANCE.get(launcher);
+ SysUINavigationMode.Mode m = mode.addModeChangeListener(listener);
+ launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
+ return () -> mode.removeModeChangeListener(listener);
+ }
+
+ public static StateHandler[] getStateHandler(Launcher launcher) {
+ return new StateHandler[] {
+ launcher.getAllAppsController(),
+ launcher.getWorkspace(),
+ createRecentsViewStateController(launcher),
+ new BackButtonAlphaHandler(launcher)};
+ }
+
+ /**
+ * Sets the back button visibility based on the current state/window focus.
+ */
+ public static void onLauncherStateOrFocusChanged(Launcher launcher) {
+ Mode mode = SysUINavigationMode.getMode(launcher);
+ boolean shouldBackButtonBeHidden = mode.hasGestures
+ && launcher != null
+ && launcher.getStateManager().getState().hideBackButton
+ && launcher.hasWindowFocus();
+ if (shouldBackButtonBeHidden) {
+ // Show the back button if there is a floating view visible.
+ shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher,
+ TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
+ }
+ UiThreadHelper.setBackButtonAlphaAsync(launcher, UiFactory.SET_BACK_BUTTON_ALPHA,
+ shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
+ if (launcher != null && launcher.getDragLayer() != null) {
+ launcher.getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
+ }
+ }
+
+ public static void onCreate(Launcher launcher) {
+ if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
+ launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ boolean swipeUpEnabled = SysUINavigationMode.INSTANCE.get(launcher).getMode()
+ .hasGestures;
+ LauncherState prevState = launcher.getStateManager().getLastState();
+
+ if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
+ && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT <=
+ launcher.getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
+ launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
+ launcher.getStateManager().removeStateListener(this);
+ }
+ }
+ });
+ }
+
+ if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
+ launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ LauncherState prevState = launcher.getStateManager().getLastState();
+
+ if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT <=
+ launcher.getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
+ launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
+ launcher.getStateManager().removeStateListener(this);
+ }
+ }
+ });
+ }
+ }
+
+ public static void onEnterAnimationComplete(Context context) {
+ // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+ // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+ // overview
+ RecentsModel.INSTANCE.get(context).getThumbnailCache()
+ .getHighResLoadingState().setVisible(true);
+ }
+
+ public static void onTrimMemory(Context context, int level) {
+ RecentsModel model = RecentsModel.INSTANCE.get(context);
+ if (model != null) {
+ model.onTrimMemory(level);
+ }
+ }
+
+ public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
+ CancellationSignal cancellationSignal) {
+ QuickstepAppTransitionManagerImpl appTransitionManager =
+ (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
+ appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
+
+ // On the first call clear the reference.
+ cancellationSignal.cancel();
+
+ ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
+ fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
+ wallpaperTargets));
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(fadeAnimation);
+ return anim;
+ }, cancellationSignal);
+ }
+
+ public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+ if (!Utilities.IS_DEBUG_DEVICE) {
+ return false;
+ }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
+ return false;
+ }
+
+ Deflater deflater = new Deflater();
+ deflater.setInput(out.toByteArray());
+ deflater.finish();
+
+ out.reset();
+ byte[] buffer = new byte[1024];
+ while (!deflater.finished()) {
+ int count = deflater.deflate(buffer); // returns the generated code... index
+ out.write(buffer, 0, count);
+ }
+
+ writer.println("--encoded-view-dump-v0--");
+ writer.println(Base64.encodeToString(
+ out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
+ return true;
+ }
+
+ public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
+ int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
+ Bundle options) {
+ StartActivityParams params = new StartActivityParams(activity, requestCode);
+ params.intentSender = intent;
+ params.fillInIntent = fillInIntent;
+ params.flagsMask = flagsMask;
+ params.flagsValues = flagsValues;
+ params.extraFlags = extraFlags;
+ params.options = options;
+ ((Context) activity).startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
+ return true;
+ }
+
+ public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
+ Bundle options) {
+ StartActivityParams params = new StartActivityParams(activity, requestCode);
+ params.intent = intent;
+ params.options = options;
+ activity.startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
+ return true;
+ }
+
+ /**
+ * Removes any active ProxyActivityStarter task and sends RESULT_CANCELED to Launcher.
+ *
+ * ProxyActivityStarter is started with clear task to reset the task after which it removes the
+ * task itself.
+ */
+ public static void resetPendingActivityResults(Launcher launcher, int requestCode) {
+ launcher.onActivityResult(requestCode, RESULT_CANCELED, null);
+ launcher.startActivity(ProxyActivityStarter.getLaunchIntent(launcher, null));
+ }
+
+ public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
+ if (SysUINavigationMode.getMode(l) == Mode.NO_BUTTON) {
+ float offscreenTranslationX = l.getDeviceProfile().widthPx
+ - l.getOverviewPanel().getPaddingStart();
+ return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
+ }
+ return new ScaleAndTranslation(1.1f, 0f, 0f);
+ }
+
+ public static Person[] getPersons(ShortcutInfo si) {
+ Person[] persons = si.getPersons();
+ return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
index 3cb0088..bb72315 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
@@ -11,10 +11,10 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationComponents;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.RecentsModel;
/**
* Touch controller for handling edge swipes in landscape/seascape UI
@@ -24,7 +24,7 @@
private static final String TAG = "LandscapeEdgeSwipeCtrl";
public LandscapeEdgeSwipeController(Launcher l) {
- super(l, SingleAxisSwipeDetector.HORIZONTAL);
+ super(l, SwipeDetector.HORIZONTAL);
}
@Override
@@ -73,7 +73,7 @@
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
super.onSwipeInteractionCompleted(targetState, logAction);
if (mStartState == NORMAL && targetState == OVERVIEW) {
- SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+ RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 99b2a81..ef6a5e2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -43,10 +43,11 @@
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.LayoutUtils;
@@ -78,7 +79,7 @@
private boolean mFinishFastOnSecondTouch;
public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {
- super(l, SingleAxisSwipeDetector.VERTICAL);
+ super(l, SwipeDetector.VERTICAL);
mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
mAllowDragToOverview = allowDragToOverview;
}
@@ -299,7 +300,7 @@
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
super.onSwipeInteractionCompleted(targetState, logAction);
if (mStartState == NORMAL && targetState == OVERVIEW) {
- SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+ RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
}
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index fd55e07..409bec6 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -32,12 +32,11 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.ShelfPeekAnim;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
-import java.util.function.Predicate;
/**
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
@@ -45,26 +44,21 @@
@TargetApi(Build.VERSION_CODES.P)
public interface BaseActivityInterface<T extends BaseDraggingActivity> {
- void onTransitionCancelled(boolean activityVisible);
+ void onTransitionCancelled(T activity, boolean activityVisible);
int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
- void onSwipeUpToRecentsComplete();
+ void onSwipeUpToRecentsComplete(T activity);
- default void onSwipeUpToHomeComplete() { }
+ default void onSwipeUpToHomeComplete(T activity) { }
void onAssistantVisibilityChanged(float visibility);
- @NonNull HomeAnimationFactory prepareHomeUI();
+ @NonNull HomeAnimationFactory prepareHomeUI(T activity);
- AnimationFactory prepareRecentsUI(boolean activityVisible, boolean animateActivity,
- Consumer<AnimatorPlaybackController> callback);
+ AnimationFactory prepareRecentsUI(T activity, boolean activityVisible,
+ boolean animateActivity, Consumer<AnimatorPlaybackController> callback);
- ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener);
-
- /**
- * Sets a callback to be run when an activity launch happens while launcher is not yet resumed.
- */
- default void setOnDeferredActivityLaunchCallback(Runnable r) {}
+ ActivityInitListener createActivityInitListener(BiPredicate<T, Boolean> onInitListener);
@Nullable
T getCreatedActivity();
@@ -90,30 +84,32 @@
}
/**
- * Updates the prediction state to the overview state.
- */
- default void updateOverviewPredictionState() {
- // By default overview predictions are not supported
- }
-
- /**
* Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
*/
int getContainerType();
boolean isInLiveTileMode();
- void onLaunchTaskFailed();
+ void onLaunchTaskFailed(T activity);
- void onLaunchTaskSuccess();
+ void onLaunchTaskSuccess(T activity);
default void closeOverlay() { }
- default void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
- Runnable runnable) {}
+ default void switchToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {}
interface AnimationFactory {
+ enum ShelfAnimState {
+ HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
+
+ ShelfAnimState(boolean shouldPreformHaptic) {
+ this.shouldPreformHaptic = shouldPreformHaptic;
+ }
+
+ public final boolean shouldPreformHaptic;
+ }
+
default void onRemoteAnimationReceived(RemoteAnimationTargets targets) { }
void createActivityInterface(long transitionLength);
@@ -122,8 +118,8 @@
default void onTransitionCancelled() { }
- default void setShelfState(ShelfPeekAnim.ShelfAnimState animState,
- Interpolator interpolator, long duration) { }
+ default void setShelfState(ShelfAnimState animState, Interpolator interpolator,
+ long duration) { }
/**
* @param attached Whether to show RecentsView alongside the app window. If false, recents
diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
index 5fcdc19..71833ad 100644
--- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
@@ -27,6 +27,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
@@ -121,17 +122,13 @@
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
- // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
- // as a part of quickstep, so that high-res thumbnails can load the next time we enter
- // overview
- RecentsModel.INSTANCE.get(this).getThumbnailCache()
- .getHighResLoadingState().setVisible(true);
+ UiFactory.onEnterAnimationComplete(this);
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
- RecentsModel.INSTANCE.get(this).onTrimMemory(level);
+ UiFactory.onTrimMemory(this, level);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index ae0886b..de64227 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -15,270 +15,22 @@
*/
package com.android.quickstep;
-import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-
-import android.app.ActivityManager;
-import android.content.Intent;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import java.util.ArrayList;
/**
* Manages the state for an active system gesture, listens for events from the system and Launcher,
* and fires events when the states change.
*/
-public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
-
- /**
- * Defines the end targets of a gesture and the associated state.
- */
- public enum GestureEndTarget {
- HOME(true, ContainerType.WORKSPACE, false),
-
- RECENTS(true, ContainerType.TASKSWITCHER, true),
-
- NEW_TASK(false, ContainerType.APP, true),
-
- LAST_TASK(false, ContainerType.APP, false);
-
- GestureEndTarget(boolean isLauncher, int containerType,
- boolean recentsAttachedToAppWindow) {
- this.isLauncher = isLauncher;
- this.containerType = containerType;
- this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
- }
-
- /** Whether the target is in the launcher activity. Implicitly, if the end target is going
- to Launcher, then we can not interrupt the animation to start another gesture. */
- public final boolean isLauncher;
- /** Used to log where the user ended up after the gesture ends */
- public final int containerType;
- /** Whether RecentsView should be attached to the window as we animate to this target */
- public final boolean recentsAttachedToAppWindow;
- }
-
- private static final String TAG = "GestureState";
-
- private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
- private static int FLAG_COUNT = 0;
- private static int getFlagForIndex(String name) {
- if (DEBUG_STATES) {
- STATE_NAMES.add(name);
- }
- int index = 1 << FLAG_COUNT;
- FLAG_COUNT++;
- return index;
- }
-
- // Called when the end target as been set
- public static final int STATE_END_TARGET_SET =
- getFlagForIndex("STATE_END_TARGET_SET");
-
- // Called when the end target animation has finished
- public static final int STATE_END_TARGET_ANIMATION_FINISHED =
- getFlagForIndex("STATE_END_TARGET_ANIMATION_FINISHED");
-
- // Called when the recents animation has been requested to start
- public static final int STATE_RECENTS_ANIMATION_INITIALIZED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_INITIALIZED");
-
- // Called when the recents animation is started and the TaskAnimationManager has been updated
- // with the controller and targets
- public static final int STATE_RECENTS_ANIMATION_STARTED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_STARTED");
-
- // Called when the recents animation is canceled
- public static final int STATE_RECENTS_ANIMATION_CANCELED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_CANCELED");
-
- // Called when the recents animation finishes
- public static final int STATE_RECENTS_ANIMATION_FINISHED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_FINISHED");
-
- // Always called when the recents animation ends (regardless of cancel or finish)
- public static final int STATE_RECENTS_ANIMATION_ENDED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED");
-
+public class GestureState {
// Needed to interact with the current activity
- private final Intent mHomeIntent;
- private final Intent mOverviewIntent;
- private final BaseActivityInterface mActivityInterface;
- private final MultiStateCallback mStateCallback;
- private final int mGestureId;
+ private BaseActivityInterface mActivityInterface;
- private ActivityManager.RunningTaskInfo mRunningTask;
- private GestureEndTarget mEndTarget;
- // TODO: This can be removed once we stop finishing the animation when starting a new task
- private int mFinishingRecentsAnimationTaskId = -1;
-
- public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
- mHomeIntent = componentObserver.getHomeIntent();
- mOverviewIntent = componentObserver.getOverviewIntent();
- mActivityInterface = componentObserver.getActivityInterface();
- mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
- mGestureId = gestureId;
+ public GestureState(BaseActivityInterface activityInterface) {
+ mActivityInterface = activityInterface;
}
- public GestureState() {
- // Do nothing, only used for initializing the gesture state prior to user unlock
- mHomeIntent = new Intent();
- mOverviewIntent = new Intent();
- mActivityInterface = null;
- mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
- mGestureId = -1;
- }
-
- /**
- * @return whether the gesture state has the provided {@param stateMask} flags set.
- */
- public boolean hasState(int stateMask) {
- return mStateCallback.hasStates(stateMask);
- }
-
- /**
- * Sets the given {@param stateFlag}s.
- */
- public void setState(int stateFlag) {
- mStateCallback.setState(stateFlag);
- }
-
- /**
- * Adds a callback for when the states matching the given {@param stateMask} is set.
- */
- public void runOnceAtState(int stateMask, Runnable callback) {
- mStateCallback.runOnceAtState(stateMask, callback);
- }
-
- /**
- * @return the intent for the Home component.
- */
- public Intent getHomeIntent() {
- return mHomeIntent;
- }
-
- /**
- * @return the intent for the Overview component.
- */
- public Intent getOverviewIntent() {
- return mOverviewIntent;
- }
-
- /**
- * @return the interface to the activity handing the UI updates for this gesture.
- */
public <T extends BaseDraggingActivity> BaseActivityInterface<T> getActivityInterface() {
return mActivityInterface;
}
-
- /**
- * @return the id for this particular gesture.
- */
- public int getGestureId() {
- return mGestureId;
- }
-
- /**
- * @return the running task for this gesture.
- */
- public ActivityManager.RunningTaskInfo getRunningTask() {
- return mRunningTask;
- }
-
- /**
- * @return the running task id for this gesture.
- */
- public int getRunningTaskId() {
- return mRunningTask != null ? mRunningTask.taskId : -1;
- }
-
- /**
- * Updates the running task for the gesture to be the given {@param runningTask}.
- */
- public void updateRunningTask(ActivityManager.RunningTaskInfo runningTask) {
- mRunningTask = runningTask;
- }
-
- /**
- * @return the end target for this gesture (if known).
- */
- public GestureEndTarget getEndTarget() {
- return mEndTarget;
- }
-
- /**
- * Sets the end target of this gesture and immediately notifies the state changes.
- */
- public void setEndTarget(GestureEndTarget target) {
- setEndTarget(target, true /* isAtomic */);
- }
-
- /**
- * Sets the end target of this gesture, but if {@param isAtomic} is {@code false}, then the
- * caller must explicitly set {@link #STATE_END_TARGET_ANIMATION_FINISHED} themselves.
- */
- public void setEndTarget(GestureEndTarget target, boolean isAtomic) {
- mEndTarget = target;
- mStateCallback.setState(STATE_END_TARGET_SET);
- if (isAtomic) {
- mStateCallback.setState(STATE_END_TARGET_ANIMATION_FINISHED);
- }
- }
-
- /**
- * @return the id for the task that was about to be launched following the finish of the recents
- * animation. Only defined between when the finish-recents call was made and the launch
- * activity call is made.
- */
- public int getFinishingRecentsAnimationTaskId() {
- return mFinishingRecentsAnimationTaskId;
- }
-
- /**
- * Sets the id for the task will be launched after the recents animation is finished. Once the
- * animation has finished then the id will be reset to -1.
- */
- public void setFinishingRecentsAnimationTaskId(int taskId) {
- mFinishingRecentsAnimationTaskId = taskId;
- mStateCallback.runOnceAtState(STATE_RECENTS_ANIMATION_FINISHED, () -> {
- mFinishingRecentsAnimationTaskId = -1;
- });
- }
-
- /**
- * @return whether the current gesture is still running a recents animation to a state in the
- * Launcher or Recents activity.
- * Updates the running task for the gesture to be the given {@param runningTask}.
- */
- public boolean isRunningAnimationToLauncher() {
- return isRecentsAnimationRunning() && mEndTarget != null && mEndTarget.isLauncher;
- }
-
- /**
- * @return whether the recents animation is started but not yet ended
- */
- public boolean isRecentsAnimationRunning() {
- return mStateCallback.hasStates(STATE_RECENTS_ANIMATION_INITIALIZED) &&
- !mStateCallback.hasStates(STATE_RECENTS_ANIMATION_ENDED);
- }
-
- @Override
- public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
- mStateCallback.setState(STATE_RECENTS_ANIMATION_STARTED);
- }
-
- @Override
- public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
- mStateCallback.setState(STATE_RECENTS_ANIMATION_CANCELED);
- mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
- }
-
- @Override
- public void onRecentsAnimationFinished(RecentsAnimationController controller) {
- mStateCallback.setState(STATE_RECENTS_ANIMATION_FINISHED);
- mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
- }
}
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 3e84e7d..62c0ded 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -33,7 +33,7 @@
int TYPE_SCREEN_PINNED = 1 << 6;
int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
int TYPE_RESET_GESTURE = 1 << 8;
- int TYPE_OVERSCROLL = 1 << 9;
+ int TYPE_QUICK_CAPTURE = 1 << 9;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -45,13 +45,17 @@
"TYPE_SCREEN_PINNED", // 6
"TYPE_OVERVIEW_WITHOUT_FOCUS", // 7
"TYPE_RESET_GESTURE", // 8
- "TYPE_OVERSCROLL", // 9
+ "TYPE_QUICK_CAPTURE", // 9
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
int getType();
+ default boolean useSharedSwipeState() {
+ return false;
+ }
+
/**
* Returns true if the user has crossed the threshold for it to be an explicit action.
*/
@@ -61,8 +65,6 @@
/**
* Called by the event queue when the consumer is about to be switched to a new consumer.
- * Consumers should update the state accordingly here before the state is passed to the new
- * consumer.
*/
default void onConsumerAboutToBeSwitched() { }
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
deleted file mode 100644
index 6c65e01..0000000
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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.quickstep;
-
-import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.os.Looper;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.StringJoiner;
-import java.util.function.Consumer;
-
-/**
- * Utility class to help manage multiple callbacks based on different states.
- */
-public class MultiStateCallback {
-
- private static final String TAG = "MultiStateCallback";
- public static final boolean DEBUG_STATES = false;
-
- private final SparseArray<LinkedList<Runnable>> mCallbacks = new SparseArray<>();
- private final SparseArray<ArrayList<Consumer<Boolean>>> mStateChangeListeners =
- new SparseArray<>();
-
- private final String[] mStateNames;
-
- private int mState = 0;
-
- public MultiStateCallback(String[] stateNames) {
- mStateNames = DEBUG_STATES ? stateNames : null;
- }
-
- /**
- * Adds the provided state flags to the global state on the UI thread and executes any callbacks
- * as a result.
- */
- public void setStateOnUiThread(int stateFlag) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- setState(stateFlag);
- } else {
- postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> setState(stateFlag));
- }
- }
-
- /**
- * Adds the provided state flags to the global state and executes any callbacks as a result.
- */
- public void setState(int stateFlag) {
- if (DEBUG_STATES) {
- Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
- + convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
- }
-
- final int oldState = mState;
- mState = mState | stateFlag;
-
- int count = mCallbacks.size();
- for (int i = 0; i < count; i++) {
- int state = mCallbacks.keyAt(i);
-
- if ((mState & state) == state) {
- LinkedList<Runnable> callbacks = mCallbacks.valueAt(i);
- while (!callbacks.isEmpty()) {
- callbacks.pollFirst().run();
- }
- }
- }
- notifyStateChangeListeners(oldState);
- }
-
- /**
- * Adds the provided state flags to the global state and executes any change handlers
- * as a result.
- */
- public void clearState(int stateFlag) {
- if (DEBUG_STATES) {
- Log.d(TAG, "[" + System.identityHashCode(this) + "] Removing "
- + convertToFlagNames(stateFlag) + " from " + convertToFlagNames(mState));
- }
-
- int oldState = mState;
- mState = mState & ~stateFlag;
- notifyStateChangeListeners(oldState);
- }
-
- private void notifyStateChangeListeners(int oldState) {
- int count = mStateChangeListeners.size();
- for (int i = 0; i < count; i++) {
- int state = mStateChangeListeners.keyAt(i);
- boolean wasOn = (state & oldState) == state;
- boolean isOn = (state & mState) == state;
-
- if (wasOn != isOn) {
- ArrayList<Consumer<Boolean>> listeners = mStateChangeListeners.valueAt(i);
- for (Consumer<Boolean> listener : listeners) {
- listener.accept(isOn);
- }
- }
- }
- }
-
- /**
- * Sets a callback to be run when the provided states in the given {@param stateMask} is
- * enabled. The callback is only run *once*, and if the states are already set at the time of
- * this call then the callback will be made immediately.
- */
- public void runOnceAtState(int stateMask, Runnable callback) {
- if ((mState & stateMask) == stateMask) {
- callback.run();
- } else {
- final LinkedList<Runnable> callbacks;
- if (mCallbacks.indexOfKey(stateMask) >= 0) {
- callbacks = mCallbacks.get(stateMask);
- if (FeatureFlags.IS_DOGFOOD_BUILD && callbacks.contains(callback)) {
- throw new IllegalStateException("Existing callback for state found");
- }
- } else {
- callbacks = new LinkedList<>();
- mCallbacks.put(stateMask, callbacks);
- }
- callbacks.add(callback);
- }
- }
-
- /**
- * Adds a persistent listener to be called states in the given {@param stateMask} are enabled
- * or disabled.
- */
- public void addChangeListener(int stateMask, Consumer<Boolean> listener) {
- final ArrayList<Consumer<Boolean>> listeners;
- if (mStateChangeListeners.indexOfKey(stateMask) >= 0) {
- listeners = mStateChangeListeners.get(stateMask);
- } else {
- listeners = new ArrayList<>();
- mStateChangeListeners.put(stateMask, listeners);
- }
- listeners.add(listener);
- }
-
- public int getState() {
- return mState;
- }
-
- public boolean hasStates(int stateMask) {
- return (mState & stateMask) == stateMask;
- }
-
- private String convertToFlagNames(int flags) {
- StringJoiner joiner = new StringJoiner(", ", "[", " (" + flags + ")]");
- for (int i = 0; i < mStateNames.length; i++) {
- if ((flags & (1 << i)) != 0) {
- joiner.add(mStateNames[i]);
- }
- }
- return joiner.toString();
- }
-
-}
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
new file mode 100644
index 0000000..bd6204a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager.TaskDescription;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.LruCache;
+import android.util.SparseArray;
+
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.systemui.shared.recents.model.IconLoader;
+import com.android.systemui.shared.recents.model.TaskKeyLruCache;
+
+/**
+ * Extension of {@link IconLoader} with icon normalization support
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class NormalizedIconLoader extends IconLoader {
+
+ private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
+ private final DrawableFactory mDrawableFactory;
+ private final boolean mDisableColorExtraction;
+
+ public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
+ LruCache<ComponentName, ActivityInfo> activityInfoCache,
+ boolean disableColorExtraction) {
+ super(context, iconCache, activityInfoCache);
+ mDrawableFactory = DrawableFactory.INSTANCE.get(context);
+ mDisableColorExtraction = disableColorExtraction;
+ }
+
+ @Override
+ public Drawable getDefaultIcon(int userId) {
+ synchronized (mDefaultIcons) {
+ BitmapInfo info = mDefaultIcons.get(userId);
+ if (info == null) {
+ info = getBitmapInfo(Resources.getSystem()
+ .getDrawable(android.R.drawable.sym_def_app_icon), userId, 0, false);
+ mDefaultIcons.put(userId, info);
+ }
+
+ return new FastBitmapDrawable(info);
+ }
+ }
+
+ @Override
+ protected Drawable createBadgedDrawable(Drawable drawable, int userId, TaskDescription desc) {
+ return new FastBitmapDrawable(getBitmapInfo(drawable, userId, desc.getPrimaryColor(),
+ false));
+ }
+
+ private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
+ int primaryColor, boolean isInstantApp) {
+ try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
+ if (mDisableColorExtraction) {
+ la.disableColorExtraction();
+ }
+ la.setWrapperBackgroundColor(primaryColor);
+
+ // User version code O, so that the icon is always wrapped in an adaptive icon container
+ return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
+ Build.VERSION_CODES.O, isInstantApp);
+ }
+ }
+
+ @Override
+ protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId,
+ TaskDescription desc) {
+ BitmapInfo bitmapInfo = getBitmapInfo(
+ activityInfo.loadUnbadgedIcon(mContext.getPackageManager()),
+ userId,
+ desc.getPrimaryColor(),
+ activityInfo.applicationInfo.isInstantApp());
+ return mDrawableFactory.newIcon(mContext, bitmapInfo, activityInfo);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index acf61b4..2918879 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -127,7 +127,7 @@
*/
public interface RecentsAnimationListener {
default void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {}
+ RecentsAnimationTargets targetSet) {}
/**
* Callback from the system when the recents animation is canceled. {@param thumbnailData}
@@ -135,9 +135,6 @@
*/
default void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {}
- /**
- * Callback made whenever the recents animation is finished.
- */
default void onRecentsAnimationFinished(RecentsAnimationController controller) {}
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 46af8bf..d938dc5 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -18,7 +18,6 @@
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -49,10 +48,11 @@
private final Consumer<RecentsAnimationController> mOnFinishedListener;
private final boolean mShouldMinimizeSplitScreen;
+ private boolean mWindowThresholdCrossed = false;
+
private InputConsumerController mInputConsumerController;
private Supplier<InputConsumer> mInputProxySupplier;
private InputConsumer mInputConsumer;
- private boolean mWindowThresholdCrossed = false;
private boolean mTouchInProgress;
private boolean mFinishPending;
@@ -62,6 +62,8 @@
mController = controller;
mOnFinishedListener = onFinishedListener;
mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+
+ setWindowThresholdCrossed(mWindowThresholdCrossed);
}
/**
@@ -69,7 +71,7 @@
* currently being animated.
*/
public ThumbnailData screenshotTask(int taskId) {
- return mController.screenshotTask(taskId);
+ return mController != null ? mController.screenshotTask(taskId) : null;
}
/**
@@ -186,11 +188,6 @@
mInputConsumerController.setInputListener(this::onInputConsumerEvent);
}
- /** @return wrapper controller. */
- public RecentsAnimationControllerCompat getController() {
- return mController;
- }
-
private void disableInputProxy() {
if (mInputConsumer != null && mTouchInProgress) {
long now = SystemClock.uptimeMillis();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 81f411e..9b094f6 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -16,14 +16,10 @@
package com.android.quickstep;
import static android.content.Intent.ACTION_USER_UNLOCKED;
-
-import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
import static com.android.launcher3.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE;
import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
@@ -37,37 +33,27 @@
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Process;
-import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
-
import androidx.annotation.BinderThread;
-
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
-import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
-
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -75,20 +61,17 @@
* Manages the state of the system during a swipe up gesture.
*/
public class RecentsAnimationDeviceState implements
- NavigationModeChangeListener,
+ SysUINavigationMode.NavigationModeChangeListener,
DefaultDisplay.DisplayInfoChangeListener {
- private final Context mContext;
- private final UserManagerCompat mUserManager;
- private final SysUINavigationMode mSysUiNavMode;
- private final DefaultDisplay mDefaultDisplay;
- private final int mDisplayId;
-
- private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
+ private Context mContext;
+ private UserManagerCompat mUserManager;
+ private SysUINavigationMode mSysUiNavMode;
+ private DefaultDisplay mDefaultDisplay;
+ private int mDisplayId;
private @SystemUiStateFlags int mSystemUiStateFlags;
private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
- private NavBarPosition mNavBarPosition;
private final RectF mSwipeUpTouchRegion = new RectF();
private final Region mDeferredGestureRegion = new Region();
@@ -115,13 +98,11 @@
private ComponentName mGestureBlockedActivity;
public RecentsAnimationDeviceState(Context context) {
- final ContentResolver resolver = context.getContentResolver();
mContext = context;
mUserManager = UserManagerCompat.getInstance(context);
mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
mDisplayId = mDefaultDisplay.getInfo().id;
- runOnDestroy(() -> mDefaultDisplay.removeChangeListener(this));
// Register for user unlocked if necessary
mIsUserUnlocked = mUserManager.isUserUnlocked(Process.myUserHandle());
@@ -129,7 +110,6 @@
mContext.registerReceiver(mUserUnlockedReceiver,
new IntentFilter(ACTION_USER_UNLOCKED));
}
- runOnDestroy(() -> Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver));
// Register for exclusion updates
mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
@@ -140,11 +120,7 @@
mExclusionRegion = region;
}
};
- runOnDestroy(mExclusionListener::unregister);
-
- // Register for navigation mode changes
onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
- runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
// Add any blocked activities
String blockingActivity = context.getString(R.string.gesture_blocking_activity);
@@ -153,33 +129,18 @@
}
}
- private void runOnDestroy(Runnable action) {
- mOnDestroyActions.add(action);
- }
-
/**
* Cleans up all the registered listeners and receivers.
*/
public void destroy() {
- for (Runnable r : mOnDestroyActions) {
- r.run();
- }
- }
-
- /**
- * Adds a listener for the nav mode change, guaranteed to be called after the device state's
- * mode has changed.
- */
- public void addNavigationModeChangedCallback(NavigationModeChangeListener listener) {
- listener.onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(listener));
- runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(listener));
+ Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver);
+ mSysUiNavMode.removeModeChangeListener(this);
+ mDefaultDisplay.removeChangeListener(this);
+ mExclusionListener.unregister();
}
@Override
public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onNavigationModeChanged " + newMode);
- }
mDefaultDisplay.removeChangeListener(this);
if (newMode.hasGestures) {
mDefaultDisplay.addChangeListener(this);
@@ -191,7 +152,6 @@
mExclusionListener.unregister();
}
mMode = newMode;
- mNavBarPosition = new NavBarPosition(mMode, mDefaultDisplay.getInfo());
}
@Override
@@ -200,46 +160,10 @@
return;
}
- mNavBarPosition = new NavBarPosition(mMode, info);
updateGestureTouchRegions();
}
/**
- * @return the current navigation mode for the device.
- */
- public SysUINavigationMode.Mode getNavMode() {
- return mMode;
- }
-
- /**
- * @return the nav bar position for the current nav bar mode and display rotation.
- */
- public NavBarPosition getNavBarPosition() {
- return mNavBarPosition;
- }
-
- /**
- * @return whether the current nav mode is fully gestural.
- */
- public boolean isFullyGesturalNavMode() {
- return mMode == NO_BUTTON;
- }
-
- /**
- * @return whether the current nav mode has some gestures (either 2 or 0 button mode).
- */
- public boolean isGesturalNavMode() {
- return mMode == TWO_BUTTONS || mMode == NO_BUTTON;
- }
-
- /**
- * @return whether the current nav mode is button-based.
- */
- public boolean isButtonNavMode() {
- return mMode == THREE_BUTTONS;
- }
-
- /**
* @return the display id for the display that Launcher is running on.
*/
public int getDisplayId() {
@@ -277,7 +201,7 @@
* @return whether the given running task info matches the gesture-blocked activity.
*/
public boolean isGestureBlockedActivity(ActivityManager.RunningTaskInfo runningTaskInfo) {
- return runningTaskInfo != null && mGestureBlockedActivity != null
+ return runningTaskInfo != null
&& mGestureBlockedActivity.equals(runningTaskInfo.topActivity);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 718c5ba..9353759 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -41,4 +41,13 @@
public boolean hasTargets() {
return unfilteredApps.length != 0;
}
+
+ /**
+ * Clones the target set without any actual targets. Used only when continuing a gesture after
+ * the actual recents animation has finished.
+ */
+ public RecentsAnimationTargets cloneWithoutTargets() {
+ return new RecentsAnimationTargets(new RemoteAnimationTargetCompat[0],
+ new RemoteAnimationTargetCompat[0], homeContentInsets, minimizedHomeBounds);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 517501a..465d464 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -25,12 +25,12 @@
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
+import android.content.pm.LauncherApps;
import android.os.Build;
import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
-import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -48,11 +48,13 @@
@TargetApi(Build.VERSION_CODES.O)
public class RecentsModel extends TaskStackChangeListener {
+ private static final String TAG = "RecentsModel";
+
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
new MainThreadInitializedObject<>(RecentsModel::new);
- private final List<TaskVisualsChangeListener> mThumbnailChangeListeners = new ArrayList<>();
+ private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
private final Context mContext;
private final RecentTasksList mTaskList;
@@ -67,10 +69,8 @@
new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
mIconCache = new TaskIconCache(context, looper);
mThumbnailCache = new TaskThumbnailCache(context, looper);
-
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
- IconProvider.registerIconChangeListener(context,
- this::onPackageIconChanged, MAIN_EXECUTOR.getHandler());
+ setupPackageListener();
}
public TaskIconCache getIconCache() {
@@ -183,40 +183,45 @@
}
}
- private void onPackageIconChanged(String pkg, UserHandle user) {
- mIconCache.invalidateCacheEntries(pkg, user);
- for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
- mThumbnailChangeListeners.get(i).onTaskIconChanged(pkg, user);
- }
+ public void onOverviewShown(boolean fromHome, String tag) {
+ SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(fromHome, tag);
}
- /**
- * Adds a listener for visuals changes
- */
- public void addThumbnailChangeListener(TaskVisualsChangeListener listener) {
+ private void setupPackageListener() {
+ mContext.getSystemService(LauncherApps.class).registerCallback(new LauncherApps.Callback() {
+ @Override
+ public void onPackageRemoved(String packageName, UserHandle user) {
+ mIconCache.invalidatePackage(packageName);
+ }
+
+ @Override
+ public void onPackageChanged(String packageName, UserHandle user) {
+ mIconCache.invalidatePackage(packageName);
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, UserHandle user) { }
+
+ @Override
+ public void onPackagesAvailable(
+ String[] packageNames, UserHandle user, boolean replacing) { }
+
+ @Override
+ public void onPackagesUnavailable(
+ String[] packageNames, UserHandle user, boolean replacing) { }
+ });
+ }
+
+ public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.add(listener);
}
- /**
- * Removes a previously added listener
- */
- public void removeThumbnailChangeListener(TaskVisualsChangeListener listener) {
+ public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.remove(listener);
}
- /**
- * Listener for receiving various task properties changes
- */
- public interface TaskVisualsChangeListener {
+ public interface TaskThumbnailChangeListener {
- /**
- * Called whn the task thumbnail changes
- */
Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
-
- /**
- * Called when the icon for a task changes
- */
- void onTaskIconChanged(String pkg, UserHandle user);
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
deleted file mode 100644
index e3e8ace..0000000
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
-
-import android.content.Intent;
-import android.util.Log;
-
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
-
- private RecentsAnimationController mController;
- private RecentsAnimationCallbacks mCallbacks;
- private RecentsAnimationTargets mTargets;
- // Temporary until we can hook into gesture state events
- private GestureState mLastGestureState;
-
- /**
- * Preloads the recents animation.
- */
- public void preloadRecentsAnimation(Intent intent) {
- // Pass null animation handler to indicate this start is for preloading
- UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
- .startRecentsActivity(intent, null, null, null, null));
- }
-
- /**
- * Starts a new recents animation for the activity with the given {@param intent}.
- */
- @UiThread
- public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
- Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
- // Notify if recents animation is still running
- if (mController != null) {
- String msg = "New recents animation started before old animation completed";
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
- throw new IllegalArgumentException(msg);
- } else {
- Log.e("TaskAnimationManager", msg, new Exception());
- }
- }
- // But force-finish it anyways
- finishRunningRecentsAnimation(false /* toHome */);
-
- final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
- mLastGestureState = gestureState;
- mCallbacks = new RecentsAnimationCallbacks(activityInterface.shouldMinimizeSplitScreen());
- mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
- @Override
- public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
- mController = controller;
- mTargets = targets;
- }
-
- @Override
- public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
- if (thumbnailData != null) {
- // If a screenshot is provided, switch to the screenshot before cleaning up
- activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
- () -> cleanUpRecentsAnimation(thumbnailData));
- } else {
- cleanUpRecentsAnimation(null /* canceledThumbnail */);
- }
- }
-
- @Override
- public void onRecentsAnimationFinished(RecentsAnimationController controller) {
- cleanUpRecentsAnimation(null /* canceledThumbnail */);
- }
- });
- mCallbacks.addListener(gestureState);
- mCallbacks.addListener(listener);
- UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
- .startRecentsActivity(intent, null, mCallbacks, null, null));
- gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
- return mCallbacks;
- }
-
- /**
- * Continues the existing running recents animation for a new gesture.
- */
- public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
- mCallbacks.removeListener(mLastGestureState);
- mLastGestureState = gestureState;
- mCallbacks.addListener(gestureState);
- return mCallbacks;
- }
-
- /**
- * Finishes the running recents animation.
- */
- public void finishRunningRecentsAnimation(boolean toHome) {
- if (mController != null) {
- mCallbacks.notifyAnimationCanceled();
- Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
- ? mController::finishAnimationToHome
- : mController::finishAnimationToApp);
- cleanUpRecentsAnimation(null /* canceledThumbnail */);
- }
- }
-
- /**
- * Used to notify a listener of the current recents animation state (used if the listener was
- * not yet added to the callbacks at the point that the listener callbacks would have been
- * made).
- */
- public void notifyRecentsAnimationState(
- RecentsAnimationCallbacks.RecentsAnimationListener listener) {
- if (isRecentsAnimationRunning()) {
- listener.onRecentsAnimationStart(mController, mTargets);
- }
- // TODO: Do we actually need to report canceled/finished?
- }
-
- /**
- * @return whether there is a recents animation running.
- */
- public boolean isRecentsAnimationRunning() {
- return mController != null;
- }
-
- /**
- * Cleans up the recents animation entirely.
- */
- private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) {
- // Clean up the screenshot if necessary
- if (mController != null && canceledThumbnail != null) {
- mController.cleanupScreenshot();
- }
-
- // Release all the target leashes
- if (mTargets != null) {
- mTargets.release();
- }
-
- // Remove gesture state from callbacks
- if (mCallbacks != null && mLastGestureState != null) {
- mCallbacks.removeListener(mLastGestureState);
- }
-
- mController = null;
- mCallbacks = null;
- mTargets = null;
- mLastGestureState = null;
- }
-
- public void dump() {
- // TODO
- }
-}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index e590aea..289a129 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -15,64 +15,67 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
-import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
+import static com.android.launcher3.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import android.app.ActivityManager.TaskDescription;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
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.Handler;
import android.os.Looper;
-import android.os.UserHandle;
-import android.util.SparseArray;
+import android.util.LruCache;
import android.view.accessibility.AccessibilityManager;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.IconProvider;
-import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
-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.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
+import java.util.Map;
import java.util.function.Consumer;
/**
* Manages the caching of task icons and related data.
+ * TODO(b/138944598): This class should later be merged into IconCache.
*/
public class TaskIconCache {
private final Handler mBackgroundHandler;
private final AccessibilityManager mAccessibilityManager;
- private final Context mContext;
- private final TaskKeyLruCache<TaskCacheEntry> mIconCache;
- private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
- private final IconProvider mIconProvider;
+ private final NormalizedIconLoader mIconLoader;
+
+ private final TaskKeyLruCache<Drawable> mIconCache;
+ private final TaskKeyLruCache<String> mContentDescriptionCache;
+ private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
+
+ private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction =
+ new TaskKeyLruCache.EvictionCallback() {
+ @Override
+ public void onEntryEvicted(Task.TaskKey key) {
+ if (key != null) {
+ mActivityInfoCache.remove(key.getComponent());
+ }
+ }
+ };
public TaskIconCache(Context context, Looper backgroundLooper) {
- mContext = context;
mBackgroundHandler = new Handler(backgroundLooper);
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Resources res = context.getResources();
int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
- mIconCache = new TaskKeyLruCache<>(cacheSize);
- mIconProvider = new IconProvider(context);
+ mIconCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
+ mContentDescriptionCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
+ mActivityInfoCache = new LruCache<>(cacheSize);
+ mIconLoader = new NormalizedIconLoader(context, mIconCache, mActivityInfoCache,
+ true /* disableColorExtraction */);
}
/**
@@ -93,14 +96,15 @@
IconLoadRequest request = new IconLoadRequest(mBackgroundHandler) {
@Override
public void run() {
- TaskCacheEntry entry = getCacheEntry(task);
+ Drawable icon = mIconLoader.getIcon(task);
+ String contentDescription = loadContentDescriptionInBackground(task);
if (isCanceled()) {
// We don't call back to the provided callback in this case
return;
}
MAIN_EXECUTOR.execute(() -> {
- task.icon = entry.icon;
- task.titleDescription = entry.contentDescription;
+ task.icon = icon;
+ task.titleDescription = contentDescription;
callback.accept(task);
onEnd();
});
@@ -112,99 +116,51 @@
public void clear() {
mIconCache.evictAll();
+ mContentDescriptionCache.evictAll();
}
+ /**
+ * Loads the content description for the given {@param task}.
+ */
+ private String loadContentDescriptionInBackground(Task task) {
+ // Return the cached content description if it exists
+ String label = mContentDescriptionCache.getAndInvalidateIfModified(task.key);
+ if (label != null) {
+ return label;
+ }
+
+ // Skip loading content descriptions if accessibility is disabled unless low RAM recents
+ // is enabled.
+ if (!GO_LOW_RAM_RECENTS_ENABLED && !mAccessibilityManager.isEnabled()) {
+ return "";
+ }
+
+ // Skip loading the content description if the activity no longer exists
+ ActivityInfo activityInfo = mIconLoader.getAndUpdateActivityInfo(task.key);
+ if (activityInfo == null) {
+ return "";
+ }
+
+ // Load the label otherwise
+ label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(activityInfo,
+ task.key.userId, task.taskDescription);
+ mContentDescriptionCache.put(task.key, label);
+ return label;
+ }
+
+
void onTaskRemoved(TaskKey taskKey) {
mIconCache.remove(taskKey);
}
- void invalidateCacheEntries(String pkg, UserHandle handle) {
- Utilities.postAsyncCallback(mBackgroundHandler,
- () -> mIconCache.removeAll(key ->
- pkg.equals(key.getPackageName()) && handle.getIdentifier() == key.userId));
- }
-
- @WorkerThread
- private TaskCacheEntry getCacheEntry(Task task) {
- TaskCacheEntry entry = mIconCache.getAndInvalidateIfModified(task.key);
- if (entry != null) {
- return entry;
- }
-
- TaskDescription desc = task.taskDescription;
- TaskKey key = task.key;
- ActivityInfo activityInfo = null;
-
- // Create new cache entry
- entry = new TaskCacheEntry();
-
- // Load icon
- // TODO: Load icon resource (b/143363444)
- Bitmap icon = desc.getIcon();
- if (icon != null) {
- entry.icon = new FastBitmapDrawable(getBitmapInfo(
- new BitmapDrawable(mContext.getResources(), icon),
- key.userId,
- desc.getPrimaryColor(),
- false /* isInstantApp */));
- } else {
- activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
- key.getComponent(), key.userId);
- if (activityInfo != null) {
- BitmapInfo bitmapInfo = getBitmapInfo(
- mIconProvider.getIcon(activityInfo, UserHandle.of(key.userId)),
- key.userId,
- desc.getPrimaryColor(),
- activityInfo.applicationInfo.isInstantApp());
- entry.icon = newIcon(mContext, bitmapInfo);
- } else {
- entry.icon = getDefaultIcon(key.userId);
+ void invalidatePackage(String packageName) {
+ // TODO(b/138944598): Merge this class into IconCache so we can do this at the base level
+ Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
+ for (ComponentName cn : activityInfoCache.keySet()) {
+ if (cn.getPackageName().equals(packageName)) {
+ mActivityInfoCache.remove(cn);
}
}
-
- // Loading content descriptions if accessibility or low RAM recents is enabled.
- if (GO_LOW_RAM_RECENTS_ENABLED || mAccessibilityManager.isEnabled()) {
- // Skip loading the content description if the activity no longer exists
- if (activityInfo == null) {
- activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
- key.getComponent(), key.userId);
- }
- if (activityInfo != null) {
- entry.contentDescription = ActivityManagerWrapper.getInstance()
- .getBadgedContentDescription(activityInfo, task.key.userId,
- task.taskDescription);
- }
- }
-
- mIconCache.put(task.key, entry);
- return entry;
- }
-
- @WorkerThread
- private Drawable getDefaultIcon(int userId) {
- synchronized (mDefaultIcons) {
- BitmapInfo info = mDefaultIcons.get(userId);
- if (info == null) {
- try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
- info = la.makeDefaultIcon(UserHandle.of(userId));
- }
- mDefaultIcons.put(userId, info);
- }
- return new FastBitmapDrawable(info);
- }
- }
-
- @WorkerThread
- private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
- int primaryColor, boolean isInstantApp) {
- try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
- la.disableColorExtraction();
- la.setWrapperBackgroundColor(primaryColor);
-
- // User version code O, so that the icon is always wrapped in an adaptive icon container
- return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
- Build.VERSION_CODES.O, isInstantApp);
- }
}
public static abstract class IconLoadRequest extends HandlerRunnable {
@@ -212,9 +168,4 @@
super(handler, null);
}
}
-
- private static class TaskCacheEntry {
- public Drawable icon;
- public String contentDescription = "";
- }
}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index e47df6c..3b50c26 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -27,9 +27,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
-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.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -41,7 +41,7 @@
private final Handler mBackgroundHandler;
private final int mCacheSize;
- private final TaskKeyLruCache<ThumbnailData> mCache;
+ private final ThumbnailCache mCache;
private final HighResLoadingState mHighResLoadingState;
public static class HighResLoadingState {
@@ -100,7 +100,7 @@
Resources res = context.getResources();
mCacheSize = res.getInteger(R.integer.recentsThumbnailCacheSize);
- mCache = new TaskKeyLruCache<>(mCacheSize);
+ mCache = new ThumbnailCache(mCacheSize);
}
/**
@@ -223,4 +223,21 @@
this.reducedResolution = reducedResolution;
}
}
+
+ private static class ThumbnailCache extends TaskKeyLruCache<ThumbnailData> {
+
+ public ThumbnailCache(int cacheSize) {
+ super(cacheSize);
+ }
+
+ /**
+ * Updates the cache entry if it is already present in the cache
+ */
+ public void updateIfAlreadyInCache(int taskId, ThumbnailData thumbnailData) {
+ ThumbnailData oldData = getCacheEntry(taskId);
+ if (oldData != null) {
+ putCacheEntry(taskId, thumbnailData);
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
index b1c72ce..fe37d60 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -31,11 +31,6 @@
private final BiPredicate<T, Boolean> mOnInitListener;
private final ActivityTracker<T> mActivityTracker;
- /**
- * @param onInitListener a callback made when the activity is initialized. The callback should
- * return true to continue receiving callbacks (ie. for if the activity is
- * recreated).
- */
public ActivityInitListener(BiPredicate<T, Boolean> onInitListener,
ActivityTracker<T> tracker) {
mOnInitListener = onInitListener;
@@ -47,10 +42,6 @@
return mOnInitListener.test(activity, alreadyOnHome);
}
- /**
- * Registers the activity-created listener. If the activity is already created, then the
- * callback provided in the constructor will be called synchronously.
- */
public void register() {
mActivityTracker.schedule(this);
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 2e118b4..050bdff 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -26,7 +26,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.quickstep.SysUINavigationMode;
+import com.android.launcher3.config.FeatureFlags;
import java.lang.annotation.Retention;
@@ -39,27 +39,12 @@
@IntDef({MULTI_WINDOW_STRATEGY_HALF_SCREEN, MULTI_WINDOW_STRATEGY_DEVICE_PROFILE})
private @interface MultiWindowStrategy {}
- /**
- * The height for the swipe up motion
- */
- public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
- float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
- if (SysUINavigationMode.getMode(context) == SysUINavigationMode.Mode.NO_BUTTON) {
- swipeHeight -= dp.getInsets().bottom;
- }
- return swipeHeight;
- }
-
public static void calculateLauncherTaskSize(Context context, DeviceProfile dp, Rect outRect) {
float extraSpace;
if (dp.isVerticalBarLayout()) {
extraSpace = 0;
} else {
- Resources res = context.getResources();
-
- extraSpace = getDefaultSwipeHeight(context, dp) + dp.verticalDragHandleSizePx
- + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size)
- + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+ extraSpace = dp.hotseatBarSizePx + dp.verticalDragHandleSizePx;
}
calculateTaskSize(context, dp, extraSpace, MULTI_WINDOW_STRATEGY_HALF_SCREEN, outRect);
}
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
deleted file mode 100644
index a4614de..0000000
--- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.view.Gravity;
-import android.view.Surface;
-
-import com.android.launcher3.graphics.RotationMode;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.quickstep.SysUINavigationMode;
-
-/**
- * Utility class to check nav bar position.
- */
-public class NavBarPosition {
-
- public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
- @Override
- public void mapRect(int left, int top, int right, int bottom, Rect out) {
- out.left = top;
- out.top = right;
- out.right = bottom;
- out.bottom = left;
- }
-
- @Override
- public void mapInsets(Context context, Rect insets, Rect out) {
- // If there is a display cutout, the top insets in portrait would also include the
- // cutout, which we will get as the left inset in landscape. Using the max of left and
- // top allows us to cover both cases (with or without cutout).
- if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
- out.top = Math.max(insets.top, insets.left);
- out.bottom = Math.max(insets.right, insets.bottom);
- out.left = out.right = 0;
- } else {
- out.top = Math.max(insets.top, insets.left);
- out.bottom = insets.right;
- out.left = insets.bottom;
- out.right = 0;
- }
- }
- };
-
- public static RotationMode ROTATION_SEASCAPE = new RotationMode(90) {
- @Override
- public void mapRect(int left, int top, int right, int bottom, Rect out) {
- out.left = bottom;
- out.top = left;
- out.right = top;
- out.bottom = right;
- }
-
- @Override
- public void mapInsets(Context context, Rect insets, Rect out) {
- if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
- out.top = Math.max(insets.top, insets.right);
- out.bottom = Math.max(insets.left, insets.bottom);
- out.left = out.right = 0;
- } else {
- out.top = Math.max(insets.top, insets.right);
- out.bottom = insets.left;
- out.right = insets.bottom;
- out.left = 0;
- }
- }
-
- @Override
- public int toNaturalGravity(int absoluteGravity) {
- int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK;
-
- if (horizontalGravity == Gravity.RIGHT) {
- horizontalGravity = Gravity.LEFT;
- } else if (horizontalGravity == Gravity.LEFT) {
- horizontalGravity = Gravity.RIGHT;
- }
-
- if (verticalGravity == Gravity.TOP) {
- verticalGravity = Gravity.BOTTOM;
- } else if (verticalGravity == Gravity.BOTTOM) {
- verticalGravity = Gravity.TOP;
- }
-
- return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK)
- & ~Gravity.VERTICAL_GRAVITY_MASK)
- | horizontalGravity | verticalGravity;
- }
- };
-
- private final SysUINavigationMode.Mode mMode;
- private final int mDisplayRotation;
-
- public NavBarPosition(SysUINavigationMode.Mode mode, DefaultDisplay.Info info) {
- mMode = mode;
- mDisplayRotation = info.rotation;
- }
-
- public boolean isRightEdge() {
- return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90;
- }
-
- public boolean isLeftEdge() {
- return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
- }
-
- public RotationMode getRotationMode() {
- return isLeftEdge() ? ROTATION_SEASCAPE
- : (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java b/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
deleted file mode 100644
index d87feec..0000000
--- a/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import android.util.Log;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-
-import java.util.LinkedHashMap;
-import java.util.function.Predicate;
-
-/**
- * A simple LRU cache for task key entries
- * @param <V> The type of the value
- */
-public class TaskKeyLruCache<V> {
-
- private final MyLinkedHashMap<V> mMap;
-
- public TaskKeyLruCache(int maxSize) {
- mMap = new MyLinkedHashMap<>(maxSize);
- }
-
- /**
- * Removes all entries from the cache
- */
- public synchronized void evictAll() {
- mMap.clear();
- }
-
- /**
- * Removes a particular entry from the cache
- */
- public synchronized void remove(TaskKey key) {
- mMap.remove(key.id);
- }
-
- /**
- * Removes all entries matching keyCheck
- */
- public synchronized void removeAll(Predicate<TaskKey> keyCheck) {
- mMap.entrySet().removeIf(e -> keyCheck.test(e.getValue().mKey));
- }
-
- /**
- * Gets the entry if it is still valid
- */
- public synchronized V getAndInvalidateIfModified(TaskKey key) {
- Entry<V> entry = mMap.get(key.id);
-
- if (entry != null && entry.mKey.windowingMode == key.windowingMode
- && entry.mKey.lastActiveTime == key.lastActiveTime) {
- return entry.mValue;
- } else {
- remove(key);
- return null;
- }
- }
-
- /**
- * Adds an entry to the cache, optionally evicting the last accessed entry
- */
- public final synchronized void put(TaskKey key, V value) {
- if (key != null && value != null) {
- mMap.put(key.id, new Entry<>(key, value));
- } else {
- Log.e("TaskKeyCache", "Unexpected null key or value: " + key + ", " + value);
- }
- }
-
- /**
- * Updates the cache entry if it is already present in the cache
- */
- public synchronized void updateIfAlreadyInCache(int taskId, V data) {
- Entry<V> entry = mMap.get(taskId);
- if (entry != null) {
- entry.mValue = data;
- }
- }
-
- private static class Entry<V> {
-
- final TaskKey mKey;
- V mValue;
-
- Entry(TaskKey key, V value) {
- mKey = key;
- mValue = value;
- }
-
- @Override
- public int hashCode() {
- return mKey.id;
- }
- }
-
- private static class MyLinkedHashMap<V> extends LinkedHashMap<Integer, Entry<V>> {
-
- private final int mMaxSize;
-
- MyLinkedHashMap(int maxSize) {
- super(0, 0.75f, true /* accessOrder */);
- mMaxSize = maxSize;
- }
-
- @Override
- protected boolean removeEldestEntry(Entry<Integer, TaskKeyLruCache.Entry<V>> eldest) {
- return size() > mMaxSize;
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 0e591ca..26e9eaf 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -38,12 +38,12 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
-import com.android.quickstep.util.LayoutUtils;
/**
* Scrim used for all-apps and shelf in Overview
@@ -163,7 +163,7 @@
int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
+ hotseatPadding.bottom + hotseatPadding.top;
float dragHandleTop =
- Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp));
+ Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(context, dp));
mDragHandleProgress = 1 - (dragHandleTop / mShiftRange);
}
mTopOffset = dp.getInsets().top - mShelfOffset;
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index ca81343..aa5fce1 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -31,9 +31,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.rule.ShellCommandRule.disableHeadsUpNotification;
import static com.android.launcher3.util.rule.ShellCommandRule.getLauncherCommand;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
-import static com.android.launcher3.util.rule.TestStabilityRule.RUN_FLAFOR;
-import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_PRESUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -102,7 +99,7 @@
}
mOrderSensitiveRules = RuleChain.outerRule(new NavigationModeSwitchRule(mLauncher))
- .around(new FailureWatcher(mDevice));
+ .around(new FailureWatcher(mDevice));
mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
getHomeIntentInPackage(context),
@@ -133,11 +130,6 @@
@NavigationModeSwitch
@Test
public void goToOverviewFromHome() {
- // b/142828227
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
- (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
- return;
- }
mDevice.pressHome();
assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
mOtherLauncherActivity.packageName)), WAIT_TIME_MS));
@@ -148,11 +140,6 @@
@NavigationModeSwitch
@Test
public void goToOverviewFromApp() {
- // b/142828227
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
- (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
- return;
- }
startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
mLauncher.getBackground().switchToOverview();
@@ -175,7 +162,7 @@
}
result[0] = f.apply(activity);
return true;
- }).get(), DEFAULT_UI_TIMEOUT, mLauncher);
+ }).get(), DEFAULT_UI_TIMEOUT);
return (T) result[0];
}
@@ -187,16 +174,11 @@
@NavigationModeSwitch
@Test
public void testOverview() {
- // b/142828227
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
- (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
- return;
- }
startAppFastAndWaitForRecentTask(getAppPackageName());
startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
startTestActivity(2);
Wait.atMost("Expected three apps in the task list",
- () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+ () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT);
BaseOverview overview = mLauncher.getBackground().switchToOverview();
executeOnRecents(recents ->
@@ -255,8 +237,7 @@
private void startAppFastAndWaitForRecentTask(String packageName) {
startAppFast(packageName);
Wait.atMost("Expected app in task list",
- () -> containsRecentTaskWithPackage(packageName), DEFAULT_ACTIVITY_TIMEOUT,
- mLauncher);
+ () -> containsRecentTaskWithPackage(packageName), DEFAULT_ACTIVITY_TIMEOUT);
}
private boolean containsRecentTaskWithPackage(String packageName) {
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index fa4c7b9..c2197ab 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -30,7 +30,6 @@
import android.content.pm.PackageManager;
import android.util.Log;
-import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -81,13 +80,7 @@
Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
return new Statement() {
private void assertTrue(String message, boolean condition) {
- if (mLauncher.getDevice().hasObject(By.textStartsWith(""))) {
- // The condition above is "screen is not empty". We are not treating
- // "Screen is empty" as an anomaly here. It's an acceptable state when
- // Launcher just starts under instrumentation.
- mLauncher.checkForAnomaly();
- }
- if (!condition) {
+ if(!condition) {
final AssertionError assertionError = new AssertionError(message);
FailureWatcher.onError(mLauncher.getDevice(), description, assertionError);
throw assertionError;
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index d60fa1e..f5b9b7e 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -18,9 +18,6 @@
import static com.android.launcher3.util.RaceConditionReproducer.enterEvt;
import static com.android.launcher3.util.RaceConditionReproducer.exitEvt;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
-import static com.android.launcher3.util.rule.TestStabilityRule.RUN_FLAFOR;
-import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_PRESUBMIT;
import android.content.Intent;
@@ -28,7 +25,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
-import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.util.RaceConditionReproducer;
import com.android.quickstep.NavigationModeSwitchRule.Mode;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 428e647..d270d76 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -248,33 +248,33 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
- @Ignore // b/143285809
+ @Ignore("Temporarily disabled b/140252765")
public void testQuickSwitchFromApp() throws Exception {
+ startAppFast(getAppPackageName());
startTestActivity(2);
- startTestActivity(3);
- startTestActivity(4);
+ String calculatorPackage = resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+ startAppFast(calculatorPackage);
Background background = getAndAssertBackground();
background.quickSwitchToPreviousApp();
assertTrue("The first app we should have quick switched to is not running",
- isTestActivityRunning(3));
+ isTestActivityRunning("TestActivity2"));
background = getAndAssertBackground();
background.quickSwitchToPreviousApp();
if (mLauncher.getNavigationModel() == NavigationModel.THREE_BUTTON) {
// 3-button mode toggles between 2 apps, rather than going back further.
assertTrue("Second quick switch should have returned to the first app.",
- isTestActivityRunning(4));
+ mDevice.wait(Until.hasObject(By.pkg(calculatorPackage)), DEFAULT_UI_TIMEOUT));
} else {
assertTrue("The second app we should have quick switched to is not running",
- isTestActivityRunning(2));
+ isTestActivityRunning("Test Pin Item"));
}
getAndAssertBackground();
}
- private boolean isTestActivityRunning(int activityNumber) {
- return mDevice.wait(Until.hasObject(By.pkg(getAppPackageName())
- .text("TestActivity" + activityNumber)),
+ private boolean isTestActivityRunning(String activityLabel) {
+ return mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text(activityLabel)),
DEFAULT_UI_TIMEOUT);
}
@@ -285,7 +285,7 @@
startTestActivity(2);
mLauncher.pressHome().quickSwitchToPreviousApp();
assertTrue("The most recent task is not running after quick switching from home",
- isTestActivityRunning(2));
+ isTestActivityRunning("TestActivity2"));
getAndAssertBackground();
}
}
diff --git a/res/anim/slide_in_right.xml b/res/anim/slide_in_right.xml
new file mode 100644
index 0000000..55d3e54
--- /dev/null
+++ b/res/anim/slide_in_right.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false" >
+ <translate
+ android:duration="@android:integer/config_shortAnimTime"
+ android:fromXDelta="100%"
+ android:toXDelta="0%"
+ />
+</set>
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index de17eb7..7be584e 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -115,6 +115,8 @@
<attr name="numFolderColumns" format="integer" />
<!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" />
+ <!-- numAllAppsColumns defaults to numColumns, if not specified -->
+ <attr name="numAllAppsColumns" format="integer" />
<attr name="defaultLayoutId" format="reference" />
<attr name="demoModeLayoutId" format="reference" />
</declare-styleable>
@@ -130,6 +132,12 @@
<attr name="iconTextSize" format="float" />
<!-- If true, this display option is used to determine the default grid -->
<attr name="canBeDefault" format="boolean" />
+
+ <!-- The following values are only enabled if grid is supported. -->
+ <!-- allAppsIconSize defaults to iconSize, if not specified -->
+ <attr name="allAppsIconSize" format="float" />
+ <!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
+ <attr name="allAppsIconTextSize" format="float" />
</declare-styleable>
<declare-styleable name="CellLayout">
diff --git a/res/values/config.xml b/res/values/config.xml
index 2a1f6f7..0387184 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -1,5 +1,5 @@
<resources>
- <!-- Miscellaneous -->
+<!-- Miscellaneous -->
<bool name="config_largeHeap">false</bool>
<bool name="is_tablet">false</bool>
<bool name="is_large_tablet">false</bool>
@@ -21,10 +21,10 @@
<!-- String representing the fragment class for settings activity.-->
<string name="settings_fragment_name" translatable="false">com.android.launcher3.settings.SettingsActivity$LauncherSettingsFragment</string>
- <!-- DragController -->
+<!-- DragController -->
<item type="id" name="drag_event_parity" />
- <!-- AllApps & Launcher transitions -->
+<!-- AllApps & Launcher transitions -->
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
<integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
@@ -34,7 +34,7 @@
<!-- View tag key used to store SpringAnimation data. -->
<item type="id" name="spring_animation_tag" />
- <!-- Workspace -->
+<!-- Workspace -->
<!-- The duration (in ms) of the fade animation on the object outlines, used when
we are dragging objects around on the home screen. -->
<integer name="config_dragOutlineFadeTime">900</integer>
@@ -57,20 +57,29 @@
<!-- The duration of the caret animation -->
<integer name="config_caretAnimationDuration">200</integer>
- <!-- Hotseat -->
+<!-- Hotseat -->
<bool name="hotseat_transpose_layout_with_orientation">true</bool>
<!-- Various classes overriden by projects/build flavors. -->
<string name="app_filter_class" translatable="false"></string>
+ <string name="icon_provider_class" translatable="false"></string>
+ <string name="drawable_factory_class" translatable="false"></string>
<string name="user_event_dispatcher_class" translatable="false"></string>
<string name="stats_log_manager_class" translatable="false"></string>
<string name="app_transition_manager_class" translatable="false"></string>
<string name="instant_app_resolver_class" translatable="false"></string>
<string name="main_process_initializer_class" translatable="false"></string>
+ <string name="system_shortcut_factory_class" translatable="false"></string>
<string name="app_launch_tracker_class" translatable="false"></string>
<string name="test_information_handler_class" translatable="false"></string>
<string name="launcher_activity_logic_class" translatable="false"></string>
+ <!-- Package name of the default wallpaper picker. -->
+ <string name="wallpaper_picker_package" translatable="false"></string>
+
+ <!-- Whitelisted package to retrieve packagename for badge. Can be empty. -->
+ <string name="shortcutinfo_badgepkg_whitelist" translatable="false"></string>
+
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
@@ -88,12 +97,7 @@
<integer name="config_popupArrowOpenCloseDuration">40</integer>
<integer name="config_removeNotificationViewDuration">300</integer>
- <!-- Default packages -->
- <string name="wallpaper_picker_package" translatable="false"></string>
- <string name="calendar_component_name" translatable="false"></string>
- <string name="clock_component_name" translatable="false"></string>
-
- <!-- Accessibility actions -->
+<!-- Accessibility actions -->
<item type="id" name="action_remove" />
<item type="id" name="action_uninstall" />
<item type="id" name="action_reconfigure" />
@@ -108,10 +112,10 @@
<item type="id" name="action_dismiss_notification" />
<item type="id" name="action_remote_action_shortcut" />
- <!-- QSB IDs. DO not change -->
+<!-- QSB IDs. DO not change -->
<item type="id" name="search_container_workspace" />
<item type="id" name="search_container_all_apps" />
- <!-- Recents -->
+<!-- Recents -->
<item type="id" name="overview_panel"/>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index dec8939..9d9c2e8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -102,11 +102,9 @@
<string name="app_info_drop_target_label">App info</string>
<!-- Label for install drop target. [CHAR_LIMIT=20] -->
<string name="install_drop_target_label">Install</string>
+
<!-- Label for install dismiss prediction. -->
<string translatable="false" name="dismiss_prediction_label">Dismiss prediction</string>
- <!-- Label for pinning predicted app. -->
- <string name="pin_prediction" translatable="false">Pin Prediction</string>
-
<!-- Permissions: -->
<skip />
diff --git a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index 5b6d94d..32eb2ec 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -202,14 +202,15 @@
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
if (entry == null) {
entry = new CacheEntry();
- entry.bitmap = getDefaultIcon(user);
+ getDefaultIcon(user).applyTo(entry);
}
return entry;
}
public void addCache(ComponentName key, String title) {
CacheEntry entry = new CacheEntry();
- entry.bitmap = BitmapInfo.of(newIcon(), Color.RED);
+ entry.icon = newIcon();
+ entry.color = Color.RED;
entry.title = title;
mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
}
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 69c5b00..81b9043 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -2,13 +2,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.BitmapInfo;
import org.junit.Before;
import org.junit.Test;
@@ -43,7 +43,7 @@
public void testCacheUpdate_update_apps() throws Exception {
// Clear all icons from apps list so that its easy to check what was updated
for (AppInfo info : allAppsList.data) {
- info.bitmap = BitmapInfo.LOW_RES_INFO;
+ info.iconBitmap = null;
}
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
@@ -56,9 +56,9 @@
assertFalse(allAppsList.data.isEmpty());
for (AppInfo info : allAppsList.data) {
if (info.componentName.getPackageName().equals("app1")) {
- assertFalse(info.bitmap.isNullOrLowRes());
+ assertNotNull(info.iconBitmap);
} else {
- assertTrue(info.bitmap.isNullOrLowRes());
+ assertNull(info.iconBitmap);
}
}
}
@@ -85,10 +85,10 @@
for (ItemInfo info : bgDataModel.itemsIdMap) {
if (updates.contains(info.id)) {
assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
- assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
+ assertNotNull(((WorkspaceItemInfo) info).iconBitmap);
} else {
assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
- assertTrue(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
+ assertNull(((WorkspaceItemInfo) info).iconBitmap);
}
}
}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 382bfdf..b28077f 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -35,7 +35,7 @@
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
@@ -81,39 +81,19 @@
protected StatsLogManager mStatsLogManager;
protected SystemUiController mSystemUiController;
-
- public static final int ACTIVITY_STATE_STARTED = 1 << 0;
- public static final int ACTIVITY_STATE_RESUMED = 1 << 1;
-
+ private static final int ACTIVITY_STATE_STARTED = 1 << 0;
+ private static final int ACTIVITY_STATE_RESUMED = 1 << 1;
/**
- * State flags indicating that the activity has received one frame after resume, and was
- * not immediately paused.
- */
- public static final int ACTIVITY_STATE_DEFERRED_RESUMED = 1 << 2;
-
- public static final int ACTIVITY_STATE_WINDOW_FOCUSED = 1 << 3;
-
- /**
- * State flag indicating if the user is active or the activity when to background as a result
+ * State flag indicating if the user is active or the actitvity when to background as a result
* of user action.
* @see #isUserActive()
*/
- public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
-
- /**
- * State flag indicating that a state transition is in progress
- */
- public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 5;
+ private static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 2;
@Retention(SOURCE)
@IntDef(
flag = true,
- value = {ACTIVITY_STATE_STARTED,
- ACTIVITY_STATE_RESUMED,
- ACTIVITY_STATE_DEFERRED_RESUMED,
- ACTIVITY_STATE_WINDOW_FOCUSED,
- ACTIVITY_STATE_USER_ACTIVE,
- ACTIVITY_STATE_TRANSITION_ACTIVE})
+ value = {ACTIVITY_STATE_STARTED, ACTIVITY_STATE_RESUMED, ACTIVITY_STATE_USER_ACTIVE})
public @interface ActivityFlags{}
@ActivityFlags
@@ -166,19 +146,19 @@
@Override
protected void onStart() {
- addActivityFlags(ACTIVITY_STATE_STARTED);
+ mActivityFlags |= ACTIVITY_STATE_STARTED;
super.onStart();
}
@Override
protected void onResume() {
- addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
+ mActivityFlags |= ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE;
super.onResume();
}
@Override
protected void onUserLeaveHint() {
- removeActivityFlags(ACTIVITY_STATE_USER_ACTIVE);
+ mActivityFlags &= ~ACTIVITY_STATE_USER_ACTIVE;
super.onUserLeaveHint();
}
@@ -192,7 +172,7 @@
@Override
protected void onStop() {
- removeActivityFlags(ACTIVITY_STATE_STARTED | ACTIVITY_STATE_USER_ACTIVE);
+ mActivityFlags &= ~ACTIVITY_STATE_STARTED & ~ACTIVITY_STATE_USER_ACTIVE;
mForceInvisible = 0;
super.onStop();
@@ -203,7 +183,7 @@
@Override
protected void onPause() {
- removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
+ mActivityFlags &= ~ACTIVITY_STATE_RESUMED;
super.onPause();
// Reset the overridden sysui flags used for the task-swipe launch animation, we do this
@@ -213,17 +193,6 @@
getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
}
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- if (hasFocus) {
- addActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
- } else {
- removeActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
- }
-
- }
-
public boolean isStarted() {
return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
}
@@ -239,22 +208,6 @@
return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
}
- public int getActivityFlags() {
- return mActivityFlags;
- }
-
- protected void addActivityFlags(int flags) {
- mActivityFlags |= flags;
- onActivityFlagsChanged(flags);
- }
-
- protected void removeActivityFlags(int flags) {
- mActivityFlags &= ~flags;
- onActivityFlagsChanged(flags);
- }
-
- protected void onActivityFlagsChanged(int changeBits) { }
-
public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
mDPChangeListeners.add(listener);
}
@@ -280,7 +233,7 @@
/**
* Used to set the override visibility state, used only to handle the transition home with the
* recents animation.
- * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner
+ * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner()
*/
public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
mForceInvisible |= flag;
@@ -307,7 +260,7 @@
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- if (!ApiWrapper.dumpActivity(this, writer)) {
+ if (!UiFactory.dumpActivity(this, writer)) {
super.dump(prefix, fd, writer, args);
}
}
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 772eb00..d24de8e 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -57,7 +57,7 @@
private ActionMode mCurrentActionMode;
protected boolean mIsSafeModeEnabled;
- private Runnable mOnStartCallback;
+ private OnStartCallback mOnStartCallback;
private int mThemeRes = R.style.AppTheme;
@@ -226,7 +226,7 @@
super.onStart();
if (mOnStartCallback != null) {
- mOnStartCallback.run();
+ mOnStartCallback.onActivityStart(this);
mOnStartCallback = null;
}
}
@@ -238,12 +238,8 @@
mRotationListener.disable();
}
- public void runOnceOnStart(Runnable action) {
- mOnStartCallback = action;
- }
-
- public void clearRunOnceOnStartCallback() {
- mOnStartCallback = null;
+ public <T extends BaseDraggingActivity> void setOnStartCallback(OnStartCallback<T> callback) {
+ mOnStartCallback = callback;
}
protected void onDeviceProfileInitiated() {
@@ -262,4 +258,12 @@
}
protected abstract void reapplyUi();
+
+ /**
+ * Callback for listening for onStart
+ */
+ public interface OnStartCallback<T extends BaseDraggingActivity> {
+
+ void onActivityStart(T activity);
+ }
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index e6f8a85..7adb6a4 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,8 +16,6 @@
package com.android.launcher3;
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
-import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.Animator;
@@ -47,6 +45,7 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.PreloadIconDrawable;
@@ -216,7 +215,6 @@
cancelDotScaleAnim();
mDotParams.scale = 0f;
mForceHideDot = false;
- setBackground(null);
}
private void cancelDotScaleAnim() {
@@ -289,8 +287,9 @@
}
private void applyIconAndLabel(ItemInfoWithIcon info) {
- FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
- mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
+ FastBitmapDrawable iconDrawable = DrawableFactory.INSTANCE.get(getContext())
+ .newIcon(getContext(), info);
+ mDotParams.color = IconPalette.getMutedColor(info.iconColor, 0.54f);
setIcon(iconDrawable);
setText(info.title);
@@ -497,8 +496,7 @@
// Text should be visible everywhere but the hotseat.
Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;
- return info == null || (info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT
- && info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
+ return info == null || info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT;
}
public void setTextVisibility(boolean visible) {
@@ -569,7 +567,8 @@
preloadDrawable = (PreloadIconDrawable) mIcon;
preloadDrawable.setLevel(progressLevel);
} else {
- preloadDrawable = newPendingIcon(getContext(), info);
+ preloadDrawable = DrawableFactory.INSTANCE.get(getContext())
+ .newPendingIcon(getContext(), info);
preloadDrawable.setLevel(progressLevel);
setIcon(preloadDrawable);
}
@@ -666,7 +665,7 @@
mDisableRelayout = true;
// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
- info.bitmap.icon.prepareToDraw();
+ info.iconBitmap.prepareToDraw();
if (info instanceof AppInfo) {
applyFromApplicationInfo((AppInfo) info);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index a35f598..736142f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -25,8 +25,6 @@
import android.util.DisplayMetrics;
import android.view.Surface;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.DotRenderer;
@@ -36,8 +34,6 @@
public class DeviceProfile {
public final InvariantDeviceProfile inv;
- // IDP with no grid override values.
- @Nullable private final InvariantDeviceProfile originalIdp;
// Device properties
public final boolean isTablet;
@@ -138,11 +134,10 @@
public DotRenderer mDotRendererAllApps;
public DeviceProfile(Context context, InvariantDeviceProfile inv,
- InvariantDeviceProfile originalIDP, Point minSize, Point maxSize,
+ Point minSize, Point maxSize,
int width, int height, boolean isLandscape, boolean isMultiWindowMode) {
this.inv = inv;
- this.originalIdp = inv;
this.isLandscape = isLandscape;
this.isMultiWindowMode = isMultiWindowMode;
@@ -234,19 +229,6 @@
// Recalculate the available dimensions using the new hotseat size.
updateAvailableDimensions(dm, res);
}
-
- if (originalIDP != null) {
- // Grid size change should not affect All Apps UI, so we use the original profile
- // measurements here.
- DeviceProfile originalProfile = isLandscape
- ? originalIDP.landscapeProfile
- : originalIDP.portraitProfile;
- allAppsIconSizePx = originalProfile.iconSizePx;
- allAppsIconTextSizePx = originalProfile.iconTextSizePx;
- allAppsCellHeightPx = originalProfile.allAppsCellHeightPx;
- allAppsIconDrawablePaddingPx = originalProfile.iconDrawablePaddingOriginalPx;
- allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
- }
updateWorkspacePadding();
// This is done last, after iconSizePx is calculated above.
@@ -259,8 +241,8 @@
public DeviceProfile copy(Context context) {
Point size = new Point(availableWidthPx, availableHeightPx);
- return new DeviceProfile(context, inv, originalIdp, size, size, widthPx, heightPx,
- isLandscape, isMultiWindowMode);
+ return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape,
+ isMultiWindowMode);
}
public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
@@ -271,8 +253,8 @@
// In multi-window mode, we can have widthPx = availableWidthPx
// and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
// widthPx and heightPx values where it's needed.
- DeviceProfile profile = new DeviceProfile(context, inv, originalIdp, mwSize, mwSize,
- mwSize.x, mwSize.y, isLandscape, true);
+ DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y,
+ isLandscape, true);
// If there isn't enough vertical cell padding with the labels displayed, hide the labels.
float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx
@@ -356,10 +338,18 @@
}
cellWidthPx = iconSizePx + iconDrawablePaddingPx;
- allAppsIconSizePx = iconSizePx;
- allAppsIconTextSizePx = iconTextSizePx;
- allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
- allAppsCellHeightPx = getCellSize().y;
+ // All apps
+ if (allAppsHasDifferentNumColumns()) {
+ allAppsIconSizePx = ResourceUtils.pxFromDp(inv.allAppsIconSize, dm);
+ allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, dm);
+ allAppsCellHeightPx = getCellSize(inv.numAllAppsColumns, inv.numAllAppsColumns).y;
+ allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
+ } else {
+ allAppsIconSizePx = iconSizePx;
+ allAppsIconTextSizePx = iconTextSizePx;
+ allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
+ allAppsCellHeightPx = getCellSize().y;
+ }
allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
if (isVerticalBarLayout()) {
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 5091684..a90025e 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import android.animation.ObjectAnimator;
-import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -36,7 +35,6 @@
import android.util.Property;
import android.util.SparseArray;
-import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.icons.BitmapInfo;
public class FastBitmapDrawable extends Drawable {
@@ -100,6 +98,10 @@
this(info.icon, info.color);
}
+ public FastBitmapDrawable(ItemInfoWithIcon info) {
+ this(info.iconBitmap, info.iconColor);
+ }
+
protected FastBitmapDrawable(Bitmap b, int iconColor) {
this(b, iconColor, false);
}
@@ -363,7 +365,7 @@
}
@Override
- public FastBitmapDrawable newDrawable() {
+ public Drawable newDrawable() {
return new FastBitmapDrawable(mBitmap, mIconColor, mIsDisabled);
}
@@ -372,37 +374,4 @@
return 0;
}
}
-
- /**
- * Interface to be implemented by custom {@link BitmapInfo} to handle drawable construction
- */
- public interface Factory {
-
- /**
- * Called to create a new drawable
- */
- FastBitmapDrawable newDrawable();
- }
-
- /**
- * Returns a FastBitmapDrawable with the icon.
- */
- public static FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
- FastBitmapDrawable drawable = newIcon(context, info.bitmap);
- drawable.setIsDisabled(info.isDisabled());
- return drawable;
- }
-
- /**
- * Creates a drawable for the provided BitmapInfo
- */
- public static FastBitmapDrawable newIcon(Context context, BitmapInfo info) {
- if (info instanceof Factory) {
- return ((Factory) info).newDrawable();
- } else if (info.isLowRes()) {
- return new PlaceHolderIconDrawable(info, context);
- } else {
- return new FastBitmapDrawable(info);
- }
- }
}
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
new file mode 100644
index 0000000..0f006f7
--- /dev/null
+++ b/src/com/android/launcher3/IconProvider.java
@@ -0,0 +1,29 @@
+package com.android.launcher3;
+
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
+import android.content.pm.LauncherActivityInfo;
+import android.graphics.drawable.Drawable;
+
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+public class IconProvider implements ResourceBasedOverride {
+
+ public static MainThreadInitializedObject<IconProvider> INSTANCE =
+ forOverride(IconProvider.class, R.string.icon_provider_class);
+
+ public IconProvider() { }
+
+ public String getSystemStateForPackage(String systemState, String packageName) {
+ return systemState;
+ }
+
+ /**
+ * @param flattenDrawable true if the caller does not care about the specification of the
+ * original icon as long as the flattened version looks the same.
+ */
+ public Drawable getIcon(LauncherActivityInfo info, int iconDpi, boolean flattenDrawable) {
+ return info.getIcon(iconDpi);
+ }
+}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 21359f1..0b79dd2 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -22,7 +22,6 @@
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -41,11 +40,9 @@
import android.util.Log;
import android.util.Pair;
-import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
@@ -242,6 +239,11 @@
return info == null ? null : (WorkspaceItemInfo) info.getItemInfo().first;
}
+ public static WorkspaceItemInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
+ return (WorkspaceItemInfo)
+ new PendingInstallShortcutInfo(info, context).getItemInfo().first;
+ }
+
public static void queueShortcut(ShortcutInfo info, Context context) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
}
@@ -317,10 +319,10 @@
private static class PendingInstallShortcutInfo {
final boolean isActivity;
- @Nullable final ShortcutInfo shortcutInfo;
- @Nullable final AppWidgetProviderInfo providerInfo;
+ final ShortcutInfo shortcutInfo;
+ final AppWidgetProviderInfo providerInfo;
- @Nullable final Intent data;
+ final Intent data;
final Context mContext;
final Intent launchIntent;
final String label;
@@ -350,12 +352,7 @@
shortcutInfo = null;
providerInfo = null;
- String packageName = info.getComponentName().getPackageName();
- data = new Intent();
- data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent(
- new ComponentName(packageName, "")).setPackage(packageName));
- data.putExtra(Intent.EXTRA_SHORTCUT_NAME, info.getLabel());
-
+ data = null;
user = info.getUser();
mContext = context;
@@ -449,26 +446,21 @@
// This name is only used for comparisons and notifications, so fall back to activity
// name if not supplied
String name = ensureValidName(mContext, launchIntent, label).toString();
- Bitmap icon = data == null ? null
- : data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
- Intent.ShortcutIconResource iconResource = data == null ? null
- : data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
+ Bitmap icon = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
+ Intent.ShortcutIconResource iconResource =
+ data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
// Only encode the parameters which are supported by the API.
JSONStringer json = new JSONStringer()
.object()
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
.key(NAME_KEY).value(name)
- .key(USER_HANDLE_KEY).value(
- UserManagerCompat.getInstance(mContext).getSerialNumberForUser(user))
.key(APP_SHORTCUT_TYPE_KEY).value(isActivity);
if (icon != null) {
byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon);
- if (iconByteArray != null) {
- json = json.key(ICON_KEY).value(
- Base64.encodeToString(
- iconByteArray, 0, iconByteArray.length, Base64.DEFAULT));
- }
+ json = json.key(ICON_KEY).value(
+ Base64.encodeToString(
+ iconByteArray, 0, iconByteArray.length, Base64.DEFAULT));
}
if (iconResource != null) {
json = json.key(ICON_RESOURCE_NAME_KEY).value(iconResource.resourceName);
@@ -484,20 +476,14 @@
public Pair<ItemInfo, Object> getItemInfo() {
if (isActivity) {
- WorkspaceItemInfo si = createWorkspaceItemInfo(data, user,
+ WorkspaceItemInfo si = createWorkspaceItemInfo(data,
LauncherAppState.getInstance(mContext));
si.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
return Pair.create(si, null);
} else if (shortcutInfo != null) {
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- fetchAndUpdateShortcutIconAsync(mContext, itemInfo, shortcutInfo, true);
- } else {
- LauncherIcons li = LauncherIcons.obtain(mContext);
- itemInfo.bitmap = li.createShortcutIcon(shortcutInfo);
- li.recycle();
- }
+ fetchAndUpdateShortcutIconAsync(mContext, itemInfo, shortcutInfo, true);
return Pair.create(itemInfo, shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
@@ -513,7 +499,7 @@
return Pair.create(widgetInfo, providerInfo);
} else {
WorkspaceItemInfo itemInfo =
- createWorkspaceItemInfo(data, user, LauncherAppState.getInstance(mContext));
+ createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
return Pair.create(itemInfo, null);
}
}
@@ -631,8 +617,7 @@
return new PendingInstallShortcutInfo(info, original.mContext);
}
- private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, UserHandle user,
- LauncherAppState app) {
+ private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, LauncherAppState app) {
if (data == null) {
Log.e(TAG, "Can't construct WorkspaceItemInfo with null data");
return null;
@@ -649,7 +634,10 @@
}
final WorkspaceItemInfo info = new WorkspaceItemInfo();
- info.user = user;
+
+ // Only support intents for current user for now. Intents sent from other
+ // users wouldn't get here without intent forwarding anyway.
+ info.user = Process.myUserHandle();
BitmapInfo iconInfo = null;
LauncherIcons li = LauncherIcons.obtain(app.getContext());
@@ -667,7 +655,7 @@
if (iconInfo == null) {
iconInfo = app.getIconCache().getDefaultIcon(info.user);
}
- info.bitmap = iconInfo;
+ info.applyFrom(iconInfo);
info.title = Utilities.trim(name);
info.contentDescription = app.getContext().getPackageManager()
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 9d87152..310a9e9 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -58,7 +58,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
public class InvariantDeviceProfile {
@@ -104,6 +103,8 @@
public int iconBitmapSize;
public int fillResIconDpi;
public float iconTextSize;
+ public float allAppsIconSize;
+ public float allAppsIconTextSize;
private SparseArray<TypedValue> mExtraAttrs;
@@ -144,6 +145,8 @@
iconTextSize = p.iconTextSize;
numHotseatIcons = p.numHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns;
+ allAppsIconSize = p.allAppsIconSize;
+ allAppsIconTextSize = p.allAppsIconTextSize;
defaultLayoutId = p.defaultLayoutId;
demoModeLayoutId = p.demoModeLayoutId;
mExtraAttrs = p.mExtraAttrs;
@@ -188,11 +191,54 @@
Point smallestSize = new Point(displayInfo.smallestSize);
Point largestSize = new Point(displayInfo.largestSize);
+ ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
// This guarantees that width < height
float minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y),
displayInfo.metrics);
float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y),
displayInfo.metrics);
+ // Sort the profiles based on the closeness to the device size
+ Collections.sort(allOptions, (a, b) ->
+ Float.compare(dist(minWidthDps, minHeightDps, a.minWidthDps, a.minHeightDps),
+ dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps)));
+ DisplayOption interpolatedDisplayOption =
+ invDistWeightedInterpolate(minWidthDps, minHeightDps, allOptions);
+
+ GridOption closestProfile = allOptions.get(0).grid;
+ numRows = closestProfile.numRows;
+ numColumns = closestProfile.numColumns;
+ numHotseatIcons = closestProfile.numHotseatIcons;
+ defaultLayoutId = closestProfile.defaultLayoutId;
+ demoModeLayoutId = closestProfile.demoModeLayoutId;
+ numFolderRows = closestProfile.numFolderRows;
+ numFolderColumns = closestProfile.numFolderColumns;
+ numAllAppsColumns = closestProfile.numAllAppsColumns;
+
+ mExtraAttrs = closestProfile.extraAttrs;
+
+ if (!closestProfile.name.equals(gridName)) {
+ Utilities.getPrefs(context).edit()
+ .putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
+ }
+
+ iconSize = interpolatedDisplayOption.iconSize;
+ iconShapePath = getIconShapePath(context);
+ landscapeIconSize = interpolatedDisplayOption.landscapeIconSize;
+ iconBitmapSize = ResourceUtils.pxFromDp(iconSize, displayInfo.metrics);
+ iconTextSize = interpolatedDisplayOption.iconTextSize;
+ fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
+
+ if (Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)) {
+ allAppsIconSize = interpolatedDisplayOption.allAppsIconSize;
+ allAppsIconTextSize = interpolatedDisplayOption.allAppsIconTextSize;
+ } else {
+ allAppsIconSize = iconSize;
+ allAppsIconTextSize = iconTextSize;
+ }
+
+ // If the partner customization apk contains any grid overrides, apply them
+ // Supported overrides: numRows, numColumns, iconSize
+ applyPartnerDeviceProfileOverrides(context, displayInfo.metrics);
Point realSize = new Point(displayInfo.realSize);
// The real size never changes. smallSide and largeSide will remain the
@@ -200,64 +246,10 @@
int smallSide = Math.min(realSize.x, realSize.y);
int largeSide = Math.max(realSize.x, realSize.y);
- // We want a list of all options as well as the list of filtered options. This allows us
- // to have a consistent UI for areas that the grid size change should not affect
- // ie. All Apps should be consistent between grid sizes.
- ArrayList<DisplayOption> allOptions = new ArrayList<>();
- ArrayList<DisplayOption> filteredOptions = new ArrayList<>();
- getPredefinedDeviceProfiles(context, gridName, filteredOptions, allOptions);
-
- if (allOptions.isEmpty() && filteredOptions.isEmpty()) {
- throw new RuntimeException("No display option with canBeDefault=true");
- }
-
- // Sort the profiles based on the closeness to the device size
- Comparator<DisplayOption> comparator = (a, b) -> Float.compare(dist(minWidthDps,
- minHeightDps, a.minWidthDps, a.minHeightDps),
- dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps));
-
- // Calculate the device profiles as if there is no grid override.
- Collections.sort(allOptions, comparator);
- DisplayOption interpolatedDisplayOption =
- invDistWeightedInterpolate(minWidthDps, minHeightDps, allOptions);
- initGridOption(context, allOptions, interpolatedDisplayOption, displayInfo.metrics);
-
- // Create IDP with no grid override values.
- InvariantDeviceProfile originalIDP = new InvariantDeviceProfile(this);
- originalIDP.landscapeProfile = new DeviceProfile(context, this, null, smallestSize,
- largestSize, largeSide, smallSide, true /* isLandscape */,
- false /* isMultiWindowMode */);
- originalIDP.portraitProfile = new DeviceProfile(context, this, null, smallestSize,
- largestSize, smallSide, largeSide, false /* isLandscape */,
- false /* isMultiWindowMode */);
-
- if (filteredOptions.isEmpty()) {
- filteredOptions = allOptions;
-
- landscapeProfile = originalIDP.landscapeProfile;
- portraitProfile = originalIDP.portraitProfile;
- } else {
- Collections.sort(filteredOptions, comparator);
- interpolatedDisplayOption =
- invDistWeightedInterpolate(minWidthDps, minHeightDps, filteredOptions);
-
- initGridOption(context, filteredOptions, interpolatedDisplayOption,
- displayInfo.metrics);
- numAllAppsColumns = originalIDP.numAllAppsColumns;
-
- landscapeProfile = new DeviceProfile(context, this, originalIDP, smallestSize,
- largestSize, largeSide, smallSide, true /* isLandscape */,
- false /* isMultiWindowMode */);
- portraitProfile = new DeviceProfile(context, this, originalIDP, smallestSize,
- largestSize, smallSide, largeSide, false /* isLandscape */,
- false /* isMultiWindowMode */);
- }
-
- GridOption closestProfile = filteredOptions.get(0).grid;
- if (!closestProfile.name.equals(gridName)) {
- Utilities.getPrefs(context).edit()
- .putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
- }
+ landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
+ largeSide, smallSide, true /* isLandscape */, false /* isMultiWindowMode */);
+ portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
+ smallSide, largeSide, false /* isLandscape */, false /* isMultiWindowMode */);
// We need to ensure that there is enough extra space in the wallpaper
// for the intended parallax effects
@@ -275,33 +267,6 @@
return closestProfile.name;
}
- private void initGridOption(Context context, ArrayList<DisplayOption> options,
- DisplayOption displayOption, DisplayMetrics metrics) {
- GridOption closestProfile = options.get(0).grid;
- numRows = closestProfile.numRows;
- numColumns = closestProfile.numColumns;
- numHotseatIcons = closestProfile.numHotseatIcons;
- defaultLayoutId = closestProfile.defaultLayoutId;
- demoModeLayoutId = closestProfile.demoModeLayoutId;
- numFolderRows = closestProfile.numFolderRows;
- numFolderColumns = closestProfile.numFolderColumns;
- numAllAppsColumns = numColumns;
-
- mExtraAttrs = closestProfile.extraAttrs;
-
- iconSize = displayOption.iconSize;
- iconShapePath = getIconShapePath(context);
- landscapeIconSize = displayOption.landscapeIconSize;
- iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
- iconTextSize = displayOption.iconTextSize;
- fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
-
- // If the partner customization apk contains any grid overrides, apply them
- // Supported overrides: numRows, numColumns, iconSize
- applyPartnerDeviceProfileOverrides(context, metrics);
- }
-
-
@Nullable
public TypedValue getAttrValue(int attr) {
return mExtraAttrs == null ? null : mExtraAttrs.get(attr);
@@ -379,13 +344,7 @@
}
}
- /**
- * @param gridName The current grid name.
- * @param filteredOptionsOut List filled with all the filtered options based on gridName.
- * @param allOptionsOut List filled with all the options that can be the default option.
- */
- static void getPredefinedDeviceProfiles(Context context, String gridName,
- ArrayList<DisplayOption> filteredOptionsOut, ArrayList<DisplayOption> allOptionsOut) {
+ static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName) {
ArrayList<DisplayOption> profiles = new ArrayList<>();
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
final int depth = parser.getDepth();
@@ -412,19 +371,26 @@
throw new RuntimeException(e);
}
+ ArrayList<DisplayOption> filteredProfiles = new ArrayList<>();
if (!TextUtils.isEmpty(gridName)) {
for (DisplayOption option : profiles) {
if (gridName.equals(option.grid.name)) {
- filteredOptionsOut.add(option);
+ filteredProfiles.add(option);
}
}
}
-
- for (DisplayOption option : profiles) {
- if (option.canBeDefault) {
- allOptionsOut.add(option);
+ if (filteredProfiles.isEmpty()) {
+ // No grid found, use the default options
+ for (DisplayOption option : profiles) {
+ if (option.canBeDefault) {
+ filteredProfiles.add(option);
+ }
}
}
+ if (filteredProfiles.isEmpty()) {
+ throw new RuntimeException("No display option with canBeDefault=true");
+ }
+ return filteredProfiles;
}
private int getLauncherIconDensity(int requiredSize) {
@@ -548,6 +514,8 @@
private final int numHotseatIcons;
+ private final int numAllAppsColumns;
+
private final int defaultLayoutId;
private final int demoModeLayoutId;
@@ -570,6 +538,8 @@
R.styleable.GridDisplayOption_numFolderRows, numRows);
numFolderColumns = a.getInt(
R.styleable.GridDisplayOption_numFolderColumns, numColumns);
+ numAllAppsColumns = a.getInt(
+ R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
a.recycle();
@@ -589,6 +559,8 @@
private float iconSize;
private float iconTextSize;
private float landscapeIconSize;
+ private float allAppsIconSize;
+ private float allAppsIconTextSize;
DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
this.grid = grid;
@@ -607,6 +579,10 @@
iconSize);
iconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
+ allAppsIconSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
+ iconSize);
+ allAppsIconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize,
+ iconTextSize);
a.recycle();
}
@@ -621,14 +597,18 @@
private DisplayOption multiply(float w) {
iconSize *= w;
landscapeIconSize *= w;
+ allAppsIconSize *= w;
iconTextSize *= w;
+ allAppsIconTextSize *= w;
return this;
}
private DisplayOption add(DisplayOption p) {
iconSize += p.iconSize;
landscapeIconSize += p.landscapeIconSize;
+ allAppsIconSize += p.allAppsIconSize;
iconTextSize += p.iconTextSize;
+ allAppsIconTextSize += p.allAppsIconTextSize;
return this;
}
}
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index 1941455..1550bb0 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -16,6 +16,10 @@
package com.android.launcher3;
+import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON;
+
+import android.graphics.Bitmap;
+
import com.android.launcher3.icons.BitmapInfo;
/**
@@ -26,9 +30,14 @@
public static final String TAG = "ItemInfoDebug";
/**
- * The bitmap for the application icon
+ * A bitmap version of the application icon.
*/
- public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO;
+ public Bitmap iconBitmap;
+
+ /**
+ * Dominant color in the {@link #iconBitmap}.
+ */
+ public int iconColor;
/**
* Indicates that the icon is disabled due to safe mode restrictions.
@@ -97,7 +106,8 @@
protected ItemInfoWithIcon(ItemInfoWithIcon info) {
super(info);
- bitmap = info.bitmap;
+ iconBitmap = info.iconBitmap;
+ iconColor = info.iconColor;
runtimeStatusFlags = info.runtimeStatusFlags;
}
@@ -110,7 +120,12 @@
* Indicates whether we're using a low res icon
*/
public boolean usingLowResIcon() {
- return bitmap.isLowRes();
+ return iconBitmap == LOW_RES_ICON;
+ }
+
+ public void applyFrom(BitmapInfo info) {
+ iconBitmap = info.icon;
+ iconColor = info.color;
}
/**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5a5f7c3..4b4d793 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -31,11 +31,8 @@
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.LoggerUtils.newTarget;
-import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
-import static com.android.launcher3.popup.SystemShortcut.DISMISS_PREDICTION;
-import static com.android.launcher3.popup.SystemShortcut.INSTALL;
-import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
+import static com.android.launcher3.testing.TestProtocol.CRASH_ADD_CUSTOM_SHORTCUT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -61,7 +58,6 @@
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
-import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Parcelable;
import android.os.Process;
@@ -86,8 +82,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore;
@@ -102,7 +96,6 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.folder.FolderNameProvider;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.CustomActionsPopup;
@@ -118,11 +111,11 @@
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
-import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.states.RotationHelper;
-import com.android.launcher3.touch.AllAppsSwipeController;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -142,7 +135,6 @@
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.ViewOnDrawExecutor;
@@ -174,7 +166,6 @@
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;
-import java.util.stream.Stream;
/**
* Default launcher application.
@@ -276,10 +267,6 @@
private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
- // Used to notify when an activity launch has been deferred because launcher is not yet resumed
- // TODO: See if we can remove this later
- private Runnable mOnDeferredActivityLaunchCallback;
-
private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
@@ -303,11 +290,12 @@
*/
private PendingRequestArgs mPendingRequestArgs;
// Request id for any pending activity result
- protected int mPendingActivityRequestCode = -1;
+ private int mPendingActivityRequestCode = -1;
public ViewGroupFocusHelper mFocusHandler;
private RotationHelper mRotationHelper;
+ private Runnable mCancelTouchController;
final Handler mHandler = new Handler();
private final Runnable mHandleDeferredResume = this::handleDeferredResume;
@@ -358,6 +346,7 @@
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new LauncherStateManager(this);
+ UiFactory.onCreate(this);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this,
@@ -481,6 +470,7 @@
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
+ UiFactory.onEnterAnimationComplete(this);
mAllAppsController.highlightWorkTabIfNecessary();
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
}
@@ -494,6 +484,7 @@
}
mOldConfig.setTo(newConfig);
+ UiFactory.onLauncherStateOrResumeChanged(this);
super.onConfigurationChanged(newConfig);
}
@@ -509,7 +500,7 @@
public void reapplyUi() {
if (supportsFakeLandscapeUI()) {
mRotationMode = mStableDeviceProfile == null
- ? RotationMode.NORMAL : getFakeRotationMode(mDeviceProfile);
+ ? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile);
}
getRootView().dispatchInsets();
getStateManager().reapplyState(true /* cancelCurrentAnimation */);
@@ -572,7 +563,7 @@
if (supportsFakeLandscapeUI() && mDeviceProfile.isVerticalBarLayout()) {
mStableDeviceProfile = mDeviceProfile.inv.portraitProfile;
- mRotationMode = getFakeRotationMode(mDeviceProfile);
+ mRotationMode = UiFactory.getRotationMode(mDeviceProfile);
} else {
mStableDeviceProfile = null;
mRotationMode = RotationMode.NORMAL;
@@ -615,10 +606,6 @@
return mStateManager;
}
- public FolderNameProvider getFolderNameProvider() {
- return new FolderNameProvider();
- }
-
@Override
public <T extends View> T findViewById(int id) {
return mLauncherView.findViewById(id);
@@ -942,6 +929,8 @@
NotificationListener.removeNotificationsChangedListener();
getStateManager().moveToRestState();
+ UiFactory.onLauncherStateOrResumeChanged(this);
+
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
}
@@ -964,6 +953,7 @@
logStopAndResume(Action.Command.RESUME);
getUserEventDispatcher().startSession();
+ UiFactory.onLauncherStateOrResumeChanged(this);
AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
// Process any items that were added while Launcher was away.
@@ -978,17 +968,15 @@
DiscoveryBounce.showForHomeIfNeeded(this);
- onDeferredResumed();
- addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
-
+ if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
+ UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode);
+ }
mDeferredResumePending = false;
} else {
mDeferredResumePending = true;
}
}
- protected void onDeferredResumed() { }
-
private void logStopAndResume(int command) {
int containerType = mStateManager.getState().containerType;
if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
@@ -1042,14 +1030,12 @@
if (mDeferOverlayCallbacks) {
scheduleDeferredCheck();
}
- addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
}
public void onStateSetEnd(LauncherState state) {
getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
getWorkspace().setClipChildren(!state.disablePageClipping);
finishAutoCancelActionMode();
- removeActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
}
@Override
@@ -1094,6 +1080,18 @@
}
}
+ @Override
+ protected void onUserLeaveHint() {
+ super.onUserLeaveHint();
+ UiFactory.onLauncherStateOrResumeChanged(this);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ mStateManager.onWindowFocusChanged();
+ }
+
class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
public void onScrollChanged(float progress) {
@@ -1157,6 +1155,7 @@
// Setup the drag layer
mDragLayer.setup(mDragController, mWorkspace);
+ mCancelTouchController = UiFactory.enableLiveUIChanges(this);
mWorkspace.setup(mDragController);
// Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
@@ -1534,6 +1533,11 @@
mWorkspace.removeFolderListeners();
PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this);
+ if (mCancelTouchController != null) {
+ mCancelTouchController.run();
+ mCancelTouchController = null;
+ }
+
// Stop callbacks from LauncherModel
// It's possible to receive onDestroy after a new Launcher activity has
// been created. In this case, don't interfere with the new Launcher.
@@ -1569,7 +1573,10 @@
if (requestCode != -1) {
mPendingActivityRequestCode = requestCode;
}
- super.startActivityForResult(intent, requestCode, options);
+ if (requestCode == -1
+ || !UiFactory.startActivityForResult(this, intent, requestCode, options)) {
+ super.startActivityForResult(intent, requestCode, options);
+ }
}
@Override
@@ -1578,11 +1585,14 @@
if (requestCode != -1) {
mPendingActivityRequestCode = requestCode;
}
- try {
- super.startIntentSenderForResult(intent, requestCode,
- fillInIntent, flagsMask, flagsValues, extraFlags, options);
- } catch (IntentSender.SendIntentException e) {
- throw new ActivityNotFoundException();
+ if (requestCode == -1 || !UiFactory.startIntentSenderForResult(this, intent, requestCode,
+ fillInIntent, flagsMask, flagsValues, extraFlags, options)) {
+ try {
+ super.startIntentSenderForResult(intent, requestCode,
+ fillInIntent, flagsMask, flagsValues, extraFlags, options);
+ } catch (IntentSender.SendIntentException e) {
+ throw new ActivityNotFoundException();
+ }
}
}
@@ -1876,10 +1886,7 @@
// recents animation into launcher. Defer launching the activity until Launcher is
// next resumed.
addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
- if (mOnDeferredActivityLaunchCallback != null) {
- mOnDeferredActivityLaunchCallback.run();
- mOnDeferredActivityLaunchCallback = null;
- }
+ UiFactory.clearSwipeSharedState(this, true /* finishAnimation */);
return true;
}
@@ -1920,6 +1927,7 @@
// This clears all widget bitmaps from the widget tray
// TODO(hyunyoungs)
}
+ UiFactory.onTrimMemory(this, level);
}
@Override
@@ -1940,14 +1948,6 @@
}
/**
- * Persistant callback which notifies when an activity launch is deferred because the activity
- * was not yet resumed.
- */
- public void setOnDeferredActivityLaunchCallback(Runnable callback) {
- mOnDeferredActivityLaunchCallback = callback;
- }
-
- /**
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
@@ -2638,40 +2638,16 @@
return super.onKeyUp(keyCode, event);
}
- protected StateHandler[] createStateHandlers() {
- return new StateHandler[] { getAllAppsController(), getWorkspace() };
+ public static Launcher getLauncher(Context context) {
+ return (Launcher) fromContext(context);
}
- public TouchController[] createTouchControllers() {
- return new TouchController[] {getDragController(), new AllAppsSwipeController(this)};
- }
-
- protected RotationMode getFakeRotationMode(DeviceProfile deviceProfile) {
- return RotationMode.NORMAL;
- }
-
- protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
- return new ScaleAndTranslation(1.1f, 0f, 0f);
- }
-
- public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { }
-
- public void onDragLayerHierarchyChanged() { }
-
@Override
public void returnToHomescreen() {
super.returnToHomescreen();
getStateManager().goToState(LauncherState.NORMAL);
}
- public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
- return Stream.of(APP_INFO, WIDGETS, INSTALL, DISMISS_PREDICTION);
- }
-
- public static Launcher getLauncher(Context context) {
- return (Launcher) fromContext(context);
- }
-
/**
* Just a wrapper around the type cast to allow easier tracking of calls.
*/
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 79f4821..c717d1a 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -30,14 +30,12 @@
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.InstallSessionTracker;
import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SecureSettingsObserver;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.widget.custom.CustomWidgetManager;
@@ -59,7 +57,6 @@
private final InstallSessionTracker mInstallSessionTracker;
private final SimpleBroadcastReceiver mModelChangeReceiver;
- private final SafeCloseable mCalendarChangeTracker;
public static LauncherAppState getInstance(final Context context) {
return INSTANCE.get(context);
@@ -95,10 +92,6 @@
if (FeatureFlags.IS_DOGFOOD_BUILD) {
mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
}
-
- mCalendarChangeTracker = IconProvider.registerIconChangeListener(mContext,
- mModel::onAppIconChanged, MODEL_EXECUTOR.getHandler());
-
// TODO: remove listener on terminate
FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
CustomWidgetManager.INSTANCE.get(mContext)
@@ -150,7 +143,6 @@
mContext.unregisterReceiver(mModelChangeReceiver);
mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
mInstallSessionTracker.unregister();
- mCalendarChangeTracker.close();
CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
if (mNotificationDotsObserver != null) {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5ff5b04..fc2e953 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
@@ -32,7 +31,6 @@
import android.util.Pair;
import androidx.annotation.Nullable;
-import androidx.annotation.WorkerThread;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -55,6 +53,7 @@
import com.android.launcher3.pm.InstallSessionTracker;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageUserKey;
@@ -94,6 +93,10 @@
private boolean mModelLoaded;
public boolean isModelLoaded() {
synchronized (mLock) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "isModelLoaded: " + mModelLoaded + ", " + mLoaderTask);
+ }
return mModelLoaded && mLoaderTask == null;
}
}
@@ -211,21 +214,9 @@
enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
}
- /**
- * Called when the icon for an app changes, outside of package event
- */
- @WorkerThread
- public void onAppIconChanged(String packageName, UserHandle user) {
- // Update the icon for the calendar package
- Context context = mApp.getContext();
- onPackageChanged(packageName, user);
-
- List<ShortcutInfo> pinnedShortcuts = DeepShortcutManager.getInstance(context)
- .queryForPinnedShortcuts(packageName, user);
- if (!pinnedShortcuts.isEmpty()) {
- enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, pinnedShortcuts, user,
- false));
- }
+ public void updatePinnedShortcuts(String packageName, List<ShortcutInfo> shortcuts,
+ UserHandle user) {
+ enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
}
public void onBroadcastIntent(Intent intent) {
@@ -534,7 +525,7 @@
updateAndBindWorkspaceItem(() -> {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
- si.bitmap = li.createShortcutIcon(info);
+ si.applyFrom(li.createShortcutIcon(info));
li.recycle();
return si;
});
@@ -570,8 +561,7 @@
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
for (AppInfo info : mBgAllAppsList.data) {
- writer.println(prefix + " title=\"" + info.title
- + "\" bitmapIcon=" + info.bitmap.icon
+ writer.println(prefix + " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
+ " componentName=" + info.componentName.getPackageName());
}
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index ec307db..c509680 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -127,7 +127,6 @@
public static final int CONTAINER_DESKTOP = -100;
public static final int CONTAINER_HOTSEAT = -101;
public static final int CONTAINER_PREDICTION = -102;
- public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
static final String containerToString(int container) {
switch (container) {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index d2b447b..6e2626b 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -26,11 +26,9 @@
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -38,11 +36,14 @@
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.view.animation.Interpolator;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.states.SpringLoadedState;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.uioverrides.states.AllAppsState;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -209,7 +210,7 @@
}
public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
- return launcher.getOverviewScaleAndTranslationForNormalState();
+ return UiFactory.getOverviewScaleAndTranslationForNormalState(launcher);
}
public float getOverviewFullscreenProgress() {
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index daf270b..848e19f 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -24,8 +24,7 @@
import android.animation.AnimatorSet;
import android.os.Handler;
import android.os.Looper;
-
-import androidx.annotation.IntDef;
+import android.util.Log;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -33,12 +32,16 @@
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.uioverrides.UiFactory;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import androidx.annotation.IntDef;
+
/**
* TODO: figure out what kind of tests we can write for this
*
@@ -143,7 +146,7 @@
public StateHandler[] getStateHandlers() {
if (mStateHandlers == null) {
- mStateHandlers = mLauncher.createStateHandlers();
+ mStateHandlers = UiFactory.getStateHandler(mLauncher);
}
return mStateHandlers;
}
@@ -411,6 +414,7 @@
// Only disable clipping if needed, otherwise leave it as previous value.
mLauncher.getWorkspace().setClipChildren(false);
}
+ UiFactory.onLauncherStateOrResumeChanged(mLauncher);
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onStateTransitionStart(state);
@@ -431,6 +435,8 @@
setRestState(null);
}
+ UiFactory.onLauncherStateOrResumeChanged(mLauncher);
+
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onStateTransitionComplete(state);
}
@@ -438,6 +444,10 @@
AccessibilityManagerCompat.sendStateEventToTest(mLauncher, state.ordinal);
}
+ public void onWindowFocusChanged() {
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+ }
+
public LauncherState getLastState() {
return mLastStableState;
}
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index e0c50e2..8dedc6c 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -71,13 +71,8 @@
SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
- if (!PackageInstaller.ACTION_SESSION_COMMITTED.equals(intent.getAction())
- || info == null || user == null) {
- // Invalid intent.
- return;
- }
-
PackageInstallerCompat packageInstallerCompat = PackageInstallerCompat.getInstance(context);
+
if (TextUtils.isEmpty(info.getAppPackageName())
|| info.getInstallReason() != PackageManager.INSTALL_REASON_USER
|| packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 2bec0ba..92f8069 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -63,7 +63,6 @@
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.TintedDrawableSpan;
-import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -519,20 +518,19 @@
}
/**
- * Returns the full drawable for info without any flattening or pre-processing.
- *
+ * Returns the full drawable for {@param info}.
* @param outObj this is set to the internal data associated with {@param info},
* eg {@link LauncherActivityInfo} or {@link ShortcutInfo}.
*/
public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
- Object[] outObj) {
+ boolean flattenDrawable, Object[] outObj) {
LauncherAppState appState = LauncherAppState.getInstance(launcher);
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
LauncherActivityInfo activityInfo = launcher.getSystemService(LauncherApps.class)
.resolveActivity(info.getIntent(), info.user);
outObj[0] = activityInfo;
- return activityInfo == null ? null : new IconProvider(launcher).getIconForUI(
- activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi);
+ return (activityInfo != null) ? appState.getIconCache()
+ .getFullResIcon(activityInfo, flattenDrawable) : null;
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
if (info instanceof PendingAddShortcutInfo) {
ShortcutConfigActivityInfo activityInfo =
@@ -584,9 +582,9 @@
}
ShortcutInfo si = (ShortcutInfo) obj;
LauncherIcons li = LauncherIcons.obtain(appState.getContext());
- Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).bitmap.icon;
+ Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
li.recycle();
- float badgeSize = LauncherIcons.getBadgeSizeForIconSize(iconSize);
+ float badgeSize = iconSize * LauncherIcons.getBadgeSizeForIconSize(iconSize);
float insetFraction = (iconSize - badgeSize) / iconSize;
return new InsetDrawable(new FastBitmapDrawable(badge),
insetFraction, insetFraction, 0, 0);
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 37b58d3..c5e74ef 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -23,19 +23,16 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.os.Process;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pair;
import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserManagerCompat;
@@ -81,9 +78,6 @@
private final UserManagerCompat mUserManager;
private final CacheDb mDb;
- private final UserHandle mMyUser = Process.myUserHandle();
- private final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
-
public WidgetPreviewLoader(Context context, IconCache iconCache) {
mContext = context;
mIconCache = iconCache;
@@ -92,51 +86,6 @@
}
/**
- * Returns a drawable that can be used as a badge for the user or null.
- */
- @UiThread
- public Drawable getBadgeForUser(UserHandle user, int badgeSize) {
- if (mMyUser.equals(user)) {
- return null;
- }
-
- Bitmap badgeBitmap = getUserBadge(user, badgeSize);
- FastBitmapDrawable d = new FastBitmapDrawable(badgeBitmap);
- d.setFilterBitmap(true);
- d.setBounds(0, 0, badgeBitmap.getWidth(), badgeBitmap.getHeight());
- return d;
- }
-
- private Bitmap getUserBadge(UserHandle user, int badgeSize) {
- synchronized (mUserBadges) {
- Bitmap badgeBitmap = mUserBadges.get(user);
- if (badgeBitmap != null) {
- return badgeBitmap;
- }
-
- final Resources res = mContext.getResources();
- badgeBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
-
- Drawable drawable = mContext.getPackageManager().getUserBadgedDrawableForDensity(
- new BitmapDrawable(res, badgeBitmap), user,
- new Rect(0, 0, badgeSize, badgeSize),
- 0);
- if (drawable instanceof BitmapDrawable) {
- badgeBitmap = ((BitmapDrawable) drawable).getBitmap();
- } else {
- badgeBitmap.eraseColor(Color.TRANSPARENT);
- Canvas c = new Canvas(badgeBitmap);
- drawable.setBounds(0, 0, badgeSize, badgeSize);
- drawable.draw(c);
- c.setBitmap(null);
- }
-
- mUserBadges.put(user, badgeBitmap);
- return badgeBitmap;
- }
- }
-
- /**
* Generates the widget preview on {@link AsyncTask#THREAD_POOL_EXECUTOR}. Must be
* called on UI thread
*
@@ -157,8 +106,8 @@
public void refresh() {
mDb.clear();
- }
+ }
/**
* The DB holds the generated previews for various components. Previews can also have different
* sizes (landscape vs portrait).
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 431a149..eca5d12 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1471,6 +1471,9 @@
public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
DragPreviewProvider previewProvider, DragOptions dragOptions) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_CONTEXT_MENU, "beginDragShared");
+ }
float iconScale = 1f;
if (child instanceof BubbleTextView) {
Drawable icon = ((BubbleTextView) child).getIcon();
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index be907e5..23795c5 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -28,7 +28,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.ContentWriter;
import java.util.Arrays;
@@ -140,7 +140,7 @@
.put(Favorites.RESTORED, status);
if (!usingLowResIcon()) {
- writer.putIcon(bitmap, user);
+ writer.putIcon(iconBitmap, user);
}
if (iconResource != null) {
writer.put(Favorites.ICON_PACKAGE, iconResource.packageName)
@@ -192,7 +192,7 @@
}
disabledMessage = shortcutInfo.getDisabledMessage();
- Person[] persons = ApiWrapper.getPersons(shortcutInfo);
+ Person[] persons = UiFactory.getPersons(shortcutInfo);
personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
: Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
}
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index 0b9d602..ea2d4d0 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -39,8 +39,7 @@
default void addInScreenFromBind(View child, ItemInfo info) {
int x = info.cellX;
int y = info.cellY;
- if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
- || info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
int screenId = info.screenId;
x = getHotseat().getCellXFromOrder(screenId);
y = getHotseat().getCellYFromOrder(screenId);
@@ -84,8 +83,7 @@
}
final CellLayout layout;
- if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
- || container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
layout = getHotseat();
// Hide folder title in the hotseat
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 7a7e1fe..40c6b5f 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -38,6 +38,7 @@
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
/**
@@ -95,13 +96,14 @@
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
if (!hotseat.getRotationMode().isTransposed) {
- // Set the hotseat's pivot point to match the workspace's, so that it scales
- // together. Since both hotseat and workspace can move, transform the point
- // manually instead of using dragLayer.getDescendantCoordRelativeToSelf and
- // related methods.
- hotseat.setPivotY(mWorkspace.getPivotY() + mWorkspace.getTop() - hotseat.getTop());
- hotseat.setPivotX(mWorkspace.getPivotX()
- + mWorkspace.getLeft() - hotseat.getLeft());
+ // Set the hotseat's pivot point to match the workspace's, so that it scales together.
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ float[] workspacePivot =
+ new float[]{ mWorkspace.getPivotX(), mWorkspace.getPivotY() };
+ dragLayer.getDescendantCoordRelativeToSelf(mWorkspace, workspacePivot);
+ dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
+ hotseat.setPivotX(workspacePivot[0]);
+ hotseat.setPivotY(workspacePivot[1]);
}
float hotseatScale = hotseatScaleAndTranslation.scale;
Interpolator hotseatScaleInterpolator = builder.getInterpolator(ANIM_HOTSEAT_SCALE,
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 08ce9c2..3836c9f 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -2,6 +2,8 @@
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
@@ -132,6 +134,15 @@
} else {
mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
}
+
+ if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+ // Translate hotseat with the shelf until reaching overview.
+ float overviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
+ if (progress >= overviewProgress || mLauncher.isInState(BACKGROUND_APP)) {
+ float hotseatShift = (progress - overviewProgress) * mShiftRange;
+ mLauncher.getHotseat().setTranslationY(hotseatShift);
+ }
+ }
}
public float getProgress() {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index f4b705e..4abdbef 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -110,10 +110,6 @@
"FAKE_LANDSCAPE_UI", false,
"Rotate launcher UI instead of using transposed layout");
- public static final TogglableFlag FOLDER_NAME_SUGGEST = new TogglableFlag(
- "FOLDER_NAME_SUGGEST", true,
- "Suggests folder names instead of blank text.");
-
public static final TogglableFlag APP_SEARCH_IMPROVEMENTS = new TogglableFlag(
"APP_SEARCH_IMPROVEMENTS", false,
"Adds localized title and keyword search and ranking");
@@ -124,16 +120,6 @@
public static final TogglableFlag ENABLE_QUICK_CAPTURE_GESTURE = new TogglableFlag(
"ENABLE_QUICK_CAPTURE_GESTURE", false, "Swipe from right to left to quick capture");
- public static final TogglableFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = new TogglableFlag(
- "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
- "Allow Launcher to handle nav bar gestures while Assistant is running over it");
-
- public static final TogglableFlag ENABLE_HYBRID_HOTSEAT = new TogglableFlag(
- "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
-
- public static final TogglableFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = new TogglableFlag(
- "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
-
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 8823bde..cdc7061 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -57,6 +57,7 @@
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.Transposable;
@@ -120,7 +121,7 @@
}
public void recreateControllers() {
- mControllers = mActivity.createTouchControllers();
+ mControllers = UiFactory.createTouchControllers(mActivity);
}
public ViewGroupFocusHelper getFocusIndicatorHelper() {
@@ -476,14 +477,14 @@
public void onViewAdded(View child) {
super.onViewAdded(child);
updateChildIndices();
- mActivity.onDragLayerHierarchyChanged();
+ UiFactory.onLauncherStateOrFocusChanged(mActivity);
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
updateChildIndices();
- mActivity.onDragLayerHierarchyChanged();
+ UiFactory.onLauncherStateOrFocusChanged(mActivity);
}
@Override
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 145885a..f66d07e 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -216,7 +216,8 @@
Object[] outObj = new Object[1];
int w = mBitmap.getWidth();
int h = mBitmap.getHeight();
- Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h, outObj);
+ Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h,
+ false /* flattenDrawable */, outObj);
if (dr instanceof AdaptiveIconDrawable) {
int blurMargin = (int) mLauncher.getResources()
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 869dd94..07eb0d6 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -32,6 +32,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -67,7 +68,7 @@
public boolean init(Launcher launcher, boolean alreadyOnHome) {
super.init(launcher, alreadyOnHome);
if (!alreadyOnHome) {
- launcher.useFadeOutAnimationForLauncherStart(mCancelSignal);
+ UiFactory.useFadeOutAnimationForLauncherStart(launcher, mCancelSignal);
}
return false;
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 22dda41..65d593c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -401,6 +401,7 @@
mFolderName.setText("");
mFolderName.setHint(R.string.folder_hint_text);
}
+
// In case any children didn't come across during loading, clean up the folder accordingly
mFolderIcon.post(() -> {
if (getItemCount() <= 1) {
@@ -409,22 +410,6 @@
});
}
-
- /**
- * Show suggested folder title.
- */
- public void showSuggestedTitle(CharSequence suggestName) {
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && mInfo.contents.size() == 2) {
- if (!TextUtils.isEmpty(suggestName)) {
- mFolderName.setHint(suggestName);
- mFolderName.setText(suggestName);
- mFolderName.showKeyboard();
- mInfo.title = suggestName;
- }
- animateOpen();
- }
- }
-
/**
* Creates a new UserFolder, inflated from R.layout.user_folder.
*
@@ -547,6 +532,8 @@
// dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
mDeleteFolderOnDropCompleted = false;
+ centerAboutIcon();
+
AnimatorSet anim = new FolderAnimationManager(this, true /* isOpening */).getAnimator();
anim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -605,6 +592,7 @@
if (mDragController.isDragging()) {
mDragController.forceTouchMove();
}
+
mContent.verifyVisibleHighResIcons(mContent.getNextPage());
}
@@ -889,6 +877,7 @@
// Reordering may have occured, and we need to save the new item locations. We do this once
// at the end to prevent unnecessary database operations.
updateItemLocationsInDatabaseBatch();
+
// Use the item count to check for multi-page as the folder UI may not have
// been refreshed yet.
if (getItemCount() <= mContent.itemsPerPage()) {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index fd6d1e3..3840639 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -58,14 +58,12 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.IconLabelDotView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -370,17 +368,12 @@
if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
final int finalIndex = index;
-
- String[] suggestedNameOut = new String[1];
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- Executors.UI_HELPER_EXECUTOR.post(() -> mLauncher.getFolderNameProvider()
- .getSuggestedFolderName(getContext(), mInfo.contents, suggestedNameOut));
- }
- postDelayed(() -> {
- mPreviewItemManager.hidePreviewItem(finalIndex, false);
- mFolder.showItem(item);
- invalidate();
- mFolder.showSuggestedTitle(suggestedNameOut[0]);
+ postDelayed(new Runnable() {
+ public void run() {
+ mPreviewItemManager.hidePreviewItem(finalIndex, false);
+ mFolder.showItem(item);
+ invalidate();
+ }
}, DROP_IN_ANIMATION_DURATION);
} else {
addItem(item);
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
deleted file mode 100644
index 0a1221e..0000000
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.folder;
-
-import android.content.ComponentName;
-import android.content.Context;
-
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.WorkspaceItemInfo;
-
-import java.util.ArrayList;
-
-/**
- * Locates provider for the folder name.
- */
-public class FolderNameProvider {
-
- /**
- * Returns suggested folder name.
- */
- public CharSequence getSuggestedFolderName(Context context,
- ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] suggestName) {
- // Currently only run the algorithm on initial folder creation.
- // For more than 2 items in the folder, the ranking algorithm for finding
- // candidate folder name should be rewritten.
- if (workspaceItemInfos.size() == 2) {
- ComponentName cmp1 = workspaceItemInfos.get(0).getTargetComponent();
- ComponentName cmp2 = workspaceItemInfos.get(1).getTargetComponent();
-
- String pkgName0 = cmp1 == null ? "" : cmp1.getPackageName();
- String pkgName1 = cmp2 == null ? "" : cmp2.getPackageName();
- // If the two icons are from the same package,
- // then assign the main icon's name
- if (pkgName0.equals(pkgName1)) {
- WorkspaceItemInfo wInfo0 = workspaceItemInfos.get(0);
- WorkspaceItemInfo wInfo1 = workspaceItemInfos.get(1);
- if (workspaceItemInfos.get(0).itemType == Favorites.ITEM_TYPE_APPLICATION) {
- suggestName[0] = wInfo0.title;
- } else if (wInfo1.itemType == Favorites.ITEM_TYPE_APPLICATION) {
- suggestName[0] = wInfo1.title;
- }
- return suggestName[0];
- // two icons are all shortcuts. Don't assign title
- }
- }
- return suggestName[0];
- }
-}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 3b5fd59..54b363e 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -277,7 +277,6 @@
page.removeAllViews();
pages.add(page);
}
- mOrganizer.setFolderInfo(mFolder.getInfo());
setupContentDimensions(itemCount);
Iterator<CellLayout> pageItr = pages.iterator();
@@ -286,6 +285,7 @@
int position = 0;
int rank = 0;
+ mOrganizer.setFolderInfo(mFolder.getInfo());
for (int i = 0; i < itemCount; i++) {
View v = list.size() > i ? list.get(i) : null;
if (currentPage == null || position >= mOrganizer.getMaxItemsPerPage()) {
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 5b3a05e..2d817e6 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -16,12 +16,10 @@
package com.android.launcher3.folder;
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ENTER_INDEX;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.EXIT_INDEX;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.folder.FolderIcon.DROP_IN_ANIMATION_DURATION;
-import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -40,6 +38,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.PreloadIconDrawable;
import java.util.ArrayList;
@@ -67,6 +66,7 @@
private final Context mContext;
private final FolderIcon mIcon;
+ private final DrawableFactory mDrawableFactory;
private final int mIconSize;
// These variables are all associated with the drawing of the preview; they are stored
@@ -94,6 +94,7 @@
public PreviewItemManager(FolderIcon icon) {
mContext = icon.getContext();
mIcon = icon;
+ mDrawableFactory = DrawableFactory.INSTANCE.get(mContext);
mIconSize = Launcher.getLauncher(mContext).getDeviceProfile().folderChildIconSizePx;
}
@@ -394,11 +395,11 @@
private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
if (item.hasPromiseIconUi()) {
- PreloadIconDrawable drawable = newPendingIcon(mContext, item);
+ PreloadIconDrawable drawable = mDrawableFactory.newPendingIcon(mContext, item);
drawable.setLevel(item.getInstallProgress());
p.drawable = drawable;
} else {
- p.drawable = newIcon(mContext, item);
+ p.drawable = mDrawableFactory.newIcon(mContext, item);
}
p.drawable.setBounds(0, 0, mIconSize, mIconSize);
p.item = item;
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
new file mode 100644
index 0000000..837301f
--- /dev/null
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 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.graphics;
+
+import static com.android.launcher3.graphics.IconShape.getShapePath;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+/**
+ * Factory for creating new drawables.
+ */
+public class DrawableFactory implements ResourceBasedOverride {
+
+ public static final MainThreadInitializedObject<DrawableFactory> INSTANCE =
+ forOverride(DrawableFactory.class, R.string.drawable_factory_class);
+
+ protected final UserHandle mMyUser = Process.myUserHandle();
+ protected final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
+
+ /**
+ * Returns a FastBitmapDrawable with the icon.
+ */
+ public FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
+ FastBitmapDrawable drawable = info.usingLowResIcon()
+ ? new PlaceHolderIconDrawable(info, getShapePath(), context)
+ : new FastBitmapDrawable(info);
+ drawable.setIsDisabled(info.isDisabled());
+ return drawable;
+ }
+
+ public FastBitmapDrawable newIcon(Context context, BitmapInfo info, ActivityInfo target) {
+ return info.isLowRes()
+ ? new PlaceHolderIconDrawable(info, getShapePath(), context)
+ : new FastBitmapDrawable(info);
+ }
+
+ /**
+ * Returns a FastBitmapDrawable with the icon.
+ */
+ public PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
+ return new PreloadIconDrawable(info, getShapePath(), context);
+ }
+
+ /**
+ * Returns a drawable that can be used as a badge for the user or null.
+ */
+ @UiThread
+ public Drawable getBadgeForUser(UserHandle user, Context context, int badgeSize) {
+ if (mMyUser.equals(user)) {
+ return null;
+ }
+
+ Bitmap badgeBitmap = getUserBadge(user, context, badgeSize);
+ FastBitmapDrawable d = new FastBitmapDrawable(badgeBitmap);
+ d.setFilterBitmap(true);
+ d.setBounds(0, 0, badgeBitmap.getWidth(), badgeBitmap.getHeight());
+ return d;
+ }
+
+ protected synchronized Bitmap getUserBadge(UserHandle user, Context context, int badgeSize) {
+ Bitmap badgeBitmap = mUserBadges.get(user);
+ if (badgeBitmap != null) {
+ return badgeBitmap;
+ }
+
+ final Resources res = context.getApplicationContext().getResources();
+ badgeBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
+
+ Drawable drawable = context.getPackageManager().getUserBadgedDrawableForDensity(
+ new BitmapDrawable(res, badgeBitmap), user, new Rect(0, 0, badgeSize, badgeSize),
+ 0);
+ if (drawable instanceof BitmapDrawable) {
+ badgeBitmap = ((BitmapDrawable) drawable).getBitmap();
+ } else {
+ badgeBitmap.eraseColor(Color.TRANSPARENT);
+ Canvas c = new Canvas(badgeBitmap);
+ drawable.setBounds(0, 0, badgeSize, badgeSize);
+ drawable.draw(c);
+ c.setBitmap(null);
+ }
+
+ mUserBadges.put(user, badgeBitmap);
+ return badgeBitmap;
+ }
+}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 2badb6e..d7b845b 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -50,8 +50,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.config.FeatureFlags;
@@ -105,7 +105,7 @@
Build.VERSION.SDK_INT);
mWorkspaceItemInfo = new WorkspaceItemInfo();
- mWorkspaceItemInfo.bitmap = iconInfo;
+ mWorkspaceItemInfo.applyFrom(iconInfo);
mWorkspaceItemInfo.intent = new Intent();
mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
context.getString(R.string.label_application);
diff --git a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
index d347e8f..23745cb 100644
--- a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
@@ -17,14 +17,14 @@
import static androidx.core.graphics.ColorUtils.compositeColors;
-import static com.android.launcher3.graphics.IconShape.getShapePath;
-
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.util.Themes;
@@ -37,12 +37,20 @@
// Path in [0, 100] bounds.
private final Path mProgressPath;
- public PlaceHolderIconDrawable(BitmapInfo info, Context context) {
- super(info);
+ public PlaceHolderIconDrawable(BitmapInfo info, Path progressPath, Context context) {
+ this(info.icon, info.color, progressPath, context);
+ }
- mProgressPath = getShapePath();
+ public PlaceHolderIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
+ this(info.iconBitmap, info.iconColor, progressPath, context);
+ }
+
+ protected PlaceHolderIconDrawable(Bitmap b, int iconColor, Path progressPath, Context context) {
+ super(b, iconColor);
+
+ mProgressPath = progressPath;
mPaint.setColor(compositeColors(
- Themes.getAttrColor(context, R.attr.loadingIconColor), info.color));
+ Themes.getAttrColor(context, R.attr.loadingIconColor), iconColor));
}
@Override
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index b0e1db1..cc4c2ef 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -18,7 +18,6 @@
package com.android.launcher3.graphics;
import static com.android.launcher3.graphics.IconShape.DEFAULT_PATH_SIZE;
-import static com.android.launcher3.graphics.IconShape.getShapePath;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -102,10 +101,13 @@
private ObjectAnimator mCurrentAnim;
- public PreloadIconDrawable(ItemInfoWithIcon info, Context context) {
- super(info.bitmap);
+ /**
+ * @param progressPath fixed path in the bounds [0, 0, 100, 100] representing a progress bar.
+ */
+ public PreloadIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
+ super(info);
mItem = info;
- mProgressPath = getShapePath();
+ mProgressPath = progressPath;
mScaledTrackPath = new Path();
mScaledProgressPath = new Path();
@@ -287,11 +289,4 @@
}
invalidateSelf();
}
-
- /**
- * Returns a FastBitmapDrawable with the icon.
- */
- public static PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
- return new PreloadIconDrawable(info, context);
- }
}
diff --git a/src/com/android/launcher3/icons/ClockDrawableWrapper.java b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
deleted file mode 100644
index b7dd092..0000000
--- a/src/com/android/launcher3/icons/ClockDrawableWrapper.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.icons;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.launcher3.FastBitmapDrawable;
-
-import java.util.Calendar;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Wrapper over {@link AdaptiveIconDrawable} to intercept icon flattening logic for dynamic
- * clock icons
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class ClockDrawableWrapper extends AdaptiveIconDrawable implements BitmapInfo.Extender {
-
- private static final String TAG = "ClockDrawableWrapper";
-
- private static final boolean DISABLE_SECONDS = true;
-
- // Time after which the clock icon should check for an update. The actual invalidate
- // will only happen in case of any change.
- public static final long TICK_MS = DISABLE_SECONDS ? TimeUnit.MINUTES.toMillis(1) : 200L;
-
- private static final String LAUNCHER_PACKAGE = "com.android.launcher3";
- private static final String ROUND_ICON_METADATA_KEY = LAUNCHER_PACKAGE
- + ".LEVEL_PER_TICK_ICON_ROUND";
- private static final String HOUR_INDEX_METADATA_KEY = LAUNCHER_PACKAGE + ".HOUR_LAYER_INDEX";
- private static final String MINUTE_INDEX_METADATA_KEY = LAUNCHER_PACKAGE
- + ".MINUTE_LAYER_INDEX";
- private static final String SECOND_INDEX_METADATA_KEY = LAUNCHER_PACKAGE
- + ".SECOND_LAYER_INDEX";
- private static final String DEFAULT_HOUR_METADATA_KEY = LAUNCHER_PACKAGE
- + ".DEFAULT_HOUR";
- private static final String DEFAULT_MINUTE_METADATA_KEY = LAUNCHER_PACKAGE
- + ".DEFAULT_MINUTE";
- private static final String DEFAULT_SECOND_METADATA_KEY = LAUNCHER_PACKAGE
- + ".DEFAULT_SECOND";
-
- /* Number of levels to jump per second for the second hand */
- private static final int LEVELS_PER_SECOND = 10;
-
- public static final int INVALID_VALUE = -1;
-
- private final AnimationInfo mAnimationInfo = new AnimationInfo();
- private int mTargetSdkVersion;
-
- public ClockDrawableWrapper(AdaptiveIconDrawable base) {
- super(base.getBackground(), base.getForeground());
- }
-
- /**
- * Loads and returns the wrapper from the provided package, or returns null
- * if it is unable to load.
- */
- public static ClockDrawableWrapper forPackage(Context context, String pkg, int iconDpi) {
- try {
- PackageManager pm = context.getPackageManager();
- ApplicationInfo appInfo = pm.getApplicationInfo(pkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
- final Bundle metadata = appInfo.metaData;
- if (metadata == null) {
- return null;
- }
- int drawableId = metadata.getInt(ROUND_ICON_METADATA_KEY, 0);
- if (drawableId == 0) {
- return null;
- }
-
- Drawable drawable = pm.getResourcesForApplication(appInfo).getDrawableForDensity(
- drawableId, iconDpi).mutate();
- if (!(drawable instanceof AdaptiveIconDrawable)) {
- return null;
- }
-
- ClockDrawableWrapper wrapper =
- new ClockDrawableWrapper((AdaptiveIconDrawable) drawable);
- wrapper.mTargetSdkVersion = appInfo.targetSdkVersion;
- AnimationInfo info = wrapper.mAnimationInfo;
-
- info.baseDrawableState = drawable.getConstantState();
-
- info.hourLayerIndex = metadata.getInt(HOUR_INDEX_METADATA_KEY, INVALID_VALUE);
- info.minuteLayerIndex = metadata.getInt(MINUTE_INDEX_METADATA_KEY, INVALID_VALUE);
- info.secondLayerIndex = metadata.getInt(SECOND_INDEX_METADATA_KEY, INVALID_VALUE);
-
- info.defaultHour = metadata.getInt(DEFAULT_HOUR_METADATA_KEY, 0);
- info.defaultMinute = metadata.getInt(DEFAULT_MINUTE_METADATA_KEY, 0);
- info.defaultSecond = metadata.getInt(DEFAULT_SECOND_METADATA_KEY, 0);
-
- LayerDrawable foreground = (LayerDrawable) wrapper.getForeground();
- int layerCount = foreground.getNumberOfLayers();
- if (info.hourLayerIndex < 0 || info.hourLayerIndex >= layerCount) {
- info.hourLayerIndex = INVALID_VALUE;
- }
- if (info.minuteLayerIndex < 0 || info.minuteLayerIndex >= layerCount) {
- info.minuteLayerIndex = INVALID_VALUE;
- }
- if (info.secondLayerIndex < 0 || info.secondLayerIndex >= layerCount) {
- info.secondLayerIndex = INVALID_VALUE;
- } else if (DISABLE_SECONDS) {
- foreground.setDrawable(info.secondLayerIndex, null);
- info.secondLayerIndex = INVALID_VALUE;
- }
- return wrapper;
- } catch (Exception e) {
- Log.d(TAG, "Unable to load clock drawable info", e);
- }
- return null;
- }
-
- @Override
- public BitmapInfo getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory) {
- iconFactory.disableColorExtraction();
- float [] scale = new float[1];
- AdaptiveIconDrawable background = new AdaptiveIconDrawable(
- getBackground().getConstantState().newDrawable(), null);
- BitmapInfo bitmapInfo = iconFactory.createBadgedIconBitmap(background,
- Process.myUserHandle(), mTargetSdkVersion, false, scale);
-
- return new ClockBitmapInfo(bitmap, color, scale[0], mAnimationInfo, bitmapInfo.icon);
- }
-
- @Override
- public void prepareToDrawOnUi() {
- mAnimationInfo.applyTime(Calendar.getInstance(), (LayerDrawable) getForeground());
- }
-
- private static class AnimationInfo {
-
- public ConstantState baseDrawableState;
-
- public int hourLayerIndex;
- public int minuteLayerIndex;
- public int secondLayerIndex;
- public int defaultHour;
- public int defaultMinute;
- public int defaultSecond;
-
- boolean applyTime(Calendar time, LayerDrawable foregroundDrawable) {
- time.setTimeInMillis(System.currentTimeMillis());
-
- // We need to rotate by the difference from the default time if one is specified.
- int convertedHour = (time.get(Calendar.HOUR) + (12 - defaultHour)) % 12;
- int convertedMinute = (time.get(Calendar.MINUTE) + (60 - defaultMinute)) % 60;
- int convertedSecond = (time.get(Calendar.SECOND) + (60 - defaultSecond)) % 60;
-
- boolean invalidate = false;
- if (hourLayerIndex != INVALID_VALUE) {
- final Drawable hour = foregroundDrawable.getDrawable(hourLayerIndex);
- if (hour.setLevel(convertedHour * 60 + time.get(Calendar.MINUTE))) {
- invalidate = true;
- }
- }
-
- if (minuteLayerIndex != INVALID_VALUE) {
- final Drawable minute = foregroundDrawable.getDrawable(minuteLayerIndex);
- if (minute.setLevel(time.get(Calendar.HOUR) * 60 + convertedMinute)) {
- invalidate = true;
- }
- }
-
- if (secondLayerIndex != INVALID_VALUE) {
- final Drawable second = foregroundDrawable.getDrawable(secondLayerIndex);
- if (second.setLevel(convertedSecond * LEVELS_PER_SECOND)) {
- invalidate = true;
- }
- }
-
- return invalidate;
- }
- }
-
- private static class ClockBitmapInfo extends BitmapInfo implements FastBitmapDrawable.Factory {
-
- public final float scale;
- public final int offset;
- public final AnimationInfo animInfo;
- public final Bitmap mFlattenedBackground;
-
- ClockBitmapInfo(Bitmap icon, int color, float scale, AnimationInfo animInfo,
- Bitmap background) {
- super(icon, color);
- this.scale = scale;
- this.animInfo = animInfo;
- this.offset = (int) Math.ceil(ShadowGenerator.BLUR_FACTOR * icon.getWidth());
- this.mFlattenedBackground = background;
- }
-
- @Override
- public FastBitmapDrawable newDrawable() {
- return new ClockIconDrawable(this);
- }
- }
-
- private static class ClockIconDrawable extends FastBitmapDrawable implements Runnable {
-
- private final Calendar mTime = Calendar.getInstance();
-
- private final ClockBitmapInfo mInfo;
-
- private final AdaptiveIconDrawable mFullDrawable;
- private final LayerDrawable mForeground;
-
- ClockIconDrawable(ClockBitmapInfo clockInfo) {
- super(clockInfo);
-
- mInfo = clockInfo;
-
- mFullDrawable = (AdaptiveIconDrawable) mInfo.animInfo.baseDrawableState.newDrawable();
- mForeground = (LayerDrawable) mFullDrawable.getForeground();
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- super.onBoundsChange(bounds);
- mFullDrawable.setBounds(bounds);
- }
-
- @Override
- public void drawInternal(Canvas canvas, Rect bounds) {
- if (mInfo == null) {
- super.drawInternal(canvas, bounds);
- return;
- }
- // draw the background that is already flattened to a bitmap
- canvas.drawBitmap(mInfo.mFlattenedBackground, null, bounds, mPaint);
-
- // prepare and draw the foreground
- mInfo.animInfo.applyTime(mTime, mForeground);
-
- canvas.scale(mInfo.scale, mInfo.scale,
- bounds.exactCenterX() + mInfo.offset, bounds.exactCenterY() + mInfo.offset);
- canvas.clipPath(mFullDrawable.getIconMask());
- mForeground.draw(canvas);
-
- reschedule();
- }
-
- @Override
- protected void updateFilter() {
- super.updateFilter();
- mFullDrawable.setColorFilter(mPaint.getColorFilter());
- }
-
- @Override
- public void run() {
- if (mInfo.animInfo.applyTime(mTime, mForeground)) {
- invalidateSelf();
- } else {
- reschedule();
- }
- }
-
- @Override
- public boolean setVisible(boolean visible, boolean restart) {
- boolean result = super.setVisible(visible, restart);
- if (visible) {
- reschedule();
- } else {
- unscheduleSelf(this);
- }
- return result;
- }
-
- private void reschedule() {
- if (!isVisible()) {
- return;
- }
-
- unscheduleSelf(this);
- final long upTime = SystemClock.uptimeMillis();
- final long step = TICK_MS; /* tick every 200 ms */
- scheduleSelf(this, upTime - ((upTime % step)) + step);
- }
-
- @Override
- public ConstantState getConstantState() {
- return new ClockConstantState(mInfo, isDisabled());
- }
-
- private static class ClockConstantState extends MyConstantState {
-
- private final ClockBitmapInfo mInfo;
-
- ClockConstantState(ClockBitmapInfo info, boolean isDisabled) {
- super(info.icon, info.color, isDisabled);
- mInfo = info;
- }
-
- @Override
- public FastBitmapDrawable newDrawable() {
- ClockIconDrawable drawable = new ClockIconDrawable(mInfo);
- drawable.setIsDisabled(mIsDisabled);
- return drawable;
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/icons/ComponentWithLabel.java b/src/com/android/launcher3/icons/ComponentWithLabel.java
index f7ee5f9..832956d 100644
--- a/src/com/android/launcher3/icons/ComponentWithLabel.java
+++ b/src/com/android/launcher3/icons/ComponentWithLabel.java
@@ -57,8 +57,10 @@
}
@Override
- public BitmapInfo loadIcon(Context context, ComponentWithLabel object) {
- return BitmapInfo.LOW_RES_INFO;
+ public void loadIcon(Context context,
+ ComponentWithLabel object, BitmapInfo target) {
+ // Do not load icon.
+ target.icon = BitmapInfo.LOW_RES_ICON;
}
@Override
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 4ac6ff4..ad01f9f 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -38,6 +38,7 @@
import androidx.annotation.NonNull;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.IconProvider;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherFiles;
@@ -51,7 +52,6 @@
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
@@ -85,7 +85,7 @@
mLauncherApps = mContext.getSystemService(LauncherApps.class);
mUserManager = UserManagerCompat.getInstance(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
- mIconProvider = new IconProvider(context);
+ mIconProvider = IconProvider.INSTANCE.get(context);
}
@Override
@@ -165,7 +165,7 @@
CacheEntry entry = cacheLocked(application.componentName,
application.user, () -> null, mLauncherActivityInfoCachingLogic,
false, application.usingLowResIcon());
- if (entry.bitmap != null && !isDefaultIcon(entry.bitmap, application.user)) {
+ if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
applyCacheEntry(entry, application);
}
}
@@ -195,7 +195,7 @@
// null info means not installed, but if we have a component from the intent then
// we should still look in the cache for restored app icons.
if (info.getTargetComponent() == null) {
- info.bitmap = getDefaultIcon(info.user);
+ info.applyFrom(getDefaultIcon(info.user));
info.title = "";
info.contentDescription = "";
} else {
@@ -238,11 +238,15 @@
protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
info.title = Utilities.trim(entry.title);
info.contentDescription = entry.contentDescription;
- info.bitmap = (entry.bitmap == null) ? getDefaultIcon(info.user) : entry.bitmap;
+ info.applyFrom((entry.icon == null) ? getDefaultIcon(info.user) : entry);
}
public Drawable getFullResIcon(LauncherActivityInfo info) {
- return mIconProvider.getIcon(info, mIconDpi);
+ return getFullResIcon(info, true);
+ }
+
+ public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
+ return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
}
public void updateSessionCache(PackageUserKey key, PackageInstaller.SessionInfo info) {
@@ -255,15 +259,6 @@
+ ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
}
- @Override
- protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
- if (mIconProvider.isClockIcon(cacheKey)) {
- // For clock icon, we always load the dynamic icon
- return false;
- }
- return super.getEntryFromDB(cacheKey, entry, lowRes);
- }
-
public static abstract class IconLoadRequest extends HandlerRunnable {
IconLoadRequest(Handler handler, Runnable endRunnable) {
super(handler, endRunnable);
diff --git a/src/com/android/launcher3/icons/IconProvider.java b/src/com/android/launcher3/icons/IconProvider.java
deleted file mode 100644
index 26b7eae..0000000
--- a/src/com/android/launcher3/icons/IconProvider.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.icons;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Process;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.launcher3.R;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.icons.BitmapInfo.Extender;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.SafeCloseable;
-
-import java.util.Calendar;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-
-/**
- * Class to handle icon loading from different packages
- */
-public class IconProvider {
-
- private static final String TAG = "IconProvider";
- private static final boolean DEBUG = false;
-
- private static final String ICON_METADATA_KEY_PREFIX = ".dynamic_icons";
-
- private static final String SYSTEM_STATE_SEPARATOR = " ";
-
- // Default value returned if there are problems getting resources.
- private static final int NO_ID = 0;
-
- private static final BiFunction<LauncherActivityInfo, Integer, Drawable> LAI_LOADER =
- LauncherActivityInfo::getIcon;
-
- private static final BiFunction<ActivityInfo, PackageManager, Drawable> AI_LOADER =
- ActivityInfo::loadUnbadgedIcon;
-
-
- private final Context mContext;
- private final ComponentName mCalendar;
- private final ComponentName mClock;
-
- public IconProvider(Context context) {
- mContext = context;
- mCalendar = parseComponentOrNull(context, R.string.calendar_component_name);
- mClock = parseComponentOrNull(context, R.string.clock_component_name);
- }
-
- /**
- * Adds any modification to the provided systemState for dynamic icons. This system state
- * is used by caches to check for icon invalidation.
- */
- public String getSystemStateForPackage(String systemState, String packageName) {
- if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) {
- return systemState + SYSTEM_STATE_SEPARATOR + getDay();
- } else {
- return systemState;
- }
- }
-
- /**
- * Loads the icon for the provided LauncherActivityInfo such that it can be drawn directly
- * on the UI
- */
- public Drawable getIconForUI(LauncherActivityInfo info, int iconDpi) {
- Drawable icon = getIcon(info, iconDpi);
- if (icon instanceof BitmapInfo.Extender) {
- ((Extender) icon).prepareToDrawOnUi();
- }
- return icon;
- }
-
- /**
- * Loads the icon for the provided LauncherActivityInfo
- */
- public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
- return getIcon(info.getApplicationInfo().packageName, info.getUser(),
- info, iconDpi, LAI_LOADER);
- }
-
- /**
- * Loads the icon for the provided activity info
- */
- public Drawable getIcon(ActivityInfo info, UserHandle user) {
- return getIcon(info.applicationInfo.packageName, user, info, mContext.getPackageManager(),
- AI_LOADER);
- }
-
- private <T, P> Drawable getIcon(String packageName, UserHandle user, T obj, P param,
- BiFunction<T, P, Drawable> loader) {
- Drawable icon = null;
- if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) {
- icon = loadCalendarDrawable(0);
- } else if (mClock != null
- && mClock.getPackageName().equals(packageName)
- && Process.myUserHandle().equals(user)) {
- icon = loadClockDrawable(0);
- }
- return icon == null ? loader.apply(obj, param) : icon;
- }
-
- private Drawable loadCalendarDrawable(int iconDpi) {
- PackageManager pm = mContext.getPackageManager();
- try {
- final Bundle metadata = pm.getActivityInfo(
- mCalendar,
- PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA)
- .metaData;
- final Resources resources = pm.getResourcesForApplication(mCalendar.getPackageName());
- final int id = getDynamicIconId(metadata, resources);
- if (id != NO_ID) {
- if (DEBUG) Log.d(TAG, "Got icon #" + id);
- return resources.getDrawableForDensity(id, iconDpi, null /* theme */);
- }
- } catch (PackageManager.NameNotFoundException e) {
- if (DEBUG) {
- Log.d(TAG, "Could not get activityinfo or resources for package: "
- + mCalendar.getPackageName());
- }
- }
- return null;
- }
-
- private Drawable loadClockDrawable(int iconDpi) {
- return ClockDrawableWrapper.forPackage(mContext, mClock.getPackageName(), iconDpi);
- }
-
- protected boolean isClockIcon(ComponentKey key) {
- return mClock != null && mClock.equals(key.componentName)
- && Process.myUserHandle().equals(key.user);
- }
-
- /**
- * @param metadata metadata of the default activity of Calendar
- * @param resources from the Calendar package
- * @return the resource id for today's Calendar icon; 0 if resources cannot be found.
- */
- private int getDynamicIconId(Bundle metadata, Resources resources) {
- if (metadata == null) {
- return NO_ID;
- }
- String key = mCalendar.getPackageName() + ICON_METADATA_KEY_PREFIX;
- final int arrayId = metadata.getInt(key, NO_ID);
- if (arrayId == NO_ID) {
- return NO_ID;
- }
- try {
- return resources.obtainTypedArray(arrayId).getResourceId(getDay(), NO_ID);
- } catch (Resources.NotFoundException e) {
- if (DEBUG) {
- Log.d(TAG, "package defines '" + key + "' but corresponding array not found");
- }
- return NO_ID;
- }
- }
-
- /**
- * @return Today's day of the month, zero-indexed.
- */
- private int getDay() {
- return Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1;
- }
-
-
- /**
- * Registers a callback to listen for calendar icon changes.
- * The callback receives the packageName for the calendar icon
- */
- public static SafeCloseable registerIconChangeListener(Context context,
- BiConsumer<String, UserHandle> callback, Handler handler) {
- ComponentName calendar = parseComponentOrNull(context, R.string.calendar_component_name);
- ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);
-
- if (calendar == null && clock == null) {
- return () -> { };
- }
-
- BroadcastReceiver receiver = new DateTimeChangeReceiver(callback);
- final IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
- if (calendar != null) {
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_DATE_CHANGED);
- }
- context.registerReceiver(receiver, filter, null, handler);
-
- return () -> context.unregisterReceiver(receiver);
- }
-
- private static class DateTimeChangeReceiver extends BroadcastReceiver {
-
- private final BiConsumer<String, UserHandle> mCallback;
-
- DateTimeChangeReceiver(BiConsumer<String, UserHandle> callback) {
- mCallback = callback;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
- ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);
- if (clock != null) {
- mCallback.accept(clock.getPackageName(), Process.myUserHandle());
- }
- }
-
- ComponentName calendar =
- parseComponentOrNull(context, R.string.calendar_component_name);
- if (calendar != null) {
- for (UserHandle user : UserManagerCompat.getInstance(context).getUserProfiles()) {
- mCallback.accept(calendar.getPackageName(), user);
- }
- }
-
- }
- }
-
- private static ComponentName parseComponentOrNull(Context context, int resId) {
- String cn = context.getString(resId);
- return TextUtils.isEmpty(cn) ? null : ComponentName.unflattenFromString(cn);
-
- }
-}
diff --git a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index 93de35a..f9a94da 100644
--- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -20,6 +20,7 @@
import android.content.pm.LauncherActivityInfo;
import android.os.UserHandle;
+import com.android.launcher3.IconProvider;
import com.android.launcher3.R;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -54,11 +55,13 @@
}
@Override
- public BitmapInfo loadIcon(Context context, LauncherActivityInfo object) {
- try (LauncherIcons li = LauncherIcons.obtain(context)) {
- return li.createBadgedIconBitmap(new IconProvider(context)
- .getIcon(object, li.mFillResIconDpi),
- object.getUser(), object.getApplicationInfo().targetSdkVersion);
- }
+ public void loadIcon(Context context, LauncherActivityInfo object,
+ BitmapInfo target) {
+ LauncherIcons li = LauncherIcons.obtain(context);
+ li.createBadgedIconBitmap(
+ IconProvider.INSTANCE.get(context)
+ .getIcon(object, li.mFillResIconDpi, true /* flattenDrawable */),
+ object.getUser(), object.getApplicationInfo().targetSdkVersion).applyTo(target);
+ li.recycle();
}
}
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 02e2d70..44c67e9 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -32,7 +32,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.R;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -46,6 +46,8 @@
*/
public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
+ private static final String EXTRA_BADGEPKG = "badge_package";
+
private static final Object sPoolSync = new Object();
private static LauncherIcons sPool;
private static int sPoolId = 0;
@@ -114,6 +116,7 @@
}
// below methods should also migrate to BaseIconFactory
+ @WorkerThread
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
return createShortcutIcon(shortcutInfo, true /* badged */);
}
@@ -123,58 +126,24 @@
return createShortcutIcon(shortcutInfo, badged, null);
}
+ @WorkerThread
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged,
@Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- return createShortcutIconCached(shortcutInfo, badged, true, fallbackIconProvider);
- } else {
- return createShortcutIconLegacy(shortcutInfo, badged, fallbackIconProvider);
- }
- }
-
- public BitmapInfo createShortcutIconLegacy(ShortcutInfo shortcutInfo, boolean badged,
- @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
- Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext)
- .getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
- IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
- final Bitmap unbadgedBitmap;
- if (bitmapInfo.icon != null) {
- unbadgedBitmap = bitmapInfo.icon;
- } else {
- if (fallbackIconProvider != null) {
- // Fallback icons are already badged and with appropriate shadow
- ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
- if (fullIcon != null && fullIcon.bitmap != null) {
- return fullIcon.bitmap;
- }
- }
- unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
- }
-
- if (!badged) {
- return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
- }
-
- final Bitmap unbadgedfinal = unbadgedBitmap;
- final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
-
- Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
- getShadowGenerator().recreateIcon(unbadgedfinal, c);
- badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
- });
- return BitmapInfo.of(icon, badge.bitmap.color);
+ return createShortcutIcon(shortcutInfo, badged, true, fallbackIconProvider);
}
@WorkerThread
- public BitmapInfo createShortcutIconCached(ShortcutInfo shortcutInfo, boolean badged,
+ public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged,
boolean useCache, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
final BitmapInfo bitmapInfo;
if (useCache) {
- bitmapInfo = cache.getDeepShortcutTitleAndIcon(shortcutInfo).bitmap;
+ bitmapInfo = cache.getDeepShortcutTitleAndIcon(shortcutInfo);
} else {
- bitmapInfo = new ShortcutCachingLogic().loadIcon(mContext, shortcutInfo);
+ bitmapInfo = new BitmapInfo();
+ new ShortcutCachingLogic().loadIcon(mContext, shortcutInfo, bitmapInfo);
}
+
final Bitmap unbadgedBitmap;
if (bitmapInfo.icon != null) {
unbadgedBitmap = bitmapInfo.icon;
@@ -182,30 +151,37 @@
if (fallbackIconProvider != null) {
// Fallback icons are already badged and with appropriate shadow
ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
- if (fullIcon != null && fullIcon.bitmap != null) {
- return fullIcon.bitmap;
+ if (fullIcon != null && fullIcon.iconBitmap != null) {
+ BitmapInfo result = new BitmapInfo();
+ result.icon = fullIcon.iconBitmap;
+ result.color = fullIcon.iconColor;
+ return result;
}
}
unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
}
+ BitmapInfo result = new BitmapInfo();
if (!badged) {
- return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
+ result.color = Themes.getColorAccent(mContext);
+ result.icon = unbadgedBitmap;
+ return result;
}
final Bitmap unbadgedfinal = unbadgedBitmap;
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
- Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
+ result.color = badge.iconColor;
+ result.icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(unbadgedfinal, c);
- badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
+ badgeWithDrawable(c, new FastBitmapDrawable(badge));
});
- return BitmapInfo.of(icon, badge.bitmap.color);
+ return result;
}
public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {
ComponentName cn = shortcutInfo.getActivity();
- String badgePkg = shortcutInfo.getPackage();
+ String badgePkg = getBadgePackage(shortcutInfo);
boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
if (cn != null && !hasBadgePkgSet) {
// Get the app info for the source activity.
@@ -223,4 +199,14 @@
return pkgInfo;
}
}
+
+ private String getBadgePackage(ShortcutInfo si) {
+ String whitelistedPkg = mContext.getString(R.string.shortcutinfo_badgepkg_whitelist);
+ if (whitelistedPkg.equals(si.getPackage())
+ && si.getExtras() != null
+ && si.getExtras().containsKey(EXTRA_BADGEPKG)) {
+ return si.getExtras().getString(EXTRA_BADGEPKG);
+ }
+ return si.getPackage();
+ }
}
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index 5c21470..5d696fd 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -23,14 +23,10 @@
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
-import androidx.annotation.NonNull;
-
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.util.Themes;
/**
* Caching logic for shortcuts.
@@ -52,23 +48,20 @@
return info.getShortLabel();
}
- @NonNull
@Override
- public BitmapInfo loadIcon(Context context, ShortcutInfo info) {
- try (LauncherIcons li = LauncherIcons.obtain(context)) {
- Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
- .getShortcutIconDrawable(info, LauncherAppState.getIDP(context).fillResIconDpi);
- if (unbadgedDrawable == null) return BitmapInfo.LOW_RES_INFO;
- return new BitmapInfo(li.createScaledBitmapWithoutShadow(
- unbadgedDrawable, 0), Themes.getColorAccent(context));
+ public void loadIcon(Context context, ShortcutInfo info, BitmapInfo target) {
+ LauncherIcons li = LauncherIcons.obtain(context);
+ Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
+ .getShortcutIconDrawable(info, LauncherAppState.getIDP(context).fillResIconDpi);
+ if (unbadgedDrawable != null) {
+ target.icon = li.createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
}
+ li.recycle();
}
@Override
public long getLastUpdatedTime(ShortcutInfo shortcutInfo, PackageInfo info) {
- if (shortcutInfo == null || !FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- return info.lastUpdateTime;
- }
+ if (shortcutInfo == null) return info.lastUpdateTime;
return Math.max(shortcutInfo.getLastChangedTimestamp(), info.lastUpdateTime);
}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index fa0fe1b..227bb22 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -76,11 +76,6 @@
if (shortcutExists(dataModel, item.getIntent(), item.user)) {
continue;
}
-
- // b/139663018 Short-circuit this logic if the icon is a system app
- if (PackageManagerHelper.isSystemApp(app.getContext(), item.getIntent())) {
- continue;
- }
}
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
@@ -122,39 +117,25 @@
}
SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user,
packageName);
- List<LauncherActivityInfo> activities = launcherApps
- .getActivityList(packageName, item.user);
- boolean hasActivity = activities != null && !activities.isEmpty();
-
if (sessionInfo == null) {
- if (!hasActivity) {
+ List<LauncherActivityInfo> activities = launcherApps
+ .getActivityList(packageName, item.user);
+ if (activities != null && !activities.isEmpty()) {
+ // App was installed while launcher was in the background.
+ itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
+ .makeWorkspaceItem();
+ WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
+ wii.title = "";
+ wii.applyFrom(app.getIconCache().getDefaultIcon(item.user));
+ app.getIconCache().getTitleAndIcon(wii,
+ ((WorkspaceItemInfo) itemInfo).usingLowResIcon());
+ } else {
// Session was cancelled, do not add.
continue;
}
} else {
workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
}
-
- if (hasActivity) {
- // App was installed while launcher was in the background,
- // or app was already installed for another user.
- itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
- .makeWorkspaceItem();
-
- if (shortcutExists(dataModel, itemInfo.getIntent(), itemInfo.user)) {
- // We need this additional check here since we treat all auto added
- // workspace items as promise icons. At this point we now have the
- // correct intent to compare against existing workspace icons.
- // Icon already exists on the workspace and should not be auto-added.
- continue;
- }
-
- WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
- wii.title = "";
- wii.bitmap = app.getIconCache().getDefaultIcon(item.user);
- app.getIconCache().getTitleAndIcon(wii,
- ((WorkspaceItemInfo) itemInfo).usingLowResIcon());
- }
}
// Add the shortcut to the db
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index a00a6bd..0a4f005 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -300,8 +300,8 @@
public LooperIdleLock newIdleLock(Object lock) {
LooperIdleLock idleLock = new LooperIdleLock(lock, Looper.getMainLooper());
- // If we are not binding or if the main looper is already idle, there is no reason to wait
- if (mCallbacks.get() == null || Looper.getMainLooper().getQueue().isIdle()) {
+ // If we are not binding, there is no reason to wait for idle.
+ if (mCallbacks.get() == null) {
idleLock.queueIdle();
}
return idleLock;
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 95268d0..6154e7e 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -153,7 +153,7 @@
info.title = getTitle();
// the fallback icon
if (!loadIcon(info)) {
- info.bitmap = mIconCache.getDefaultIcon(info.user);
+ info.applyFrom(mIconCache.getDefaultIcon(info.user));
}
// TODO: If there's an explicit component and we can't install that, delete it.
@@ -183,7 +183,7 @@
info.iconResource.resourceName = resourceName;
BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
if (iconInfo != null) {
- info.bitmap = iconInfo;
+ info.applyFrom(iconInfo);
return true;
}
}
@@ -192,7 +192,7 @@
// Failed to load from resource, try loading from DB.
byte[] data = getBlob(iconIndex);
try {
- info.bitmap = li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length));
+ info.applyFrom(li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)));
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to decode byte array for info " + info, e);
@@ -273,7 +273,7 @@
info.intent = newIntent;
mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
- if (mIconCache.isDefaultIcon(info.bitmap, user)) {
+ if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
loadIcon(info);
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 5893a08..81b701d 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -181,7 +181,16 @@
}
Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
- TimingLogger logger = new TimingLogger(TAG, "run");
+ TimingLogger logger = TestProtocol.sDebugTracing ?
+ new TimingLogger(TAG, "run") {
+ @Override
+ public void addSplit(String splitLabel) {
+ super.addSplit(splitLabel);
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "LoaderTask.addSplit " + splitLabel);
+ }
+ }
+ : new TimingLogger(TAG, "run");
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
loadWorkspace(allShortcuts);
@@ -216,12 +225,10 @@
mApp.getModel()::onPackageIconsUpdated);
logger.addSplit("update icon cache");
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- verifyNotStopped();
- logger.addSplit("save shortcuts in icon cache");
- updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
- mApp.getModel()::onPackageIconsUpdated);
- }
+ verifyNotStopped();
+ logger.addSplit("save shortcuts in icon cache");
+ updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
+ mApp.getModel()::onPackageIconsUpdated);
// Take a break
waitForIdle();
@@ -236,12 +243,10 @@
mResults.bindDeepShortcuts();
logger.addSplit("bindDeepShortcuts");
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- verifyNotStopped();
- logger.addSplit("save deep shortcuts in icon cache");
- updateHandler.updateIcons(allDeepShortcuts,
- new ShortcutCachingLogic(), (pkgs, user) -> { });
- }
+ verifyNotStopped();
+ logger.addSplit("save deep shortcuts in icon cache");
+ updateHandler.updateIcons(allDeepShortcuts,
+ new ShortcutCachingLogic(), (pkgs, user) -> { });
// Take a break
waitForIdle();
@@ -531,9 +536,8 @@
// use the last saved icon instead of the default.
Supplier<ItemInfoWithIcon> fallbackIconProvider = () ->
c.loadIcon(finalInfo, li) ? finalInfo : null;
- info.bitmap = li.createShortcutIcon(
- pinnedShortcut, true /* badged */,
- fallbackIconProvider);
+ info.applyFrom(li.createShortcutIcon(pinnedShortcut,
+ true /* badged */, fallbackIconProvider));
li.recycle();
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 1e614bd..db63b7c 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -189,7 +189,7 @@
BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
li.recycle();
if (iconInfo != null) {
- si.bitmap = iconInfo;
+ si.applyFrom(iconInfo);
infoUpdated = true;
}
}
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 05225d4..6c358b1 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -93,8 +93,8 @@
// If the shortcut is pinned but no longer has an icon in the system,
// keep the current icon instead of reverting to the default icon.
LauncherIcons li = LauncherIcons.obtain(context);
- workspaceItemInfo.bitmap = li.createShortcutIcon(
- fullDetails, true, () -> workspaceItemInfo);
+ workspaceItemInfo.applyFrom(li.createShortcutIcon(fullDetails, true,
+ () -> workspaceItemInfo));
li.recycle();
updatedWorkspaceItemInfos.add(workspaceItemInfo);
}
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index db1c307..4b773d7 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -92,7 +92,7 @@
// If the shortcut is pinned but no longer has an icon in the system,
// keep the current icon instead of reverting to the default icon.
LauncherIcons li = LauncherIcons.obtain(context);
- si.bitmap = li.createShortcutIcon(shortcut, true, () -> si);
+ si.applyFrom(li.createShortcutIcon(shortcut, true, () -> si));
li.recycle();
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 021fb30..717a7e9 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -16,7 +16,7 @@
package com.android.launcher3.notification;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
+import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
import android.app.Notification;
import android.content.Context;
@@ -30,7 +30,7 @@
import com.android.launcher3.R;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.util.Themes;
import java.util.List;
@@ -49,7 +49,7 @@
private final TextView mHeaderCount;
private final NotificationMainView mMainView;
private final NotificationFooterLayout mFooter;
- private final SingleAxisSwipeDetector mSwipeDetector;
+ private final SwipeDetector mSwipeDetector;
private final View mIconView;
private final View mHeader;
@@ -74,8 +74,8 @@
mHeader = container.findViewById(R.id.header);
mDivider = container.findViewById(R.id.divider);
- mSwipeDetector = new SingleAxisSwipeDetector(mContext, mMainView, HORIZONTAL);
- mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_BOTH, false);
+ mSwipeDetector = new SwipeDetector(mContext, mMainView, HORIZONTAL);
+ mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
mMainView.setSwipeDetector(mSwipeDetector);
mFooter.setContainer(this);
}
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 059ad18..10378ee 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,7 +16,6 @@
package com.android.launcher3.notification;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
@@ -33,10 +32,9 @@
import android.util.Log;
import android.util.Pair;
-import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
-import androidx.annotation.WorkerThread;
+import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.SecureSettingsObserver;
@@ -46,7 +44,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
/**
* A {@link NotificationListenerService} that sends updates to its
@@ -62,17 +59,16 @@
private static final int MSG_NOTIFICATION_POSTED = 1;
private static final int MSG_NOTIFICATION_REMOVED = 2;
private static final int MSG_NOTIFICATION_FULL_REFRESH = 3;
- private static final int MSG_CANCEL_NOTIFICATION = 4;
- private static final int MSG_RANKING_UPDATE = 5;
private static NotificationListener sNotificationListenerInstance = null;
private static NotificationsChangedListener sNotificationsChangedListener;
+ private static StatusBarNotificationsChangedListener sStatusBarNotificationsChangedListener;
private static boolean sIsConnected;
+ private static boolean sIsCreated;
private final Handler mWorkerHandler;
private final Handler mUiHandler;
private final Ranking mTempRanking = new Ranking();
-
/** Maps groupKey's to the corresponding group of notifications. */
private final Map<String, NotificationGroup> mNotificationGroupMap = new HashMap<>();
/** Maps keys to their corresponding current group key */
@@ -83,12 +79,85 @@
private SecureSettingsObserver mNotificationDotsObserver;
+ private final Handler.Callback mWorkerCallback = new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_NOTIFICATION_POSTED:
+ mUiHandler.obtainMessage(message.what, message.obj).sendToTarget();
+ break;
+ case MSG_NOTIFICATION_REMOVED:
+ mUiHandler.obtainMessage(message.what, message.obj).sendToTarget();
+ break;
+ case MSG_NOTIFICATION_FULL_REFRESH:
+ List<StatusBarNotification> activeNotifications;
+ if (sIsConnected) {
+ try {
+ activeNotifications = filterNotifications(getActiveNotifications());
+ } catch (SecurityException ex) {
+ Log.e(TAG, "SecurityException: failed to fetch notifications");
+ activeNotifications = new ArrayList<StatusBarNotification>();
+
+ }
+ } else {
+ activeNotifications = new ArrayList<StatusBarNotification>();
+ }
+
+ mUiHandler.obtainMessage(message.what, activeNotifications).sendToTarget();
+ break;
+ }
+ return true;
+ }
+ };
+
+ private final Handler.Callback mUiCallback = new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_NOTIFICATION_POSTED:
+ if (sNotificationsChangedListener != null) {
+ NotificationPostedMsg msg = (NotificationPostedMsg) message.obj;
+ sNotificationsChangedListener.onNotificationPosted(msg.packageUserKey,
+ msg.notificationKey, msg.shouldBeFilteredOut);
+ }
+ break;
+ case MSG_NOTIFICATION_REMOVED:
+ if (sNotificationsChangedListener != null) {
+ Pair<PackageUserKey, NotificationKeyData> pair
+ = (Pair<PackageUserKey, NotificationKeyData>) message.obj;
+ sNotificationsChangedListener.onNotificationRemoved(pair.first, pair.second);
+ }
+ break;
+ case MSG_NOTIFICATION_FULL_REFRESH:
+ if (sNotificationsChangedListener != null) {
+ sNotificationsChangedListener.onNotificationFullRefresh(
+ (List<StatusBarNotification>) message.obj);
+ }
+ break;
+ }
+ return true;
+ }
+ };
+
public NotificationListener() {
- mWorkerHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage);
- mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
+ super();
+ mWorkerHandler = new Handler(MODEL_EXECUTOR.getLooper(), mWorkerCallback);
+ mUiHandler = new Handler(Looper.getMainLooper(), mUiCallback);
sNotificationListenerInstance = this;
}
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ sIsCreated = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ sIsCreated = false;
+ }
+
public static @Nullable NotificationListener getInstanceIfConnected() {
return sIsConnected ? sNotificationListenerInstance : null;
}
@@ -99,107 +168,25 @@
NotificationListener notificationListener = getInstanceIfConnected();
if (notificationListener != null) {
notificationListener.onNotificationFullRefresh();
- } else {
+ } else if (!sIsCreated && sNotificationsChangedListener != null) {
// User turned off dots globally, so we unbound this service;
// tell the listener that there are no notifications to remove dots.
- MODEL_EXECUTOR.submit(() -> MAIN_EXECUTOR.submit(() ->
- listener.onNotificationFullRefresh(Collections.emptyList())));
+ sNotificationsChangedListener.onNotificationFullRefresh(
+ Collections.<StatusBarNotification>emptyList());
}
}
+ public static void setStatusBarNotificationsChangedListener
+ (StatusBarNotificationsChangedListener listener) {
+ sStatusBarNotificationsChangedListener = listener;
+ }
+
public static void removeNotificationsChangedListener() {
sNotificationsChangedListener = null;
}
- private boolean handleWorkerMessage(Message message) {
- switch (message.what) {
- case MSG_NOTIFICATION_POSTED: {
- StatusBarNotification sbn = (StatusBarNotification) message.obj;
- mUiHandler.obtainMessage(notificationIsValidForUI(sbn)
- ? MSG_NOTIFICATION_POSTED : MSG_NOTIFICATION_REMOVED,
- toKeyPair(sbn)).sendToTarget();
- return true;
- }
- case MSG_NOTIFICATION_REMOVED: {
- StatusBarNotification sbn = (StatusBarNotification) message.obj;
- mUiHandler.obtainMessage(MSG_NOTIFICATION_REMOVED,
- toKeyPair(sbn)).sendToTarget();
-
- NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
- String key = sbn.getKey();
- if (notificationGroup != null) {
- notificationGroup.removeChildKey(key);
- if (notificationGroup.isEmpty()) {
- if (key.equals(mLastKeyDismissedByLauncher)) {
- // Only cancel the group notification if launcher dismissed the
- // last child.
- cancelNotification(notificationGroup.getGroupSummaryKey());
- }
- mNotificationGroupMap.remove(sbn.getGroupKey());
- }
- }
- if (key.equals(mLastKeyDismissedByLauncher)) {
- mLastKeyDismissedByLauncher = null;
- }
- return true;
- }
- case MSG_NOTIFICATION_FULL_REFRESH:
- List<StatusBarNotification> activeNotifications = null;
- if (sIsConnected) {
- try {
- activeNotifications = Arrays.stream(getActiveNotifications())
- .filter(this::notificationIsValidForUI)
- .collect(Collectors.toList());
- } catch (SecurityException ex) {
- Log.e(TAG, "SecurityException: failed to fetch notifications");
- activeNotifications = new ArrayList<>();
- }
- } else {
- activeNotifications = new ArrayList<>();
- }
-
- mUiHandler.obtainMessage(message.what, activeNotifications).sendToTarget();
- return true;
- case MSG_CANCEL_NOTIFICATION: {
- mLastKeyDismissedByLauncher = (String) message.obj;
- cancelNotification(mLastKeyDismissedByLauncher);
- return true;
- }
- case MSG_RANKING_UPDATE: {
- String[] keys = ((RankingMap) message.obj).getOrderedKeys();
- for (StatusBarNotification sbn : getActiveNotifications(keys)) {
- updateGroupKeyIfNecessary(sbn);
- }
- return true;
- }
- }
- return false;
- }
-
- private boolean handleUiMessage(Message message) {
- switch (message.what) {
- case MSG_NOTIFICATION_POSTED:
- if (sNotificationsChangedListener != null) {
- Pair<PackageUserKey, NotificationKeyData> msg = (Pair) message.obj;
- sNotificationsChangedListener.onNotificationPosted(
- msg.first, msg.second);
- }
- break;
- case MSG_NOTIFICATION_REMOVED:
- if (sNotificationsChangedListener != null) {
- Pair<PackageUserKey, NotificationKeyData> msg = (Pair) message.obj;
- sNotificationsChangedListener.onNotificationRemoved(
- msg.first, msg.second);
- }
- break;
- case MSG_NOTIFICATION_FULL_REFRESH:
- if (sNotificationsChangedListener != null) {
- sNotificationsChangedListener.onNotificationFullRefresh(
- (List<StatusBarNotification>) message.obj);
- }
- break;
- }
- return true;
+ public static void removeStatusBarNotificationsChangedListener() {
+ sStatusBarNotificationsChangedListener = null;
}
@Override
@@ -230,37 +217,84 @@
super.onListenerDisconnected();
sIsConnected = false;
mNotificationDotsObserver.unregister();
- onNotificationFullRefresh();
}
@Override
public void onNotificationPosted(final StatusBarNotification sbn) {
- if (sbn != null) {
- mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, sbn).sendToTarget();
+ super.onNotificationPosted(sbn);
+ if (sbn == null) {
+ // There is a bug in platform where we can get a null notification; just ignore it.
+ return;
+ }
+ mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, new NotificationPostedMsg(sbn))
+ .sendToTarget();
+ if (sStatusBarNotificationsChangedListener != null) {
+ sStatusBarNotificationsChangedListener.onNotificationPosted(sbn);
+ }
+ }
+
+ /**
+ * An object containing data to send to MSG_NOTIFICATION_POSTED targets.
+ */
+ private class NotificationPostedMsg {
+ final PackageUserKey packageUserKey;
+ final NotificationKeyData notificationKey;
+ final boolean shouldBeFilteredOut;
+
+ NotificationPostedMsg(StatusBarNotification sbn) {
+ packageUserKey = PackageUserKey.fromNotification(sbn);
+ notificationKey = NotificationKeyData.fromNotification(sbn);
+ shouldBeFilteredOut = shouldBeFilteredOut(sbn);
}
}
@Override
public void onNotificationRemoved(final StatusBarNotification sbn) {
- if (sbn != null) {
- mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, sbn).sendToTarget();
+ super.onNotificationRemoved(sbn);
+ if (sbn == null) {
+ // There is a bug in platform where we can get a null notification; just ignore it.
+ return;
}
+ Pair<PackageUserKey, NotificationKeyData> packageUserKeyAndNotificationKey
+ = new Pair<>(PackageUserKey.fromNotification(sbn),
+ NotificationKeyData.fromNotification(sbn));
+ mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey)
+ .sendToTarget();
+ if (sStatusBarNotificationsChangedListener != null) {
+ sStatusBarNotificationsChangedListener.onNotificationRemoved(sbn);
+ }
+
+ NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
+ String key = sbn.getKey();
+ if (notificationGroup != null) {
+ notificationGroup.removeChildKey(key);
+ if (notificationGroup.isEmpty()) {
+ if (key.equals(mLastKeyDismissedByLauncher)) {
+ // Only cancel the group notification if launcher dismissed the last child.
+ cancelNotification(notificationGroup.getGroupSummaryKey());
+ }
+ mNotificationGroupMap.remove(sbn.getGroupKey());
+ }
+ }
+ if (key.equals(mLastKeyDismissedByLauncher)) {
+ mLastKeyDismissedByLauncher = null;
+ }
+ }
+
+ public void cancelNotificationFromLauncher(String key) {
+ mLastKeyDismissedByLauncher = key;
+ cancelNotification(key);
}
@Override
public void onNotificationRankingUpdate(RankingMap rankingMap) {
- mWorkerHandler.obtainMessage(MSG_RANKING_UPDATE, rankingMap).sendToTarget();
+ super.onNotificationRankingUpdate(rankingMap);
+ String[] keys = rankingMap.getOrderedKeys();
+ for (StatusBarNotification sbn : getActiveNotifications(keys)) {
+ updateGroupKeyIfNecessary(sbn);
+ }
}
- /**
- * Cancels a notification
- */
- @AnyThread
- public void cancelNotificationFromLauncher(String key) {
- mWorkerHandler.obtainMessage(MSG_CANCEL_NOTIFICATION, key).sendToTarget();
- }
-
- @WorkerThread
private void updateGroupKeyIfNecessary(StatusBarNotification sbn) {
String childKey = sbn.getKey();
String oldGroupKey = mNotificationGroupKeyMap.get(childKey);
@@ -294,33 +328,53 @@
}
}
- /**
- * This makes a potentially expensive binder call and should be run on a background thread.
- */
- @WorkerThread
+ /** This makes a potentially expensive binder call and should be run on a background thread. */
public List<StatusBarNotification> getNotificationsForKeys(List<NotificationKeyData> keys) {
- StatusBarNotification[] notifications = getActiveNotifications(
- keys.stream().map(n -> n.notificationKey).toArray(String[]::new));
- return notifications == null ? Collections.emptyList() : Arrays.asList(notifications);
+ StatusBarNotification[] notifications = NotificationListener.this
+ .getActiveNotifications(NotificationKeyData.extractKeysOnly(keys)
+ .toArray(new String[keys.size()]));
+ return notifications == null
+ ? Collections.<StatusBarNotification>emptyList() : Arrays.asList(notifications);
}
/**
- * Returns true for notifications that have an intent and are not headers for grouped
- * notifications and should be shown in the notification popup.
+ * Filter out notifications that don't have an intent
+ * or are headers for grouped notifications.
+ *
+ * @see #shouldBeFilteredOut(StatusBarNotification)
*/
- @WorkerThread
- private boolean notificationIsValidForUI(StatusBarNotification sbn) {
+ private List<StatusBarNotification> filterNotifications(
+ StatusBarNotification[] notifications) {
+ if (notifications == null) return null;
+ IntSet removedNotifications = new IntSet();
+ for (int i = 0; i < notifications.length; i++) {
+ if (shouldBeFilteredOut(notifications[i])) {
+ removedNotifications.add(i);
+ }
+ }
+ List<StatusBarNotification> filteredNotifications = new ArrayList<>(
+ notifications.length - removedNotifications.size());
+ for (int i = 0; i < notifications.length; i++) {
+ if (!removedNotifications.contains(i)) {
+ filteredNotifications.add(notifications[i]);
+ }
+ }
+ return filteredNotifications;
+ }
+
+ private boolean shouldBeFilteredOut(StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
+
updateGroupKeyIfNecessary(sbn);
getCurrentRanking().getRanking(sbn.getKey(), mTempRanking);
if (!mTempRanking.canShowBadge()) {
- return false;
+ return true;
}
if (mTempRanking.getChannel().getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
// Special filtering for the default, legacy "Miscellaneous" channel.
if ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0) {
- return false;
+ return true;
}
}
@@ -328,19 +382,19 @@
CharSequence text = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
boolean missingTitleAndText = TextUtils.isEmpty(title) && TextUtils.isEmpty(text);
boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
- return !isGroupHeader && !missingTitleAndText;
- }
-
- private static Pair<PackageUserKey, NotificationKeyData> toKeyPair(StatusBarNotification sbn) {
- return Pair.create(PackageUserKey.fromNotification(sbn),
- NotificationKeyData.fromNotification(sbn));
+ return (isGroupHeader || missingTitleAndText);
}
public interface NotificationsChangedListener {
void onNotificationPosted(PackageUserKey postedPackageUserKey,
- NotificationKeyData notificationKey);
+ NotificationKeyData notificationKey, boolean shouldBeFilteredOut);
void onNotificationRemoved(PackageUserKey removedPackageUserKey,
NotificationKeyData notificationKey);
void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications);
}
+
+ public interface StatusBarNotificationsChangedListener {
+ void onNotificationPosted(StatusBarNotification sbn);
+ void onNotificationRemoved(StatusBarNotification sbn);
+ }
}
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index b67adbb..78627ec 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -38,9 +38,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.OverScroll;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Themes;
@@ -49,7 +48,7 @@
* e.g. icon + title + text.
*/
@TargetApi(Build.VERSION_CODES.N)
-public class NotificationMainView extends FrameLayout implements SingleAxisSwipeDetector.Listener {
+public class NotificationMainView extends FrameLayout implements SwipeDetector.Listener {
private static FloatProperty<NotificationMainView> CONTENT_TRANSLATION =
new FloatProperty<NotificationMainView>("contentTranslation") {
@@ -76,7 +75,7 @@
private TextView mTextView;
private View mIconView;
- private SingleAxisSwipeDetector mSwipeDetector;
+ private SwipeDetector mSwipeDetector;
public NotificationMainView(Context context) {
this(context, null, 0);
@@ -108,7 +107,7 @@
mIconView = findViewById(R.id.popup_item_icon);
}
- public void setSwipeDetector(SingleAxisSwipeDetector swipeDetector) {
+ public void setSwipeDetector(SwipeDetector swipeDetector) {
mSwipeDetector = swipeDetector;
}
@@ -174,7 +173,7 @@
LauncherLogProto.ItemType.NOTIFICATION);
}
- // SingleAxisSwipeDetector.Listener's
+ // SwipeDetector.Listener's
@Override
public void onDragStart(boolean start) { }
@@ -188,7 +187,7 @@
}
@Override
- public void onDragEnd(float velocity) {
+ public void onDragEnd(float velocity, boolean fling) {
final boolean willExit;
final float endTranslation;
final float startTranslation = mTextAndBackground.getTranslationX();
@@ -196,7 +195,7 @@
if (!canChildBeDismissed()) {
willExit = false;
endTranslation = 0;
- } else if (mSwipeDetector.isFling(velocity)) {
+ } else if (fling) {
willExit = true;
endTranslation = velocity < 0 ? - getWidth() : getWidth();
} else if (Math.abs(startTranslation) > getWidth() / 2) {
@@ -207,7 +206,7 @@
endTranslation = 0;
}
- long duration = BaseSwipeDetector.calculateDuration(velocity,
+ long duration = SwipeDetector.calculateDuration(velocity,
(endTranslation - startTranslation) / getWidth());
mContentTranslateAnimator.removeAllListeners();
diff --git a/src/com/android/launcher3/pm/PinRequestHelper.java b/src/com/android/launcher3/pm/PinRequestHelper.java
index 02f96a1..5b6b56d 100644
--- a/src/com/android/launcher3/pm/PinRequestHelper.java
+++ b/src/com/android/launcher3/pm/PinRequestHelper.java
@@ -31,8 +31,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.LauncherIcons;
public class PinRequestHelper {
@@ -82,15 +80,7 @@
ShortcutInfo si = request.getShortcutInfo();
WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
// Apply the unbadged icon and fetch the actual icon asynchronously.
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- fetchAndUpdateShortcutIconAsync(context, info, si, false);
- } else {
- LauncherIcons li = LauncherIcons.obtain(context);
- info.bitmap = li.createShortcutIcon(si, false /* badged */);
- li.recycle();
- LauncherAppState.getInstance(context).getModel()
- .updateAndBindWorkspaceItem(info, si);
- }
+ fetchAndUpdateShortcutIconAsync(context, info, si, false);
return info;
} else {
return null;
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 98f7fd8..28000b9 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -360,14 +360,10 @@
final TimeInterpolator revealInterpolator = ACCEL_DEACCEL;
// Rectangular reveal.
- mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
.createRevealAnimator(this, false);
revealAnim.setDuration(revealDuration);
revealAnim.setInterpolator(revealInterpolator);
- // Clip the popup to the initial outline while the notification dot and arrow animate.
- revealAnim.start();
- revealAnim.pause();
ValueAnimator fadeIn = ValueAnimator.ofFloat(0, 1);
fadeIn.setDuration(revealDuration + arrowDuration);
@@ -403,6 +399,7 @@
if (!mIsOpen) {
return;
}
+ mEndRect.setEmpty();
if (getOutlineProvider() instanceof RevealOutlineAnimation) {
((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
}
@@ -474,6 +471,9 @@
mStartRect.set(arrowCenterX - halfArrowWidth, arrowCenterY, arrowCenterX + halfArrowWidth,
arrowCenterY);
+ if (mEndRect.isEmpty()) {
+ mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ }
return new RoundedRectRevealOutlineProvider
(arrowCornerRadius, mOutlineRadius, mStartRect, mEndRect);
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index e70673a..4833c26 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -77,7 +77,6 @@
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
/**
* A container for shortcuts to deep links and notifications associated with an app.
@@ -197,6 +196,9 @@
* @return the container if shown or null.
*/
public static PopupContainerWithArrow showForIcon(BubbleTextView icon) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_CONTEXT_MENU, "showForIcon");
+ }
Launcher launcher = Launcher.getLauncher(icon.getContext());
if (getOpen(launcher) != null) {
// There is already an items container open, so don't open this one.
@@ -211,7 +213,7 @@
final PopupContainerWithArrow container =
(PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
- container.populateAndShow(icon, itemInfo);
+ container.populateAndShow(icon, itemInfo, SystemShortcutFactory.INSTANCE.get(launcher));
return container;
}
@@ -236,15 +238,16 @@
}
}
- protected void populateAndShow(BubbleTextView icon, ItemInfo item) {
+ protected void populateAndShow(
+ BubbleTextView icon, ItemInfo item, SystemShortcutFactory factory) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_CONTEXT_MENU, "populateAndShow");
+ }
PopupDataProvider popupDataProvider = mLauncher.getPopupDataProvider();
populateAndShow(icon,
popupDataProvider.getShortcutCountForItem(item),
popupDataProvider.getNotificationKeysForItem(item),
- mLauncher.getSupportedShortcuts()
- .map(s -> s.getShortcut(mLauncher, item))
- .filter(s -> s != null)
- .collect(Collectors.toList()));
+ factory.getEnabledShortcuts(mLauncher, item));
}
public ViewGroup getSystemShortcutContainerForTesting() {
@@ -379,7 +382,8 @@
@Override
public void onWidgetsBound() {
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
- SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
+ SystemShortcut widgetInfo = new SystemShortcut.Widgets();
+ View.OnClickListener onClickListener = widgetInfo.getOnClickListener(mLauncher, itemInfo);
View widgetsView = null;
int count = mSystemShortcutContainer.getChildCount();
for (int i = 0; i < count; i++) {
@@ -390,7 +394,7 @@
}
}
- if (widgetInfo != null && widgetsView == null) {
+ if (onClickListener != null && widgetsView == null) {
// We didn't have any widgets cached but now there are some, so enable the shortcut.
if (mSystemShortcutContainer != this) {
initializeSystemShortcut(
@@ -403,7 +407,7 @@
close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
- } else if (widgetInfo == null && widgetsView != null) {
+ } else if (onClickListener == null && widgetsView != null) {
// No widgets exist, but we previously added the shortcut so remove it.
if (mSystemShortcutContainer != this) {
mSystemShortcutContainer.removeView(widgetsView);
@@ -426,7 +430,8 @@
info.setIconAndContentDescriptionFor((ImageView) view);
}
view.setTag(info);
- view.setOnClickListener(info);
+ view.setOnClickListener(info.getOnClickListener(mLauncher,
+ (ItemInfo) mOriginalIcon.getTag()));
}
/**
@@ -501,7 +506,7 @@
DotInfo dotInfo = mLauncher.getDotInfoForItem(itemInfo);
if (mNotificationItemView != null && dotInfo != null) {
mNotificationItemView.updateHeader(
- dotInfo.getNotificationCount(), itemInfo.bitmap.color);
+ dotInfo.getNotificationCount(), itemInfo.iconColor);
}
}
@@ -570,11 +575,8 @@
@Override
protected void closeComplete() {
- PopupContainerWithArrow openPopup = getOpen(mLauncher);
- if (openPopup == null || openPopup.mOriginalIcon != mOriginalIcon) {
- mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
- mOriginalIcon.setForceHideDot(false);
- }
+ mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
+ mOriginalIcon.setForceHideDot(false);
super.closeComplete();
}
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index c5aa836..4612b2a 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -20,15 +20,13 @@
import android.service.notification.StatusBarNotification;
import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.ShortcutUtil;
@@ -41,9 +39,13 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/**
* Provides data for the popup menu that appears after long-clicking on apps.
*/
@@ -74,14 +76,28 @@
@Override
public void onNotificationPosted(PackageUserKey postedPackageUserKey,
- NotificationKeyData notificationKey) {
+ NotificationKeyData notificationKey, boolean shouldBeFilteredOut) {
DotInfo dotInfo = mPackageUserToDotInfos.get(postedPackageUserKey);
+ boolean dotShouldBeRefreshed;
if (dotInfo == null) {
- dotInfo = new DotInfo();
- mPackageUserToDotInfos.put(postedPackageUserKey, dotInfo);
+ if (!shouldBeFilteredOut) {
+ DotInfo newDotInfo = new DotInfo();
+ newDotInfo.addOrUpdateNotificationKey(notificationKey);
+ mPackageUserToDotInfos.put(postedPackageUserKey, newDotInfo);
+ dotShouldBeRefreshed = true;
+ } else {
+ dotShouldBeRefreshed = false;
+ }
+ } else {
+ dotShouldBeRefreshed = shouldBeFilteredOut
+ ? dotInfo.removeNotificationKey(notificationKey)
+ : dotInfo.addOrUpdateNotificationKey(notificationKey);
+ if (dotInfo.getNotificationKeys().size() == 0) {
+ mPackageUserToDotInfos.remove(postedPackageUserKey);
+ }
}
- if (dotInfo.addOrUpdateNotificationKey(notificationKey)) {
- updateNotificationDots(postedPackageUserKey::equals);
+ if (dotShouldBeRefreshed) {
+ updateNotificationDots(t -> postedPackageUserKey.equals(t));
}
}
@@ -93,7 +109,7 @@
if (oldDotInfo.getNotificationKeys().size() == 0) {
mPackageUserToDotInfos.remove(removedPackageUserKey);
}
- updateNotificationDots(removedPackageUserKey::equals);
+ updateNotificationDots(t -> removedPackageUserKey.equals(t));
trimNotifications(mPackageUserToDotInfos);
}
}
@@ -179,6 +195,14 @@
: getNotificationsForItem(info, dotInfo.getNotificationKeys());
}
+ /** This makes a potentially expensive binder call and should be run on a background thread. */
+ public @NonNull List<StatusBarNotification> getStatusBarNotificationsForKeys(
+ List<NotificationKeyData> notificationKeys) {
+ NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
+ return notificationListener == null ? Collections.EMPTY_LIST
+ : notificationListener.getNotificationsForKeys(notificationKeys);
+ }
+
public void cancelNotification(String notificationKey) {
NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
if (notificationListener == null) {
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 80c6683..dbfe988 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -20,9 +20,7 @@
import android.content.pm.ShortcutInfo;
import android.os.Handler;
import android.os.UserHandle;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
+import android.service.notification.StatusBarNotification;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
@@ -30,7 +28,6 @@
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationKeyData;
-import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.util.PackageUserKey;
@@ -40,7 +37,9 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
-import java.util.stream.Collectors;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
/**
* Contains logic relevant to populating a {@link PopupContainerWithArrow}. In particular,
@@ -131,15 +130,12 @@
final UserHandle user = originalInfo.user;
return () -> {
if (!notificationKeys.isEmpty()) {
- NotificationListener notificationListener =
- NotificationListener.getInstanceIfConnected();
- final List<NotificationInfo> infos;
- if (notificationListener == null) {
- infos = Collections.emptyList();
- } else {
- infos = notificationListener.getNotificationsForKeys(notificationKeys).stream()
- .map(sbn -> new NotificationInfo(launcher, sbn))
- .collect(Collectors.toList());
+ List<StatusBarNotification> notifications = launcher.getPopupDataProvider()
+ .getStatusBarNotificationsForKeys(notificationKeys);
+ List<NotificationInfo> infos = new ArrayList<>(notifications.size());
+ for (int i = 0; i < notifications.size(); i++) {
+ StatusBarNotification notification = notifications.get(i);
+ infos.add(new NotificationInfo(launcher, notification));
}
uiHandler.post(() -> container.applyNotificationInfos(infos));
}
@@ -154,7 +150,7 @@
final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
// Use unbadged icon for the menu.
LauncherIcons li = LauncherIcons.obtain(launcher);
- si.bitmap = li.createShortcutIcon(shortcut, false /* badged */);
+ si.applyFrom(li.createShortcutIcon(shortcut, false /* badged */));
li.recycle();
si.rank = i;
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 8751202..5a5fbab 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -16,19 +16,13 @@
package com.android.launcher3.popup;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.RemoteAction;
-import android.content.Context;
import android.content.Intent;
-import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.ImageView;
-import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.AbstractFloatingView;
@@ -38,75 +32,55 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
-@TargetApi(Build.VERSION_CODES.Q)
public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
private static final String TAG = "RemoteActionShortcut";
private static final boolean DEBUG = Utilities.IS_DEBUG_DEVICE;
private final RemoteAction mAction;
- public RemoteActionShortcut(RemoteAction action,
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- super(0, R.id.action_remote_action_shortcut, activity, itemInfo);
+ public RemoteActionShortcut(RemoteAction action) {
+ super(action.getIcon(), action.getTitle(), action.getContentDescription(),
+ R.id.action_remote_action_shortcut);
mAction = action;
}
@Override
- public void setIconAndLabelFor(View iconView, TextView labelView) {
- mAction.getIcon().loadDrawableAsync(iconView.getContext(),
- iconView::setBackground,
- MAIN_EXECUTOR.getHandler());
- labelView.setText(mAction.getTitle());
- }
+ public View.OnClickListener getOnClickListener(
+ final BaseDraggingActivity activity, final ItemInfo itemInfo) {
+ return view -> {
+ AbstractFloatingView.closeAllOpenViews(activity);
- @Override
- public void setIconAndContentDescriptionFor(ImageView view) {
- mAction.getIcon().loadDrawableAsync(view.getContext(),
- view::setImageDrawable,
- MAIN_EXECUTOR.getHandler());
- view.setContentDescription(mAction.getContentDescription());
- }
+ final String actionIdentity = mAction.getTitle() + ", " +
+ itemInfo.getTargetComponent().getPackageName();
+ try {
+ if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
+ mAction.getActionIntent().send(
+ activity,
+ 0,
+ new Intent().putExtra(
+ Intent.EXTRA_PACKAGE_NAME,
+ itemInfo.getTargetComponent().getPackageName()),
+ (pendingIntent, intent, resultCode, resultData, resultExtras) -> {
+ if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
+ if (resultData != null && !resultData.isEmpty()) {
+ Log.e(TAG, "Remote action returned result: " + actionIdentity
+ + " : " + resultData);
+ Toast.makeText(activity, resultData, Toast.LENGTH_SHORT).show();
+ }
+ },
+ new Handler(Looper.getMainLooper()));
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
+ Toast.makeText(activity, activity.getString(
+ R.string.remote_action_failed,
+ mAction.getTitle()),
+ Toast.LENGTH_SHORT)
+ .show();
+ }
- @Override
- public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
- return new AccessibilityNodeInfo.AccessibilityAction(
- R.id.action_remote_action_shortcut, mAction.getContentDescription());
- }
-
- @Override
- public void onClick(View view) {
- AbstractFloatingView.closeAllOpenViews(mTarget);
-
- final String actionIdentity = mAction.getTitle() + ", "
- + mItemInfo.getTargetComponent().getPackageName();
- try {
- if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
- mAction.getActionIntent().send(
- mTarget,
- 0,
- new Intent().putExtra(
- Intent.EXTRA_PACKAGE_NAME,
- mItemInfo.getTargetComponent().getPackageName()),
- (pendingIntent, intent, resultCode, resultData, resultExtras) -> {
- if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
- if (resultData != null && !resultData.isEmpty()) {
- Log.e(TAG, "Remote action returned result: " + actionIdentity
- + " : " + resultData);
- Toast.makeText(mTarget, resultData, Toast.LENGTH_SHORT).show();
- }
- },
- MAIN_EXECUTOR.getHandler());
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
- Toast.makeText(mTarget, mTarget.getString(
- R.string.remote_action_failed,
- mAction.getTitle()),
- Toast.LENGTH_SHORT)
- .show();
- }
-
- mTarget.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
- LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
+ activity.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
+ LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
+ };
}
@Override
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 222c6c9..a87b7b8 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -5,13 +5,14 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.os.Looper;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.TextView;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
@@ -38,30 +39,41 @@
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
* @param <T>
*/
-public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo
- implements View.OnClickListener {
-
+public abstract class SystemShortcut<T extends BaseDraggingActivity>
+ extends ItemInfo {
private final int mIconResId;
private final int mLabelResId;
+ private final Icon mIcon;
+ private final CharSequence mLabel;
+ private final CharSequence mContentDescription;
private final int mAccessibilityActionId;
- protected final T mTarget;
- protected final ItemInfo mItemInfo;
-
- public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) {
+ public SystemShortcut(int iconResId, int labelResId) {
mIconResId = iconResId;
mLabelResId = labelResId;
mAccessibilityActionId = labelResId;
- mTarget = target;
- mItemInfo = itemInfo;
+ mIcon = null;
+ mLabel = null;
+ mContentDescription = null;
}
- public SystemShortcut(SystemShortcut<T> other) {
+ public SystemShortcut(Icon icon, CharSequence label, CharSequence contentDescription,
+ int accessibilityActionId) {
+ mIcon = icon;
+ mLabel = label;
+ mContentDescription = contentDescription;
+ mAccessibilityActionId = accessibilityActionId;
+ mIconResId = 0;
+ mLabelResId = 0;
+ }
+
+ public SystemShortcut(SystemShortcut other) {
mIconResId = other.mIconResId;
mLabelResId = other.mLabelResId;
+ mIcon = other.mIcon;
+ mLabel = other.mLabel;
+ mContentDescription = other.mContentDescription;
mAccessibilityActionId = other.mAccessibilityActionId;
- mTarget = other.mTarget;
- mItemInfo = other.mItemInfo;
}
/**
@@ -72,135 +84,150 @@
}
public void setIconAndLabelFor(View iconView, TextView labelView) {
- iconView.setBackgroundResource(mIconResId);
- labelView.setText(mLabelResId);
+ if (mIcon != null) {
+ mIcon.loadDrawableAsync(iconView.getContext(),
+ iconView::setBackground,
+ new Handler(Looper.getMainLooper()));
+ } else {
+ iconView.setBackgroundResource(mIconResId);
+ }
+
+ if (mLabel != null) {
+ labelView.setText(mLabel);
+ } else {
+ labelView.setText(mLabelResId);
+ }
}
public void setIconAndContentDescriptionFor(ImageView view) {
- view.setImageResource(mIconResId);
- view.setContentDescription(view.getContext().getText(mLabelResId));
+ if (mIcon != null) {
+ mIcon.loadDrawableAsync(view.getContext(),
+ view::setImageDrawable,
+ new Handler(Looper.getMainLooper()));
+ } else {
+ view.setImageResource(mIconResId);
+ }
+
+ view.setContentDescription(getContentDescription(view.getContext()));
+ }
+
+ private CharSequence getContentDescription(Context context) {
+ return mContentDescription != null ? mContentDescription : context.getText(mLabelResId);
}
public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
- return new AccessibilityNodeInfo.AccessibilityAction(
- mAccessibilityActionId, context.getText(mLabelResId));
+ return new AccessibilityNodeInfo.AccessibilityAction(mAccessibilityActionId,
+ getContentDescription(context));
}
public boolean hasHandlerForAction(int action) {
return mAccessibilityActionId == action;
}
- public interface Factory<T extends BaseDraggingActivity> {
-
- @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo);
- }
-
- public static final Factory<Launcher> WIDGETS = (launcher, itemInfo) -> {
- if (itemInfo.getTargetComponent() == null) return null;
- final List<WidgetItem> widgets =
- launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
- itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
- if (widgets == null) {
- return null;
- }
- return new Widgets(launcher, itemInfo);
- };
+ public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo);
public static class Widgets extends SystemShortcut<Launcher> {
- public Widgets(Launcher target, ItemInfo itemInfo) {
- super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo);
+ public Widgets() {
+ super(R.drawable.ic_widget, R.string.widget_button_text);
}
@Override
- public void onClick(View view) {
- AbstractFloatingView.closeAllOpenViews(mTarget);
- WidgetsBottomSheet widgetsBottomSheet =
- (WidgetsBottomSheet) mTarget.getLayoutInflater().inflate(
- R.layout.widgets_bottom_sheet, mTarget.getDragLayer(), false);
- widgetsBottomSheet.populateAndShow(mItemInfo);
- mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.WIDGETS_BUTTON, view);
+ public View.OnClickListener getOnClickListener(final Launcher launcher,
+ final ItemInfo itemInfo) {
+ if (itemInfo.getTargetComponent() == null) return null;
+ final List<WidgetItem> widgets =
+ launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
+ itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
+ if (widgets == null) {
+ return null;
+ }
+ return (view) -> {
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ WidgetsBottomSheet widgetsBottomSheet =
+ (WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
+ R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
+ widgetsBottomSheet.populateAndShow(itemInfo);
+ launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.WIDGETS_BUTTON, view);
+ };
}
}
- public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
-
public static class AppInfo extends SystemShortcut {
-
- public AppInfo(BaseDraggingActivity target, ItemInfo itemInfo) {
- super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
- itemInfo);
+ public AppInfo() {
+ super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label);
}
@Override
- public void onClick(View view) {
- dismissTaskMenuView(mTarget);
- Rect sourceBounds = mTarget.getViewBounds(view);
- new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
- mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
- mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.APPINFO_TARGET, view);
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
+ return (view) -> {
+ dismissTaskMenuView(activity);
+ Rect sourceBounds = activity.getViewBounds(view);
+ new PackageManagerHelper(activity).startDetailsActivityForInfo(
+ itemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
+ activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.APPINFO_TARGET, view);
+ };
}
}
- public static Factory<BaseDraggingActivity> INSTALL = (activity, itemInfo) -> {
- boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
- && ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
- WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
- boolean isInstantApp = false;
- if (itemInfo instanceof com.android.launcher3.AppInfo) {
- com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
- isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
- }
- boolean enabled = supportsWebUI || isInstantApp;
- if (!enabled) {
- return null;
- }
- return new Install(activity, itemInfo);
- };
-
public static class Install extends SystemShortcut {
-
- public Install(BaseDraggingActivity target, ItemInfo itemInfo) {
- super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
- target, itemInfo);
+ public Install() {
+ super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label);
}
@Override
- public void onClick(View view) {
- Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
- mItemInfo.getTargetComponent().getPackageName());
- mTarget.startActivitySafely(view, intent, mItemInfo, null);
- AbstractFloatingView.closeAllOpenViews(mTarget);
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
+ boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo) &&
+ ((WorkspaceItemInfo) itemInfo).hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
+ boolean isInstantApp = false;
+ if (itemInfo instanceof com.android.launcher3.AppInfo) {
+ com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
+ isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
+ }
+ boolean enabled = supportsWebUI || isInstantApp;
+ if (!enabled) {
+ return null;
+ }
+ return createOnClickListener(activity, itemInfo);
+ }
+
+ public View.OnClickListener createOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
+ return view -> {
+ Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
+ itemInfo.getTargetComponent().getPackageName());
+ activity.startActivitySafely(view, intent, itemInfo, null);
+ AbstractFloatingView.closeAllOpenViews(activity);
+ };
}
}
- public static Factory<Launcher> DISMISS_PREDICTION = (launcher, itemInfo) -> {
- if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
- if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null;
- return new DismissPrediction(launcher, itemInfo);
- };
-
public static class DismissPrediction extends SystemShortcut<Launcher> {
- public DismissPrediction(Launcher launcher, ItemInfo itemInfo) {
- super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label, launcher,
- itemInfo);
+ public DismissPrediction() {
+ super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label);
}
@Override
- public void onClick(View view) {
- PopupContainerWithArrow.closeAllOpenViews(mTarget);
- mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
- AppLaunchTracker.INSTANCE.get(view.getContext()).onDismissApp(
- mItemInfo.getTargetComponent(),
- mItemInfo.user,
- AppLaunchTracker.CONTAINER_PREDICTIONS);
+ public View.OnClickListener getOnClickListener(Launcher activity, ItemInfo itemInfo) {
+ if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
+ if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null;
+ return (view) -> {
+ PopupContainerWithArrow.closeAllOpenViews(activity);
+ activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
+ AppLaunchTracker.INSTANCE.get(view.getContext())
+ .onDismissApp(itemInfo.getTargetComponent(),
+ itemInfo.user,
+ AppLaunchTracker.CONTAINER_PREDICTIONS);
+ };
}
}
- public static void dismissTaskMenuView(BaseDraggingActivity activity) {
+ protected static void dismissTaskMenuView(BaseDraggingActivity activity) {
AbstractFloatingView.closeOpenViews(activity, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
}
diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java
new file mode 100644
index 0000000..dfcc2f8
--- /dev/null
+++ b/src/com/android/launcher3/popup/SystemShortcutFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.popup;
+
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SystemShortcutFactory implements ResourceBasedOverride {
+
+ public static final MainThreadInitializedObject<SystemShortcutFactory> INSTANCE =
+ forOverride(SystemShortcutFactory.class, R.string.system_shortcut_factory_class);
+
+ /** Note that these are in order of priority. */
+ private final SystemShortcut[] mAllShortcuts;
+
+ @SuppressWarnings("unused")
+ public SystemShortcutFactory() {
+ this(new SystemShortcut.AppInfo(),
+ new SystemShortcut.Widgets(),
+ new SystemShortcut.Install(),
+ new SystemShortcut.DismissPrediction());
+ }
+
+ protected SystemShortcutFactory(SystemShortcut... shortcuts) {
+ mAllShortcuts = shortcuts;
+ }
+
+ public @NonNull List<SystemShortcut> getEnabledShortcuts(Launcher launcher, ItemInfo info) {
+ List<SystemShortcut> systemShortcuts = new ArrayList<>();
+ for (SystemShortcut systemShortcut : mAllShortcuts) {
+ if (systemShortcut.getOnClickListener(launcher, info) != null) {
+ systemShortcuts.add(systemShortcut);
+ }
+ }
+
+ return systemShortcuts;
+ }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index 8dd90e4..408ced2 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -25,7 +25,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.icons.BitmapRenderer;
@@ -43,36 +42,16 @@
@Override
public Bitmap createDragBitmap() {
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
- return BitmapRenderer.createHardwareBitmap(
- size + blurSizeOutline,
- size + blurSizeOutline,
- (c) -> drawDragViewOnBackground(c, size));
- } else {
- return createDragBitmapLegacy();
- }
- }
-
- private Bitmap createDragBitmapLegacy() {
- Drawable d = mView.getBackground();
- Rect bounds = getDrawableBounds(d);
int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
return BitmapRenderer.createHardwareBitmap(
size + blurSizeOutline,
size + blurSizeOutline,
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(b);
- canvas.translate(blurSizeOutline / 2, blurSizeOutline / 2);
- canvas.scale(size / bounds.width(), size / bounds.height(), 0, 0);
- canvas.translate(bounds.left, bounds.top);
- d.draw(canvas);
+ (c) -> drawDragViewOnBackground(c, size));
}
private void drawDragViewOnBackground(Canvas canvas, float size) {
Drawable d = mView.getBackground();
Rect bounds = getDrawableBounds(d);
-
canvas.translate(blurSizeOutline / 2, blurSizeOutline / 2);
canvas.scale(size / bounds.width(), size / bounds.height(), 0, 0);
canvas.translate(bounds.left, bounds.top);
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 64df384..d0e648f 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -24,10 +24,9 @@
import android.graphics.Color;
import android.os.Bundle;
import android.os.Debug;
+import android.util.Log;
import android.view.View;
-import androidx.annotation.Keep;
-
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
@@ -178,6 +177,11 @@
}
protected boolean isLauncherInitialized() {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "isLauncherInitialized " + Launcher.ACTIVITY_TRACKER.getCreatedActivity() + ", "
+ + LauncherAppState.getInstance(mContext).getModel().isModelLoaded());
+ }
return Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null
|| LauncherAppState.getInstance(mContext).getModel().isModelLoaded();
}
@@ -187,22 +191,6 @@
Runtime.getRuntime().runFinalization();
final CountDownLatch fence = new CountDownLatch(1);
- createFinalizationObserver(fence);
- try {
- do {
- Runtime.getRuntime().gc();
- Runtime.getRuntime().runFinalization();
- } while (!fence.await(100, TimeUnit.MILLISECONDS));
- } catch (InterruptedException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- // Create the observer in the scope of a method to minimize the chance that
- // it remains live in a DEX/machine register at the point of the fence guard.
- // This must be kept to avoid R8 inlining it.
- @Keep
- private static void createFinalizationObserver(CountDownLatch fence) {
new Object() {
@Override
protected void finalize() throws Throwable {
@@ -213,5 +201,13 @@
}
}
};
+ try {
+ do {
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ } while (!fence.await(100, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
}
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 1cfa4af..923c466 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -84,4 +84,7 @@
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
public static final String APP_NOT_DISABLED = "b/139891609";
+ public static final String NO_CONTEXT_MENU = "b/141770616";
+ public static final String LAUNCHER_DIDNT_INITIALIZE = "b/142514365";
+ public static final String CRASH_ADD_CUSTOM_SHORTCUT = "b/141568904";
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 60f6ee9..c5ba5ba 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -53,7 +53,7 @@
* TouchController for handling state changes
*/
public abstract class AbstractStateChangeTouchController
- implements TouchController, SingleAxisSwipeDetector.Listener {
+ implements TouchController, SwipeDetector.Listener {
// Progress after which the transition is assumed to be a success in case user does not fling
public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
@@ -65,8 +65,8 @@
protected final long ATOMIC_DURATION = getAtomicDuration();
protected final Launcher mLauncher;
- protected final SingleAxisSwipeDetector mDetector;
- protected final SingleAxisSwipeDetector.Direction mSwipeDirection;
+ protected final SwipeDetector mDetector;
+ protected final SwipeDetector.Direction mSwipeDirection;
private boolean mNoIntercept;
private boolean mIsLogContainerSet;
@@ -101,9 +101,9 @@
private float mAtomicComponentsStartProgress;
- public AbstractStateChangeTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
+ public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
mLauncher = l;
- mDetector = new SingleAxisSwipeDetector(l, this, dir);
+ mDetector = new SwipeDetector(l, this, dir);
mSwipeDirection = dir;
}
@@ -127,7 +127,7 @@
boolean ignoreSlopWhenSettling = false;
if (mCurrentAnimation != null) {
- directionsToDetectScroll = SingleAxisSwipeDetector.DIRECTION_BOTH;
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
ignoreSlopWhenSettling = true;
} else {
directionsToDetectScroll = getSwipeDirection();
@@ -152,10 +152,10 @@
LauncherState fromState = mLauncher.getStateManager().getState();
int swipeDirection = 0;
if (getTargetState(fromState, true /* isDragTowardPositive */) != fromState) {
- swipeDirection |= SingleAxisSwipeDetector.DIRECTION_POSITIVE;
+ swipeDirection |= SwipeDetector.DIRECTION_POSITIVE;
}
if (getTargetState(fromState, false /* isDragTowardPositive */) != fromState) {
- swipeDirection |= SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
+ swipeDirection |= SwipeDetector.DIRECTION_NEGATIVE;
}
return swipeDirection;
}
@@ -369,8 +369,7 @@
}
@Override
- public void onDragEnd(float velocity) {
- boolean fling = mDetector.isFling(velocity);
+ public void onDragEnd(float velocity, boolean fling) {
final int logAction = fling ? Touch.FLING : Touch.SWIPE;
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
@@ -407,7 +406,7 @@
} else {
startProgress = Utilities.boundToRange(progress
+ velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
- duration = BaseSwipeDetector.calculateDuration(velocity,
+ duration = SwipeDetector.calculateDuration(velocity,
endProgress - Math.max(progress, 0)) * durationMultiplier;
}
} else {
@@ -425,7 +424,7 @@
} else {
startProgress = Utilities.boundToRange(progress
+ velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
- duration = BaseSwipeDetector.calculateDuration(velocity,
+ duration = SwipeDetector.calculateDuration(velocity,
Math.min(progress, 1) - endProgress) * durationMultiplier;
}
}
diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java
deleted file mode 100644
index 12ca5ee..0000000
--- a/src/com/android/launcher3/touch/BaseSwipeDetector.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * 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.touch;
-
-import static android.view.MotionEvent.INVALID_POINTER_ID;
-
-import android.graphics.PointF;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.NonNull;
-
-/**
- * Scroll/drag/swipe gesture detector.
- *
- * Definition of swipe is different from android system in that this detector handles
- * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
- * swipe action happens.
- *
- * @see SingleAxisSwipeDetector
- * @see BothAxesSwipeDetector
- */
-public abstract class BaseSwipeDetector {
-
- private static final boolean DBG = false;
- private static final String TAG = "BaseSwipeDetector";
- private static final float ANIMATION_DURATION = 1200;
- /** The minimum release velocity in pixels per millisecond that triggers fling.*/
- private static final float RELEASE_VELOCITY_PX_MS = 1.0f;
- private static final PointF sTempPoint = new PointF();
-
- private final PointF mDownPos = new PointF();
- private final PointF mLastPos = new PointF();
- protected final boolean mIsRtl;
- protected final float mTouchSlop;
- protected final float mMaxVelocity;
-
- private int mActivePointerId = INVALID_POINTER_ID;
- private VelocityTracker mVelocityTracker;
- private PointF mLastDisplacement = new PointF();
- private PointF mDisplacement = new PointF();
- protected PointF mSubtractDisplacement = new PointF();
- private ScrollState mState = ScrollState.IDLE;
-
- protected boolean mIgnoreSlopWhenSettling;
-
- private enum ScrollState {
- IDLE,
- DRAGGING, // onDragStart, onDrag
- SETTLING // onDragEnd
- }
-
- protected BaseSwipeDetector(@NonNull ViewConfiguration config, boolean isRtl) {
- mTouchSlop = config.getScaledTouchSlop();
- mMaxVelocity = config.getScaledMaximumFlingVelocity();
- mIsRtl = isRtl;
- }
-
- public static long calculateDuration(float velocity, float progressNeeded) {
- // TODO: make these values constants after tuning.
- float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
- float travelDistance = Math.max(0.2f, progressNeeded);
- long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
- if (DBG) {
- Log.d(TAG, String.format(
- "calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
- }
- return duration;
- }
-
- public int getDownX() {
- return (int) mDownPos.x;
- }
-
- public int getDownY() {
- return (int) mDownPos.y;
- }
- /**
- * There's no touch and there's no animation.
- */
- public boolean isIdleState() {
- return mState == ScrollState.IDLE;
- }
-
- public boolean isSettlingState() {
- return mState == ScrollState.SETTLING;
- }
-
- public boolean isDraggingState() {
- return mState == ScrollState.DRAGGING;
- }
-
- public boolean isDraggingOrSettling() {
- return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
- }
-
- public void finishedScrolling() {
- setState(ScrollState.IDLE);
- }
-
- public boolean isFling(float velocity) {
- return Math.abs(velocity) > RELEASE_VELOCITY_PX_MS;
- }
-
- public boolean onTouchEvent(MotionEvent ev) {
- int actionMasked = ev.getActionMasked();
- if (actionMasked == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
-
- switch (actionMasked) {
- case MotionEvent.ACTION_DOWN:
- mActivePointerId = ev.getPointerId(0);
- mDownPos.set(ev.getX(), ev.getY());
- mLastPos.set(mDownPos);
- mLastDisplacement.set(0, 0);
- mDisplacement.set(0, 0);
-
- if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
- setState(ScrollState.DRAGGING);
- }
- break;
- //case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_POINTER_UP:
- int ptrIdx = ev.getActionIndex();
- int ptrId = ev.getPointerId(ptrIdx);
- if (ptrId == mActivePointerId) {
- final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
- mDownPos.set(
- ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
- ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
- mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
- mActivePointerId = ev.getPointerId(newPointerIdx);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == INVALID_POINTER_ID) {
- break;
- }
- mDisplacement.set(ev.getX(pointerIndex) - mDownPos.x,
- ev.getY(pointerIndex) - mDownPos.y);
- if (mIsRtl) {
- mDisplacement.x = -mDisplacement.x;
- }
-
- // handle state and listener calls.
- if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
- setState(ScrollState.DRAGGING);
- }
- if (mState == ScrollState.DRAGGING) {
- reportDragging(ev);
- }
- mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- // These are synthetic events and there is no need to update internal values.
- if (mState == ScrollState.DRAGGING) {
- setState(ScrollState.SETTLING);
- }
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- break;
- default:
- break;
- }
- return true;
- }
-
- //------------------- ScrollState transition diagram -----------------------------------
- //
- // IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
- // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
- // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
- // SETTLING -> (View settled) -> IDLE
-
- private void setState(ScrollState newState) {
- if (DBG) {
- Log.d(TAG, "setState:" + mState + "->" + newState);
- }
- // onDragStart and onDragEnd is reported ONLY on state transition
- if (newState == ScrollState.DRAGGING) {
- initializeDragging();
- if (mState == ScrollState.IDLE) {
- reportDragStart(false /* recatch */);
- } else if (mState == ScrollState.SETTLING) {
- reportDragStart(true /* recatch */);
- }
- }
- if (newState == ScrollState.SETTLING) {
- reportDragEnd();
- }
-
- mState = newState;
- }
-
- private void initializeDragging() {
- if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
- mSubtractDisplacement.set(0, 0);
- } else {
- mSubtractDisplacement.x = mDisplacement.x > 0 ? mTouchSlop : -mTouchSlop;
- mSubtractDisplacement.y = mDisplacement.y > 0 ? mTouchSlop : -mTouchSlop;
- }
- }
-
- protected abstract boolean shouldScrollStart(PointF displacement);
-
- private void reportDragStart(boolean recatch) {
- reportDragStartInternal(recatch);
- if (DBG) {
- Log.d(TAG, "onDragStart recatch:" + recatch);
- }
- }
-
- protected abstract void reportDragStartInternal(boolean recatch);
-
- private void reportDragging(MotionEvent event) {
- if (mDisplacement != mLastDisplacement) {
- if (DBG) {
- Log.d(TAG, String.format("onDrag disp=%s", mDisplacement));
- }
-
- mLastDisplacement.set(mDisplacement);
- sTempPoint.set(mDisplacement.x - mSubtractDisplacement.x,
- mDisplacement.y - mSubtractDisplacement.y);
- reportDraggingInternal(sTempPoint, event);
- }
- }
-
- protected abstract void reportDraggingInternal(PointF displacement, MotionEvent event);
-
- private void reportDragEnd() {
- mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
- PointF velocity = new PointF(mVelocityTracker.getXVelocity() / 1000,
- mVelocityTracker.getYVelocity() / 1000);
- if (mIsRtl) {
- velocity.x = -velocity.x;
- }
- if (DBG) {
- Log.d(TAG, String.format("onScrollEnd disp=%.1s, velocity=%.1s",
- mDisplacement, velocity));
- }
-
- reportDragEndInternal(velocity);
- }
-
- protected abstract void reportDragEndInternal(PointF velocity);
-}
diff --git a/src/com/android/launcher3/touch/BothAxesSwipeDetector.java b/src/com/android/launcher3/touch/BothAxesSwipeDetector.java
deleted file mode 100644
index 944391e..0000000
--- a/src/com/android/launcher3/touch/BothAxesSwipeDetector.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.touch;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.launcher3.Utilities;
-
-/**
- * Two dimensional scroll/drag/swipe gesture detector that reports x and y displacement/velocity.
- */
-public class BothAxesSwipeDetector extends BaseSwipeDetector {
-
- public static final int DIRECTION_UP = 1 << 0;
- // Note that this will track left instead of right in RTL.
- public static final int DIRECTION_RIGHT = 1 << 1;
- public static final int DIRECTION_DOWN = 1 << 2;
- // Note that this will track right instead of left in RTL.
- public static final int DIRECTION_LEFT = 1 << 3;
-
- /* Client of this gesture detector can register a callback. */
- private final Listener mListener;
-
- private int mScrollDirections;
-
- public BothAxesSwipeDetector(@NonNull Context context, @NonNull Listener l) {
- this(ViewConfiguration.get(context), l, Utilities.isRtl(context.getResources()));
- }
-
- @VisibleForTesting
- protected BothAxesSwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
- boolean isRtl) {
- super(config, isRtl);
- mListener = l;
- }
-
- public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
- mScrollDirections = scrollDirectionFlags;
- mIgnoreSlopWhenSettling = ignoreSlop;
- }
-
- @Override
- protected boolean shouldScrollStart(PointF displacement) {
- // Check if the client is interested in scroll in current direction.
- boolean canScrollUp = (mScrollDirections & DIRECTION_UP) > 0
- && displacement.y <= -mTouchSlop;
- boolean canScrollRight = (mScrollDirections & DIRECTION_RIGHT) > 0
- && displacement.x >= mTouchSlop;
- boolean canScrollDown = (mScrollDirections & DIRECTION_DOWN) > 0
- && displacement.y >= mTouchSlop;
- boolean canScrollLeft = (mScrollDirections & DIRECTION_LEFT) > 0
- && displacement.x <= -mTouchSlop;
- return canScrollUp || canScrollRight || canScrollDown || canScrollLeft;
- }
-
- @Override
- protected void reportDragStartInternal(boolean recatch) {
- mListener.onDragStart(!recatch);
- }
-
- @Override
- protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
- mListener.onDrag(displacement, event);
- }
-
- @Override
- protected void reportDragEndInternal(PointF velocity) {
- mListener.onDragEnd(velocity);
- }
-
- /** Listener to receive updates on the swipe. */
- public interface Listener {
- /** @param start whether this was the original drag start, as opposed to a recatch. */
- void onDragStart(boolean start);
-
- boolean onDrag(PointF displacement, MotionEvent motionEvent);
-
- void onDragEnd(PointF velocity);
- }
-}
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index aa02d0a..86d2b39 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -79,10 +79,19 @@
}
private static boolean onAllAppsItemLongClick(View v) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_CONTEXT_MENU, "onAllAppsItemLongClick1");
+ }
Launcher launcher = Launcher.getLauncher(v.getContext());
if (!canStartDrag(launcher)) return false;
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_CONTEXT_MENU, "onAllAppsItemLongClick2");
+ }
// When we have exited all apps or are in transition, disregard long clicks
if (!launcher.isInState(ALL_APPS) && !launcher.isInState(OVERVIEW)) return false;
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_CONTEXT_MENU, "onAllAppsItemLongClick3");
+ }
if (launcher.getWorkspace().isSwitchingState()) return false;
// Start the drag
diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
deleted file mode 100644
index f2ebc45..0000000
--- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.touch;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.launcher3.Utilities;
-
-/**
- * One dimensional scroll/drag/swipe gesture detector (either HORIZONTAL or VERTICAL).
- */
-public class SingleAxisSwipeDetector extends BaseSwipeDetector {
-
- public static final int DIRECTION_POSITIVE = 1 << 0;
- public static final int DIRECTION_NEGATIVE = 1 << 1;
- public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
-
- public static final Direction VERTICAL = new Direction() {
-
- @Override
- boolean isPositive(float displacement) {
- // Up
- return displacement < 0;
- }
-
- @Override
- boolean isNegative(float displacement) {
- // Down
- return displacement > 0;
- }
-
- @Override
- float extractDirection(PointF direction) {
- return direction.y;
- }
-
- @Override
- boolean canScrollStart(PointF displacement, float touchSlop) {
- return Math.abs(displacement.y) >= Math.max(Math.abs(displacement.x), touchSlop);
- }
-
- };
-
- public static final Direction HORIZONTAL = new Direction() {
-
- @Override
- boolean isPositive(float displacement) {
- // Right
- return displacement > 0;
- }
-
- @Override
- boolean isNegative(float displacement) {
- // Left
- return displacement < 0;
- }
-
- @Override
- float extractDirection(PointF direction) {
- return direction.x;
- }
-
- @Override
- boolean canScrollStart(PointF displacement, float touchSlop) {
- return Math.abs(displacement.x) >= Math.max(Math.abs(displacement.y), touchSlop);
- }
- };
-
- private final Direction mDir;
- /* Client of this gesture detector can register a callback. */
- private final Listener mListener;
-
- private int mScrollDirections;
-
- public SingleAxisSwipeDetector(@NonNull Context context, @NonNull Listener l,
- @NonNull Direction dir) {
- this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
- }
-
- @VisibleForTesting
- protected SingleAxisSwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
- @NonNull Direction dir, boolean isRtl) {
- super(config, isRtl);
- mListener = l;
- mDir = dir;
- }
-
- public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
- mScrollDirections = scrollDirectionFlags;
- mIgnoreSlopWhenSettling = ignoreSlop;
- }
-
- public int getScrollDirections() {
- return mScrollDirections;
- }
-
- /**
- * Returns if the start drag was towards the positive direction or negative.
- *
- * @see #setDetectableScrollConditions(int, boolean)
- * @see #DIRECTION_BOTH
- */
- public boolean wasInitialTouchPositive() {
- return mDir.isPositive(mDir.extractDirection(mSubtractDisplacement));
- }
-
- @Override
- protected boolean shouldScrollStart(PointF displacement) {
- // Reject cases where the angle or slop condition is not met.
- if (!mDir.canScrollStart(displacement, mTouchSlop)) {
- return false;
- }
-
- // Check if the client is interested in scroll in current direction.
- float displacementComponent = mDir.extractDirection(displacement);
- return canScrollNegative(displacementComponent) || canScrollPositive(displacementComponent);
- }
-
- private boolean canScrollNegative(float displacement) {
- return (mScrollDirections & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(displacement);
- }
-
- private boolean canScrollPositive(float displacement) {
- return (mScrollDirections & DIRECTION_POSITIVE) > 0 && mDir.isPositive(displacement);
- }
-
- @Override
- protected void reportDragStartInternal(boolean recatch) {
- mListener.onDragStart(!recatch);
- }
-
- @Override
- protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
- mListener.onDrag(mDir.extractDirection(displacement), event);
- }
-
- @Override
- protected void reportDragEndInternal(PointF velocity) {
- float velocityComponent = mDir.extractDirection(velocity);
- mListener.onDragEnd(velocityComponent);
- }
-
- /** Listener to receive updates on the swipe. */
- public interface Listener {
- /** @param start whether this was the original drag start, as opposed to a recatch. */
- void onDragStart(boolean start);
-
- // TODO remove
- boolean onDrag(float displacement);
-
- default boolean onDrag(float displacement, MotionEvent event) {
- return onDrag(displacement);
- }
-
- void onDragEnd(float velocity);
- }
-
- public abstract static class Direction {
-
- abstract boolean isPositive(float displacement);
-
- abstract boolean isNegative(float displacement);
-
- /** Returns the part of the given {@link PointF} that is relevant to this direction. */
- abstract float extractDirection(PointF point);
-
- /** Reject cases where the angle or slop condition is not met. */
- abstract boolean canScrollStart(PointF displacement, float touchSlop);
-
- }
-}
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
new file mode 100644
index 0000000..c38ca24
--- /dev/null
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -0,0 +1,391 @@
+/*
+ * 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.touch;
+
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * One dimensional scroll/drag/swipe gesture detector.
+ *
+ * Definition of swipe is different from android system in that this detector handles
+ * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
+ * swipe action happens
+ */
+public class SwipeDetector {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "SwipeDetector";
+ private static final float ANIMATION_DURATION = 1200;
+ /** The minimum release velocity in pixels per millisecond that triggers fling.*/
+ private static final float RELEASE_VELOCITY_PX_MS = 1.0f;
+
+ public static final int DIRECTION_POSITIVE = 1 << 0;
+ public static final int DIRECTION_NEGATIVE = 1 << 1;
+ public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
+
+ public static final Direction VERTICAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
+ return ev.getY(pointerIndex) - refPoint.y;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getX(pointerIndex) - downPos.x);
+ }
+
+ @Override
+ float getVelocity(VelocityTracker tracker, boolean isRtl) {
+ return tracker.getYVelocity();
+ }
+
+ @Override
+ boolean isPositive(float displacement) {
+ // Up
+ return displacement < 0;
+ }
+
+ @Override
+ boolean isNegative(float displacement) {
+ // Down
+ return displacement > 0;
+ }
+ };
+
+ public static final Direction HORIZONTAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
+ float displacement = ev.getX(pointerIndex) - refPoint.x;
+ if (isRtl) {
+ displacement = -displacement;
+ }
+ return displacement;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getY(pointerIndex) - downPos.y);
+ }
+
+ @Override
+ float getVelocity(VelocityTracker tracker, boolean isRtl) {
+ float velocity = tracker.getXVelocity();
+ if (isRtl) {
+ velocity = -velocity;
+ }
+ return velocity;
+ }
+
+ @Override
+ boolean isPositive(float displacement) {
+ // Right
+ return displacement > 0;
+ }
+
+ @Override
+ boolean isNegative(float displacement) {
+ // Left
+ return displacement < 0;
+ }
+ };
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final Direction mDir;
+ private final boolean mIsRtl;
+ private final float mTouchSlop;
+ private final float mMaxVelocity;
+ /* Client of this gesture detector can register a callback. */
+ private final Listener mListener;
+
+ private int mActivePointerId = INVALID_POINTER_ID;
+ private VelocityTracker mVelocityTracker;
+ private float mLastDisplacement;
+ private float mDisplacement;
+ private float mSubtractDisplacement;
+ private boolean mIgnoreSlopWhenSettling;
+ private int mScrollDirections;
+ private ScrollState mState = ScrollState.IDLE;
+
+ private enum ScrollState {
+ IDLE,
+ DRAGGING, // onDragStart, onDrag
+ SETTLING // onDragEnd
+ }
+
+ public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
+ this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
+ }
+
+ @VisibleForTesting
+ protected SwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
+ @NonNull Direction dir, boolean isRtl) {
+ mListener = l;
+ mDir = dir;
+ mIsRtl = isRtl;
+ mTouchSlop = config.getScaledTouchSlop();
+ mMaxVelocity = config.getScaledMaximumFlingVelocity();
+ }
+
+ public static long calculateDuration(float velocity, float progressNeeded) {
+ // TODO: make these values constants after tuning.
+ float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
+ float travelDistance = Math.max(0.2f, progressNeeded);
+ long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
+ if (DBG) {
+ Log.d(TAG, String.format(
+ "calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
+ }
+ return duration;
+ }
+
+ public int getDownX() {
+ return (int) mDownPos.x;
+ }
+
+ public int getDownY() {
+ return (int) mDownPos.y;
+ }
+ /**
+ * There's no touch and there's no animation.
+ */
+ public boolean isIdleState() {
+ return mState == ScrollState.IDLE;
+ }
+
+ public boolean isSettlingState() {
+ return mState == ScrollState.SETTLING;
+ }
+
+ public boolean isDraggingState() {
+ return mState == ScrollState.DRAGGING;
+ }
+
+ public boolean isDraggingOrSettling() {
+ return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
+ }
+
+ public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
+ mScrollDirections = scrollDirectionFlags;
+ mIgnoreSlopWhenSettling = ignoreSlop;
+ }
+
+ public int getScrollDirections() {
+ return mScrollDirections;
+ }
+
+ public void finishedScrolling() {
+ setState(ScrollState.IDLE);
+ }
+
+ /**
+ * Returns if the start drag was towards the positive direction or negative.
+ *
+ * @see #setDetectableScrollConditions(int, boolean)
+ * @see #DIRECTION_BOTH
+ */
+ public boolean wasInitialTouchPositive() {
+ return mDir.isPositive(mSubtractDisplacement);
+ }
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ int actionMasked = ev.getActionMasked();
+ if (actionMasked == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ mLastDisplacement = 0;
+ mDisplacement = 0;
+
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ setState(ScrollState.DRAGGING);
+ }
+ break;
+ //case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos, mIsRtl);
+
+ // handle state and listener calls.
+ if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
+ setState(ScrollState.DRAGGING);
+ }
+ if (mState == ScrollState.DRAGGING) {
+ reportDragging(ev);
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // These are synthetic events and there is no need to update internal values.
+ if (mState == ScrollState.DRAGGING) {
+ setState(ScrollState.SETTLING);
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ //------------------- ScrollState transition diagram -----------------------------------
+ //
+ // IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
+ // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
+ // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
+ // SETTLING -> (View settled) -> IDLE
+
+ private void setState(ScrollState newState) {
+ if (DBG) {
+ Log.d(TAG, "setState:" + mState + "->" + newState);
+ }
+ // onDragStart and onDragEnd is reported ONLY on state transition
+ if (newState == ScrollState.DRAGGING) {
+ initializeDragging();
+ if (mState == ScrollState.IDLE) {
+ reportDragStart(false /* recatch */);
+ } else if (mState == ScrollState.SETTLING) {
+ reportDragStart(true /* recatch */);
+ }
+ }
+ if (newState == ScrollState.SETTLING) {
+ reportDragEnd();
+ }
+
+ mState = newState;
+ }
+
+ private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
+ // reject cases where the angle or slop condition is not met.
+ if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)
+ > Math.abs(mDisplacement)) {
+ return false;
+ }
+
+ // Check if the client is interested in scroll in current direction.
+ return ((mScrollDirections & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(mDisplacement))
+ || ((mScrollDirections & DIRECTION_POSITIVE) > 0 && mDir.isPositive(mDisplacement));
+ }
+
+ private void reportDragStart(boolean recatch) {
+ mListener.onDragStart(!recatch);
+ if (DBG) {
+ Log.d(TAG, "onDragStart recatch:" + recatch);
+ }
+ }
+
+ private void initializeDragging() {
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ mSubtractDisplacement = 0;
+ } else if (mDisplacement > 0) {
+ mSubtractDisplacement = mTouchSlop;
+ } else {
+ mSubtractDisplacement = -mTouchSlop;
+ }
+ }
+
+ private void reportDragging(MotionEvent event) {
+ if (mDisplacement != mLastDisplacement) {
+ if (DBG) {
+ Log.d(TAG, String.format("onDrag disp=%.1f", mDisplacement));
+ }
+
+ mLastDisplacement = mDisplacement;
+ mListener.onDrag(mDisplacement - mSubtractDisplacement, event);
+ }
+ }
+
+ private void reportDragEnd() {
+ mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
+ float velocity = mDir.getVelocity(mVelocityTracker, mIsRtl) / 1000;
+ if (DBG) {
+ Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
+ mDisplacement, velocity));
+ }
+
+ mListener.onDragEnd(velocity, Math.abs(velocity) > RELEASE_VELOCITY_PX_MS);
+ }
+
+ /** Listener to receive updates on the swipe. */
+ public interface Listener {
+ void onDragStart(boolean start);
+
+ boolean onDrag(float displacement);
+
+ default boolean onDrag(float displacement, MotionEvent event) {
+ return onDrag(displacement);
+ }
+
+ void onDragEnd(float velocity, boolean fling);
+ }
+
+ public abstract static class Direction {
+
+ abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint,
+ boolean isRtl);
+
+ /**
+ * Distance in pixels a touch can wander before we think the user is scrolling.
+ */
+ abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
+
+ abstract float getVelocity(VelocityTracker tracker, boolean isRtl);
+
+ abstract boolean isPositive(float displacement);
+
+ abstract boolean isNegative(float displacement);
+ }
+}
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
index 499f655..b4f361f 100644
--- a/src/com/android/launcher3/util/ActivityTracker.java
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -45,46 +45,33 @@
}
public void onActivityDestroyed(T activity) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "onActivityDestroyed");
+ }
if (mCurrentActivity.get() == activity) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "onActivityDestroyed: clear");
+ }
mCurrentActivity.clear();
}
}
- /**
- * Schedules the callback to be notified when the activity is created.
- * @return true if the activity is already created, false otherwise
- */
- public boolean schedule(SchedulerCallback<? extends T> callback) {
+ public void schedule(SchedulerCallback<? extends T> callback) {
synchronized (this) {
mPendingCallback = new WeakReference<>((SchedulerCallback<T>) callback);
}
- if (!notifyInitIfPending()) {
- // If the activity doesn't already exist, then post and wait for the activity to start
- MAIN_EXECUTOR.execute(this);
- return false;
- }
- return true;
+ MAIN_EXECUTOR.execute(this);
}
@Override
public void run() {
- notifyInitIfPending();
- }
-
- /**
- * Notifies the pending callback if the activity is now created.
- * @return true if the activity is now created.
- */
- private boolean notifyInitIfPending() {
T activity = mCurrentActivity.get();
if (activity != null) {
- notifyInitIfPending(activity, activity.isStarted());
- return true;
+ initIfPending(activity, activity.isStarted());
}
- return false;
}
- public boolean notifyInitIfPending(T activity, boolean alreadyOnHome) {
+ public boolean initIfPending(T activity, boolean alreadyOnHome) {
SchedulerCallback<T> pendingCallback = mPendingCallback.get();
if (pendingCallback != null) {
if (!pendingCallback.init(activity, alreadyOnHome)) {
@@ -110,6 +97,10 @@
}
public boolean handleCreate(T activity) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "ActivityTracker.handleCreate " + mCurrentActivity.get() + " => " + activity);
+ }
mCurrentActivity = new WeakReference<>(activity);
return handleIntent(activity, activity.getIntent(), false, false);
}
@@ -133,7 +124,7 @@
}
}
if (!result && !explicitIntent) {
- result = notifyInitIfPending(activity, alreadyOnHome);
+ result = initIfPending(activity, alreadyOnHome);
}
return result;
}
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index 2d64353..00adf10 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -26,7 +26,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
/**
@@ -38,7 +37,7 @@
private final Context mContext;
private CommitParams mCommitParams;
- private BitmapInfo mIcon;
+ private Bitmap mIcon;
private UserHandle mUser;
public ContentWriter(Context context, CommitParams commitParams) {
@@ -80,7 +79,7 @@
return this;
}
- public ContentWriter putIcon(BitmapInfo value, UserHandle user) {
+ public ContentWriter putIcon(Bitmap value, UserHandle user) {
mIcon = value;
mUser = user;
return this;
@@ -98,7 +97,7 @@
Preconditions.assertNonUiThread();
if (mIcon != null && !LauncherAppState.getInstance(context).getIconCache()
.isDefaultIcon(mIcon, mUser)) {
- mValues.put(LauncherSettings.Favorites.ICON, GraphicsUtils.flattenBitmap(mIcon.icon));
+ mValues.put(LauncherSettings.Favorites.ICON, GraphicsUtils.flattenBitmap(mIcon));
mIcon = null;
}
return mValues;
diff --git a/src/com/android/launcher3/util/LooperIdleLock.java b/src/com/android/launcher3/util/LooperIdleLock.java
index f4ccf42..2896535 100644
--- a/src/com/android/launcher3/util/LooperIdleLock.java
+++ b/src/com/android/launcher3/util/LooperIdleLock.java
@@ -22,30 +22,29 @@
/**
* Utility class to block execution until the UI looper is idle.
*/
-public class LooperIdleLock implements MessageQueue.IdleHandler {
+public class LooperIdleLock implements MessageQueue.IdleHandler, Runnable {
private final Object mLock;
private boolean mIsLocked;
- private Looper mLooper;
public LooperIdleLock(Object lock, Looper looper) {
mLock = lock;
- mLooper = looper;
mIsLocked = true;
looper.getQueue().addIdleHandler(this);
}
@Override
+ public void run() {
+ Looper.myQueue().addIdleHandler(this);
+ }
+
+ @Override
public boolean queueIdle() {
synchronized (mLock) {
mIsLocked = false;
mLock.notify();
}
- // Manually remove from the list in case we're calling this outside of the idle callbacks
- // (this is Ok in the normal flow as well because MessageQueue makes a copy of all handlers
- // before calling back)
- mLooper.getQueue().removeIdleHandler(this);
return false;
}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 91f687e..7b4e0c6 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -274,9 +274,6 @@
} else {
packageName = cn.getPackageName();
}
- if (packageName == null) {
- packageName = intent.getPackage();
- }
if (packageName != null) {
try {
PackageInfo info = pm.getPackageInfo(packageName, 0);
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index a03b743..a69cd6c 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -27,6 +27,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -76,12 +77,17 @@
public static void fetchAndUpdateShortcutIconAsync(
@NonNull Context context, @NonNull WorkspaceItemInfo info, @NonNull ShortcutInfo si,
boolean badged) {
+ if (info.iconBitmap == null) {
+ // use low res icon as placeholder while the actual icon is being fetched.
+ info.iconBitmap = BitmapInfo.LOW_RES_ICON;
+ info.iconColor = Themes.getColorAccent(context);
+ }
MODEL_EXECUTOR.execute(() -> {
- try (LauncherIcons li = LauncherIcons.obtain(context)) {
- info.bitmap = li.createShortcutIcon(si, badged, null);
- LauncherAppState.getInstance(context).getModel()
- .updateAndBindWorkspaceItem(info, si);
- }
+ LauncherIcons li = LauncherIcons.obtain(context);
+ BitmapInfo bitmapInfo = li.createShortcutIcon(si, badged, true, null);
+ info.applyFrom(bitmapInfo);
+ li.recycle();
+ LauncherAppState.getInstance(context).getModel().updateAndBindWorkspaceItem(info, si);
});
}
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index ec87e79..a133f01 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -23,6 +23,7 @@
import android.os.IBinder;
import android.os.Message;
import android.view.inputmethod.InputMethodManager;
+import com.android.launcher3.uioverrides.UiFactory;
/**
* Utility class for offloading some class from UI thread
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
deleted file mode 100644
index 04741a1..0000000
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import static android.os.VibrationEffect.createPredefined;
-import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.annotation.TargetApi;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Build;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.provider.Settings;
-
-/**
- * Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public class VibratorWrapper {
-
- public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE =
- new MainThreadInitializedObject<>(VibratorWrapper::new);
-
- private static final VibrationEffect EFFECT_CLICK =
- createPredefined(VibrationEffect.EFFECT_CLICK);
-
- /**
- * Haptic when entering overview.
- */
- public static final VibrationEffect OVERVIEW_HAPTIC = EFFECT_CLICK;
-
- private final Vibrator mVibrator;
- private final boolean mHasVibrator;
-
- private boolean mIsHapticFeedbackEnabled;
-
- public VibratorWrapper(Context context) {
- mVibrator = context.getSystemService(Vibrator.class);
- mHasVibrator = mVibrator.hasVibrator();
- if (mHasVibrator) {
- final ContentResolver resolver = context.getContentResolver();
- mIsHapticFeedbackEnabled = isHapticFeedbackEnabled(resolver);
- final ContentObserver observer = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
- @Override
- public void onChange(boolean selfChange) {
- mIsHapticFeedbackEnabled = isHapticFeedbackEnabled(resolver);
- }
- };
- resolver.registerContentObserver(Settings.System.getUriFor(HAPTIC_FEEDBACK_ENABLED),
- false /* notifyForDescendents */, observer);
- } else {
- mIsHapticFeedbackEnabled = false;
- }
- }
-
- private boolean isHapticFeedbackEnabled(ContentResolver resolver) {
- return Settings.System.getInt(resolver, HAPTIC_FEEDBACK_ENABLED, 0) == 1;
- }
-
- /** Vibrates with the given effect if haptic feedback is available and enabled. */
- public void vibrate(VibrationEffect vibrationEffect) {
- if (mHasVibrator && mIsHapticFeedbackEnabled) {
- UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect));
- }
- }
-}
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index 5a131c8..61ba4e5 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -55,9 +55,7 @@
mLoadAnimationCompleted = true;
}
- if (mAttachedView.isAttachedToWindow()) {
- attachObserver();
- }
+ attachObserver();
}
private void attachObserver() {
diff --git a/src/com/android/launcher3/util/ViewPool.java b/src/com/android/launcher3/util/ViewPool.java
index 5b33f18..8af048d 100644
--- a/src/com/android/launcher3/util/ViewPool.java
+++ b/src/com/android/launcher3/util/ViewPool.java
@@ -21,12 +21,12 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.launcher3.util.ViewPool.Reusable;
+
import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.util.ViewPool.Reusable;
-
/**
* Utility class to maintain a pool of reusable views.
* During initialization, views are inflated on the background thread.
@@ -58,18 +58,14 @@
Preconditions.assertUIThread();
Handler handler = new Handler();
- // LayoutInflater is not thread save as it maintains a global variable 'mConstructorArgs'.
- // Create a different copy to use on the background thread.
- LayoutInflater inflater = mInflater.cloneInContext(mInflater.getContext());
-
// Inflate views on a non looper thread. This allows us to catch errors like calling
// "new Handler()" in constructor easily.
new Thread(() -> {
for (int i = 0; i < initialSize; i++) {
- T view = inflateNewView(inflater);
+ T view = inflateNewView();
handler.post(() -> addToPool(view));
}
- }, "ViewPool-init").start();
+ }).start();
}
@UiThread
@@ -98,12 +94,12 @@
mCurrentSize--;
return (T) mPool[mCurrentSize];
}
- return inflateNewView(mInflater);
+ return inflateNewView();
}
@AnyThread
- private T inflateNewView(LayoutInflater inflater) {
- return (T) inflater.inflate(mLayoutId, mParent, false);
+ private T inflateNewView() {
+ return (T) mInflater.inflate(mLayoutId, mParent, false);
}
/**
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 195a77a..a4518ba 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -32,14 +32,13 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.touch.BaseSwipeDetector;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.touch.SwipeDetector;
/**
* Extension of AbstractFloatingView with common methods for sliding in from bottom
*/
public abstract class AbstractSlideInView extends AbstractFloatingView
- implements SingleAxisSwipeDetector.Listener {
+ implements SwipeDetector.Listener {
protected static Property<AbstractSlideInView, Float> TRANSLATION_SHIFT =
new Property<AbstractSlideInView, Float>(Float.class, "translationShift") {
@@ -58,7 +57,7 @@
protected static final float TRANSLATION_SHIFT_OPENED = 0f;
protected final Launcher mLauncher;
- protected final SingleAxisSwipeDetector mSwipeDetector;
+ protected final SwipeDetector mSwipeDetector;
protected final ObjectAnimator mOpenCloseAnimator;
protected View mContent;
@@ -74,8 +73,7 @@
mLauncher = Launcher.getLauncher(context);
mScrollInterpolator = Interpolators.SCROLL_CUBIC;
- mSwipeDetector = new SingleAxisSwipeDetector(context, this,
- SingleAxisSwipeDetector.VERTICAL);
+ mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@@ -99,7 +97,7 @@
}
int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
- SingleAxisSwipeDetector.DIRECTION_NEGATIVE : 0;
+ SwipeDetector.DIRECTION_NEGATIVE : 0;
mSwipeDetector.setDetectableScrollConditions(
directionsToDetectScroll, false);
mSwipeDetector.onTouchEvent(ev);
@@ -124,7 +122,7 @@
return mIsOpen && mOpenCloseAnimator.isRunning();
}
- /* SingleAxisSwipeDetector.Listener */
+ /* SwipeDetector.Listener */
@Override
public void onDragStart(boolean start) { }
@@ -138,17 +136,17 @@
}
@Override
- public void onDragEnd(float velocity) {
- if ((mSwipeDetector.isFling(velocity) && velocity > 0) || mTranslationShift > 0.5f) {
+ public void onDragEnd(float velocity, boolean fling) {
+ if ((fling && velocity > 0) || mTranslationShift > 0.5f) {
mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
- mOpenCloseAnimator.setDuration(BaseSwipeDetector.calculateDuration(
+ mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(
velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift));
close(true);
} else {
mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(
TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
mOpenCloseAnimator.setDuration(
- BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
+ SwipeDetector.calculateDuration(velocity, mTranslationShift))
.setInterpolator(Interpolators.DEACCEL);
mOpenCloseAnimator.start();
}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index e43fc8a..2a4c5a7 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -170,8 +170,10 @@
// Only look for controllers if we are not dispatching from gesture area and proxy is
// not active
mActiveController = findControllerToHandleTouch(ev);
+
+ if (mActiveController != null) return true;
}
- return mActiveController != null;
+ return false;
}
@Override
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index c63d745..45c0d90 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -415,7 +415,7 @@
int width = isFolderIcon ? originalView.getWidth() : (int) pos.width();
int height = isFolderIcon ? originalView.getHeight() : (int) pos.height();
if (supportsAdaptiveIcons) {
- drawable = getFullDrawable(l, info, width, height, sTmpObjArray);
+ drawable = getFullDrawable(l, info, width, height, false, sTmpObjArray);
if (drawable instanceof AdaptiveIconDrawable) {
badge = getBadge(l, info, sTmpObjArray[0]);
} else {
@@ -428,7 +428,7 @@
// Similar to DragView, we simply use the BubbleTextView icon here.
drawable = btvIcon;
} else {
- drawable = getFullDrawable(l, info, width, height, sTmpObjArray);
+ drawable = getFullDrawable(l, info, width, height, false, sTmpObjArray);
}
}
}
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 6038873..50db40f 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -16,9 +16,6 @@
package com.android.launcher3.widget;
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
-import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
-
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -36,11 +33,12 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.R;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Themes;
@@ -130,22 +128,24 @@
mCenterDrawable.setCallback(null);
mCenterDrawable = null;
}
- if (info.bitmap.icon != null) {
+ if (info.iconBitmap != null) {
// The view displays three modes,
// 1) App icon in the center
// 2) Preload icon in the center
// 3) Setup icon in the center and app icon in the top right corner.
+ DrawableFactory drawableFactory = DrawableFactory.INSTANCE.get(getContext());
if (mDisabledForSafeMode) {
- FastBitmapDrawable disabledIcon = newIcon(getContext(), info);
+ FastBitmapDrawable disabledIcon = drawableFactory.newIcon(getContext(), info);
disabledIcon.setIsDisabled(true);
mCenterDrawable = disabledIcon;
mSettingIconDrawable = null;
} else if (isReadyForClickSetup()) {
- mCenterDrawable = newIcon(getContext(), info);
+ mCenterDrawable = drawableFactory.newIcon(getContext(), info);
mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
- updateSettingColor(info.bitmap.color);
+ updateSettingColor(info.iconColor);
} else {
- mCenterDrawable = newPendingIcon(getContext(), info);
+ mCenterDrawable = DrawableFactory.INSTANCE.get(getContext())
+ .newPendingIcon(getContext(), info);
mSettingIconDrawable = null;
applyState();
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index f713b33..6944879 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -36,6 +36,7 @@
import com.android.launcher3.SimpleOnStylusPressListener;
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.model.WidgetItem;
@@ -181,8 +182,10 @@
return;
}
if (bitmap != null) {
- mWidgetImage.setBitmap(bitmap, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
- BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx)));
+ mWidgetImage.setBitmap(bitmap,
+ DrawableFactory.INSTANCE.get(getContext()).getBadgeForUser(mItem.user,
+ getContext(), BaseIconFactory.getBadgeSizeForIconSize(
+ mDeviceProfile.allAppsIconSizePx)));
if (mAnimatePreview) {
mWidgetImage.setAlpha(0f);
ViewPropertyAnimator anim = mWidgetImage.animate();
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
index f3b325d..435125b 100644
--- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java
+++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
@@ -18,8 +18,6 @@
import android.util.Log;
-import androidx.recyclerview.widget.RecyclerView;
-
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
@@ -27,6 +25,8 @@
import java.util.ArrayList;
import java.util.Iterator;
+import androidx.recyclerview.widget.RecyclerView;
+
/**
* Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
* methods accordingly.
@@ -137,7 +137,7 @@
}
private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
- return curInfo.bitmap.icon.equals(newInfo.bitmap.icon)
- && !mIconCache.isDefaultIcon(curInfo.bitmap, curInfo.user);
+ return curInfo.iconBitmap.equals(newInfo.iconBitmap) &&
+ !mIconCache.isDefaultIcon(curInfo.iconBitmap, curInfo.user);
}
}
diff --git a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
deleted file mode 100644
index 60eb304..0000000
--- a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.plugins;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Implement this interface to receive a callback when the user swipes right
- * to left on the gesture area. It won't fire if the user has quick switched to a previous app
- * (swiped right) and the current app isn't yet the active one (i.e., if swiping left would take
- * the user to a more recent app).
- */
-@ProvidesInterface(action = com.android.systemui.plugins.OverscrollPlugin.ACTION,
- version = com.android.systemui.plugins.OverlayPlugin.VERSION)
-public interface OverscrollPlugin extends Plugin {
-
- String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERSCROLL";
- int VERSION = 1;
-
- String DEVICE_STATE_LOCKED = "Locked";
- String DEVICE_STATE_LAUNCHER = "Launcher";
- String DEVICE_STATE_APP = "App";
- String DEVICE_STATE_UNKNOWN = "Unknown";
-
- /**
- * Called when the user completed a right to left swipe in the gesture area.
- *
- * @param deviceState One of the DEVICE_STATE_* constants.
- */
- void onOverscroll(String deviceState);
-}
diff --git a/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java b/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
index cd9f33d..0ebea3d 100644
--- a/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
+++ b/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
@@ -34,9 +34,9 @@
/**
* Sets up the recents overview extra card and fills in data.
*
- * @param context Plugin context
+ * @param context Plugin context
* @param frameLayout PlaceholderView
- * @param activity Recents activity to hold extra view
+ * @param activity Recents activity to hold extra view
*/
void setupView(Context context, FrameLayout frameLayout, Activity activity);
}
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
similarity index 75%
rename from src/com/android/launcher3/touch/AllAppsSwipeController.java
rename to src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
index 31a5d79..bd6ea50 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
@@ -1,19 +1,4 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.touch;
+package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -24,6 +9,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationComponents;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
@@ -34,7 +21,7 @@
private MotionEvent mTouchDownEvent;
public AllAppsSwipeController(Launcher l) {
- super(l, SingleAxisSwipeDetector.VERTICAL);
+ super(l, SwipeDetector.VERTICAL);
}
@Override
@@ -71,8 +58,8 @@
@Override
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
- return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent)
- ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
+ return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent) ?
+ ContainerType.HOTSEAT : ContainerType.WORKSPACE;
}
@Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
deleted file mode 100644
index 5407ea3..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.uioverrides;
-
-import android.app.Activity;
-import android.app.Person;
-import android.content.pm.ShortcutInfo;
-
-import com.android.launcher3.Utilities;
-
-import java.io.PrintWriter;
-
-public class ApiWrapper {
-
- public static boolean dumpActivity(Activity activity, PrintWriter writer) {
- return false;
- }
-
- public static Person[] getPersons(ShortcutInfo si) {
- return Utilities.EMPTY_PERSON_ARRAY;
- }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
new file mode 100644
index 0000000..6d9ed88
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -0,0 +1,105 @@
+/*
+ * 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.uioverrides;
+
+import android.app.Activity;
+import android.app.Person;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.util.TouchController;
+
+import java.io.PrintWriter;
+
+public class UiFactory {
+
+ public static TouchController[] createTouchControllers(Launcher launcher) {
+ return new TouchController[] {
+ launcher.getDragController(), new AllAppsSwipeController(launcher)};
+ }
+
+ public static Runnable enableLiveUIChanges(Launcher l) {
+ return null;
+ }
+
+ public static StateHandler[] getStateHandler(Launcher launcher) {
+ return new StateHandler[] {
+ launcher.getAllAppsController(), launcher.getWorkspace() };
+ }
+
+ public static void resetOverview(Launcher launcher) { }
+
+ public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
+
+ public static void onCreate(Launcher launcher) { }
+
+ public static void onStart(Launcher launcher) { }
+
+ public static void onEnterAnimationComplete(Context context) {}
+
+ public static void onLauncherStateOrResumeChanged(Launcher launcher) { }
+
+ public static void onTrimMemory(Launcher launcher, int level) { }
+
+ public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
+ CancellationSignal cancellationSignal) { }
+
+ public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+ return false;
+ }
+
+ public static void setBackButtonAlpha(Launcher launcher, float alpha, boolean animate) { }
+
+
+ public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
+ return new ScaleAndTranslation(1.1f, 0f, 0f);
+ }
+
+ public static RotationMode getRotationMode(DeviceProfile dp) {
+ return RotationMode.NORMAL;
+ }
+
+ public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
+ int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
+ Bundle options) {
+ return false;
+ }
+
+ public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
+ Bundle options) {
+ return false;
+ }
+
+ public static void resetPendingActivityResults(Launcher launcher, int requestCode) { }
+
+ /** No-op. */
+ public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) { }
+
+ public static Person[] getPersons(ShortcutInfo si) {
+ return Utilities.EMPTY_PERSON_ARRAY;
+ }
+}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 5cf96c8..24b5b02 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -18,7 +18,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3.tests">
- <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="25"
tools:overrideLibrary="android.support.test.uiautomator.v18"/>
<application android:debuggable="true">
diff --git a/tests/dummy_app/AndroidManifest.xml b/tests/dummy_app/AndroidManifest.xml
index f00138c..9d0a74a 100644
--- a/tests/dummy_app/AndroidManifest.xml
+++ b/tests/dummy_app/AndroidManifest.xml
@@ -21,7 +21,7 @@
to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.aardwolf">
- <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"/>
<application android:label="Aardwolf">
<activity
android:name="Activity1"
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 0dcfaa8..7029ad5 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -142,7 +142,7 @@
when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
.thenReturn(BitmapInfo.fromBitmap(icon));
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
- assertEquals(icon, info.bitmap.icon);
+ assertEquals(icon, info.iconBitmap);
assertEquals("my-shortcut", info.title);
assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
}
diff --git a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
similarity index 72%
rename from tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
rename to tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
index 5174e4d..f209fae 100644
--- a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
@@ -15,12 +15,6 @@
*/
package com.android.launcher3.touch;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
-
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyObject;
@@ -45,19 +39,19 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SingleAxisSwipeDetectorTest {
+public class SwipeDetectorTest {
- private static final String TAG = SingleAxisSwipeDetectorTest.class.getSimpleName();
+ private static final String TAG = SwipeDetectorTest.class.getSimpleName();
public static void L(String s, Object... parts) {
Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
}
private TouchEventGenerator mGenerator;
- private SingleAxisSwipeDetector mDetector;
+ private SwipeDetector mDetector;
private int mTouchSlop;
@Mock
- private SingleAxisSwipeDetector.Listener mMockListener;
+ private SwipeDetector.Listener mMockListener;
@Mock
private ViewConfiguration mMockConfig;
@@ -71,8 +65,8 @@
doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
.getScaledMaximumFlingVelocity();
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
- mDetector.setDetectableScrollConditions(DIRECTION_BOTH, false);
+ mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
+ mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
mTouchSlop = orgConfig.getScaledTouchSlop();
doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
@@ -81,8 +75,8 @@
@Test
public void testDragStart_verticalPositive() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
- mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
+ mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
+ mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 - mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
@@ -91,8 +85,8 @@
@Test
public void testDragStart_verticalNegative() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
- mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
+ mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
+ mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
@@ -109,8 +103,8 @@
@Test
public void testDragStart_horizontalPositive() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
- mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
+ mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
+ mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 + mTouchSlop, 100);
@@ -120,8 +114,8 @@
@Test
public void testDragStart_horizontalNegative() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
- mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
+ mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
+ mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 - mTouchSlop, 100);
@@ -131,8 +125,8 @@
@Test
public void testDragStart_horizontalRtlPositive() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
- mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
+ mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
+ mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 - mTouchSlop, 100);
@@ -142,8 +136,8 @@
@Test
public void testDragStart_horizontalRtlNegative() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
- mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
+ mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
+ mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 + mTouchSlop, 100);
@@ -166,6 +160,6 @@
mGenerator.move(0, 100, 100 + mTouchSlop * 2);
mGenerator.lift(0);
// TODO: actually calculate the following parameters and do exact value checks.
- verify(mMockListener).onDragEnd(anyFloat());
+ verify(mMockListener).onDragEnd(anyFloat(), anyBoolean());
}
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index bb19515..62989a3 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -304,7 +304,7 @@
protected void waitForLauncherCondition(
String message, Function<Launcher, Boolean> condition, long timeout) {
if (!TestHelpers.isInLauncherProcess()) return;
- Wait.atMost(message, () -> getFromLauncher(condition), timeout, mLauncher);
+ Wait.atMost(message, () -> getFromLauncher(condition), timeout);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
@@ -317,7 +317,7 @@
final Object fromLauncher = getFromLauncher(f);
output[0] = fromLauncher;
return fromLauncher != null;
- }, timeout, mLauncher);
+ }, timeout);
return (T) output[0];
}
@@ -331,7 +331,7 @@
Wait.atMost(message, () -> {
testThreadAction.run();
return getFromLauncher(condition);
- }, timeout, mLauncher);
+ }, timeout);
}
protected LauncherActivityInfo getSettingsApp() {
@@ -373,8 +373,7 @@
startIntent(
getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
packageName),
- By.pkg(packageName).depth(0),
- true /* newTask */);
+ By.pkg(packageName).depth(0));
}
public static void startTestActivity(int activityNumber) {
@@ -383,17 +382,12 @@
getLaunchIntentForPackage(packageName);
intent.setComponent(new ComponentName(packageName,
"com.android.launcher3.tests.Activity" + activityNumber));
- startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber),
- false /* newTask */);
+ startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber));
}
- private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
+ private static void startIntent(Intent intent, BySelector selector) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
- if (newTask) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- } else {
- intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- }
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
UiDevice.getInstance(getInstrumentation())
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 0321bcd..5e87612 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -18,10 +18,6 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
-import static com.android.launcher3.util.rule.TestStabilityRule.RUN_FLAFOR;
-import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_PRESUBMIT;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -38,7 +34,6 @@
import com.android.launcher3.tapl.AppIcon;
import com.android.launcher3.tapl.AppIconMenu;
import com.android.launcher3.tapl.AppIconMenuItem;
-import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.views.OptionsPopupView;
@@ -354,7 +349,6 @@
@Ignore("Temporarily disabled to unblock merging to master")
@PortraitLandscape
public void testDragCustomShortcut() {
- if (!TestHelpers.isInLauncherProcess()) return; // b/143725213
mLauncher.getWorkspace().openAllWidgets()
.getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
.dragToWorkspace();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 0472ce1..e1b3ede 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -103,12 +103,12 @@
setResult(acceptConfig);
if (acceptConfig) {
- Wait.atMost(null, new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+ Wait.atMost(null, new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT);
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
} else {
// Verify that the widget id is deleted.
Wait.atMost(null, () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
- DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+ DEFAULT_ACTIVITY_TIMEOUT);
}
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 259f9ed..b8ca5de 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -16,7 +16,6 @@
package com.android.launcher3.ui.widget;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
-import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -30,7 +29,6 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import org.junit.Rule;
import org.junit.Test;
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index d909ad7..07129dd 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -59,8 +59,7 @@
@RunWith(AndroidJUnit4.class)
public class RequestPinItemTest extends AbstractLauncherUiTest {
- @Rule
- public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+ @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
private String mCallbackAction;
private String mShortcutId;
@@ -85,10 +84,10 @@
.equals(AppWidgetNoConfig.class.getName()));
}
- @Test
+ @Test
public void testPinWidgetNoConfig_customPreview() throws Throwable {
// Command to set custom preview
- Intent command = RequestPinItemActivity.getCommandIntent(
+ Intent command = RequestPinItemActivity.getCommandIntent(
RequestPinItemActivity.class, "setRemoteViewColor").putExtra(
RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED);
@@ -170,8 +169,7 @@
// Go back to home
mLauncher.pressHome();
- Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT,
- mLauncher);
+ Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT);
}
/**
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index 2663d02..899686b 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -3,8 +3,6 @@
import android.os.SystemClock;
import android.util.Log;
-import com.android.launcher3.tapl.LauncherInstrumentation;
-
import org.junit.Assert;
/**
@@ -14,13 +12,11 @@
private static final long DEFAULT_SLEEP_MS = 200;
- public static void atMost(String message, Condition condition, long timeout,
- LauncherInstrumentation launcher) {
- atMost(message, condition, timeout, DEFAULT_SLEEP_MS, launcher);
+ public static void atMost(String message, Condition condition, long timeout) {
+ atMost(message, condition, timeout, DEFAULT_SLEEP_MS);
}
- public static void atMost(String message, Condition condition, long timeout, long sleepMillis,
- LauncherInstrumentation launcher) {
+ public static void atMost(String message, Condition condition, long timeout, long sleepMillis) {
final long startTime = SystemClock.uptimeMillis();
long endTime = startTime + timeout;
Log.d("Wait", "atMost: " + startTime + " - " + endTime);
@@ -44,7 +40,6 @@
throw new RuntimeException(t);
}
Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
- launcher.checkForAnomaly();
Assert.fail(message);
}
}
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index 858cb38..69bf01d 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -21,7 +21,6 @@
import android.os.Build;
import android.util.Log;
-import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
import org.junit.rules.TestRule;
@@ -58,7 +57,7 @@
public static final int PLATFORM_PRESUBMIT = 0x8;
public static final int PLATFORM_POSTSUBMIT = 0x10;
- public static final int RUN_FLAFOR = getRunFlavor();
+ private static final int RUN_FLAFOR = getRunFlavor();
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@@ -87,19 +86,6 @@
}
private static int getRunFlavor() {
- final String flavorOverride = InstrumentationRegistry.getArguments().getString("flavor");
-
- if (flavorOverride != null) {
- Log.d(TAG, "Flavor override: " + flavorOverride);
- try {
- return (int) TestStabilityRule.class.getField(flavorOverride).get(null);
- } catch (NoSuchFieldException e) {
- throw new AssertionError("Unrecognized run flavor override: " + flavorOverride);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- }
-
final String launcherVersion;
try {
launcherVersion = getInstrumentation().
diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
index c7f7cd6..a31d8a6 100644
--- a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
+++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
@@ -23,19 +23,16 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.graphics.Bitmap;
-import android.view.LayoutInflater;
-
-import androidx.recyclerview.widget.RecyclerView;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import android.view.LayoutInflater;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.MultiHashMap;
@@ -49,6 +46,8 @@
import java.util.ArrayList;
import java.util.Map;
+import androidx.recyclerview.widget.RecyclerView;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WidgetsListAdapterTest {
@@ -137,7 +136,7 @@
PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
pInfo.title = pInfo.packageName;
pInfo.user = wi.user;
- pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+ pInfo.iconBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
newMap.addToList(pInfo, wi);
if (newMap.size() == num) {
break;
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 1ecfff7..e1e9b8d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -29,8 +29,6 @@
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.testing.TestProtocol;
-import java.util.stream.Collectors;
-
/**
* Operations on AllApps opened from Home. Also a parent for All Apps opened from Overview.
*/
@@ -69,7 +67,7 @@
return false;
}
if (iconBounds.bottom > displayBottom) {
- LauncherInstrumentation.log("hasClickableIcon: icon bottom below bottom offset");
+ LauncherInstrumentation.log("hasClickableIcon: icon center bellow bottom offset");
return false;
}
LauncherInstrumentation.log("hasClickableIcon: icon is clickable");
@@ -118,12 +116,7 @@
displayBottom)) {
mLauncher.scrollToLastVisibleRow(
allAppsContainer,
- mLauncher.getObjectsInContainer(allAppsContainer, "icon")
- .stream()
- .filter(object ->
- object.getVisibleBounds().bottom
- <= displayBottom)
- .collect(Collectors.toList()),
+ mLauncher.getObjectsInContainer(allAppsContainer, "icon"),
searchBox.getVisibleBounds().bottom
- allAppsContainer.getVisibleBounds().top);
final int newScroll = getAllAppsScroll();
@@ -170,7 +163,7 @@
"Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
++attempts <= MAX_SCROLL_ATTEMPTS);
- mLauncher.scroll(allAppsContainer, Direction.UP, margins, 12);
+ mLauncher.scroll(allAppsContainer, Direction.UP, margins, 50);
}
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 2650cf9..0879404 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -64,12 +64,10 @@
import com.android.launcher3.testing.TestProtocol;
import com.android.systemui.shared.system.QuickStepContract;
-import org.junit.Assert;
-
+import java.util.ArrayList;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
@@ -79,6 +77,8 @@
import java.util.function.Consumer;
import java.util.function.Function;
+import org.junit.Assert;
+
/**
* The main tapl object. The only object that can be explicitly constructed by the using code. It
* produces all other objects.
@@ -298,14 +298,6 @@
return null;
}
- public void checkForAnomaly() {
- final String anomalyMessage = getAnomalyMessage();
- if (anomalyMessage != null) {
- failWithSystemHealth(
- "Tests are broken by a non-Launcher system error: " + anomalyMessage);
- }
- }
-
private String getVisibleStateMessage() {
if (hasLauncherObject(WIDGETS_RES_ID)) return "Widgets";
if (hasLauncherObject(OVERVIEW_RES_ID)) return "Overview";
@@ -339,17 +331,20 @@
}
private void fail(String message) {
- checkForAnomaly();
+ message = "http://go/tapl : " + getContextDescription() + message;
- failWithSystemHealth("http://go/tapl : " + getContextDescription() + message +
- " (visible state: " + getVisibleStateMessage() + ")");
- }
+ final String anomaly = getAnomalyMessage();
+ if (anomaly != null) {
+ message = anomaly + ", which causes:\n" + message;
+ } else {
+ message = message + " (visible state: " + getVisibleStateMessage() + ")";
+ }
- private void failWithSystemHealth(String message) {
final String systemHealth = getSystemHealthMessage();
if (systemHealth != null) {
message = message
- + ", perhaps because of system health problems:\n<<<<<<<<<<<<<<<<<<\n"
+ + ", which might be a consequence of system health "
+ + "problems:\n<<<<<<<<<<<<<<<<<<\n"
+ systemHealth + "\n>>>>>>>>>>>>>>>>>>";
}
@@ -429,7 +424,11 @@
// b/136278866
for (int i = 0; i != 100; ++i) {
if (getNavigationModeMismatchError() == null) break;
- sleep(100);
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
}
final String error = getNavigationModeMismatchError();
@@ -536,7 +535,8 @@
// accessibility events prior to pressing Home.
final String action;
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
- checkForAnomaly();
+ final String anomaly = getAnomalyMessage();
+ if (anomaly != null) fail("Can't swipe up to Home: " + anomaly);
final Point displaySize = getRealDisplaySize();
@@ -795,18 +795,15 @@
final int distance = gestureStart - container.getVisibleBounds().top - topPadding;
final int bottomMargin = container.getVisibleBounds().height() - distance;
- // TODO: Make the gesture steps dependent on the distance so that it can run for various
- // screen sizes
- final int totalMargin = Math.max(bottomMargin, getBottomGestureMargin(container));
scroll(
container,
Direction.DOWN,
new Rect(
0,
- totalMargin / 2,
0,
- totalMargin / 2),
- 80);
+ 0,
+ Math.max(bottomMargin, getBottomGestureMargin(container))),
+ 150);
}
void scroll(UiObject2 container, Direction direction, Rect margins, int steps) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index 16a64a7..4f8aeb1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -58,7 +58,7 @@
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD),
mLauncher.getDevice().getDisplayWidth() / 2,
0,
- 12,
+ 50,
ALL_APPS_STATE_ORDINAL);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index db3314e..d1261e0 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -38,7 +38,7 @@
* Operations on the workspace screen.
*/
public final class Workspace extends Home {
- private static final int DRAG_DURATION = 500;
+ private static final int DRAG_DURACTION = 2000;
private static final int FLING_STEPS = 10;
private final UiObject2 mHotseat;
@@ -72,7 +72,7 @@
start.y,
start.x,
start.y - swipeHeight - mLauncher.getTouchSlop(),
- 12,
+ 60,
ALL_APPS_STATE_ORDINAL);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
@@ -166,7 +166,7 @@
launcher.waitForLauncherObject(longPressIndicator);
LauncherInstrumentation.log("dragIconToWorkspace: indicator");
launcher.movePointer(
- downTime, SystemClock.uptimeMillis(), DRAG_DURATION, launchableCenter, dest);
+ downTime, SystemClock.uptimeMillis(), DRAG_DURACTION, launchableCenter, dest);
LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
launcher.sendPointer(
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);