[automerger skipped] DO NOT MERGE - qt-qpr1-dev-plus-aosp-without-vendor@5915889 into stage-aosp-master am: 2b329c75c3
am: d9006a8021 -s ours
am skip reason: subject contains skip directive
Change-Id: I173b8093f739a7d17dfe48477e1307a87f22d6b6
diff --git a/Android.mk b/Android.mk
index 9d113d9..3d1d996 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,12 +23,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
-
-ifneq (,$(wildcard frameworks/base))
- LOCAL_STATIC_JAVA_LIBRARIES:= PluginCoreLib
-else
- LOCAL_STATIC_JAVA_LIBRARIES:= libPluginCore
-endif
+LOCAL_STATIC_JAVA_LIBRARIES:= PluginCoreLib
LOCAL_SRC_FILES := \
$(call all-java-files-under, src_plugins)
@@ -89,8 +84,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, src_shortcuts_overrides) \
- $(call all-java-files-under, src_ui_overrides) \
- $(call all-java-files-under, src_flags)
+ $(call all-java-files-under, src_ui_overrides)
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
# Proguard is disable for testing. Derivarive prjects to keep proguard enabled
@@ -151,11 +145,10 @@
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
ifneq (,$(wildcard frameworks/base))
- LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
LOCAL_PRIVATE_PLATFORM_APIS := true
else
- LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
LOCAL_SDK_VERSION := system_current
LOCAL_MIN_SDK_VERSION := 26
endif
@@ -169,7 +162,6 @@
$(call all-java-files-under, src) \
$(call all-java-files-under, quickstep/src) \
$(call all-java-files-under, quickstep/recents_ui_overrides/src) \
- $(call all-java-files-under, src_flags) \
$(call all-java-files-under, src_shortcuts_overrides)
LOCAL_RESOURCE_DIR := \
@@ -224,11 +216,10 @@
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
ifneq (,$(wildcard frameworks/base))
- LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
LOCAL_PRIVATE_PLATFORM_APIS := true
else
- LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
LOCAL_SDK_VERSION := system_current
LOCAL_MIN_SDK_VERSION := 26
endif
@@ -271,11 +262,10 @@
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
ifneq (,$(wildcard frameworks/base))
- LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
LOCAL_PRIVATE_PLATFORM_APIS := true
else
- LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
LOCAL_SDK_VERSION := system_current
LOCAL_MIN_SDK_VERSION := 26
endif
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..f3db20e
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,2 @@
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
index bcb1f5c..3953fd0 100644
--- a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
+++ b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
@@ -40,7 +40,9 @@
@Override
protected void composeRecentsLaunchAnimator(AnimatorSet anim, View v,
- RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ boolean launcherClosing) {
// Stubbed. Recents launch animation will come from the recents view itself and will not
// use remote animations.
}
@@ -74,21 +76,23 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
AnimationResult result) {
boolean isGoingToRecents =
- taskIsATargetWithMode(targetCompats, mLauncher.getTaskId(), MODE_OPENING)
+ taskIsATargetWithMode(appTargets, mLauncher.getTaskId(), MODE_OPENING)
&& (mLauncher.getStateManager().getState() == LauncherState.OVERVIEW);
if (isGoingToRecents) {
IconRecentsView recentsView = mLauncher.getOverviewPanel();
if (!recentsView.isReadyForRemoteAnim()) {
recentsView.setOnReadyForRemoteAnimCallback(() ->
- postAsyncCallback(mHandler, () -> onCreateAnimation(targetCompats, result))
+ postAsyncCallback(mHandler, () -> onCreateAnimation(appTargets,
+ wallpaperTargets, result))
);
return;
}
}
- super.onCreateAnimation(targetCompats, result);
+ super.onCreateAnimation(appTargets, wallpaperTargets, result);
}
}
}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index cbc77d2..f2aa842 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -20,7 +20,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
@@ -51,7 +50,7 @@
.getMode().hasGestures;
list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));
}
- if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
+ if (Utilities.IS_DEBUG_DEVICE
&& !launcher.getDeviceProfile().isMultiWindowMode
&& !launcher.getDeviceProfile().isVerticalBarLayout()) {
list.add(new StatusBarTouchController(launcher));
@@ -90,5 +89,5 @@
return RotationMode.NORMAL;
}
- public static void clearSwipeSharedState(boolean finishAnimation) {}
+ 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 92900f2..6b50088 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -32,7 +32,6 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherAnimationRunner;
import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.views.IconRecentsView;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -47,12 +46,12 @@
RemoteAnimationProvider {
private static final String TAG = "AppToOverviewAnimationProvider";
- private final ActivityControlHelper<T> mHelper;
+ private final BaseActivityInterface<T> mHelper;
private final int mTargetTaskId;
private IconRecentsView mRecentsView;
private AppToOverviewAnimationListener mAnimationReadyListener;
- AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
+ AppToOverviewAnimationProvider(BaseActivityInterface<T> helper, int targetTaskId) {
mHelper = helper;
mTargetTaskId = targetTaskId;
}
@@ -76,7 +75,7 @@
if (mAnimationReadyListener != null) {
mAnimationReadyListener.onActivityReady(activity);
}
- ActivityControlHelper.AnimationFactory factory =
+ BaseActivityInterface.AnimationFactory factory =
mHelper.prepareRecentsUI(activity, wasVisible,
false /* animate activity */, (controller) -> {
controller.dispatchOnStart();
@@ -86,7 +85,7 @@
anim.start();
});
factory.onRemoteAnimationReceived(null);
- factory.createActivityController(getRecentsLaunchDuration());
+ factory.createActivityInterface(getRecentsLaunchDuration());
mRecentsView = activity.getOverviewPanel();
return false;
}
@@ -95,11 +94,12 @@
* Create remote window animation from the currently running app to the overview panel. Should
* be called after {@link #onActivityReady}.
*
- * @param targetCompats the target apps
+ * @param appTargets the target apps
* @return animation from app to overview
*/
@Override
- public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+ public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
if (mAnimationReadyListener != null) {
mAnimationReadyListener.onWindowAnimationCreated();
}
@@ -112,14 +112,14 @@
return anim;
}
- RemoteAnimationTargetSet targetSet =
- new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
- mRecentsView.setTransitionedFromApp(!targetSet.isAnimatingHome());
+ RemoteAnimationTargets targets =
+ new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_CLOSING);
+ mRecentsView.setTransitionedFromApp(!targets.isAnimatingHome());
RemoteAnimationTargetCompat recentsTarget = null;
RemoteAnimationTargetCompat closingAppTarget = null;
- for (RemoteAnimationTargetCompat target : targetCompats) {
+ for (RemoteAnimationTargetCompat target : appTargets) {
if (target.mode == MODE_OPENING) {
recentsTarget = target;
} else if (target.mode == MODE_CLOSING && target.taskId == mTargetTaskId) {
@@ -157,16 +157,17 @@
false /* startAtFrontOfQueue */) {
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
AnimationResult result) {
IconRecentsView recentsView = mRecentsView;
if (!recentsView.isReadyForRemoteAnim()) {
recentsView.setOnReadyForRemoteAnimCallback(() -> postAsyncCallback(handler,
- () -> onCreateAnimation(targetCompats, result))
+ () -> onCreateAnimation(appTargets, wallpaperTargets, result))
);
return;
}
- result.setAnimation(createWindowAnimation(targetCompats), context);
+ result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
}
};
return ActivityOptionsCompat.makeRemoteAnimation(
diff --git a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
similarity index 85%
rename from go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
rename to go/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 057b48b..2af8441 100644
--- a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -26,21 +26,21 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.views.IconRecentsView;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
/**
- * {@link ActivityControlHelper} for recents when the default launcher is different than the
+ * {@link BaseActivityInterface} for recents when the default launcher is different than the
* currently running one and apps should interact with the {@link RecentsActivity} as opposed
* to the in-launcher one.
*/
-public final class FallbackActivityControllerHelper extends
- GoActivityControlHelper<RecentsActivity> {
+public final class FallbackActivityInterface extends
+ GoActivityInterface<RecentsActivity> {
- public FallbackActivityControllerHelper() { }
+ public FallbackActivityInterface() { }
@Override
public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
@@ -58,17 +58,17 @@
boolean isAnimatingToRecents = false;
@Override
- public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
+ public void onRemoteAnimationReceived(RemoteAnimationTargets targets) {
isAnimatingToRecents = targets != null && targets.isAnimatingHome();
if (!isAnimatingToRecents) {
rv.setAlpha(1);
}
- createActivityController(getSwipeUpDestinationAndLength(
+ createActivityInterface(getSwipeUpDestinationAndLength(
activity.getDeviceProfile(), activity, new Rect()));
}
@Override
- public void createActivityController(long transitionLength) {
+ public void createActivityInterface(long transitionLength) {
if (!isAnimatingToRecents) {
return;
}
@@ -85,13 +85,13 @@
@Override
public ActivityInitListener createActivityInitListener(
BiPredicate<RecentsActivity, Boolean> onInitListener) {
- return new RecentsActivityTracker(onInitListener);
+ return new ActivityInitListener(onInitListener, RecentsActivity.ACTIVITY_TRACKER);
}
@Nullable
@Override
public RecentsActivity getCreatedActivity() {
- return RecentsActivityTracker.getCurrentActivity();
+ return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
diff --git a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java b/go/quickstep/src/com/android/quickstep/GoActivityInterface.java
similarity index 93%
rename from go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
rename to go/quickstep/src/com/android/quickstep/GoActivityInterface.java
index 274a347..5ce0f4c 100644
--- a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
+++ b/go/quickstep/src/com/android/quickstep/GoActivityInterface.java
@@ -13,8 +13,8 @@
*
* @param <T> activity that contains the overview
*/
-public abstract class GoActivityControlHelper<T extends BaseDraggingActivity> implements
- ActivityControlHelper<T> {
+public abstract class GoActivityInterface<T extends BaseDraggingActivity> implements
+ BaseActivityInterface<T> {
@Override
public void onTransitionCancelled(T activity, boolean activityVisible) {
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
similarity index 92%
rename from go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
rename to go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index b0d9cda..5bff8e8 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -30,10 +30,10 @@
import java.util.function.Consumer;
/**
- * {@link ActivityControlHelper} for the in-launcher recents.
+ * {@link BaseActivityInterface} for the in-launcher recents.
* TODO: Implement the app to overview animation functionality
*/
-public final class LauncherActivityControllerHelper extends GoActivityControlHelper<Launcher> {
+public final class LauncherActivityInterface extends GoActivityInterface<Launcher> {
@Override
public AnimationFactory prepareRecentsUI(Launcher activity,
@@ -43,8 +43,7 @@
activity.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(true);
//TODO: Implement this based off where the recents view needs to be for app => recents anim.
return new AnimationFactory() {
- @Override
- public void createActivityController(long transitionLength) {
+ public void createActivityInterface(long transitionLength) {
callback.accept(activity.getStateManager().createAnimationToNewWorkspace(
fromState, OVERVIEW, transitionLength));
}
@@ -55,7 +54,7 @@
}
@Override
- public ActivityInitListener createActivityInitListener(
+ public LauncherInitListener createActivityInitListener(
BiPredicate<Launcher, Boolean> onInitListener) {
return new LauncherInitListener(onInitListener);
}
diff --git a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 0fa3d86..a436ce7 100644
--- a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -15,8 +15,8 @@
*/
package com.android.quickstep;
-import static com.android.systemui.shared.system.ActivityManagerWrapper
- .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.annotation.TargetApi;
import android.content.Context;
@@ -25,11 +25,10 @@
import android.view.ViewConfiguration;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.AppToOverviewAnimationProvider.AppToOverviewAnimationListener;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.views.IconRecentsView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.LatencyTrackerCompat;
@@ -41,41 +40,41 @@
public class OverviewCommandHelper {
private final Context mContext;
- private final ActivityManagerWrapper mAM;
+ private final RecentsAnimationDeviceState mDeviceState;
private final RecentsModel mRecentsModel;
- private final MainThreadExecutor mMainThreadExecutor;
private final OverviewComponentObserver mOverviewComponentObserver;
private long mLastToggleTime;
- public OverviewCommandHelper(Context context, OverviewComponentObserver observer) {
+ public OverviewCommandHelper(Context context, RecentsAnimationDeviceState deviceState,
+ OverviewComponentObserver observer) {
mContext = context;
- mAM = ActivityManagerWrapper.getInstance();
- mMainThreadExecutor = new MainThreadExecutor();
+ mDeviceState = deviceState;
mRecentsModel = RecentsModel.INSTANCE.get(mContext);
mOverviewComponentObserver = observer;
}
public void onOverviewToggle() {
// If currently screen pinning, do not enter overview
- if (mAM.isScreenPinningActive()) {
+ if (mDeviceState.isScreenPinningActive()) {
return;
}
- mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- mMainThreadExecutor.execute(new RecentsActivityCommand<>());
+ ActivityManagerWrapper.getInstance()
+ .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
}
public void onOverviewShown(boolean triggeredFromAltTab) {
- mMainThreadExecutor.execute(new ShowRecentsCommand());
+ MAIN_EXECUTOR.execute(new ShowRecentsCommand());
}
public void onOverviewHidden() {
- mMainThreadExecutor.execute(new HideRecentsCommand());
+ MAIN_EXECUTOR.execute(new HideRecentsCommand());
}
public void onTip(int actionType, int viewType) {
- mMainThreadExecutor.execute(() ->
+ MAIN_EXECUTOR.execute(() ->
UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
}
@@ -102,15 +101,15 @@
private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
- protected final ActivityControlHelper<T> mHelper;
+ protected final BaseActivityInterface<T> mHelper;
private final long mCreateTime;
private final long mToggleClickedTime = SystemClock.uptimeMillis();
private boolean mUserEventLogged;
- private ActivityInitListener mListener;
+ private ActivityInitListener<T> mListener;
public RecentsActivityCommand() {
- mHelper = mOverviewComponentObserver.getActivityControlHelper();
+ mHelper = mOverviewComponentObserver.getActivityInterface();
mCreateTime = SystemClock.elapsedRealtime();
// Preload the plan
@@ -161,7 +160,7 @@
// Otherwise, start overview.
mListener = mHelper.createActivityInitListener(provider::onActivityReady);
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
- provider, mContext, mMainThreadExecutor.getHandler(),
+ provider, mContext, MAIN_EXECUTOR.getHandler(),
provider.getRecentsLaunchDuration());
}
diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 577b175..f743663 100644
--- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -34,8 +34,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -46,15 +47,6 @@
public class TouchInteractionService extends Service {
private static final String TAG = "GoTouchInteractionService";
- private boolean mIsUserUnlocked;
- private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
- initWhenUserUnlocked();
- }
- }
- };
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@@ -65,26 +57,26 @@
public void onInitialize(Bundle bundle) throws RemoteException {
ISystemUiProxy iSystemUiProxy = ISystemUiProxy.Stub
.asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
- mRecentsModel.setSystemUiProxy(iSystemUiProxy);
+ SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(iSystemUiProxy);
}
@Override
public void onOverviewToggle() {
- if (mIsUserUnlocked) {
+ if (mDeviceState.isUserUnlocked()) {
mOverviewCommandHelper.onOverviewToggle();
}
}
@Override
public void onOverviewShown(boolean triggeredFromAltTab) {
- if (mIsUserUnlocked) {
+ if (mDeviceState.isUserUnlocked()) {
mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
}
}
@Override
public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- if (mIsUserUnlocked && triggeredFromAltTab && !triggeredFromHomeKey) {
+ if (mDeviceState.isUserUnlocked() && triggeredFromAltTab && !triggeredFromHomeKey) {
// onOverviewShownFromAltTab hides the overview and ends at the target app
mOverviewCommandHelper.onOverviewHidden();
}
@@ -92,7 +84,7 @@
@Override
public void onTip(int actionType, int viewType) {
- if (mIsUserUnlocked) {
+ if (mDeviceState.isUserUnlocked()) {
mOverviewCommandHelper.onTip(actionType, viewType);
}
}
@@ -129,7 +121,7 @@
public void onMotionEvent(MotionEvent ev) { }
public void onBind(ISystemUiProxy iSystemUiProxy) {
- mRecentsModel.setSystemUiProxy(iSystemUiProxy);
+ SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(iSystemUiProxy);
}
};
@@ -139,41 +131,33 @@
return sConnected;
}
- public static final LooperExecutor BACKGROUND_EXECUTOR =
- new LooperExecutor(UiThreadHelper.getBackgroundLooper());
-
private RecentsModel mRecentsModel;
private OverviewComponentObserver mOverviewComponentObserver;
private OverviewCommandHelper mOverviewCommandHelper;
+ private RecentsAnimationDeviceState mDeviceState;
@Override
public void onCreate() {
super.onCreate();
- if (UserManagerCompat.getInstance(this).isUserUnlocked(Process.myUserHandle())) {
- initWhenUserUnlocked();
- } else {
- mIsUserUnlocked = false;
- registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
- }
+ mDeviceState = new RecentsAnimationDeviceState(this);
+ mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
sConnected = true;
}
- private void initWhenUserUnlocked() {
+ public void onUserUnlocked() {
mRecentsModel = RecentsModel.INSTANCE.get(this);
- mOverviewComponentObserver = new OverviewComponentObserver(this);
- mOverviewCommandHelper = new OverviewCommandHelper(this,
+ mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
+ mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
mOverviewComponentObserver);
- mIsUserUnlocked = true;
- Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
}
@Override
public void onDestroy() {
- if (mIsUserUnlocked) {
+ if (mDeviceState.isUserUnlocked()) {
mOverviewComponentObserver.onDestroy();
}
- Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
+ mDeviceState.destroy();
sConnected = false;
super.onDestroy();
}
diff --git a/go/src/com/android/launcher3/config/FeatureFlags.java b/go/src/com/android/launcher3/config/FeatureFlags.java
deleted file mode 100644
index a90808c..0000000
--- a/go/src/com/android/launcher3/config/FeatureFlags.java
+++ /dev/null
@@ -1,32 +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.config;
-
-import android.content.Context;
-
-/**
- * Defines a set of flags used to control various launcher behaviors
- */
-public final class FeatureFlags extends BaseFlags {
- private FeatureFlags() {
- // Prevent instantiation
- }
-
- // Features to control Launcher3Go behavior
- public static final boolean GO_DISABLE_WIDGETS = true;
- public static final boolean LAUNCHER3_SPRING_ICONS = false;
-}
diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LoaderResults.java
index b82f362..26c3313 100644
--- a/go/src/com/android/launcher3/model/LoaderResults.java
+++ b/go/src/com/android/launcher3/model/LoaderResults.java
@@ -16,9 +16,8 @@
package com.android.launcher3.model;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import java.lang.ref.WeakReference;
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 18f3f9d..7b8f4e6 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -37,6 +37,10 @@
* <p> The widgets and shortcuts are organized using package name as its index.
*/
public class WidgetsModel {
+
+ // True is the widget support is disabled.
+ public static final boolean GO_DISABLE_WIDGETS = false;
+
private static final ArrayList<WidgetListRowEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
/**
diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index ee113df..42b1194 100644
--- a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -27,34 +27,23 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.notification.NotificationKeyData;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.List;
/**
* Performs operations related to deep shortcuts, such as querying for them, pinning them, etc.
*/
public class DeepShortcutManager {
- private static DeepShortcutManager sInstance;
- private static final Object sInstanceLock = new Object();
+
+ private static final DeepShortcutManager sInstance = new DeepShortcutManager();
public static DeepShortcutManager getInstance(Context context) {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new DeepShortcutManager(context.getApplicationContext());
- }
- return sInstance;
- }
+ return sInstance;
}
- private DeepShortcutManager(Context context) {
- }
+ private final QueryResult mFailure = new QueryResult();
- public boolean wasLastCallSuccess() {
- return false;
- }
-
- public void onShortcutsChanged(List<ShortcutInfo> shortcuts) {
- }
+ private DeepShortcutManager() { }
/**
* Queries for the shortcuts with the package name and provided ids.
@@ -62,18 +51,18 @@
* This method is intended to get the full details for shortcuts when they are added or updated,
* because we only get "key" fields in onShortcutsChanged().
*/
- public List<ShortcutInfo> queryForFullDetails(String packageName,
+ public QueryResult queryForFullDetails(String packageName,
List<String> shortcutIds, UserHandle user) {
- return Collections.emptyList();
+ return mFailure;
}
/**
* Gets all the manifest and dynamic shortcuts associated with the given package and user,
* to be displayed in the shortcuts container on long press.
*/
- public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
+ public QueryResult queryForShortcutsContainer(ComponentName activity,
UserHandle user) {
- return Collections.emptyList();
+ return mFailure;
}
/**
@@ -103,20 +92,28 @@
*
* If packageName is null, returns all pinned shortcuts regardless of package.
*/
- public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
- return Collections.emptyList();
+ public QueryResult queryForPinnedShortcuts(String packageName, UserHandle user) {
+ return mFailure;
}
- public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
+ public QueryResult queryForPinnedShortcuts(String packageName,
List<String> shortcutIds, UserHandle user) {
- return Collections.emptyList();
+ return mFailure;
}
- public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
- return Collections.emptyList();
+ public QueryResult queryForAllShortcuts(UserHandle user) {
+ return mFailure;
}
public boolean hasHostPermission() {
return false;
}
+
+
+ public static class QueryResult extends ArrayList<ShortcutInfo> {
+
+ public boolean wasSuccess() {
+ return true;
+ }
+ }
}
diff --git a/gradle.properties b/gradle.properties
index 5b90f08..a77f52a 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -10,4 +10,4 @@
PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7
BUILD_TOOLS_VERSION=28.0.3
-COMPILE_SDK=android-Q
\ No newline at end of file
+COMPILE_SDK=android-R
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index e1b71a0..5c4f37c 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -22,6 +22,7 @@
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
+
import androidx.annotation.NonNull;
/**
@@ -35,6 +36,8 @@
static final boolean ATLEAST_OREO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
+ private static final float ICON_BADGE_SCALE = 0.444f;
+
private final Rect mOldBounds = new Rect();
protected final Context mContext;
private final Canvas mCanvas;
@@ -251,7 +254,7 @@
* Adds the {@param badge} on top of {@param target} using the badge dimensions.
*/
public void badgeWithDrawable(Canvas target, Drawable badge) {
- int badgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
+ int badgeSize = getBadgeSizeForIconSize(mIconBitmapSize);
badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
mIconBitmapSize, mIconBitmapSize);
badge.draw(target);
@@ -332,6 +335,13 @@
}
/**
+ * Returns the correct badge size given an icon size
+ */
+ public static int getBadgeSizeForIconSize(int iconSize) {
+ return (int) (ICON_BADGE_SCALE * iconSize);
+ }
+
+ /**
* An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size.
* This allows the badging to be done based on the action bitmap size rather than
* the scaled bitmap size.
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index 36d1c3e..93f0538 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -267,9 +267,13 @@
entry = new CacheEntry();
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.icon == null) return;
entry.title = cachingLogic.getLabel(object);
entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
- mCache.put(key, entry);
+ if (cachingLogic.addToMemCache()) mCache.put(key, entry);
ContentValues values = newContentValues(entry, entry.title.toString(),
componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
@@ -308,20 +312,12 @@
@NonNull ComponentName componentName, @NonNull UserHandle user,
@NonNull Supplier<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
boolean usePackageIcon, boolean useLowResIcon) {
- return cacheLocked(componentName, user, infoProvider, cachingLogic, usePackageIcon,
- useLowResIcon, true);
- }
-
- protected <T> CacheEntry cacheLocked(
- @NonNull ComponentName componentName, @NonNull UserHandle user,
- @NonNull Supplier<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
- boolean usePackageIcon, boolean useLowResIcon, boolean addToMemCache) {
assertWorkerThread();
ComponentKey cacheKey = new ComponentKey(componentName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
- if (addToMemCache) {
+ if (cachingLogic.addToMemCache()) {
mCache.put(cacheKey, entry);
}
@@ -378,7 +374,7 @@
* Adds a default package entry in the cache. This entry is not persisted and will be removed
* when the cache is flushed.
*/
- public synchronized void cachePackageInstallInfo(String packageName, UserHandle user,
+ protected synchronized void cachePackageInstallInfo(String packageName, UserHandle user,
Bitmap icon, CharSequence title) {
removeFromMemCacheLocked(packageName, user);
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
index 09f59b8..3aa783a 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
@@ -17,6 +17,7 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageInfo;
import android.os.LocaleList;
import android.os.UserHandle;
@@ -41,4 +42,18 @@
default String getKeywords(T object, LocaleList localeList) {
return null;
}
+
+ /**
+ * Returns the timestamp the entry was last updated in cache.
+ */
+ default long getLastUpdatedTime(T object, PackageInfo info) {
+ return info.lastUpdateTime;
+ }
+
+ /**
+ * Returns true the object should be added to mem cache; otherwise returns false.
+ */
+ default boolean addToMemCache() {
+ return true;
+ }
}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
index 3c71bd0..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,8 @@
long updateTime = c.getLong(indexLastUpdate);
int version = c.getInt(indexVersion);
T app = componentMap.remove(component);
- if (version == info.versionCode && updateTime == info.lastUpdateTime &&
- TextUtils.equals(c.getString(systemStateIndex),
+ if (version == info.versionCode && updateTime == info.lastUpdateTime
+ && TextUtils.equals(c.getString(systemStateIndex),
mIconCache.getIconSystemState(info.packageName))) {
if (mFilterMode == MODE_CLEAR_VALID_ITEMS) {
@@ -231,7 +231,6 @@
}
}
-
/**
* A runnable that updates invalid icons and adds missing icons in the DB for the provided
* LauncherActivityInfo list. Items are updated/added one at a time, so that the
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 49fd436..055ade5 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -118,6 +118,7 @@
APP_USAGE_SETTINGS = 18;
BACK_GESTURE = 19;
UNDO = 20;
+ DISMISS_PREDICTION = 21;
}
enum TipType {
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 5465480..826a275 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -88,6 +88,7 @@
<activity android:name="com.android.quickstep.LockScreenRecentsActivity"
android:theme="@android:style/Theme.NoDisplay"
android:showOnLockScreen="true"
+ android:taskAffinity="${packageName}.locktask"
android:directBootAware="true" />
</application>
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 7115943..d842484 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -30,17 +30,17 @@
import android.content.Context;
import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
/**
* A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
* {@link RecentsView}.
@@ -64,15 +64,16 @@
@Override
protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+ @NonNull RemoteAnimationTargetCompat[] appTargets,
+ @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
RecentsView recentsView = mLauncher.getOverviewPanel();
boolean skipLauncherChanges = !launcherClosing;
- TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
+ TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
- ClipAnimationHelper helper = new ClipAnimationHelper(mLauncher);
- anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
- .setDuration(RECENTS_LAUNCH_DURATION));
+ AppWindowAnimationHelper helper = new AppWindowAnimationHelper(mLauncher);
+ anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets,
+ wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION));
Animator childStateAnimation = null;
// Found a visible recents task that matches the opening app, lets launch the app from there
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
index c5c4add..76050d5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
@@ -27,7 +27,7 @@
}
@Override
- protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+ 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/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
index 4ecc39c..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
@@ -17,6 +17,8 @@
import static android.content.pm.PackageManager.MATCH_INSTANT;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -30,8 +32,12 @@
import android.util.ArrayMap;
import android.util.Log;
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
@@ -45,11 +51,6 @@
import java.util.List;
import java.util.Map;
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
/**
* Utility class which loads and caches predicted items like instant apps and shortcuts, before
* they can be displayed on the UI
@@ -77,7 +78,7 @@
public DynamicItemCache(Context context, Runnable onUpdateCallback) {
mContext = context;
- mWorker = new Handler(LauncherModel.getWorkerLooper(), this::handleWorkerMessage);
+ mWorker = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage);
mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
mInstantAppResolver = InstantAppResolver.newInstance(context);
mOnUpdateCallback = onUpdateCallback;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
index 24fc61b..4c7943b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,6 +16,7 @@
package com.android.launcher3.appprediction;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.annotation.TargetApi;
import android.app.prediction.AppPredictionContext;
@@ -34,19 +35,25 @@
import android.util.Log;
import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.model.AppLaunchTracker;
-import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.systemui.plugins.AppLaunchEventsPlugin;
+import com.android.systemui.plugins.PluginListener;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
+import java.util.ArrayList;
+import java.util.List;
/**
* Subclass of app tracker which publishes the data to the prediction engine and gets back results.
*/
@TargetApi(Build.VERSION_CODES.Q)
-public class PredictionAppTracker extends AppLaunchTracker {
+public class PredictionAppTracker extends AppLaunchTracker
+ implements PluginListener<AppLaunchEventsPlugin> {
private static final String TAG = "PredictionAppTracker";
private static final boolean DBG = false;
@@ -58,6 +65,7 @@
protected final Context mContext;
private final Handler mMessageHandler;
+ private final List<AppLaunchEventsPlugin> mAppLaunchEventsPluginsList;
// Accessed only on worker thread
private AppPredictor mHomeAppPredictor;
@@ -65,10 +73,14 @@
public PredictionAppTracker(Context context) {
mContext = context;
- mMessageHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleMessage);
+ mMessageHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessage);
InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);
mMessageHandler.sendEmptyMessage(MSG_INIT);
+
+ mAppLaunchEventsPluginsList = new ArrayList<>();
+ PluginManagerWrapper.INSTANCE.get(context)
+ .addPluginListener(this, AppLaunchEventsPlugin.class, true);
}
@UiThread
@@ -96,7 +108,7 @@
AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
if (apm == null) {
- return null;
+ return null;
}
AppPredictor predictor = apm.createAppPredictionSession(
@@ -116,7 +128,7 @@
*/
@WorkerThread
@Nullable
- public Bundle getAppPredictionContextExtras(Client client){
+ public Bundle getAppPredictionContextExtras(Client client) {
return null;
}
@@ -128,7 +140,7 @@
destroy();
// Initialize the clients
- int count = InvariantDeviceProfile.INSTANCE.get(mContext).numColumns;
+ int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns;
mHomeAppPredictor = createPredictor(Client.HOME, count);
mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count);
return true;
@@ -167,37 +179,98 @@
if (DBG) {
Log.d(TAG, String.format("Sent immediate message to update %s", client));
}
+
+ // Relay onReturnedToHome to every plugin.
+ mAppLaunchEventsPluginsList.forEach(AppLaunchEventsPlugin::onReturnedToHome);
}
@Override
@UiThread
public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
- String container) {
+ String container) {
// TODO: Use the full shortcut info
- AppTarget target = new AppTarget
- .Builder(new AppTargetId("shortcut:" + shortcutId), packageName, user)
- .setClassName(shortcutId)
- .build();
+ AppTarget target = new AppTarget.Builder(
+ new AppTargetId("shortcut:" + shortcutId), packageName, user)
+ .setClassName(shortcutId)
+ .build();
+
sendLaunch(target, container);
+
+ // Relay onStartShortcut info to every connected plugin.
+ mAppLaunchEventsPluginsList
+ .forEach(plugin -> plugin.onStartShortcut(
+ packageName,
+ shortcutId,
+ user,
+ container != null ? container : CONTAINER_DEFAULT)
+ );
+
}
@Override
@UiThread
public void onStartApp(ComponentName cn, UserHandle user, String container) {
if (cn != null) {
- AppTarget target = new AppTarget
- .Builder(new AppTargetId("app:" + cn), cn.getPackageName(), user)
- .setClassName(cn.getClassName())
- .build();
+ AppTarget target = new AppTarget.Builder(
+ new AppTargetId("app:" + cn), cn.getPackageName(), user)
+ .setClassName(cn.getClassName())
+ .build();
sendLaunch(target, container);
+
+ // Relay onStartApp to every connected plugin.
+ mAppLaunchEventsPluginsList
+ .forEach(plugin -> plugin.onStartApp(
+ cn,
+ user,
+ container != null ? container : CONTAINER_DEFAULT)
+ );
}
}
+ @Override
+ @UiThread
+ public void onDismissApp(ComponentName cn, UserHandle user, String container) {
+ if (cn == null) return;
+ AppTarget target = new AppTarget.Builder(
+ new AppTargetId("app: " + cn), cn.getPackageName(), user)
+ .setClassName(cn.getClassName())
+ .build();
+ sendDismiss(target, container);
+
+ // Relay onDismissApp to every connected plugin.
+ mAppLaunchEventsPluginsList
+ .forEach(plugin -> plugin.onDismissApp(
+ cn,
+ user,
+ container != null ? container : CONTAINER_DEFAULT)
+ );
+ }
+
+ @UiThread
+ private void sendEvent(AppTarget target, String container, int eventId) {
+ AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
+ .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
+ .build();
+ Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
+ }
+
@UiThread
private void sendLaunch(AppTarget target, String container) {
- AppTargetEvent event = new AppTargetEvent.Builder(target, AppTargetEvent.ACTION_LAUNCH)
- .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
- .build();
- Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
+ sendEvent(target, container, AppTargetEvent.ACTION_LAUNCH);
+ }
+
+ @UiThread
+ private void sendDismiss(AppTarget target, String container) {
+ sendEvent(target, container, AppTargetEvent.ACTION_DISMISS);
+ }
+
+ @Override
+ public void onPluginConnected(AppLaunchEventsPlugin appLaunchEventsPlugin, Context context) {
+ mAppLaunchEventsPluginsList.add(appLaunchEventsPlugin);
+ }
+
+ @Override
+ public void onPluginDisconnected(AppLaunchEventsPlugin appLaunchEventsPlugin) {
+ mAppLaunchEventsPluginsList.remove(appLaunchEventsPlugin);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index 0c7ba9c..23db5df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -43,6 +43,7 @@
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore;
@@ -92,7 +93,7 @@
private final Launcher mLauncher;
private final PredictionUiStateManager mPredictionUiStateManager;
- private final int mNumPredictedAppsPerRow;
+ private int mNumPredictedAppsPerRow;
// The set of predicted app component names
private final List<ComponentKeyMapper> mPredictedAppComponents = new ArrayList<>();
@@ -128,7 +129,7 @@
mFocusHelper = new SimpleFocusIndicatorHelper(this);
- mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numColumns;
+ mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns;
mLauncher = Launcher.getLauncher(context);
mLauncher.addOnDeviceProfileChangeListener(this);
@@ -226,6 +227,7 @@
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
+ mNumPredictedAppsPerRow = dp.inv.numAllAppsColumns;
removeAllViews();
applyPredictionApps();
}
@@ -281,7 +283,7 @@
}
private List<ItemInfoWithIcon> processPredictedAppComponents(List<ComponentKeyMapper> components) {
- if (getAppsStore().getApps().isEmpty()) {
+ if (getAppsStore().getApps().length == 0) {
// Apps have not been bound yet.
return Collections.emptyList();
}
@@ -290,7 +292,9 @@
for (ComponentKeyMapper mapper : components) {
ItemInfoWithIcon info = mapper.getApp(getAppsStore());
if (info != null) {
- predictedApps.add(info);
+ ItemInfoWithIcon predictedApp = info.clone();
+ predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION;
+ predictedApps.add(predictedApp);
} else {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.e(TAG, "Predicted app not found: " + mapper);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 596bc4f..4c04b29 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -18,37 +18,39 @@
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.graphics.Rect;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.Gravity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
+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 com.android.systemui.shared.system.WindowManagerWrapper;
import java.util.ArrayList;
@@ -57,9 +59,16 @@
*/
public abstract class RecentsUiFactory {
+ private static final String TAG = RecentsUiFactory.class.getSimpleName();
+
public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
- private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
- WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
+
+ /**
+ * 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 RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
@Override
@@ -164,8 +173,7 @@
}
}
- if (FeatureFlags.PULL_DOWN_STATUS_BAR
- && !launcher.getDeviceProfile().isMultiWindowMode) {
+ if (!launcher.getDeviceProfile().isMultiWindowMode) {
list.add(new StatusBarTouchController(launcher));
}
@@ -183,11 +191,15 @@
return new RecentsViewStateController(launcher);
}
- /**
- * Clears the swipe shared state for the current swipe gesture.
- */
- public static void clearSwipeSharedState(boolean finishAnimation) {
- TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation);
+ /** 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);
+ }
}
/**
@@ -200,9 +212,8 @@
DeviceProfile profile = launcher.getDeviceProfile();
boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
&& !profile.isVerticalBarLayout();
- UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
- visible ? 1 : 0, profile.hotseatBarSizePx);
-
+ UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT, visible ? 1 : 0,
+ profile.hotseatBarSizePx);
if (state == NORMAL) {
launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
}
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 50cfac8..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
@@ -19,7 +19,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.LayoutUtils;
@@ -69,9 +68,17 @@
if (taskCount == 0) {
return super.getOverviewScaleAndTranslation(launcher);
}
- TaskView dummyTask = recentsView.getTaskViewAt(Utilities.boundToRange(
- recentsView.getCurrentPage(), 0, taskCount - 1));
- return recentsView.getTempClipAnimationHelper().updateForFullscreenOverview(dummyTask)
+ TaskView dummyTask;
+ if (recentsView.getCurrentPage() >= 0) {
+ if (recentsView.getCurrentPage() <= taskCount - 1) {
+ dummyTask = recentsView.getCurrentPageTaskView();
+ } else {
+ dummyTask = recentsView.getTaskViewAt(taskCount - 1);
+ }
+ } else {
+ dummyTask = recentsView.getTaskViewAt(0);
+ }
+ return recentsView.getTempAppWindowAnimationHelper().updateForFullscreenOverview(dummyTask)
.getScaleAndTranslation();
}
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 ab346c0..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
@@ -32,8 +32,8 @@
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -42,6 +42,7 @@
import android.animation.AnimatorSet;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewConfiguration;
import com.android.launcher3.Launcher;
@@ -49,7 +50,7 @@
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.views.RecentsView;
@@ -119,7 +120,7 @@
* having it as part of the existing animation to the target state.
*/
private boolean handlingOverviewAnim() {
- int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher).getSystemUiStateFlags();
+ int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
return mStartState == NORMAL && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
}
@@ -131,16 +132,33 @@
// Fade in prediction icons quickly, then rest of all apps after reaching overview.
float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher)
- OVERVIEW.getVerticalProgress(mLauncher);
- builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(ACCEL,
- 0, ALL_APPS_CONTENT_FADE_THRESHOLD));
- builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(LINEAR,
- progressToReachOverview, 1));
+ builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
+ ACCEL,
+ 0,
+ ALL_APPS_CONTENT_FADE_THRESHOLD));
+ builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
+ ACCEL,
+ progressToReachOverview,
+ progressToReachOverview + ALL_APPS_CONTENT_FADE_THRESHOLD));
// Get workspace out of the way quickly, to prepare for potential pause.
builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3);
builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3);
builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3);
return builder;
+ } else if (fromState == ALL_APPS && toState == NORMAL) {
+ AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ // Keep all apps/predictions opaque until the very end of the transition.
+ float progressToReachOverview = OVERVIEW.getVerticalProgress(mLauncher);
+ builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
+ DEACCEL,
+ progressToReachOverview - ALL_APPS_CONTENT_FADE_THRESHOLD,
+ progressToReachOverview));
+ builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
+ DEACCEL,
+ 1 - ALL_APPS_CONTENT_FADE_THRESHOLD,
+ 1));
+ return builder;
}
return super.getAnimatorSetBuilderForStates(fromState, toState);
}
@@ -163,6 +181,7 @@
AnimatorSetBuilder builder = new AnimatorSetBuilder();
builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
+ builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
@@ -179,6 +198,11 @@
} else {
super.onDragEnd(velocity, fling);
}
+
+ View searchView = mLauncher.getAppsView().getSearchView();
+ if (searchView instanceof FeedbackHandler) {
+ ((FeedbackHandler) searchView).resetFeedback();
+ }
mMotionPauseDetector.clear();
}
@@ -205,4 +229,16 @@
builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW);
}
}
+
+ /**
+ * Interface for views with feedback animation requiring reset
+ */
+ public interface FeedbackHandler {
+
+ /**
+ * reset searchWidget feedback
+ */
+ void resetFeedback();
+ }
+
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
index 20a2487..03862db 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -47,9 +47,9 @@
* @return true if we should intercept the motion event
*/
boolean canInterceptTouch(MotionEvent ev) {
- if (mRecentsView.getChildCount() > 0) {
+ if (mRecentsView.getTaskViewCount() > 0) {
// Allow swiping up in the gap between the hotseat and overview.
- return ev.getY() >= mRecentsView.getChildAt(0).getBottom();
+ return ev.getY() >= mRecentsView.getTaskViewAt(0).getBottom();
} else {
// If there are no tasks, we only intercept if we're below the hotseat height.
return isTouchOverHotseat(mLauncher, ev);
@@ -63,7 +63,7 @@
* @return true if going back should take the user to the currently running task
*/
boolean shouldSwipeDownReturnToApp() {
- TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getNextPage());
+ TaskView taskView = mRecentsView.getNextPageTaskView();
return taskView != null && mRecentsView.shouldSwipeDownLaunchApp();
}
@@ -76,7 +76,7 @@
*/
PendingAnimation createSwipeDownToTaskAppAnimation(long duration) {
mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
- TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getCurrentPage());
+ TaskView taskView = mRecentsView.getCurrentPageTaskView();
if (taskView == null) {
throw new IllegalStateException("There is no task view to animate to.");
}
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 eb571f6..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
@@ -45,12 +45,11 @@
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.OverviewInteractionState;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.QuickStepContract;
/**
* Handles quick switching to a recent task from the home screen.
@@ -83,7 +82,7 @@
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
- int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher).getSystemUiStateFlags();
+ int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
return NORMAL;
}
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 ad90e16..8a11ac8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -30,9 +30,8 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
@@ -49,14 +48,14 @@
private static final long RECENTS_LAUNCH_DURATION = 250;
private static final String TAG = "AppToOverviewAnimationProvider";
- private final ActivityControlHelper<T> mHelper;
+ private final BaseActivityInterface<T> mHelper;
// The id of the currently running task that is transitioning to overview.
private final int mTargetTaskId;
private T mActivity;
private RecentsView mRecentsView;
- AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
+ AppToOverviewAnimationProvider(BaseActivityInterface<T> helper, int targetTaskId) {
mHelper = helper;
mTargetTaskId = targetTaskId;
}
@@ -70,7 +69,7 @@
boolean onActivityReady(T activity, Boolean wasVisible) {
activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
- ActivityControlHelper.AnimationFactory factory =
+ BaseActivityInterface.AnimationFactory factory =
mHelper.prepareRecentsUI(activity, wasVisible,
false /* animate activity */, (controller) -> {
controller.dispatchOnStart();
@@ -80,7 +79,7 @@
anim.start();
});
factory.onRemoteAnimationReceived(null);
- factory.createActivityController(RECENTS_LAUNCH_DURATION);
+ factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
factory.setRecentsAttachedToAppWindow(true, false);
mActivity = activity;
mRecentsView = mActivity.getOverviewPanel();
@@ -90,11 +89,12 @@
/**
* Create remote window animation from the currently running app to the overview panel.
*
- * @param targetCompats the target apps
+ * @param appTargets the target apps
* @return animation from app to overview
*/
@Override
- public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+ public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
if (mRecentsView != null) {
mRecentsView.setRunningTaskIconScaledDown(true);
}
@@ -114,18 +114,18 @@
return anim;
}
- RemoteAnimationTargetSet targetSet =
- new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
+ RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
+ wallpaperTargets, MODE_CLOSING);
// Use the top closing app to determine the insets for the animation
- RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mTargetTaskId);
+ RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
if (runningTaskTarget == null) {
Log.e(TAG, "No closing app");
anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
return anim;
}
- final ClipAnimationHelper clipHelper = new ClipAnimationHelper(mActivity);
+ final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper(mActivity);
// At this point, the activity is already started and laid-out. Get the home-bounds
// relative to the screen using the rootView of the activity.
@@ -141,20 +141,22 @@
clipHelper.updateTargetRect(targetRect);
clipHelper.prepareAnimation(mActivity.getDeviceProfile(), false /* isOpening */);
- ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
+ AppWindowAnimationHelper.TransformParams params = new AppWindowAnimationHelper.TransformParams()
.setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
valueAnimator.addUpdateListener((v) -> {
- params.setProgress((float) v.getAnimatedValue());
- clipHelper.applyTransform(targetSet, params);
+ params.setProgress((float) v.getAnimatedValue())
+ .setTargetSet(targets)
+ .setLauncherOnTop(true);
+ clipHelper.applyTransform(params);
});
- if (targetSet.isAnimatingHome()) {
+ if (targets.isAnimatingHome()) {
// If we are animating home, fade in the opening targets
- RemoteAnimationTargetSet openingSet =
- new RemoteAnimationTargetSet(targetCompats, MODE_OPENING);
+ RemoteAnimationTargets openingSet = new RemoteAnimationTargets(appTargets,
+ wallpaperTargets, MODE_OPENING);
TransactionCompat transaction = new TransactionCompat();
valueAnimator.addUpdateListener((v) -> {
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 d627a7f..e1e994c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -22,10 +22,9 @@
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.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
-import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
-import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
-import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import android.animation.Animator;
import android.annotation.TargetApi;
@@ -44,9 +43,10 @@
import android.provider.Settings;
import android.view.MotionEvent;
import android.view.View;
-import android.view.WindowManager;
import android.view.animation.Interpolator;
+import androidx.annotation.UiThread;
+
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
@@ -54,35 +54,32 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.views.FloatingIconView;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
-import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
+import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.inputconsumers.InputConsumer;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
+import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.util.SwipeAnimationTargetSet;
-import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import java.util.ArrayList;
import java.util.function.Consumer;
-import androidx.annotation.UiThread;
-
/**
* Base class for swipe up handler with some utility methods
*/
@TargetApi(Build.VERSION_CODES.Q)
public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q extends RecentsView>
- implements SwipeAnimationListener {
+ implements RecentsAnimationListener {
private static final String TAG = "BaseSwipeUpHandler";
protected static final Rect TEMP_RECT = new Rect();
@@ -100,11 +97,11 @@
protected final Context mContext;
protected final OverviewComponentObserver mOverviewComponentObserver;
- protected final ActivityControlHelper<T> mActivityControlHelper;
+ protected final BaseActivityInterface<T> mActivityInterface;
protected final RecentsModel mRecentsModel;
protected final int mRunningTaskId;
- protected final ClipAnimationHelper mClipAnimationHelper;
+ protected final AppWindowAnimationHelper mAppWindowAnimationHelper;
protected final TransformParams mTransformParams = new TransformParams();
private final Vibrator mVibrator;
@@ -117,7 +114,13 @@
protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
protected final ActivityInitListener mActivityInitListener;
- protected final RecentsAnimationWrapper mRecentsAnimationWrapper;
+ protected final InputConsumerController mInputConsumer;
+
+ protected RecentsAnimationController mRecentsAnimationController;
+ protected RecentsAnimationTargets mRecentsAnimationTargets;
+
+ // Callbacks to be made once the recents animation starts
+ private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
protected T mActivity;
protected Q mRecentsView;
@@ -126,27 +129,26 @@
protected Runnable mGestureEndCallback;
- protected final Handler mMainThreadHandler = MAIN_THREAD_EXECUTOR.getHandler();
+ protected final Handler mMainThreadHandler = MAIN_EXECUTOR.getHandler();
protected MultiStateCallback mStateCallback;
protected boolean mCanceled;
protected int mFinishingRecentsAnimationForNewTaskId = -1;
- protected BaseSwipeUpHandler(Context context,
+ protected BaseSwipeUpHandler(Context context, GestureState gestureState,
OverviewComponentObserver overviewComponentObserver,
RecentsModel recentsModel, InputConsumerController inputConsumer, int runningTaskId) {
mContext = context;
mOverviewComponentObserver = overviewComponentObserver;
- mActivityControlHelper = overviewComponentObserver.getActivityControlHelper();
+ mActivityInterface = gestureState.getActivityInterface();
mRecentsModel = recentsModel;
mActivityInitListener =
- mActivityControlHelper.createActivityInitListener(this::onActivityInit);
+ mActivityInterface.createActivityInitListener(this::onActivityInit);
mRunningTaskId = runningTaskId;
- mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
- this::createNewInputProxyHandler);
+ mInputConsumer = inputConsumer;
mMode = SysUINavigationMode.getMode(context);
- mClipAnimationHelper = new ClipAnimationHelper(context);
+ mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
mVibrator = context.getSystemService(Vibrator.class);
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
@@ -174,7 +176,7 @@
if (effect == null) {
return;
}
- BACKGROUND_EXECUTOR.execute(() -> mVibrator.vibrate(effect));
+ UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(effect));
}
public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode rotationMode) {
@@ -212,8 +214,8 @@
protected void linkRecentsViewScroll() {
SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> {
mTransformParams.setSyncTransactionApplier(applier);
- mRecentsAnimationWrapper.runOnInit(() ->
- mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier));
+ runOnRecentsAnimationStart(() ->
+ mRecentsAnimationTargets.addDependentTransactionApplier(applier));
});
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
@@ -221,20 +223,22 @@
updateFinalShift();
}
});
- mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
- mRecentsView.setClipAnimationHelper(mClipAnimationHelper);
+ mRecentsView.setAppWindowAnimationHelper(mAppWindowAnimationHelper);
+ runOnRecentsAnimationStart(() ->
+ mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
+ mRecentsAnimationTargets));
}
protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
// Launch the task user scrolled to (mRecentsView.getNextPage()).
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// We finish recents animation inside launchTask() when live tile is enabled.
- mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */,
+ mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
true /* freezeTaskList */);
} else {
- int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id;
+ int taskId = mRecentsView.getNextPageTaskView().getTask().key.id;
mFinishingRecentsAnimationForNewTaskId = taskId;
- mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
+ mRecentsAnimationController.finish(true /* toRecents */, () -> {
if (!mCanceled) {
TaskView nextTask = mRecentsView.getTaskView(taskId);
if (nextTask != null) {
@@ -242,10 +246,10 @@
success -> {
resultCallback.accept(success);
if (!success) {
- mActivityControlHelper.onLaunchTaskFailed(mActivity);
+ mActivityInterface.onLaunchTaskFailed(mActivity);
nextTask.notifyTaskLaunchFailed(TAG);
} else {
- mActivityControlHelper.onLaunchTaskSuccess(mActivity);
+ mActivityInterface.onLaunchTaskSuccess(mActivity);
}
}, mMainThreadHandler);
}
@@ -255,18 +259,40 @@
mFinishingRecentsAnimationForNewTaskId = -1;
});
}
- TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
+ }
+
+ /**
+ * Runs the given {@param action} if the recents animation has already started, or queues it to
+ * be run when it is next started.
+ */
+ protected void runOnRecentsAnimationStart(Runnable action) {
+ if (mRecentsAnimationTargets == null) {
+ mRecentsAnimationStartCallbacks.add(action);
+ } else {
+ action.run();
+ }
+ }
+
+ /**
+ * @return whether the recents animation has started and there are valid app targets.
+ */
+ protected boolean hasTargets() {
+ return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
}
@Override
- public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
+ public void onRecentsAnimationStart(RecentsAnimationController recentsAnimationController,
+ RecentsAnimationTargets targets) {
+ mRecentsAnimationController = recentsAnimationController;
+ mRecentsAnimationTargets = targets;
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
final Rect overviewStackBounds;
- RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
+ RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mRunningTaskId);
- if (targetSet.minimizedHomeBounds != null && runningTaskTarget != null) {
- overviewStackBounds = mActivityControlHelper
- .getOverviewWindowBounds(targetSet.minimizedHomeBounds, runningTaskTarget);
+ if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
+ overviewStackBounds = mActivityInterface
+ .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
dp = dp.getMultiWindowProfile(mContext, new Point(
overviewStackBounds.width(), overviewStackBounds.height()));
} else {
@@ -274,16 +300,34 @@
dp = dp.copy(mContext);
overviewStackBounds = getStackBounds(dp);
}
- dp.updateInsets(targetSet.homeContentInsets);
- dp.updateIsSeascape(mContext.getSystemService(WindowManager.class));
+ dp.updateInsets(targets.homeContentInsets);
+ dp.updateIsSeascape(mContext);
if (runningTaskTarget != null) {
- mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
+ mAppWindowAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
}
- mClipAnimationHelper.prepareAnimation(dp, false /* isOpening */);
+ mAppWindowAnimationHelper.prepareAnimation(dp, false /* isOpening */);
initTransitionEndpoints(dp);
- mRecentsAnimationWrapper.setController(targetSet);
+ // Notify when the animation starts
+ if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+ for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
+ action.run();
+ }
+ mRecentsAnimationStartCallbacks.clear();
+ }
+ }
+
+ @Override
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
}
private Rect getStackBounds(DeviceProfile dp) {
@@ -301,16 +345,16 @@
protected void initTransitionEndpoints(DeviceProfile dp) {
mDp = dp;
- mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
+ mTransitionDragLength = mActivityInterface.getSwipeUpDestinationAndLength(
dp, mContext, TEMP_RECT);
if (!dp.isMultiWindowMode) {
// When updating the target rect, also update the home bounds since the location on
// screen of the launcher window may be stale (position is not updated until first
// traversal after the window is resized). We only do this for non-multiwindow because
// we otherwise use the minimized home bounds provided by the system.
- mClipAnimationHelper.updateHomeBounds(getStackBounds(dp));
+ mAppWindowAnimationHelper.updateHomeBounds(getStackBounds(dp));
}
- mClipAnimationHelper.updateTargetRect(TEMP_RECT);
+ mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
if (mMode == Mode.NO_BUTTON) {
// We can drag all the way to the top of the screen.
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
@@ -368,10 +412,13 @@
float shift = mCurrentShift.value;
float offsetX = mRecentsView == null ? 0 : mRecentsView.getScrollOffset();
float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
- mClipAnimationHelper.getTargetRect().width());
- mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale);
- mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
- mTransformParams);
+ mAppWindowAnimationHelper.getTargetRect().width());
+ mTransformParams.setProgress(shift)
+ .setOffsetX(offsetX)
+ .setOffsetScale(offsetScale)
+ .setTargetSet(mRecentsAnimationTargets)
+ .setLauncherOnTop(true);
+ mAppWindowAnimationHelper.applyTransform(mTransformParams);
}
private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
@@ -387,9 +434,11 @@
*/
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
- final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
- final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
- mTransformParams.setProgress(startProgress), false /* launcherOnTop */));
+ final RectF startRect = new RectF(
+ mAppWindowAnimationHelper.applyTransform(
+ mTransformParams.setProgress(startProgress)
+ .setTargetSet(mRecentsAnimationTargets)
+ .setLauncherOnTop(false)));
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
final View floatingView = homeAnimationFactory.getFloatingView();
@@ -405,7 +454,7 @@
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
// rounding at the end of the animation.
- float startRadius = mClipAnimationHelper.getCurrentCornerRadius();
+ float startRadius = mAppWindowAnimationHelper.getCurrentCornerRadius();
float endRadius = startRect.width() / 6f;
// We want the window alpha to be 0 once this threshold is met, so that the
// FolderIconView can be seen morphing into the icon shape.
@@ -436,12 +485,11 @@
mTransformParams.setCornerRadius(endRadius * progress + startRadius
* (1f - progress));
}
- mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
- false /* launcherOnTop */);
+ mAppWindowAnimationHelper.applyTransform(mTransformParams);
if (isFloatingIconView) {
((FloatingIconView) floatingView).update(currentRect, 1f, progress,
- windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(),
+ windowAlphaThreshold, mAppWindowAnimationHelper.getCurrentCornerRadius(),
false);
}
}
@@ -469,7 +517,7 @@
public interface Factory {
- BaseSwipeUpHandler newHandler(RunningTaskInfo runningTask,
+ BaseSwipeUpHandler newHandler(GestureState gestureState, RunningTaskInfo runningTask,
long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
similarity index 92%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index 8c5a788..8deb835 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -35,8 +35,8 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -44,14 +44,14 @@
import java.util.function.Consumer;
/**
- * {@link ActivityControlHelper} for recents when the default launcher is different than the
+ * {@link BaseActivityInterface} for recents when the default launcher is different than the
* currently running one and apps should interact with the {@link RecentsActivity} as opposed
* to the in-launcher one.
*/
-public final class FallbackActivityControllerHelper implements
- ActivityControlHelper<RecentsActivity> {
+public final class FallbackActivityInterface implements
+ BaseActivityInterface<RecentsActivity> {
- public FallbackActivityControllerHelper() { }
+ public FallbackActivityInterface() { }
@Override
public void onTransitionCancelled(RecentsActivity activity, boolean activityVisible) {
@@ -137,17 +137,17 @@
boolean isAnimatingToRecents = false;
@Override
- public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
+ public void onRemoteAnimationReceived(RemoteAnimationTargets targets) {
isAnimatingToRecents = targets != null && targets.isAnimatingHome();
if (!isAnimatingToRecents) {
rv.setContentAlpha(1);
}
- createActivityController(getSwipeUpDestinationAndLength(
+ createActivityInterface(getSwipeUpDestinationAndLength(
activity.getDeviceProfile(), activity, new Rect()));
}
@Override
- public void createActivityController(long transitionLength) {
+ public void createActivityInterface(long transitionLength) {
AnimatorSet animatorSet = new AnimatorSet();
if (isAnimatingToRecents) {
ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
@@ -177,13 +177,13 @@
@Override
public ActivityInitListener createActivityInitListener(
BiPredicate<RecentsActivity, Boolean> onInitListener) {
- return new RecentsActivityTracker(onInitListener);
+ return new ActivityInitListener(onInitListener, RecentsActivity.ACTIVITY_TRACKER);
}
@Nullable
@Override
public RecentsActivity getCreatedActivity() {
- return RecentsActivityTracker.getCurrentActivity();
+ return BaseRecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
similarity index 88%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 36eb8a1..f6b3654 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -38,7 +38,6 @@
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.Region;
import android.os.UserHandle;
import android.view.MotionEvent;
import android.view.View;
@@ -50,7 +49,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherInitListenerEx;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
@@ -61,20 +59,23 @@
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.StaggeredWorkspaceAnim;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.plugins.shared.LauncherOverlayManager;
+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;
/**
- * {@link ActivityControlHelper} for the in-launcher recents.
+ * {@link BaseActivityInterface} for the in-launcher recents.
*/
-public final class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher> {
+public final class LauncherActivityInterface implements BaseActivityInterface<Launcher> {
private Runnable mAdjustInterpolatorsRunnable;
@@ -108,6 +109,14 @@
// 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.
activity.getStateManager().reapplyState();
+ setLauncherHideBackArrow(false);
+ }
+
+ private void setLauncherHideBackArrow(boolean hideBackArrow) {
+ Launcher launcher = getCreatedActivity();
+ if (launcher != null) {
+ launcher.getRootView().setForceHideBackArrow(hideBackArrow);
+ }
}
@Override
@@ -138,7 +147,7 @@
? FloatingIconView.getFloatingIconView(activity, workspaceView,
true /* hideOriginal */, iconLocation, false /* isOpening */)
: null;
-
+ setLauncherHideBackArrow(true);
return new HomeAnimationFactory() {
@Nullable
@Override
@@ -205,8 +214,8 @@
private boolean mIsAttachedToWindow;
@Override
- public void createActivityController(long transitionLength) {
- createActivityControllerInternal(activity, fromState, transitionLength, callback);
+ public void createActivityInterface(long transitionLength) {
+ 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.
@@ -268,22 +277,12 @@
INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
int runningTaskIndex = recentsView.getRunningTaskIndex();
- if (runningTaskIndex == 0) {
+ if (runningTaskIndex == recentsView.getTaskViewStartIndex()) {
// If we are on the first task (we haven't quick switched), translate recents in
// from the side. Calculate the start translation based on current scale/scroll.
float currScale = recentsView.getScaleX();
float scrollOffsetX = recentsView.getScrollOffset();
-
- float offscreenX = NORMAL.getOverviewScaleAndTranslation(activity).translationX;
- // The first task is hidden, so offset by its width.
- int firstTaskWidth = recentsView.getTaskViewAt(0).getWidth();
- offscreenX -= (firstTaskWidth + recentsView.getPageSpacing()) * currScale;
- // Offset since scale pushes tasks outwards.
- offscreenX += firstTaskWidth * (currScale - 1) / 2;
- offscreenX = Math.max(0, offscreenX);
- if (recentsView.isRtl()) {
- offscreenX = -offscreenX;
- }
+ float offscreenX = recentsView.getOffscreenTranslationX(currScale);
float fromTranslationX = attached ? offscreenX - scrollOffsetX : 0;
float toTranslationX = attached ? 0 : offscreenX - scrollOffsetX;
@@ -313,7 +312,7 @@
};
}
- private void createActivityControllerInternal(Launcher activity, LauncherState fromState,
+ private void createActivityInterfaceInternal(Launcher activity, LauncherState fromState,
long transitionLength, Consumer<AnimatorPlaybackController> callback) {
LauncherState endState = OVERVIEW;
if (fromState == endState) {
@@ -351,8 +350,7 @@
private void playScaleDownAnim(AnimatorSet anim, Launcher launcher, LauncherState fromState,
LauncherState endState) {
RecentsView recentsView = launcher.getOverviewPanel();
- TaskView v = recentsView.getTaskViewAt(recentsView.getCurrentPage());
- if (v == null) {
+ if (recentsView.getCurrentPageTaskView() == null) {
return;
}
@@ -380,7 +378,7 @@
// recents as a whole needs to translate further to keep up with the app window.
TaskView runningTaskView = recentsView.getRunningTaskView();
if (runningTaskView == null) {
- runningTaskView = recentsView.getTaskViewAt(recentsView.getCurrentPage());
+ runningTaskView = recentsView.getCurrentPageTaskView();
if (runningTaskView == null) {
// There are no task views in LockTask mode when Overview is enabled.
return;
@@ -406,11 +404,7 @@
@Nullable
@Override
public Launcher getCreatedActivity() {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app == null) {
- return null;
- }
- return (Launcher) app.getModel().getCallback();
+ return Launcher.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@@ -446,8 +440,8 @@
}
@Override
- public boolean deferStartingActivity(Region activeNavBarRegion, MotionEvent ev) {
- return activeNavBarRegion.contains((int) ev.getX(), (int) ev.getY());
+ public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+ return deviceState.isInDeferredGestureRegion(ev);
}
@Override
@@ -483,4 +477,38 @@
public void onLaunchTaskSuccess(Launcher launcher) {
launcher.getStateManager().moveToRestState();
}
+
+ @Override
+ public void closeOverlay() {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
+ LauncherOverlayManager om = launcher.getOverlayManager();
+ if (!launcher.isStarted() || launcher.isForceInvisible()) {
+ om.hideOverlay(false /* animate */);
+ } else {
+ om.hideOverlay(150);
+ }
+ }
+
+ @Override
+ public void switchToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {
+ Launcher launcher = getCreatedActivity();
+ RecentsView recentsView = launcher.getOverviewPanel();
+ if (recentsView == null) {
+ if (runnable != null) {
+ runnable.run();
+ }
+ 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();
+ }
+ }
}
\ No newline at end of file
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 14ff47b..150c44d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -15,8 +15,8 @@
*/
package com.android.quickstep;
-import static com.android.systemui.shared.system.ActivityManagerWrapper
- .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -25,15 +25,13 @@
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
-import android.util.Log;
import android.view.ViewConfiguration;
+import androidx.annotation.BinderThread;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -47,41 +45,45 @@
public class OverviewCommandHelper {
private final Context mContext;
- private final ActivityManagerWrapper mAM;
+ private final RecentsAnimationDeviceState mDeviceState;
private final RecentsModel mRecentsModel;
- private final MainThreadExecutor mMainThreadExecutor;
private final OverviewComponentObserver mOverviewComponentObserver;
private long mLastToggleTime;
- public OverviewCommandHelper(Context context, OverviewComponentObserver observer) {
+ public OverviewCommandHelper(Context context, RecentsAnimationDeviceState deviceState,
+ OverviewComponentObserver observer) {
mContext = context;
- mAM = ActivityManagerWrapper.getInstance();
- mMainThreadExecutor = new MainThreadExecutor();
+ mDeviceState = deviceState;
mRecentsModel = RecentsModel.INSTANCE.get(mContext);
mOverviewComponentObserver = observer;
}
+ @BinderThread
public void onOverviewToggle() {
// If currently screen pinning, do not enter overview
- if (mAM.isScreenPinningActive()) {
+ if (mDeviceState.isScreenPinningActive()) {
return;
}
- mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- mMainThreadExecutor.execute(new RecentsActivityCommand<>());
+ ActivityManagerWrapper.getInstance()
+ .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
}
+ @BinderThread
public void onOverviewShown(boolean triggeredFromAltTab) {
- mMainThreadExecutor.execute(new ShowRecentsCommand(triggeredFromAltTab));
+ MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab));
}
+ @BinderThread
public void onOverviewHidden() {
- mMainThreadExecutor.execute(new HideRecentsCommand());
+ MAIN_EXECUTOR.execute(new HideRecentsCommand());
}
+ @BinderThread
public void onTip(int actionType, int viewType) {
- mMainThreadExecutor.execute(() ->
+ MAIN_EXECUTOR.execute(() ->
UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
}
@@ -96,14 +98,14 @@
@Override
protected boolean handleCommand(long elapsedTime) {
// TODO: Go to the next page if started from alt-tab.
- return mHelper.getVisibleRecentsView() != null;
+ return mActivityInterface.getVisibleRecentsView() != null;
}
@Override
protected void onTransitionComplete() {
// TODO(b/138729100) This doesn't execute first time launcher is run
if (mTriggeredFromAltTab) {
- RecentsView rv = (RecentsView) mHelper.getVisibleRecentsView();
+ RecentsView rv = (RecentsView) mActivityInterface.getVisibleRecentsView();
if (rv == null) {
return;
}
@@ -112,7 +114,7 @@
TaskView taskView = rv.getNextTaskView();
if (taskView == null) {
if (rv.getTaskViewCount() > 0) {
- taskView = (TaskView) rv.getPageAt(0);
+ taskView = rv.getTaskViewAt(0);
taskView.requestFocus();
} else {
rv.requestFocus();
@@ -128,7 +130,7 @@
@Override
protected boolean handleCommand(long elapsedTime) {
- RecentsView recents = (RecentsView) mHelper.getVisibleRecentsView();
+ RecentsView recents = (RecentsView) mActivityInterface.getVisibleRecentsView();
if (recents == null) {
return false;
}
@@ -144,7 +146,7 @@
private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
- protected final ActivityControlHelper<T> mHelper;
+ protected final BaseActivityInterface<T> mActivityInterface;
private final long mCreateTime;
private final AppToOverviewAnimationProvider<T> mAnimationProvider;
@@ -153,10 +155,10 @@
private ActivityInitListener mListener;
public RecentsActivityCommand() {
- mHelper = mOverviewComponentObserver.getActivityControlHelper();
+ mActivityInterface = mOverviewComponentObserver.getActivityInterface();
mCreateTime = SystemClock.elapsedRealtime();
- mAnimationProvider =
- new AppToOverviewAnimationProvider<>(mHelper, RecentsModel.getRunningTaskId());
+ mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,
+ RecentsModel.getRunningTaskId());
// Preload the plan
mRecentsModel.getTasks(null);
@@ -164,9 +166,6 @@
@Override
public void run() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "RecentsActivityCommand.run");
- }
long elapsedTime = mCreateTime - mLastToggleTime;
mLastToggleTime = mCreateTime;
@@ -175,15 +174,15 @@
return;
}
- if (mHelper.switchToRecentsIfVisible(this::onTransitionComplete)) {
+ if (mActivityInterface.switchToRecentsIfVisible(this::onTransitionComplete)) {
// If successfully switched, then return
return;
}
// Otherwise, start overview.
- mListener = mHelper.createActivityInitListener(this::onActivityReady);
+ mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
- this::createWindowAnimation, mContext, mMainThreadExecutor.getHandler(),
+ this::createWindowAnimation, mContext, MAIN_EXECUTOR.getHandler(),
mAnimationProvider.getRecentsLaunchDuration());
}
@@ -191,7 +190,7 @@
// TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
// the menu activity which takes window focus, preventing the right condition from
// being run below
- RecentsView recents = mHelper.getVisibleRecentsView();
+ RecentsView recents = mActivityInterface.getVisibleRecentsView();
if (recents != null) {
// Launch the next task
recents.showNextTask();
@@ -208,14 +207,15 @@
if (!mUserEventLogged) {
activity.getUserEventDispatcher().logActionCommand(
LauncherLogProto.Action.Command.RECENTS_BUTTON,
- mHelper.getContainerType(),
+ mActivityInterface.getContainerType(),
LauncherLogProto.ContainerType.TASKSWITCHER);
mUserEventLogged = true;
}
return mAnimationProvider.onActivityReady(activity, wasVisible);
}
- private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+ private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
if (LatencyTrackerCompat.isEnabled(mContext)) {
LatencyTrackerCompat.logToggleRecents(
(int) (SystemClock.uptimeMillis() - mToggleClickedTime));
@@ -223,7 +223,8 @@
mListener.unregister();
- AnimatorSet animatorSet = mAnimationProvider.createWindowAnimation(targetCompats);
+ AnimatorSet animatorSet = mAnimationProvider.createWindowAnimation(appTargets,
+ wallpaperTargets);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
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 da46426..ce533a6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,21 +1,31 @@
package com.android.quickstep;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.content.Context;
import android.os.Bundle;
+import android.util.Log;
-import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.Launcher;
+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;
+import com.android.systemui.shared.recents.model.Task;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
public class QuickstepTestInformationHandler extends TestInformationHandler {
+ private final Context mContext;
public QuickstepTestInformationHandler(Context context) {
+ mContext = context;
}
@Override
@@ -36,12 +46,6 @@
return response;
}
- case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: {
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- TouchInteractionService.isInitialized());
- return response;
- }
-
case TestProtocol.REQUEST_HOTSEAT_TOP: {
if (mLauncher == null) return null;
@@ -52,32 +56,67 @@
case TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN: {
try {
- final int leftMargin = new MainThreadExecutor().submit(() ->
- mLauncher.<RecentsView>getOverviewPanel().getLeftGestureMargin()).get();
+ final int leftMargin = MAIN_EXECUTOR.submit(() ->
+ getRecentsView().getLeftGestureMargin()).get();
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, leftMargin);
- } catch (ExecutionException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
}
return response;
}
case TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN: {
try {
- final int rightMargin = new MainThreadExecutor().submit(() ->
- mLauncher.<RecentsView>getOverviewPanel().getRightGestureMargin()).
- get();
+ final int rightMargin = MAIN_EXECUTOR.submit(() ->
+ getRecentsView().getRightGestureMargin()).get();
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, rightMargin);
- } catch (ExecutionException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
}
return response;
}
+
+ case TestProtocol.REQUEST_RECENT_TASKS_LIST: {
+ ArrayList<String> taskBaseIntentComponents = new ArrayList<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ RecentsModel.INSTANCE.get(mContext).getTasks((tasks) -> {
+ for (Task t : tasks) {
+ taskBaseIntentComponents.add(
+ t.key.baseIntent.getComponent().flattenToString());
+ }
+ latch.countDown();
+ });
+ try {
+ latch.await(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ response.putStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ taskBaseIntentComponents);
+ return response;
+ }
}
return super.call(method);
}
+
+ private RecentsView getRecentsView() {
+ OverviewComponentObserver observer = new OverviewComponentObserver(mContext,
+ new RecentsAnimationDeviceState(mContext));
+ try {
+ return observer.getActivityInterface().getCreatedActivity().getOverviewPanel();
+ } finally {
+ observer.onDestroy();
+ }
+ }
+
+ @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/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index f08ae4a..7b8ec4d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -16,12 +16,10 @@
package com.android.quickstep;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl
- .STATUS_BAR_TRANSITION_DURATION;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl
- .STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
+import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
@@ -40,11 +38,11 @@
import com.android.launcher3.LauncherAnimationRunner;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsRootView;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.ObjectWrapper;
+import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -136,6 +134,12 @@
}
@Override
+ public void returnToHomescreen() {
+ super.returnToHomescreen();
+ // TODO(b/137318995) This should go home, but doing so removes freeform windows
+ }
+
+ @Override
public ActivityOptions getActivityLaunchOptions(final View v) {
if (!(v instanceof TaskView)) {
return null;
@@ -146,9 +150,10 @@
true /* startAtFrontOfQueue */) {
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
- AnimationResult result) {
- AnimatorSet anim = composeRecentsLaunchAnimator(taskView, targetCompats);
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
+ wallpaperTargets);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -168,12 +173,13 @@
* Composes the animations for a launch from the recents list if possible.
*/
private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
- RemoteAnimationTargetCompat[] targets) {
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
AnimatorSet target = new AnimatorSet();
- boolean activityClosing = taskIsATargetWithMode(targets, getTaskId(), MODE_CLOSING);
- ClipAnimationHelper helper = new ClipAnimationHelper(this);
- target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets, helper)
- .setDuration(RECENTS_LAUNCH_DURATION));
+ boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
+ AppWindowAnimationHelper helper = new AppWindowAnimationHelper(this);
+ target.play(getRecentsWindowAnimator(taskView, !activityClosing, appTargets,
+ wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION));
// Found a visible recents task that matches the opening app, lets launch the app from there
if (activityClosing) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
deleted file mode 100644
index c4d3fa0..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ /dev/null
@@ -1,227 +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.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_UP;
-
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.inputconsumers.InputConsumer;
-import com.android.quickstep.util.SwipeAnimationTargetSet;
-import com.android.systemui.shared.system.InputConsumerController;
-
-import java.util.ArrayList;
-import java.util.function.Supplier;
-
-/**
- * Wrapper around RecentsAnimationController to help with some synchronization
- */
-public class RecentsAnimationWrapper {
-
- private static final String TAG = "RecentsAnimationWrapper";
-
- // A list of callbacks to run when we receive the recents animation target. There are different
- // than the state callbacks as these run on the current worker thread.
- private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
-
- public SwipeAnimationTargetSet targetSet;
-
- private boolean mWindowThresholdCrossed = false;
-
- private final InputConsumerController mInputConsumerController;
- private final Supplier<InputConsumer> mInputProxySupplier;
-
- private InputConsumer mInputConsumer;
- private boolean mTouchInProgress;
-
- private boolean mFinishPending;
-
- public RecentsAnimationWrapper(InputConsumerController inputConsumerController,
- Supplier<InputConsumer> inputProxySupplier) {
- mInputConsumerController = inputConsumerController;
- mInputProxySupplier = inputProxySupplier;
- }
-
- public boolean hasTargets() {
- return targetSet != null && targetSet.hasTargets();
- }
-
- @UiThread
- public synchronized void setController(SwipeAnimationTargetSet targetSet) {
- Preconditions.assertUIThread();
- this.targetSet = targetSet;
-
- if (targetSet == null) {
- return;
- }
- targetSet.setWindowThresholdCrossed(mWindowThresholdCrossed);
-
- if (!mCallbacks.isEmpty()) {
- for (Runnable action : new ArrayList<>(mCallbacks)) {
- action.run();
- }
- mCallbacks.clear();
- }
- }
-
- public synchronized void runOnInit(Runnable action) {
- if (targetSet == null) {
- mCallbacks.add(action);
- } else {
- action.run();
- }
- }
-
- /** See {@link #finish(boolean, Runnable, boolean)} */
- @UiThread
- public void finish(boolean toRecents, Runnable onFinishComplete) {
- finish(toRecents, onFinishComplete, false /* sendUserLeaveHint */);
- }
-
- /**
- * @param onFinishComplete A callback that runs on the main thread after the animation
- * controller has finished on the background thread.
- * @param sendUserLeaveHint Determines whether userLeaveHint flag will be set on the pausing
- * activity. If userLeaveHint is true, the activity will enter into
- * picture-in-picture mode upon being paused.
- */
- @UiThread
- public void finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) {
- Preconditions.assertUIThread();
- if (!toRecents) {
- finishAndClear(false, onFinishComplete, sendUserLeaveHint);
- } else {
- if (mTouchInProgress) {
- mFinishPending = true;
- // Execute the callback
- if (onFinishComplete != null) {
- onFinishComplete.run();
- }
- } else {
- finishAndClear(true, onFinishComplete, sendUserLeaveHint);
- }
- }
- }
-
- private void finishAndClear(boolean toRecents, Runnable onFinishComplete,
- boolean sendUserLeaveHint) {
- SwipeAnimationTargetSet controller = targetSet;
- targetSet = null;
- disableInputProxy();
- if (controller != null) {
- controller.finishController(toRecents, onFinishComplete, sendUserLeaveHint);
- }
- }
-
- public void enableInputConsumer() {
- if (targetSet != null) {
- targetSet.enableInputConsumer();
- }
- }
-
- /**
- * Indicates that the gesture has crossed the window boundary threshold and system UI can be
- * update the represent the window behind
- */
- public void setWindowThresholdCrossed(boolean windowThresholdCrossed) {
- if (mWindowThresholdCrossed != windowThresholdCrossed) {
- mWindowThresholdCrossed = windowThresholdCrossed;
- if (targetSet != null) {
- targetSet.setWindowThresholdCrossed(windowThresholdCrossed);
- }
- }
- }
-
- public void enableInputProxy() {
- mInputConsumerController.setInputListener(this::onInputConsumerEvent);
- }
-
- private void disableInputProxy() {
- if (mInputConsumer != null && mTouchInProgress) {
- long now = SystemClock.uptimeMillis();
- MotionEvent dummyCancel = MotionEvent.obtain(now, now, ACTION_CANCEL, 0, 0, 0);
- mInputConsumer.onMotionEvent(dummyCancel);
- dummyCancel.recycle();
- }
- mInputConsumerController.setInputListener(null);
- }
-
- private boolean onInputConsumerEvent(InputEvent ev) {
- if (ev instanceof MotionEvent) {
- onInputConsumerMotionEvent((MotionEvent) ev);
- } else if (ev instanceof KeyEvent) {
- if (mInputConsumer == null) {
- mInputConsumer = mInputProxySupplier.get();
- }
- mInputConsumer.onKeyEvent((KeyEvent) ev);
- return true;
- }
- return false;
- }
-
- private boolean onInputConsumerMotionEvent(MotionEvent ev) {
- int action = ev.getAction();
-
- // Just to be safe, verify that ACTION_DOWN comes before any other action,
- // and ignore any ACTION_DOWN after the first one (though that should not happen).
- if (!mTouchInProgress && action != ACTION_DOWN) {
- Log.w(TAG, "Received non-down motion before down motion: " + action);
- return false;
- }
- if (mTouchInProgress && action == ACTION_DOWN) {
- Log.w(TAG, "Received down motion while touch was already in progress");
- return false;
- }
-
- if (action == ACTION_DOWN) {
- mTouchInProgress = true;
- if (mInputConsumer == null) {
- mInputConsumer = mInputProxySupplier.get();
- }
- } else if (action == ACTION_CANCEL || action == ACTION_UP) {
- // Finish any pending actions
- mTouchInProgress = false;
- if (mFinishPending) {
- mFinishPending = false;
- finishAndClear(true /* toRecents */, null, false /* sendUserLeaveHint */);
- }
- }
- if (mInputConsumer != null) {
- mInputConsumer.onMotionEvent(ev);
- }
-
- return true;
- }
-
- public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
- if (targetSet != null) {
- targetSet.controller.setDeferCancelUntilNextTransition(defer, screenshot);
- }
- }
-
- public SwipeAnimationTargetSet getController() {
- return targetSet;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
index c55f656..cd8e1a4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
@@ -15,27 +15,29 @@
*/
package com.android.quickstep;
-import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
+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.util.RecentsAnimationListenerSet;
-import com.android.quickstep.util.SwipeAnimationTargetSet;
-import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+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 SwipeAnimationListener {
+public class SwipeSharedState implements RecentsAnimationListener {
private OverviewComponentObserver mOverviewComponentObserver;
- private RecentsAnimationListenerSet mRecentsAnimationListener;
- private SwipeAnimationTargetSet mLastAnimationTarget;
+ private RecentsAnimationCallbacks mRecentsAnimationListener;
+ private RecentsAnimationController mLastRecentsAnimationController;
+ private RecentsAnimationTargets mLastAnimationTarget;
private boolean mLastAnimationCancelled = false;
private boolean mLastAnimationRunning = false;
@@ -44,19 +46,42 @@
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(SwipeAnimationTargetSet targetSet) {
- mLastAnimationTarget = targetSet;
+ 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();
@@ -64,8 +89,7 @@
}
}
- @Override
- public final void onRecentsAnimationCanceled() {
+ private void clearAnimationState() {
clearAnimationTarget();
mLastAnimationCancelled = true;
@@ -75,12 +99,13 @@
private void clearListenerState(boolean finishAnimation) {
if (mRecentsAnimationListener != null) {
mRecentsAnimationListener.removeListener(this);
- mRecentsAnimationListener.cancelListener();
- if (mLastAnimationRunning && mLastAnimationTarget != null) {
- Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(),
+ mRecentsAnimationListener.notifyAnimationCanceled();
+ if (mLastAnimationRunning && mLastRecentsAnimationController != null) {
+ Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
finishAnimation
- ? mLastAnimationTarget::finishAnimation
- : mLastAnimationTarget::cancelAnimation);
+ ? mLastRecentsAnimationController::finishAnimationToHome
+ : mLastRecentsAnimationController::finishAnimationToApp);
+ mLastRecentsAnimationController = null;
mLastAnimationTarget = null;
}
}
@@ -90,13 +115,7 @@
mLastAnimationRunning = false;
}
- private void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet) {
- if (mLastAnimationTarget == targetSet) {
- mLastAnimationRunning = false;
- }
- }
-
- public RecentsAnimationListenerSet newRecentsAnimationListenerSet() {
+ public RecentsAnimationCallbacks newRecentsAnimationCallbacks() {
Preconditions.assertUIThread();
if (mLastAnimationRunning) {
@@ -110,22 +129,22 @@
clearListenerState(false /* finishAnimation */);
boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
- : mOverviewComponentObserver.getActivityControlHelper().shouldMinimizeSplitScreen();
- mRecentsAnimationListener = new RecentsAnimationListenerSet(
- shouldMinimiseSplitScreen, this::onSwipeAnimationFinished);
+ : mOverviewComponentObserver.getActivityInterface().shouldMinimizeSplitScreen();
+ mRecentsAnimationListener = new RecentsAnimationCallbacks(shouldMinimiseSplitScreen);
mRecentsAnimationListener.addListener(this);
return mRecentsAnimationListener;
}
- public RecentsAnimationListenerSet getActiveListener() {
+ public RecentsAnimationCallbacks getActiveListener() {
return mRecentsAnimationListener;
}
- public void applyActiveRecentsAnimationState(SwipeAnimationListener listener) {
- if (mLastAnimationTarget != null) {
- listener.onRecentsAnimationStart(mLastAnimationTarget);
+ public void applyActiveRecentsAnimationState(RecentsAnimationListener listener) {
+ if (mLastRecentsAnimationController != null) {
+ listener.onRecentsAnimationStart(mLastRecentsAnimationController,
+ mLastAnimationTarget);
} else if (mLastAnimationCancelled) {
- listener.onRecentsAnimationCanceled();
+ listener.onRecentsAnimationCanceled(null);
}
}
@@ -155,5 +174,10 @@
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/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
index fd45923..5a2e3ff 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
@@ -28,16 +28,12 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
-import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.Log;
import android.view.View;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
@@ -46,7 +42,6 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
@@ -242,13 +237,7 @@
@Override
protected boolean onActivityStarted(BaseDraggingActivity activity) {
- ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
- try {
- sysUiProxy.onSplitScreenInvoked();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to notify SysUI of split screen: ", e);
- return false;
- }
+ SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
activity.getUserEventDispatcher().logActionOnControl(TAP,
LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
return true;
@@ -267,12 +256,16 @@
@Override
protected ActivityOptions makeLaunchOptions(Activity activity) {
- return ActivityOptionsCompat.makeFreeformOptions();
+ 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) {
- Launcher.getLauncher(activity).getStateManager().goToState(LauncherState.NORMAL);
+ activity.returnToHomescreen();
return true;
}
}
@@ -291,8 +284,7 @@
@Override
public View.OnClickListener getOnClickListener(
BaseDraggingActivity activity, TaskView taskView) {
- ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
- if (sysUiProxy == null) {
+ if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
return null;
}
if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
@@ -305,11 +297,8 @@
return view -> {
Consumer<Boolean> resultCallback = success -> {
if (success) {
- try {
- sysUiProxy.startScreenPinning(taskView.getTask().key.id);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to start screen pinning: ", e);
- }
+ SystemUiProxy.INSTANCE.get(activity).startScreenPinning(
+ taskView.getTask().key.id);
} else {
taskView.notifyTaskLaunchFailed(TAG);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 6897c1e..2522c0f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
@@ -30,14 +31,18 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Utilities;
-import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.MultiValueUpdateListener;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Utility class for helpful methods related to {@link TaskView} objects and their tasks.
@@ -111,15 +116,18 @@
* animation.
*/
public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
- RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, final AppWindowAnimationHelper inOutHelper) {
SyncRtSurfaceTransactionApplierCompat applier =
new SyncRtSurfaceTransactionApplierCompat(v);
- ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
- .setSyncTransactionApplier(applier);
-
- final RemoteAnimationTargetSet targetSet =
- new RemoteAnimationTargetSet(targets, MODE_OPENING);
- targetSet.addDependentTransactionApplier(applier);
+ final RemoteAnimationTargets targets =
+ new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING);
+ targets.addDependentTransactionApplier(applier);
+ AppWindowAnimationHelper.TransformParams params =
+ new AppWindowAnimationHelper.TransformParams()
+ .setSyncTransactionApplier(applier)
+ .setTargetSet(targets)
+ .setLauncherOnTop(true);
final RecentsView recentsView = v.getRecentsView();
final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
@@ -140,7 +148,7 @@
BaseActivity.fromContext(v.getContext()).getDeviceProfile(),
true /* isOpening */);
inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
- targetSet.apps.length == 0 ? null : targetSet.apps[0]);
+ targets.apps.length == 0 ? null : targets.apps[0]);
mThumbnailRect = new RectF(inOutHelper.getTargetRect());
mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
@@ -151,7 +159,33 @@
public void onUpdate(float percent) {
// TODO: Take into account the current fullscreen progress for animating the insets
params.setProgress(1 - percent);
- RectF taskBounds = inOutHelper.applyTransform(targetSet, params);
+ RectF taskBounds;
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ List<SurfaceParams> surfaceParamsList = new ArrayList<>();
+ // Append the surface transform params for the app that's being opened.
+ Collections.addAll(surfaceParamsList, inOutHelper.getSurfaceParams(params));
+
+ AppWindowAnimationHelper liveTileAnimationHelper =
+ v.getRecentsView().getClipAnimationHelper();
+ if (liveTileAnimationHelper != null) {
+ // Append the surface transform params for the live tile app.
+ AppWindowAnimationHelper.TransformParams liveTileParams =
+ v.getRecentsView().getLiveTileParams(true /* mightNeedToRefill */);
+ if (liveTileParams != null) {
+ Collections.addAll(surfaceParamsList,
+ liveTileAnimationHelper.getSurfaceParams(liveTileParams));
+ }
+ }
+ // Apply surface transform using the surface params list.
+ AppWindowAnimationHelper.applySurfaceParams(params.syncTransactionApplier,
+ surfaceParamsList.toArray(new SurfaceParams[surfaceParamsList.size()]));
+ // Get the task bounds for the app that's being opened after surface transform
+ // update.
+ taskBounds = inOutHelper.updateCurrentRect(params);
+ } else {
+ taskBounds = inOutHelper.applyTransform(params);
+ }
+
int taskIndex = recentsView.indexOfChild(v);
int centerTaskIndex = recentsView.getCurrentPage();
boolean parallaxCenterAndAdjacentTask = taskIndex != centerTaskIndex;
@@ -168,7 +202,7 @@
appAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- targetSet.release();
+ targets.release();
}
});
return appAnimator;
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 86ba855..5591e40 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -23,17 +23,10 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.FAKE_LANDSCAPE_UI;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
-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;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT;
import android.annotation.TargetApi;
@@ -41,73 +34,57 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.Service;
import android.app.TaskInfo;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
-import android.graphics.Point;
-import android.graphics.RectF;
import android.graphics.Region;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Process;
-import android.os.RemoteException;
-import android.text.TextUtils;
import android.util.Log;
import android.view.Choreographer;
-import android.view.Display;
import android.view.InputEvent;
import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.WindowManager;
import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.logging.EventLogArray;
+import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.UiThreadHelper;
+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.AssistantTouchConsumer;
+import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
import com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer;
-import com.android.quickstep.inputconsumers.InputConsumer;
import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
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.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
-
import com.android.systemui.shared.system.TaskInfoCompat;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -136,47 +113,45 @@
*/
@TargetApi(Build.VERSION_CODES.Q)
public class TouchInteractionService extends Service implements
- NavigationModeChangeListener, DisplayListener {
-
- public static final MainThreadExecutor MAIN_THREAD_EXECUTOR = new MainThreadExecutor();
- public static final LooperExecutor BACKGROUND_EXECUTOR =
- new LooperExecutor(UiThreadHelper.getBackgroundLooper());
-
- public static final EventLogArray TOUCH_INTERACTION_LOG =
- new EventLogArray("touch_interaction_log", 40);
+ NavigationModeChangeListener {
private static final String TAG = "TouchInteractionService";
private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
+ 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;
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
- public void onActiveNavBarRegionChanges(Region region) {
- mActiveNavBarRegion = region;
- }
-
+ @BinderThread
public void onInitialize(Bundle bundle) {
- mISystemUiProxy = ISystemUiProxy.Stub
- .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
- MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor);
- MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet);
- MAIN_THREAD_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */));
+ ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
+ bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
+ 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;
}
+ @BinderThread
@Override
public void onOverviewToggle() {
mOverviewCommandHelper.onOverviewToggle();
}
+ @BinderThread
@Override
public void onOverviewShown(boolean triggeredFromAltTab) {
mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
}
+ @BinderThread
@Override
public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
if (triggeredFromAltTab && !triggeredFromHomeKey) {
@@ -185,42 +160,52 @@
}
}
+ @BinderThread
@Override
public void onTip(int actionType, int viewType) {
mOverviewCommandHelper.onTip(actionType, viewType);
}
+ @BinderThread
@Override
public void onAssistantAvailable(boolean available) {
- mAssistantAvailable = available;
+ MAIN_EXECUTOR.execute(() -> mDeviceState.setAssistantAvailable(available));
+ MAIN_EXECUTOR.execute(TouchInteractionService.this::onAssistantVisibilityChanged);
}
+ @BinderThread
@Override
public void onAssistantVisibilityChanged(float visibility) {
- mLastAssistantVisibility = visibility;
- MAIN_THREAD_EXECUTOR.execute(
- TouchInteractionService.this::onAssistantVisibilityChanged);
+ MAIN_EXECUTOR.execute(() -> mDeviceState.setAssistantVisibility(visibility));
+ MAIN_EXECUTOR.execute(TouchInteractionService.this::onAssistantVisibilityChanged);
}
+ @BinderThread
public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
boolean gestureSwipeLeft) {
if (mOverviewComponentObserver == null) {
return;
}
- final ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
+ final BaseActivityInterface activityInterface =
+ mOverviewComponentObserver.getActivityInterface();
UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY,
- isButton, gestureSwipeLeft, activityControl.getContainerType());
+ isButton, gestureSwipeLeft, activityInterface.getContainerType());
if (completed && !isButton && shouldNotifyBackGesture()) {
- BACKGROUND_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture);
+ UI_HELPER_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture);
}
}
+ @BinderThread
public void onSystemUiStateChanged(int stateFlags) {
- mSystemUiStateFlags = stateFlags;
- MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiFlagsChanged);
+ MAIN_EXECUTOR.execute(() -> mDeviceState.setSystemUiFlags(stateFlags));
+ MAIN_EXECUTOR.execute(TouchInteractionService.this::onSystemUiFlagsChanged);
+ }
+
+ @BinderThread
+ public void onActiveNavBarRegionChanges(Region region) {
+ MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
}
/** Deprecated methods **/
@@ -244,6 +229,7 @@
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;
@@ -267,77 +253,30 @@
private ActivityManagerWrapper mAM;
private RecentsModel mRecentsModel;
- private ISystemUiProxy mISystemUiProxy;
private OverviewCommandHelper mOverviewCommandHelper;
private OverviewComponentObserver mOverviewComponentObserver;
- private OverviewInteractionState mOverviewInteractionState;
- private OverviewCallbacks mOverviewCallbacks;
- private TaskOverlayFactory mTaskOverlayFactory;
private InputConsumerController mInputConsumer;
- private boolean mAssistantAvailable;
- private float mLastAssistantVisibility = 0;
- private @SystemUiStateFlags int mSystemUiStateFlags;
-
- private boolean mIsUserUnlocked;
- private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
- initWhenUserUnlocked();
- }
- }
- };
+ private RecentsAnimationDeviceState mDeviceState;
private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
private InputConsumer mConsumer = InputConsumer.NO_OP;
private Choreographer mMainChoreographer;
- private Region mActiveNavBarRegion = new Region();
-
private InputMonitorCompat mInputMonitorCompat;
private InputEventReceiver mInputEventReceiver;
private Mode mMode = Mode.THREE_BUTTONS;
- private int mDefaultDisplayId;
- private final RectF mSwipeTouchRegion = new RectF();
- private final RectF mAssistantLeftRegion = new RectF();
- private final RectF mAssistantRightRegion = new RectF();
-
- private ComponentName mGestureBlockingActivity;
-
- private Region mExclusionRegion;
- private SystemGestureExclusionListenerCompat mExclusionListener;
@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 initWhenUserUnlocked() below.
+ // Everything else should be initialized in onUserUnlocked() below.
mMainChoreographer = Choreographer.getInstance();
mAM = ActivityManagerWrapper.getInstance();
- if (UserManagerCompat.getInstance(this).isUserUnlocked(Process.myUserHandle())) {
- initWhenUserUnlocked();
- } else {
- mIsUserUnlocked = false;
- registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
- }
-
- mDefaultDisplayId = getSystemService(WindowManager.class).getDefaultDisplay()
- .getDisplayId();
- String blockingActivity = getString(R.string.gesture_blocking_activity);
- mGestureBlockingActivity = TextUtils.isEmpty(blockingActivity) ? null :
- ComponentName.unflattenFromString(blockingActivity);
-
- mExclusionListener = new SystemGestureExclusionListenerCompat(mDefaultDisplayId) {
- @Override
- @BinderThread
- public void onExclusionChanged(Region region) {
- // Assignments are atomic, it should be safe on binder thread
- mExclusionRegion = region;
- }
- };
-
onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
sConnected = true;
}
@@ -360,75 +299,24 @@
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 1");
}
- if (!mMode.hasGestures || mISystemUiProxy == null) {
+ disposeEventHandlers();
+ if (!mMode.hasGestures || !SystemUiProxy.INSTANCE.get(this).isActive()) {
return;
}
- disposeEventHandlers();
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 2");
}
- try {
- mInputMonitorCompat = InputMonitorCompat.fromBundle(mISystemUiProxy
- .monitorGestureInput("swipe-up", mDefaultDisplayId), KEY_EXTRA_INPUT_MONITOR);
- mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
- mMainChoreographer, this::onInputEvent);
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 3");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to create input monitor", e);
- }
- initTouchBounds();
- }
-
- private int getNavbarSize(String resName) {
- return ResourceUtils.getNavbarSize(resName, getResources());
- }
-
- private void initTouchBounds() {
- if (!mMode.hasGestures) {
- return;
+ Bundle bundle = SystemUiProxy.INSTANCE.get(this).monitorGestureInput("swipe-up",
+ mDeviceState.getDisplayId());
+ mInputMonitorCompat = InputMonitorCompat.fromBundle(bundle, KEY_EXTRA_INPUT_MONITOR);
+ mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
+ mMainChoreographer, this::onInputEvent);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 3");
}
- Display defaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay();
- Point realSize = new Point();
- defaultDisplay.getRealSize(realSize);
- mSwipeTouchRegion.set(0, 0, realSize.x, realSize.y);
- if (mMode == Mode.NO_BUTTON) {
- int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
- mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - touchHeight;
-
- final int assistantWidth = getResources()
- .getDimensionPixelSize(R.dimen.gestures_assistant_width);
- final float assistantHeight = Math.max(touchHeight,
- QuickStepContract.getWindowCornerRadius(getResources()));
- mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mSwipeTouchRegion.bottom;
- mAssistantLeftRegion.top = mAssistantRightRegion.top =
- mSwipeTouchRegion.bottom - assistantHeight;
-
- mAssistantLeftRegion.left = 0;
- mAssistantLeftRegion.right = assistantWidth;
-
- mAssistantRightRegion.right = mSwipeTouchRegion.right;
- mAssistantRightRegion.left = mSwipeTouchRegion.right - assistantWidth;
- } else {
- mAssistantLeftRegion.setEmpty();
- mAssistantRightRegion.setEmpty();
- switch (defaultDisplay.getRotation()) {
- case Surface.ROTATION_90:
- mSwipeTouchRegion.left = mSwipeTouchRegion.right
- - getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
- break;
- case Surface.ROTATION_270:
- mSwipeTouchRegion.right = mSwipeTouchRegion.left
- + getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
- break;
- default:
- mSwipeTouchRegion.top = mSwipeTouchRegion.bottom
- - getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
- }
- }
+ mDeviceState.updateGestureTouchRegions();
}
@Override
@@ -436,55 +324,21 @@
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onNavigationModeChanged " + newMode);
}
- if (mMode.hasGestures != newMode.hasGestures) {
- if (newMode.hasGestures) {
- getSystemService(DisplayManager.class).registerDisplayListener(
- this, MAIN_THREAD_EXECUTOR.getHandler());
- } else {
- getSystemService(DisplayManager.class).unregisterDisplayListener(this);
- }
- }
mMode = newMode;
-
- disposeEventHandlers();
initInputMonitor();
-
- if (mMode == Mode.NO_BUTTON) {
- mExclusionListener.register();
- } else {
- mExclusionListener.unregister();
- }
+ resetHomeBounceSeenOnQuickstepEnabledFirstTime();
}
- @Override
- public void onDisplayAdded(int i) { }
-
- @Override
- public void onDisplayRemoved(int i) { }
-
- @Override
- public void onDisplayChanged(int displayId) {
- if (displayId != mDefaultDisplayId) {
- return;
- }
-
- initTouchBounds();
- }
-
- private void initWhenUserUnlocked() {
+ @UiThread
+ public void onUserUnlocked() {
mRecentsModel = RecentsModel.INSTANCE.get(this);
- mOverviewComponentObserver = new OverviewComponentObserver(this);
-
- mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
- mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
- mOverviewCallbacks = OverviewCallbacks.get(this);
- mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
+ mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
+ mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
+ mOverviewComponentObserver);
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
- mIsUserUnlocked = true;
sSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver);
mInputConsumer.registerInputConsumer();
- onSystemUiProxySet();
onSystemUiFlagsChanged();
onAssistantVisibilityChanged();
@@ -492,51 +346,59 @@
// new ModelPreload().start(this);
mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
.getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
-
- Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
+ resetHomeBounceSeenOnQuickstepEnabledFirstTime();
}
- @UiThread
- private void onSystemUiProxySet() {
- if (mIsUserUnlocked) {
- mRecentsModel.setSystemUiProxy(mISystemUiProxy);
- mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
+ private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
+ 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;
+ }
+
+ // Reset home bounce seen on quick step enabled for first time
+ SharedPreferences sharedPrefs = Utilities.getPrefs(this);
+ if (!sharedPrefs.getBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)) {
+ sharedPrefs.edit()
+ .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
+ .putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
+ .apply();
}
}
@UiThread
private void onSystemUiFlagsChanged() {
- if (mIsUserUnlocked) {
- mOverviewInteractionState.setSystemUiStateFlags(mSystemUiStateFlags);
- mOverviewComponentObserver.onSystemUiStateChanged(mSystemUiStateFlags);
+ if (mDeviceState.isUserUnlocked()) {
+ SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(
+ mDeviceState.getSystemUiStateFlags());
+ mOverviewComponentObserver.onSystemUiStateChanged();
}
}
@UiThread
private void onAssistantVisibilityChanged() {
- if (mIsUserUnlocked) {
- mOverviewComponentObserver.getActivityControlHelper().onAssistantVisibilityChanged(
- mLastAssistantVisibility);
+ if (mDeviceState.isUserUnlocked()) {
+ mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
+ mDeviceState.getAssistantVisibility());
}
}
@Override
public void onDestroy() {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "TIS destroyed");
+ }
sIsInitialized = false;
- if (mIsUserUnlocked) {
+ if (mDeviceState.isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.onDestroy();
}
disposeEventHandlers();
- if (mMode.hasGestures) {
- getSystemService(DisplayManager.class).unregisterDisplayListener(this);
- }
+ mDeviceState.destroy();
+ SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+ SystemUiProxy.INSTANCE.get(this).setProxy(null);
sConnected = false;
- Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
- SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
- mExclusionListener.unregister();
-
super.onDestroy();
}
@@ -554,55 +416,53 @@
Log.e(TAG, "Unknown event " + ev);
return;
}
+ if (!mDeviceState.isUserUnlocked()) {
+ return;
+ }
+
+ Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
+ TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
MotionEvent event = (MotionEvent) ev;
- TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
if (event.getAction() == ACTION_DOWN) {
- if (mSwipeTouchRegion.contains(event.getX(), event.getY())) {
+ 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(useSharedState, event);
- TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
+ mConsumer = newConsumer(newGestureState, useSharedState, event);
+ ActiveGestureLog.INSTANCE.addLog("setInputConsumer", mConsumer.getType());
mUncheckedConsumer = mConsumer;
- } else if (mIsUserUnlocked && mMode == Mode.NO_BUTTON
- && canTriggerAssistantAction(event)) {
+ } 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
// next gesture is also quick switch.
- mUncheckedConsumer =
- new AssistantTouchConsumer(this, mISystemUiProxy,
- mOverviewComponentObserver.getActivityControlHelper(),
- InputConsumer.NO_OP, mInputMonitorCompat);
+ mUncheckedConsumer = new AssistantInputConsumer(this, newGestureState,
+ InputConsumer.NO_OP, mInputMonitorCompat);
} else {
mUncheckedConsumer = InputConsumer.NO_OP;
}
}
+
+ ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
mUncheckedConsumer.onMotionEvent(event);
+ TraceHelper.INSTANCE.endFlagsOverride(traceToken);
}
- private boolean validSystemUiFlags() {
- return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
- && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
- && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
- && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
- || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
- }
+ private InputConsumer newConsumer(GestureState gestureState, boolean useSharedState,
+ MotionEvent event) {
+ boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
- private boolean canTriggerAssistantAction(MotionEvent ev) {
- return mAssistantAvailable
- && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
- && (mAssistantLeftRegion.contains(ev.getX(), ev.getY()) ||
- mAssistantRightRegion.contains(ev.getX(), ev.getY()))
- && !ActivityManagerWrapper.getInstance().isLockToAppActive();
- }
-
- private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) {
- boolean isInValidSystemUiState = validSystemUiFlags();
-
- if (!mIsUserUnlocked) {
- if (isInValidSystemUiState) {
+ 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(mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
+ return createDeviceLockedInputConsumer(gestureState,
+ mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
} else {
return mResetGestureInputConsumer;
}
@@ -610,58 +470,60 @@
// When using sharedState, bypass systemState check as this is a followup gesture and the
// first gesture started in a valid system state.
- InputConsumer base = isInValidSystemUiState || useSharedState
- ? newBaseConsumer(useSharedState, event) : mResetGestureInputConsumer;
+ InputConsumer base = canStartSystemGesture || useSharedState
+ ? newBaseConsumer(gestureState, useSharedState, event) : mResetGestureInputConsumer;
if (mMode == Mode.NO_BUTTON) {
- final ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
- if (canTriggerAssistantAction(event)) {
- base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
- mInputMonitorCompat);
+ if (mDeviceState.canTriggerAssistantAction(event)) {
+ base = new AssistantInputConsumer(this, gestureState, base, mInputMonitorCompat);
}
- if ((mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0) {
+ 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, mISystemUiProxy, activityControl);
+ base = new ScreenPinnedInputConsumer(this, gestureState);
}
- if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) {
- base = new AccessibilityInputConsumer(this, mISystemUiProxy,
- (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base,
- mInputMonitorCompat, mSwipeTouchRegion);
+ if (mDeviceState.isAccessibilityMenuAvailable()) {
+ base = new AccessibilityInputConsumer(this, mDeviceState, base,
+ mInputMonitorCompat);
}
} else {
- if ((mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0) {
+ if (mDeviceState.isScreenPinningActive()) {
base = mResetGestureInputConsumer;
}
}
return base;
}
- private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) {
- RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
+ private InputConsumer newBaseConsumer(GestureState gestureState, boolean useSharedState,
+ MotionEvent event) {
+ RunningTaskInfo runningTaskInfo = TraceHelper.whitelistIpcs("getRunningTask.0",
+ () -> mAM.getRunningTask(0));
if (!useSharedState) {
sSwipeSharedState.clearAllState(false /* finishAnimation */);
}
- if ((mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0) {
+ if (mDeviceState.isKeyguardShowingOccluded()) {
// This handles apps showing over the lockscreen (e.g. camera)
- return createDeviceLockedInputConsumer(runningTaskInfo);
+ return createDeviceLockedInputConsumer(gestureState, runningTaskInfo);
}
- final ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
-
boolean forceOverviewInputConsumer = false;
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
- runningTaskInfo = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT);
+
+ runningTaskInfo = TraceHelper.whitelistIpcs("getRunningTask.assistant",
+ () -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) {
final ComponentName homeComponent =
mOverviewComponentObserver.getHomeIntent().getComponent();
forceOverviewInputConsumer =
- runningTaskInfo.baseIntent.getComponent().equals(homeComponent);
+ runningTaskInfo.baseIntent.getComponent(). equals(homeComponent);
}
}
@@ -673,17 +535,18 @@
// consumer but with the next task as the running task
RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.id = sSwipeSharedState.nextRunningTaskId;
- return createOtherActivityInputConsumer(event, info);
- } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed()
+ return createOtherActivityInputConsumer(gestureState, event, info);
+ } else if (sSwipeSharedState.goingToLauncher
+ || gestureState.getActivityInterface().isResumed()
|| forceOverviewInputConsumer) {
- return createOverviewInputConsumer(event);
- } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) {
- return createOverviewInputConsumer(event);
- } else if (mGestureBlockingActivity != null && runningTaskInfo != null
- && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) {
+ return createOverviewInputConsumer(gestureState, event);
+ } else if (ENABLE_QUICKSTEP_LIVE_TILE.get()
+ && gestureState.getActivityInterface().isInLiveTileMode()) {
+ return createOverviewInputConsumer(gestureState, event);
+ } else if (mDeviceState.isGestureBlockedActivity(runningTaskInfo)) {
return mResetGestureInputConsumer;
} else {
- return createOtherActivityInputConsumer(event, runningTaskInfo);
+ return createOtherActivityInputConsumer(gestureState, event, runningTaskInfo);
}
}
@@ -693,15 +556,8 @@
&& (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
}
- private boolean disableHorizontalSwipe(MotionEvent event) {
- // mExclusionRegion can change on binder thread, use a local instance here.
- Region exclusionRegion = mExclusionRegion;
- return mMode == Mode.NO_BUTTON && exclusionRegion != null
- && exclusionRegion.contains((int) event.getX(), (int) event.getY());
- }
-
- private InputConsumer createOtherActivityInputConsumer(MotionEvent event,
- RunningTaskInfo runningTaskInfo) {
+ private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
+ MotionEvent event, RunningTaskInfo runningTaskInfo) {
final boolean shouldDefer;
final BaseSwipeUpHandler.Factory factory;
@@ -710,40 +566,40 @@
shouldDefer = !sSwipeSharedState.recentsAnimationFinishInterrupted;
factory = mFallbackNoButtonFactory;
} else {
- shouldDefer = mOverviewComponentObserver.getActivityControlHelper()
- .deferStartingActivity(mActiveNavBarRegion, event);
+ shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
+ event);
factory = mWindowTreansformFactory;
}
- return new OtherActivityInputConsumer(this, runningTaskInfo,
- shouldDefer, mOverviewCallbacks, this::onConsumerInactive,
- sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion,
- disableHorizontalSwipe(event), factory);
+ final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
+ return new OtherActivityInputConsumer(this, mDeviceState, gestureState, runningTaskInfo,
+ shouldDefer, this::onConsumerInactive, sSwipeSharedState, mInputMonitorCompat,
+ disableHorizontalSwipe, factory, mLogId);
}
- private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) {
+ private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState,
+ RunningTaskInfo taskInfo) {
if (mMode == Mode.NO_BUTTON && taskInfo != null) {
- return new DeviceLockedInputConsumer(this, sSwipeSharedState, mInputMonitorCompat,
- mSwipeTouchRegion, taskInfo.taskId);
+ return new DeviceLockedInputConsumer(this, mDeviceState, gestureState,
+ sSwipeSharedState, mInputMonitorCompat, taskInfo.taskId, mLogId);
} else {
return mResetGestureInputConsumer;
}
}
- public InputConsumer createOverviewInputConsumer(MotionEvent event) {
- final ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
- BaseDraggingActivity activity = activityControl.getCreatedActivity();
+ public InputConsumer createOverviewInputConsumer(GestureState gestureState, MotionEvent event) {
+ BaseDraggingActivity activity = gestureState.getActivityInterface().getCreatedActivity();
if (activity == null) {
return mResetGestureInputConsumer;
}
if (activity.getRootView().hasWindowFocus() || sSwipeSharedState.goingToLauncher) {
- return new OverviewInputConsumer(activity, mInputMonitorCompat,
+ return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat,
false /* startingInActivityBounds */);
} else {
- return new OverviewWithoutFocusInputConsumer(activity, mInputMonitorCompat,
- disableHorizontalSwipe(event));
+ final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
+ return new OverviewWithoutFocusInputConsumer(activity, gestureState,
+ mInputMonitorCompat, disableHorizontalSwipe);
}
}
@@ -758,7 +614,7 @@
}
private void preloadOverview(boolean fromInit) {
- if (!mIsUserUnlocked) {
+ if (!mDeviceState.isUserUnlocked()) {
return;
}
if (!mMode.hasGestures && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
@@ -772,11 +628,11 @@
return;
}
- final ActivityControlHelper<BaseDraggingActivity> activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
- if (activityControl.getCreatedActivity() == null) {
+ final BaseActivityInterface<BaseDraggingActivity> activityInterface =
+ mOverviewComponentObserver.getActivityInterface();
+ if (activityInterface.getCreatedActivity() == null) {
// Make sure that UI states will be initialized.
- activityControl.createActivityInitListener((activity, wasVisible) -> {
+ activityInterface.createActivityInitListener((activity, wasVisible) -> {
AppLaunchTracker.INSTANCE.get(activity);
return false;
}).register();
@@ -788,17 +644,18 @@
}
// Pass null animation handler to indicate this start is preload.
- startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(), null);
+ startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(),
+ null);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
- if (!mIsUserUnlocked) {
+ if (!mDeviceState.isUserUnlocked()) {
return;
}
- final ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
- final BaseDraggingActivity activity = activityControl.getCreatedActivity();
+ final BaseActivityInterface activityInterface =
+ mOverviewComponentObserver.getActivityInterface();
+ final BaseDraggingActivity activity = activityInterface.getCreatedActivity();
if (activity == null || activity.isStarted()) {
// We only care about the existing background activity.
return;
@@ -826,17 +683,11 @@
}
} else {
// Dump everything
+ mDeviceState.dump(pw);
pw.println("TouchState:");
pw.println(" navMode=" + mMode);
- pw.println(" validSystemUiFlags=" + validSystemUiFlags());
- pw.println(" systemUiFlags=" + mSystemUiStateFlags);
- pw.println(" systemUiFlagsDesc="
- + QuickStepContract.getSystemUiStateString(mSystemUiStateFlags));
- pw.println(" assistantAvailable=" + mAssistantAvailable);
- pw.println(" assistantDisabled="
- + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
boolean resumed = mOverviewComponentObserver != null
- && mOverviewComponentObserver.getActivityControlHelper().isResumed();
+ && mOverviewComponentObserver.getActivityInterface().isResumed();
pw.println(" resumed=" + resumed);
pw.println(" useSharedState=" + mConsumer.useSharedSwipeState());
if (mConsumer.useSharedSwipeState()) {
@@ -850,8 +701,7 @@
pw.println(" ENABLE_QUICKSTEP_LIVE_TILE=" + ENABLE_QUICKSTEP_LIVE_TILE.get());
pw.println(" ENABLE_HINTS_IN_OVERVIEW=" + ENABLE_HINTS_IN_OVERVIEW.get());
pw.println(" FAKE_LANDSCAPE_UI=" + FAKE_LANDSCAPE_UI.get());
- TOUCH_INTERACTION_LOG.dump("", pw);
-
+ ActiveGestureLog.INSTANCE.dump("", pw);
}
}
@@ -863,26 +713,30 @@
private void onCommand(PrintWriter pw, ArgList args) {
switch (args.nextArg()) {
case "clear-touch-log":
- TOUCH_INTERACTION_LOG.clear();
+ ActiveGestureLog.INSTANCE.clear();
break;
}
}
- private BaseSwipeUpHandler createWindowTransformSwipeHandler(RunningTaskInfo runningTask,
- long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
- return new WindowTransformSwipeHandler(runningTask, this, touchTimeMs,
- mOverviewComponentObserver, continuingLastGesture, mInputConsumer, mRecentsModel);
+ 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 createFallbackNoButtonSwipeHandler(RunningTaskInfo runningTask,
- long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
- return new FallbackNoButtonInputConsumer(this, mOverviewComponentObserver, runningTask,
- mRecentsModel, 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() {
return mBackGestureNotificationCounter > 0 &&
- mGestureBlockingActivity != null;
+ mDeviceState.getGestureBlockedActivityPackage() != null;
}
@WorkerThread
@@ -892,12 +746,12 @@
Utilities.getDevicePrefs(this).edit()
.putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply();
sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(
- mGestureBlockingActivity.getPackageName()));
+ mDeviceState.getGestureBlockedActivityPackage()));
}
}
public static void startRecentsActivityAsync(Intent intent, RecentsAnimationListener listener) {
- BACKGROUND_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+ UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
.startRecentsActivity(intent, null, listener, null, null));
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java
new file mode 100644
index 0000000..cbb6ad4
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.graphics.Canvas;
+import android.view.View;
+
+import com.android.systemui.shared.system.WindowCallbacksCompat;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * Utility class for helpful methods related to {@link View} objects.
+ */
+public class ViewUtils {
+
+ /** See {@link #postDraw(View, Runnable, BooleanSupplier)}} */
+ public static boolean postDraw(View view, Runnable onFinishRunnable) {
+ return postDraw(view, onFinishRunnable, () -> false);
+ }
+
+ /**
+ * Inject some addition logic in order to make sure that the view is updated smoothly post
+ * draw, and allow addition task to be run after view update.
+ *
+ * @param onFinishRunnable runnable to be run right after the view finishes drawing.
+ */
+ public static boolean postDraw(View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
+ // Defer finishing the animation until the next launcher frame with the
+ // new thumbnail
+ return new WindowCallbacksCompat(view) {
+ // The number of frames to defer until we actually finish the animation
+ private int mDeferFrameCount = 2;
+
+ @Override
+ public void onPostDraw(Canvas canvas) {
+ // If we were cancelled after this was attached, do not update
+ // the state.
+ if (canceled.getAsBoolean()) {
+ detach();
+ return;
+ }
+
+ if (mDeferFrameCount > 0) {
+ mDeferFrameCount--;
+ // Workaround, detach and reattach to invalidate the root node for
+ // another draw
+ detach();
+ attach();
+ view.invalidate();
+ return;
+ }
+
+ if (onFinishRunnable != null) {
+ onFinishRunnable.run();
+ }
+ detach();
+ }
+ }.attach();
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index e1085e6..1168758 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -23,19 +23,15 @@
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.util.DefaultDisplay.getSingleFrameMs;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
-import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
-import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
+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.TouchInteractionService.TOUCH_INTERACTION_LOG;
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 static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -45,7 +41,6 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Intent;
-import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
@@ -71,17 +66,15 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.ActivityControlHelper.AnimationFactory;
-import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
-import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
+import com.android.quickstep.BaseActivityInterface.AnimationFactory;
+import com.android.quickstep.BaseActivityInterface.AnimationFactory.ShelfAnimState;
+import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.inputconsumers.InputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
-import com.android.quickstep.util.ClipAnimationHelper.TargetAlphaProvider;
+import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.AppWindowAnimationHelper.TargetAlphaProvider;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.views.LiveTileOverlay;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -89,12 +82,10 @@
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.WindowCallbacksCompat;
@TargetApi(Build.VERSION_CODES.O)
public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
- extends BaseSwipeUpHandler<T, RecentsView>
- implements OnApplyWindowInsetsListener {
+ extends BaseSwipeUpHandler<T, RecentsView> implements OnApplyWindowInsetsListener {
private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
@@ -200,6 +191,9 @@
*/
private static final int LOG_NO_OP_PAGE_INDEX = -1;
+ private final RecentsAnimationDeviceState mDeviceState;
+ private final GestureState mGestureState;
+
private GestureEndTarget mGestureEndTarget;
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim mRunningWindowAnim;
@@ -231,11 +225,13 @@
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
- public WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context,
- long touchTimeMs, OverviewComponentObserver overviewComponentObserver,
- boolean continuingLastGesture,
+ public WindowTransformSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
+ GestureState gestureState, RunningTaskInfo runningTaskInfo, long touchTimeMs,
+ OverviewComponentObserver overviewComponentObserver, boolean continuingLastGesture,
InputConsumerController inputConsumer, RecentsModel recentsModel) {
- super(context, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id);
+ super(context, gestureState, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id);
+ mDeviceState = deviceState;
+ mGestureState = gestureState;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
initStateCallbacks();
@@ -291,9 +287,6 @@
mStateCallback.addCallback(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
this::notifyTransitionCancelled);
- mStateCallback.addCallback(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
- mRecentsAnimationWrapper::enableInputConsumer);
-
if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mStateCallback.addChangeHandler(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
| STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
@@ -354,7 +347,7 @@
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
if (mGestureEndTarget != HOME) {
Runnable initAnimFactory = () -> {
- mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity,
+ mAnimationFactory = mActivityInterface.prepareRecentsUI(mActivity,
mWasLauncherAlreadyVisible, true,
this::onAnimatorPlaybackControllerCreated);
maybeUpdateRecentsAttachedState(false /* animate */);
@@ -374,13 +367,19 @@
if (mWasLauncherAlreadyVisible) {
mStateCallback.setState(STATE_LAUNCHER_DRAWN);
} else {
- TraceHelper.beginSection("WTS-init");
+ Object traceToken = TraceHelper.INSTANCE.beginSection("WTS-init");
View dragLayer = activity.getDragLayer();
dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
+ boolean mHandled = false;
@Override
public void onDraw() {
- TraceHelper.endSection("WTS-init", "Launcher frame is drawn");
+ if (mHandled) {
+ return;
+ }
+ mHandled = true;
+
+ TraceHelper.INSTANCE.endSection(traceToken);
dragLayer.post(() ->
dragLayer.getViewTreeObserver().removeOnDrawListener(this));
if (activity != mActivity) {
@@ -417,15 +416,20 @@
}
private void sendRemoteAnimationsToAnimationFactory() {
- mAnimationFactory.onRemoteAnimationReceived(mRecentsAnimationWrapper.targetSet);
+ mAnimationFactory.onRemoteAnimationReceived(mRecentsAnimationTargets);
}
private void initializeLauncherAnimationController() {
buildAnimationController();
+ Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
+ TraceHelper.FLAG_IGNORE_BINDERS);
+ // Only used in debug builds
if (LatencyTrackerCompat.isEnabled(mContext)) {
- LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs));
+ LatencyTrackerCompat.logToggleRecents(
+ (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
}
+ TraceHelper.INSTANCE.endSection(traceToken);
// This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
// high-res thumbnail loader here once we are sure that we will end up in an overview state
@@ -460,9 +464,9 @@
if (mMode != Mode.NO_BUTTON || mRecentsView == null) {
return;
}
- RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationWrapper.targetSet == null
+ RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets == null
? null
- : mRecentsAnimationWrapper.targetSet.findTask(mRunningTaskId);
+ : mRecentsAnimationTargets.findTask(mRunningTaskId);
final boolean recentsAttachedToAppWindow;
int runningTaskIndex = mRecentsView.getRunningTaskIndex();
if (mGestureEndTarget != null) {
@@ -479,8 +483,8 @@
recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask;
if (animate) {
// Only animate if an adjacent task view is visible on screen.
- TaskView adjacentTask1 = mRecentsView.getTaskViewAt(runningTaskIndex + 1);
- TaskView adjacentTask2 = mRecentsView.getTaskViewAt(runningTaskIndex - 1);
+ TaskView adjacentTask1 = mRecentsView.getNextTaskView();
+ TaskView adjacentTask2 = mRecentsView.getPreviousTaskView();
float prevTranslationX = mRecentsView.getTranslationX();
mRecentsView.setTranslationX(0);
animate = (adjacentTask1 != null && adjacentTask1.getGlobalVisibleRect(TEMP_RECT))
@@ -519,7 +523,7 @@
return;
}
initTransitionEndpoints(mActivity.getDeviceProfile());
- mAnimationFactory.createActivityController(mTransitionDragLength);
+ mAnimationFactory.createActivityInterface(mTransitionDragLength);
}
@Override
@@ -544,17 +548,15 @@
@Override
public void updateFinalShift() {
-
- SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
- if (controller != null) {
+ if (mRecentsAnimationTargets != null) {
applyTransformUnchecked();
updateSysUiFlags(mCurrentShift.value);
}
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (mRecentsAnimationWrapper.getController() != null) {
- mLiveTileOverlay.update(mClipAnimationHelper.getCurrentRectWithInsets(),
- mClipAnimationHelper.getCurrentCornerRadius());
+ if (mRecentsAnimationTargets != null) {
+ mLiveTileOverlay.update(mAppWindowAnimationHelper.getCurrentRectWithInsets(),
+ mAppWindowAnimationHelper.getCurrentCornerRadius());
}
}
@@ -590,34 +592,41 @@
*/
private void updateSysUiFlags(float windowProgress) {
if (mRecentsView != null) {
- TaskView centermostTask = mRecentsView.getTaskViewAt(mRecentsView
- .getPageNearestToCenterOfScreen());
+ TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen();
int centermostTaskFlags = centermostTask == null ? 0
: centermostTask.getThumbnail().getSysUiStatusNavFlags();
boolean useHomeScreenFlags = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
// We will handle the sysui flags based on the centermost task view.
- mRecentsAnimationWrapper.setWindowThresholdCrossed(centermostTaskFlags != 0
- || useHomeScreenFlags);
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.setWindowThresholdCrossed(centermostTaskFlags != 0
+ || useHomeScreenFlags);
+ }
int sysuiFlags = useHomeScreenFlags ? 0 : centermostTaskFlags;
mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
}
}
@Override
- public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
- super.onRecentsAnimationStart(targetSet);
- TOUCH_INTERACTION_LOG.addLog("startRecentsAnimationCallback", targetSet.apps.length);
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
+ super.onRecentsAnimationStart(controller, targets);
+
+ // Only add the callback to enable the input consumer after we actually have the controller
+ mStateCallback.addCallback(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
+ mRecentsAnimationController::enableInputConsumer);
setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
mPassedOverviewThreshold = false;
}
@Override
- public void onRecentsAnimationCanceled() {
- mRecentsAnimationWrapper.setController(null);
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ super.onRecentsAnimationCanceled(thumbnailData);
+ mRecentsView.setRecentsAnimationTargets(null, null);
mActivityInitListener.unregister();
setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
- TOUCH_INTERACTION_LOG.addLog("cancelRecentsAnimation");
+ ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
}
@Override
@@ -684,9 +693,9 @@
setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
}
- BaseDraggingActivity activity = mActivityControlHelper.getCreatedActivity();
- return activity == null
- ? InputConsumer.NO_OP : new OverviewInputConsumer(activity, null, true);
+ BaseDraggingActivity activity = mActivityInterface.getCreatedActivity();
+ return activity == null ? InputConsumer.NO_OP
+ : new OverviewInputConsumer(mGestureState, activity, null, true);
}
private void endRunningWindowAnim(boolean cancel) {
@@ -704,7 +713,7 @@
final GestureEndTarget endTarget;
final boolean goingToNewTask;
if (mRecentsView != null) {
- if (!mRecentsAnimationWrapper.hasTargets()) {
+ if (!hasTargets()) {
// If there are no running tasks, then we can assume that this is a continuation of
// the last gesture, but after the recents animation has finished
goingToNewTask = true;
@@ -754,9 +763,7 @@
}
}
- int stateFlags = OverviewInteractionState.INSTANCE.get(mActivity).getSystemUiStateFlags();
- if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0
- && (endTarget == RECENTS || endTarget == LAST_TASK)) {
+ if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) {
return LAST_TASK;
}
return endTarget;
@@ -809,8 +816,9 @@
}
}
- if (endTarget.isLauncher) {
- mRecentsAnimationWrapper.enableInputProxy();
+ if (endTarget.isLauncher && mRecentsAnimationController != null) {
+ mRecentsAnimationController.enableInputProxy(mInputConsumer,
+ this::createNewInputProxyHandler);
}
if (endTarget == HOME) {
@@ -865,7 +873,7 @@
@UiThread
private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
GestureEndTarget target, PointF velocityPxPerMs) {
- mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
+ runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration,
interpolator, target, velocityPxPerMs));
}
@@ -879,13 +887,13 @@
if (mGestureEndTarget == HOME) {
HomeAnimationFactory homeAnimFactory;
if (mActivity != null) {
- homeAnimFactory = mActivityControlHelper.prepareHomeUI(mActivity);
+ homeAnimFactory = mActivityInterface.prepareHomeUI(mActivity);
} else {
homeAnimFactory = new HomeAnimationFactory() {
@NonNull
@Override
public RectF getWindowTargetRect() {
- RectF fallbackTarget = new RectF(mClipAnimationHelper.getTargetRect());
+ RectF fallbackTarget = new RectF(mAppWindowAnimationHelper.getTargetRect());
Utilities.scaleRectFAboutCenter(fallbackTarget, 0.25f);
return fallbackTarget;
}
@@ -991,7 +999,7 @@
}
// Make sure recents is in its final state
maybeUpdateRecentsAttachedState(false);
- mActivityControlHelper.onSwipeUpToHomeComplete(mActivity);
+ mActivityInterface.onSwipeUpToHomeComplete(mActivity);
}
});
return anim;
@@ -1011,10 +1019,14 @@
}
}
+ public boolean isCanceled() {
+ return mCanceled;
+ }
+
@UiThread
private void resumeLastTask() {
- mRecentsAnimationWrapper.finish(false /* toRecents */, null);
- TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
+ mRecentsAnimationController.finish(false /* toRecents */, null);
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
doLogGesture(LAST_TASK);
reset();
}
@@ -1096,7 +1108,7 @@
private void resetStateForAnimationCancel() {
boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
- mActivityControlHelper.onTransitionCancelled(mActivity, 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);
@@ -1104,17 +1116,23 @@
private void switchToScreenshot() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (mRecentsAnimationController != null) {
+ // Update the screenshot of the task
+ if (mTaskSnapshot == null) {
+ mTaskSnapshot = mRecentsAnimationController.screenshotTask(mRunningTaskId);
+ }
+ mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot, false /* refreshNow */);
+ }
setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
- } else if (!mRecentsAnimationWrapper.hasTargets()) {
+ } else if (!hasTargets()) {
// If there are no targets, then we don't need to capture anything
setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
} else {
boolean finishTransitionPosted = false;
- SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
- if (controller != null) {
+ if (mRecentsAnimationController != null) {
// Update the screenshot of the task
if (mTaskSnapshot == null) {
- mTaskSnapshot = controller.screenshotTask(mRunningTaskId);
+ mTaskSnapshot = mRecentsAnimationController.screenshotTask(mRunningTaskId);
}
final TaskView taskView;
if (mGestureEndTarget == HOME) {
@@ -1127,41 +1145,16 @@
if (taskView != null && !mCanceled) {
// Defer finishing the animation until the next launcher frame with the
// new thumbnail
- finishTransitionPosted = new WindowCallbacksCompat(taskView) {
-
- // The number of frames to defer until we actually finish the animation
- private int mDeferFrameCount = 2;
-
- @Override
- public void onPostDraw(Canvas canvas) {
- // If we were cancelled after this was attached, do not update
- // the state.
- if (mCanceled) {
- detach();
- return;
- }
-
- if (mDeferFrameCount > 0) {
- mDeferFrameCount--;
- // Workaround, detach and reattach to invalidate the root node for
- // another draw
- detach();
- attach();
- taskView.invalidate();
- return;
- }
-
- setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
- detach();
- }
- }.attach();
+ finishTransitionPosted = ViewUtils.postDraw(taskView,
+ () -> setStateOnUiThread(STATE_SCREENSHOT_CAPTURED), this::isCanceled);
}
}
if (!finishTransitionPosted) {
// If we haven't posted a draw callback, set the state immediately.
- RaceConditionTracker.onEvent(SCREENSHOT_CAPTURED_EVT, ENTER);
+ Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
+ TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
- RaceConditionTracker.onEvent(SCREENSHOT_CAPTURED_EVT, EXIT);
+ TraceHelper.INSTANCE.endSection(traceToken);
}
}
}
@@ -1169,33 +1162,35 @@
private void finishCurrentTransitionToRecents() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
- } else if (!mRecentsAnimationWrapper.hasTargets()) {
+ } else if (!hasTargets()) {
// If there are no targets, then there is nothing to finish
setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
} else {
- synchronized (mRecentsAnimationWrapper) {
- mRecentsAnimationWrapper.finish(true /* toRecents */,
+ synchronized (mRecentsAnimationController) {
+ mRecentsAnimationController.finish(true /* toRecents */,
() -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
}
- TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
}
private void finishCurrentTransitionToHome() {
- synchronized (mRecentsAnimationWrapper) {
- mRecentsAnimationWrapper.finish(true /* toRecents */,
+ synchronized (mRecentsAnimationController) {
+ mRecentsAnimationController.finish(true /* toRecents */,
() -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED),
true /* sendUserLeaveHint */);
}
- TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
doLogGesture(HOME);
}
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
endLauncherTransitionController();
- mActivityControlHelper.onSwipeUpToRecentsComplete(mActivity);
- mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */,
- true /* screenshot */);
+ mActivityInterface.onSwipeUpToRecentsComplete(mActivity);
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
+ true /* screenshot */);
+ }
mRecentsView.onSwipeUpAnimationSuccess();
RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
@@ -1205,7 +1200,7 @@
}
private void setTargetAlphaProvider(TargetAlphaProvider provider) {
- mClipAnimationHelper.setTaskAlphaCallback(provider);
+ mAppWindowAnimationHelper.setTaskAlphaCallback(provider);
updateFinalShift();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 2e9c0a3..79e71a1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -142,7 +142,7 @@
mZoomTranslationY = 0f;
} else {
TaskView dummyTask = getTaskViewAt(0);
- ScaleAndTranslation sat = getTempClipAnimationHelper()
+ ScaleAndTranslation sat = getTempAppWindowAnimationHelper()
.updateForFullscreenOverview(dummyTask)
.getScaleAndTranslation();
mZoomScale = sat.scale;
@@ -176,7 +176,7 @@
protected void applyLoadPlan(ArrayList<Task> tasks) {
// When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
- // track the index of the next task appropriately, as it we are switching on any other app.
+ // track the index of the next task appropriately, as if we are switching on any other app.
if (mRunningTaskInfo != null && mRunningTaskInfo.taskId == mRunningTaskId) {
// Check if the task list has running task
boolean found = false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index 1f73a28..d3765c5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -23,31 +23,29 @@
import static android.view.MotionEvent.ACTION_UP;
import android.content.Context;
-import android.graphics.RectF;
-import android.os.RemoteException;
-import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import com.android.launcher3.R;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.util.MotionPauseDetector;
-import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
- * Touch consumer for two finger swipe actions for accessibility actions
+ * Input consumer for two finger swipe actions for accessibility actions
*/
public class AccessibilityInputConsumer extends DelegateInputConsumer {
private static final String TAG = "A11yInputConsumer";
- private final ISystemUiProxy mSystemUiProxy;
+ private final Context mContext;
private final VelocityTracker mVelocityTracker;
private final MotionPauseDetector mMotionPauseDetector;
- private final boolean mAllowLongClick;
- private final RectF mSwipeTouchRegion;
+ private final RecentsAnimationDeviceState mDeviceState;
private final float mMinGestureDistance;
private final float mMinFlingVelocity;
@@ -56,19 +54,17 @@
private float mDownY;
private float mTotalY;
- public AccessibilityInputConsumer(Context context, ISystemUiProxy systemUiProxy,
- boolean allowLongClick, InputConsumer delegate, InputMonitorCompat inputMonitor,
- RectF swipeTouchRegion) {
+ public AccessibilityInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
+ InputConsumer delegate, InputMonitorCompat inputMonitor) {
super(delegate, inputMonitor);
- mSystemUiProxy = systemUiProxy;
+ mContext = context;
mVelocityTracker = VelocityTracker.obtain();
mMinGestureDistance = context.getResources()
.getDimension(R.dimen.accessibility_gesture_min_swipe_distance);
mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
- mSwipeTouchRegion = swipeTouchRegion;
+ mDeviceState = deviceState;
mMotionPauseDetector = new MotionPauseDetector(context);
- mAllowLongClick = allowLongClick;
}
@Override
@@ -103,7 +99,7 @@
case ACTION_POINTER_DOWN: {
if (mState == STATE_INACTIVE) {
int pointerIndex = ev.getActionIndex();
- if (mSwipeTouchRegion.contains(ev.getX(pointerIndex), ev.getY(pointerIndex))
+ if (mDeviceState.isInSwipeUpTouchRegion(ev, pointerIndex)
&& mDelegate.allowInterceptByParent()) {
setActive(ev);
@@ -116,7 +112,7 @@
break;
}
case ACTION_MOVE: {
- if (mState == STATE_ACTIVE && mAllowLongClick) {
+ if (mState == STATE_ACTIVE && mDeviceState.isAccessibilityMenuShortcutAvailable()) {
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
break;
@@ -129,21 +125,18 @@
}
case ACTION_UP:
if (mState == STATE_ACTIVE) {
- try {
- if (mAllowLongClick && mMotionPauseDetector.isPaused()) {
- mSystemUiProxy.notifyAccessibilityButtonLongClicked();
- } else {
- mTotalY += (ev.getY() - mDownY);
- mVelocityTracker.computeCurrentVelocity(1000);
+ if (mDeviceState.isAccessibilityMenuShortcutAvailable()
+ && mMotionPauseDetector.isPaused()) {
+ SystemUiProxy.INSTANCE.get(mContext).notifyAccessibilityButtonLongClicked();
+ } else {
+ mTotalY += (ev.getY() - mDownY);
+ mVelocityTracker.computeCurrentVelocity(1000);
- if ((-mTotalY) > mMinGestureDistance
- || (-mVelocityTracker.getYVelocity()) > mMinFlingVelocity) {
- mSystemUiProxy.notifyAccessibilityButtonClicked(
- Display.DEFAULT_DISPLAY);
- }
+ if ((-mTotalY) > mMinGestureDistance
+ || (-mVelocityTracker.getYVelocity()) > mMinFlingVelocity) {
+ SystemUiProxy.INSTANCE.get(mContext).notifyAccessibilityButtonClicked(
+ Display.DEFAULT_DISPLAY);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to notify accessibility event", e);
}
}
// Follow through
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
similarity index 78%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 346969e..94126ff 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -37,9 +37,7 @@
import android.content.res.Resources;
import android.graphics.PointF;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.SystemClock;
-import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.HapticFeedbackConstants;
@@ -50,16 +48,18 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.quickstep.ActivityControlHelper;
-import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
* Touch consumer for handling events to launch assistant from launcher
*/
-public class AssistantTouchConsumer extends DelegateInputConsumer {
+public class AssistantInputConsumer extends DelegateInputConsumer {
- private static final String TAG = "AssistantTouchConsumer";
+ private static final String TAG = "AssistantInputConsumer";
private static final long RETRACT_ANIMATION_DURATION_MS = 300;
// From //java/com/google/android/apps/gsa/search/shared/util/OpaContract.java.
@@ -81,24 +81,21 @@
private long mDragTime;
private float mLastProgress;
private int mDirection;
- private ActivityControlHelper mActivityControlHelper;
+ private BaseActivityInterface mActivityInterface;
private final float mDragDistThreshold;
private final float mFlingDistThreshold;
private final long mTimeThreshold;
private final int mAngleThreshold;
private final float mSquaredSlop;
- private final ISystemUiProxy mSysUiProxy;
private final Context mContext;
private final GestureDetector mGestureDetector;
- public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
- ActivityControlHelper activityControlHelper, InputConsumer delegate,
- InputMonitorCompat inputMonitor) {
+ public AssistantInputConsumer(Context context, GestureState gestureState,
+ InputConsumer delegate, InputMonitorCompat inputMonitor) {
super(delegate, inputMonitor);
final Resources res = context.getResources();
mContext = context;
- mSysUiProxy = systemUiProxy;
mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
mFlingDistThreshold = res.getDimension(R.dimen.gestures_assistant_fling_threshold);
mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
@@ -107,7 +104,7 @@
float slop = ViewConfiguration.get(context).getScaledTouchSlop();
mSquaredSlop = slop * slop;
- mActivityControlHelper = activityControlHelper;
+ mActivityInterface = gestureState.getActivityInterface();
mGestureDetector = new GestureDetector(context, new AssistantGestureListener());
}
@@ -131,8 +128,8 @@
case ACTION_POINTER_DOWN: {
if (mState != STATE_ACTIVE) {
mState = STATE_DELEGATE_ACTIVE;
- break;
}
+ break;
}
case ACTION_POINTER_UP: {
int ptrIdx = ev.getActionIndex();
@@ -198,13 +195,7 @@
SWIPE_NOOP, mDirection, NAVBAR);
animator.addUpdateListener(valueAnimator -> {
float progress = (float) valueAnimator.getAnimatedValue();
- try {
-
- mSysUiProxy.onAssistantProgress(progress);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to send SysUI start/send assistant progress: "
- + progress, e);
- }
+ SystemUiProxy.INSTANCE.get(mContext).onAssistantProgress(progress);
});
animator.setInterpolator(Interpolators.DEACCEL_2);
animator.start();
@@ -224,22 +215,17 @@
private void updateAssistantProgress() {
if (!mLaunchedAssistant) {
mLastProgress = Math.min(mDistance * 1f / mDragDistThreshold, 1) * mTimeFraction;
- try {
- if (mDistance >= mDragDistThreshold && mTimeFraction >= 1) {
- mSysUiProxy.onAssistantGestureCompletion(0);
- startAssistantInternal(SWIPE);
+ if (mDistance >= mDragDistThreshold && mTimeFraction >= 1) {
+ SystemUiProxy.INSTANCE.get(mContext).onAssistantGestureCompletion(0);
+ startAssistantInternal(SWIPE);
- Bundle args = new Bundle();
- args.putInt(OPA_BUNDLE_TRIGGER, OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE);
- args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
- mSysUiProxy.startAssistant(args);
- mLaunchedAssistant = true;
- } else {
- mSysUiProxy.onAssistantProgress(mLastProgress);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + mLastProgress,
- e);
+ Bundle args = new Bundle();
+ args.putInt(OPA_BUNDLE_TRIGGER, OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE);
+ args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
+ SystemUiProxy.INSTANCE.get(mContext).startAssistant(args);
+ mLaunchedAssistant = true;
+ } else {
+ SystemUiProxy.INSTANCE.get(mContext).onAssistantProgress(mLastProgress);
}
}
}
@@ -248,8 +234,7 @@
UserEventDispatcher.newInstance(mContext)
.logActionOnContainer(gestureType, mDirection, NAVBAR);
- BaseDraggingActivity launcherActivity = mActivityControlHelper
- .getCreatedActivity();
+ BaseDraggingActivity launcherActivity = mActivityInterface.getCreatedActivity();
if (launcherActivity != null) {
launcherActivity.getRootView().performHapticFeedback(
13, // HapticFeedbackConstants.GESTURE_END
@@ -274,24 +259,18 @@
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (isValidAssistantGestureAngle(velocityX, -velocityY)
- && mDistance >= mFlingDistThreshold
- && !mLaunchedAssistant
- && mState != STATE_DELEGATE_ACTIVE) {
+ && mDistance >= mFlingDistThreshold
+ && !mLaunchedAssistant
+ && mState != STATE_DELEGATE_ACTIVE) {
mLastProgress = 1;
- try {
- mSysUiProxy.onAssistantGestureCompletion(
- (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY));
- startAssistantInternal(FLING);
+ SystemUiProxy.INSTANCE.get(mContext).onAssistantGestureCompletion(
+ (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY));
+ startAssistantInternal(FLING);
- Bundle args = new Bundle();
- args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
- mSysUiProxy.startAssistant(args);
- mLaunchedAssistant = true;
- } catch (RemoteException e) {
- Log.w(TAG,
- "Failed to send SysUI start/send assistant progress: " + mLastProgress,
- e);
- }
+ Bundle args = new Bundle();
+ args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
+ SystemUiProxy.INSTANCE.get(mContext).startAssistant(args);
+ mLaunchedAssistant = true;
}
return true;
}
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 311ddd2..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
@@ -2,6 +2,7 @@
import android.view.MotionEvent;
+import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
public abstract class DelegateInputConsumer implements InputConsumer {
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 3d763ab..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,8 +22,9 @@
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.WindowTransformSwipeHandler.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;
import android.content.Context;
@@ -31,20 +32,24 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
-import android.view.WindowManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
import com.android.quickstep.LockScreenRecentsActivity;
import com.android.quickstep.MultiStateCallback;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.SwipeSharedState;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.RecentsAnimationListenerSet;
-import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -52,7 +57,7 @@
* A dummy input consumer used when the device is still locked, e.g. from secure camera.
*/
public class DeviceLockedInputConsumer implements InputConsumer,
- SwipeAnimationTargetSet.SwipeAnimationListener {
+ RecentsAnimationCallbacks.RecentsAnimationListener {
private static final float SCALE_DOWN = 0.75f;
@@ -70,16 +75,18 @@
getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
private final Context mContext;
+ private final RecentsAnimationDeviceState mDeviceState;
+ private final GestureState mGestureState;
private final float mTouchSlopSquared;
private final SwipeSharedState mSwipeSharedState;
private final InputMonitorCompat mInputMonitorCompat;
private final PointF mTouchDown = new PointF();
- private final ClipAnimationHelper mClipAnimationHelper;
- private final ClipAnimationHelper.TransformParams mTransformParams;
+ private final AppWindowAnimationHelper mAppWindowAnimationHelper;
+ private int mLogId;
+ private final AppWindowAnimationHelper.TransformParams mTransformParams;
private final Point mDisplaySize;
private final MultiStateCallback mStateCallback;
- private final RectF mSwipeTouchRegion;
public final int mRunningTaskId;
private VelocityTracker mVelocityTracker;
@@ -87,22 +94,25 @@
private boolean mThresholdCrossed = false;
- private SwipeAnimationTargetSet mTargetSet;
+ private RecentsAnimationController mRecentsAnimationController;
+ private RecentsAnimationTargets mRecentsAnimationTargets;
- public DeviceLockedInputConsumer(Context context, SwipeSharedState swipeSharedState,
- InputMonitorCompat inputMonitorCompat, RectF swipeTouchRegion, int runningTaskId) {
+ public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
+ GestureState gestureState, SwipeSharedState swipeSharedState,
+ InputMonitorCompat inputMonitorCompat, int runningTaskId, int logId) {
mContext = context;
+ mDeviceState = deviceState;
+ mGestureState = gestureState;
mTouchSlopSquared = squaredTouchSlop(context);
mSwipeSharedState = swipeSharedState;
- mClipAnimationHelper = new ClipAnimationHelper(context);
- mTransformParams = new ClipAnimationHelper.TransformParams();
+ mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
+ mLogId = logId;
+ mTransformParams = new AppWindowAnimationHelper.TransformParams();
mInputMonitorCompat = inputMonitorCompat;
- mSwipeTouchRegion = swipeTouchRegion;
mRunningTaskId = runningTaskId;
// Do not use DeviceProfile as the user data might be locked
- mDisplaySize = new Point();
- context.getSystemService(WindowManager.class).getDefaultDisplay().getRealSize(mDisplaySize);
+ mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize;
// Init states
mStateCallback = new MultiStateCallback(STATE_NAMES);
@@ -134,7 +144,7 @@
if (!mThresholdCrossed) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
+ if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) {
int action = ev.getAction();
ev.setAction(ACTION_CANCEL);
finishTouchTracking(ev);
@@ -152,9 +162,7 @@
float dy = Math.max(mTouchDown.y - y, 0);
mProgress = dy / mDisplaySize.y;
mTransformParams.setProgress(mProgress);
- if (mTargetSet != null) {
- mClipAnimationHelper.applyTransform(mTargetSet, mTransformParams);
- }
+ mAppWindowAnimationHelper.applyTransform(mTransformParams);
}
break;
}
@@ -199,44 +207,49 @@
private void startRecentsTransition() {
mThresholdCrossed = true;
- RecentsAnimationListenerSet newListenerSet =
- mSwipeSharedState.newRecentsAnimationListenerSet();
- newListenerSet.addListener(this);
+ 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);
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ .putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
mInputMonitorCompat.pilferPointers();
- startRecentsActivityAsync(intent, newListenerSet);
+ startRecentsActivityAsync(intent, callbacks);
}
@Override
- public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
- mTargetSet = targetSet;
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ mRecentsAnimationController = controller;
+ mRecentsAnimationTargets = targets;
Rect displaySize = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
- RemoteAnimationTargetCompat targetCompat = targetSet.findTask(mRunningTaskId);
+ RemoteAnimationTargetCompat targetCompat = targets.findTask(mRunningTaskId);
if (targetCompat != null) {
- mClipAnimationHelper.updateSource(displaySize, targetCompat);
+ mAppWindowAnimationHelper.updateSource(displaySize, targetCompat);
}
Utilities.scaleRectAboutCenter(displaySize, SCALE_DOWN);
displaySize.offsetTo(displaySize.left, 0);
- mClipAnimationHelper.updateTargetRect(displaySize);
- mClipAnimationHelper.applyTransform(mTargetSet, mTransformParams);
+ mTransformParams.setTargetSet(mRecentsAnimationTargets)
+ .setLauncherOnTop(true);
+ mAppWindowAnimationHelper.updateTargetRect(displaySize);
+ mAppWindowAnimationHelper.applyTransform(mTransformParams);
mStateCallback.setState(STATE_TARGET_RECEIVED);
}
@Override
- public void onRecentsAnimationCanceled() {
- mTargetSet = null;
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
}
private void endRemoteAnimation() {
- if (mTargetSet != null) {
- mTargetSet.finishController(
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.finishController(
false /* toRecents */, null /* callback */, false /* sendUserLeaveHint */);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
index 6ec1da0..370b487 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
@@ -38,19 +38,21 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
+import com.android.launcher3.util.ObjectWrapper;
+import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
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.ObjectWrapper;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SwipeAnimationTargetSet;
-import com.android.quickstep.views.RecentsView;
+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;
@@ -113,12 +115,13 @@
private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
private RunningWindowAnim mFinishAnimation;
- public FallbackNoButtonInputConsumer(Context context,
+ public FallbackNoButtonInputConsumer(Context context, GestureState gestureState,
OverviewComponentObserver overviewComponentObserver,
RunningTaskInfo runningTaskInfo, RecentsModel recentsModel,
InputConsumerController inputConsumer,
boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
- super(context, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id);
+ super(context, gestureState, overviewComponentObserver, recentsModel, inputConsumer,
+ runningTaskInfo.id);
mLauncherAlpha.value = 1;
mRunningTaskInfo = runningTaskInfo;
@@ -128,9 +131,9 @@
mSwipeUpOverHome = mRunningOverHome && !mInQuickSwitchMode;
if (mSwipeUpOverHome) {
- mClipAnimationHelper.setBaseAlphaCallback((t, a) -> 1 - mLauncherAlpha.value);
+ mAppWindowAnimationHelper.setBaseAlphaCallback((t, a) -> 1 - mLauncherAlpha.value);
} else {
- mClipAnimationHelper.setBaseAlphaCallback((t, a) -> mLauncherAlpha.value);
+ mAppWindowAnimationHelper.setBaseAlphaCallback((t, a) -> mLauncherAlpha.value);
}
initStateCallbacks();
@@ -158,7 +161,7 @@
}
private void onLauncherAlphaChanged() {
- if (mRecentsAnimationWrapper.targetSet != null && mEndTarget == null) {
+ if (mRecentsAnimationTargets != null && mEndTarget == null) {
applyTransformUnchecked();
}
}
@@ -232,9 +235,11 @@
@Override
public void updateFinalShift() {
mTransformParams.setProgress(mCurrentShift.value);
- mRecentsAnimationWrapper.setWindowThresholdCrossed(!mInQuickSwitchMode
- && (mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD));
- if (mRecentsAnimationWrapper.targetSet != null) {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.setWindowThresholdCrossed(!mInQuickSwitchMode
+ && (mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD));
+ }
+ if (mRecentsAnimationTargets != null) {
applyTransformUnchecked();
}
}
@@ -317,26 +322,25 @@
switch (mEndTarget) {
case HOME: {
if (mSwipeUpOverHome) {
- mRecentsAnimationWrapper.finish(false, null, false);
+ mRecentsAnimationController.finish(false, null, false);
// Send a home intent to clear the task stack
mContext.startActivity(mOverviewComponentObserver.getHomeIntent());
} else {
- mRecentsAnimationWrapper.finish(true, null, true);
+ mRecentsAnimationController.finish(true, null, true);
}
break;
}
case LAST_TASK:
- mRecentsAnimationWrapper.finish(false, null, false);
+ mRecentsAnimationController.finish(false, null, false);
break;
case RECENTS: {
if (mSwipeUpOverHome) {
- mRecentsAnimationWrapper.finish(true, null, true);
+ mRecentsAnimationController.finish(true, null, true);
break;
}
- ThumbnailData thumbnail =
- mRecentsAnimationWrapper.targetSet.controller.screenshotTask(mRunningTaskId);
- mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */,
+ ThumbnailData thumbnail = mRecentsAnimationController.screenshotTask(mRunningTaskId);
+ mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
false /* screenshot */);
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
@@ -349,7 +353,7 @@
Intent intent = new Intent(mOverviewComponentObserver.getOverviewIntent())
.putExtras(extras);
mContext.startActivity(intent, options.toBundle());
- mRecentsAnimationWrapper.targetSet.controller.cleanupScreenshot();
+ mRecentsAnimationController.cleanupScreenshot();
break;
}
case NEW_TASK: {
@@ -365,7 +369,7 @@
if (mInQuickSwitchMode) {
// Recalculate the end target, some views might have been initialized after
// gesture has ended.
- if (mRecentsView == null || !mRecentsAnimationWrapper.hasTargets()) {
+ if (mRecentsView == null || !hasTargets()) {
mEndTarget = LAST_TASK;
} else {
final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
@@ -415,12 +419,13 @@
}
@Override
- public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
- super.onRecentsAnimationStart(targetSet);
- mRecentsAnimationWrapper.enableInputConsumer();
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ super.onRecentsAnimationStart(controller, targets);
+ mRecentsAnimationController.enableInputConsumer();
if (mRunningOverHome) {
- mClipAnimationHelper.prepareAnimation(mDp, true);
+ mAppWindowAnimationHelper.prepareAnimation(mDp, true);
}
applyTransformUnchecked();
@@ -428,8 +433,8 @@
}
@Override
- public void onRecentsAnimationCanceled() {
- mRecentsAnimationWrapper.setController(null);
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ mRecentsView.setRecentsAnimationTargets(null, null);
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 86766d9..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
@@ -25,18 +25,17 @@
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT;
-import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
+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;
import android.graphics.PointF;
-import android.graphics.RectF;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -44,26 +43,30 @@
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
+import androidx.annotation.UiThread;
+
import com.android.launcher3.R;
import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.BaseSwipeUpHandler;
-import com.android.quickstep.OverviewCallbacks;
+import com.android.quickstep.BaseSwipeUpHandler.Factory;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationDeviceState;
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.quickstep.util.RecentsAnimationListenerSet;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
import java.util.function.Consumer;
-import androidx.annotation.UiThread;
-
/**
* Input consumer for handling events originating from an activity other than Launcher
*/
@@ -76,13 +79,14 @@
// TODO: Move to quickstep contract
public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
+ private final RecentsAnimationDeviceState mDeviceState;
+ private final GestureState mGestureState;
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
private final RunningTaskInfo mRunningTask;
- private final OverviewCallbacks mOverviewCallbacks;
private final SwipeSharedState mSwipeSharedState;
private final InputMonitorCompat mInputMonitorCompat;
private final SysUINavigationMode.Mode mMode;
- private final RectF mSwipeTouchRegion;
+ private final BaseActivityInterface mActivityInterface;
private final BaseSwipeUpHandler.Factory mHandlerFactory;
@@ -119,20 +123,22 @@
ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
true /* restoreHomeStackPosition */);
};
+ private int mLogId;
- public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo,
- boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks,
- Consumer<OtherActivityInputConsumer> onCompleteCallback,
+ public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
+ GestureState gestureState, RunningTaskInfo runningTaskInfo,
+ boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
- RectF swipeTouchRegion, boolean disableHorizontalSwipe,
- BaseSwipeUpHandler.Factory handlerFactory) {
+ boolean disableHorizontalSwipe, Factory handlerFactory, int logId) {
super(base);
-
+ mLogId = logId;
+ mDeviceState = deviceState;
+ mGestureState = gestureState;
mMainThreadHandler = new Handler(Looper.getMainLooper());
mRunningTask = runningTaskInfo;
mMode = SysUINavigationMode.getMode(base);
- mSwipeTouchRegion = swipeTouchRegion;
mHandlerFactory = handlerFactory;
+ mActivityInterface = mGestureState.getActivityInterface();
mMotionPauseDetector = new MotionPauseDetector(base);
mMotionPauseMinDisplacement = base.getResources().getDimension(
@@ -143,7 +149,6 @@
boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null;
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
- mOverviewCallbacks = overviewCallbacks;
mSwipeSharedState = swipeSharedState;
mNavBarPosition = new NavBarPosition(base);
@@ -193,8 +198,8 @@
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
- RaceConditionTracker.onEvent(DOWN_EVT, ENTER);
- TraceHelper.beginSection("TouchInt");
+ Object traceToken = TraceHelper.INSTANCE.beginSection(DOWN_EVT,
+ FLAG_CHECK_FOR_RACE_CONDITIONS);
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
@@ -205,14 +210,14 @@
startTouchTrackingForWindowAnimation(ev.getEventTime(), false);
}
- RaceConditionTracker.onEvent(DOWN_EVT, EXIT);
+ TraceHelper.INSTANCE.endSection(traceToken);
break;
}
case ACTION_POINTER_DOWN: {
if (!mPassedPilferInputSlop) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
+ if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) {
forceCancelGesture(ev);
}
}
@@ -306,13 +311,13 @@
}
private void notifyGestureStarted() {
- TOUCH_INTERACTION_LOG.addLog("startQuickstep");
+ ActiveGestureLog.INSTANCE.addLog("startQuickstep");
if (mInteractionHandler == null) {
return;
}
mInputMonitorCompat.pilferPointers();
- mOverviewCallbacks.closeAllWindows();
+ mActivityInterface.closeOverlay();
ActivityManagerWrapper.getInstance().closeSystemWindows(
CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
@@ -322,11 +327,11 @@
private void startTouchTrackingForWindowAnimation(
long touchTimeMs, boolean isLikelyToStartNewTask) {
- TOUCH_INTERACTION_LOG.addLog("startRecentsAnimation");
+ ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
- RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
- final BaseSwipeUpHandler handler = mHandlerFactory.newHandler(mRunningTask, touchTimeMs,
- listenerSet != null, isLikelyToStartNewTask);
+ RecentsAnimationCallbacks listenerSet = mSwipeSharedState.getActiveListener();
+ final BaseSwipeUpHandler handler = mHandlerFactory.newHandler(mGestureState, mRunningTask,
+ touchTimeMs, listenerSet != null, isLikelyToStartNewTask);
mInteractionHandler = handler;
handler.setGestureEndCallback(this::onInteractionGestureFinished);
@@ -338,10 +343,11 @@
mSwipeSharedState.applyActiveRecentsAnimationState(handler);
notifyGestureStarted();
} else {
- RecentsAnimationListenerSet newListenerSet =
- mSwipeSharedState.newRecentsAnimationListenerSet();
- newListenerSet.addListener(handler);
- startRecentsActivityAsync(handler.getLaunchIntent(), newListenerSet);
+ RecentsAnimationCallbacks callbacks = mSwipeSharedState.newRecentsAnimationCallbacks();
+ callbacks.addListener(handler);
+ Intent intent = handler.getLaunchIntent();
+ intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
+ startRecentsActivityAsync(intent, callbacks);
}
}
@@ -350,8 +356,8 @@
* the animation can still be running.
*/
private void finishTouchTracking(MotionEvent ev) {
- RaceConditionTracker.onEvent(UP_EVT, ENTER);
- TraceHelper.endSection("TouchInt");
+ Object traceToken = TraceHelper.INSTANCE.beginSection(UP_EVT,
+ FLAG_CHECK_FOR_RACE_CONDITIONS);
if (mPassedWindowMoveSlop && mInteractionHandler != null) {
if (ev.getActionMasked() == ACTION_CANCEL) {
@@ -385,7 +391,7 @@
mVelocityTracker.recycle();
mVelocityTracker = null;
mMotionPauseDetector.clear();
- RaceConditionTracker.onEvent(UP_EVT, EXIT);
+ TraceHelper.INSTANCE.endSection(traceToken);
}
@Override
@@ -409,7 +415,7 @@
}
private void removeListener() {
- RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
+ RecentsAnimationCallbacks listenerSet = mSwipeSharedState.getActiveListener();
if (listenerSet != null) {
listenerSet.removeListener(mInteractionHandler);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 581ab6d..8149e64 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -16,10 +16,8 @@
package com.android.quickstep.inputconsumers;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -27,9 +25,11 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.OverviewCallbacks;
+import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -42,6 +42,7 @@
implements InputConsumer {
private final T mActivity;
+ private final BaseActivityInterface<T> mActivityInterface;
private final BaseDragLayer mTarget;
private final InputMonitorCompat mInputMonitor;
@@ -52,11 +53,12 @@
private final boolean mStartingInActivityBounds;
private boolean mTargetHandledTouch;
- public OverviewInputConsumer(T activity, @Nullable InputMonitorCompat inputMonitor,
- boolean startingInActivityBounds) {
+ public OverviewInputConsumer(GestureState gestureState, T activity,
+ @Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) {
mActivity = activity;
mInputMonitor = inputMonitor;
mStartingInActivityBounds = startingInActivityBounds;
+ mActivityInterface = gestureState.getActivityInterface();
mTarget = activity.getDragLayer();
if (startingInActivityBounds) {
@@ -98,10 +100,10 @@
if (!mTargetHandledTouch && handled) {
mTargetHandledTouch = true;
if (!mStartingInActivityBounds) {
- OverviewCallbacks.get(mActivity).closeAllWindows();
+ mActivityInterface.closeOverlay();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- TOUCH_INTERACTION_LOG.addLog("startQuickstep");
+ ActiveGestureLog.INSTANCE.addLog("startQuickstep");
}
if (mInputMonitor != null) {
mInputMonitor.pilferPointers();
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 05cbb78..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,7 +21,6 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.content.Context;
@@ -34,10 +33,12 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogUtils;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.OverviewCallbacks;
+import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.InputConsumer;
+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;
@@ -50,16 +51,17 @@
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, InputMonitorCompat inputMonitor,
- boolean disableHorizontalSwipe) {
+ public OverviewWithoutFocusInputConsumer(Context context, GestureState gestureState,
+ InputMonitorCompat inputMonitor, boolean disableHorizontalSwipe) {
mInputMonitor = inputMonitor;
mDisableHorizontalSwipe = disableHorizontalSwipe;
mContext = context;
+ mActivityInterface = gestureState.getActivityInterface();
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
mNavBarPosition = new NavBarPosition(context);
@@ -148,10 +150,10 @@
}
if (triggerQuickstep) {
- OverviewCallbacks.get(mContext).closeAllWindows();
+ mActivityInterface.closeOverlay();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- TOUCH_INTERACTION_LOG.addLog("startQuickstep");
+ ActiveGestureLog.INSTANCE.addLog("startQuickstep");
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
int pageIndex = -1; // This number doesn't reflect workspace page index.
// It only indicates that launcher client screen was shown.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/QuickCaptureInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/QuickCaptureInputConsumer.java
new file mode 100644
index 0000000..97ca730
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/QuickCaptureInputConsumer.java
@@ -0,0 +1,221 @@
+/*
+ * 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.inputconsumers;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+
+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 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.shared.system.InputMonitorCompat;
+
+/**
+ * Input consumer for handling events to launch quick capture from launcher
+ * @param <T> Draggable activity subclass used by RecentsView
+ */
+public class QuickCaptureInputConsumer<T extends BaseDraggingActivity>
+ extends DelegateInputConsumer {
+
+ 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
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final PointF mStartDragPos = new PointF();
+
+ private int mActivePointerId = -1;
+ private boolean mPassedSlop = false;
+
+ private final float mSquaredSlop;
+
+ private Context mContext;
+
+ private RecentsView mRecentsView;
+
+ public QuickCaptureInputConsumer(Context context, GestureState gestureState,
+ InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ super(delegate, inputMonitor);
+ mContext = context;
+
+ float slop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mSquaredSlop = slop * slop;
+
+ gestureState.getActivityInterface().createActivityInitListener(this::onActivityInit)
+ .register();
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_QUICK_CAPTURE | mDelegate.getType();
+ }
+
+ private boolean onActivityInit(final BaseDraggingActivity activity, Boolean alreadyOnHome) {
+ mRecentsView = activity.getOverviewPanel();
+
+ return true;
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case ACTION_DOWN: {
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+
+ break;
+ }
+ case ACTION_POINTER_DOWN: {
+ if (mState != STATE_ACTIVE) {
+ mState = STATE_DELEGATE_ACTIVE;
+ }
+ break;
+ }
+ case 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 ACTION_MOVE: {
+ if (mState == STATE_DELEGATE_ACTIVE) {
+ break;
+ }
+ if (!mDelegate.allowInterceptByParent()) {
+ mState = STATE_DELEGATE_ACTIVE;
+ break;
+ }
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == -1) {
+ break;
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+
+ if (!mPassedSlop) {
+ // Normal gesture, ensure we pass the slop before we start tracking the gesture
+ if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
+ > mSquaredSlop) {
+
+ mPassedSlop = true;
+ mStartDragPos.set(mLastPos.x, mLastPos.y);
+
+ if (isValidQuickCaptureGesture()) {
+ setActive(ev);
+ } else {
+ mState = STATE_DELEGATE_ACTIVE;
+ }
+ }
+ }
+
+ break;
+ }
+ case ACTION_CANCEL:
+ case ACTION_UP:
+ if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop) {
+ startQuickCapture();
+ }
+
+ mPassedSlop = false;
+ mState = STATE_INACTIVE;
+ break;
+ }
+
+ if (mState != STATE_ACTIVE) {
+ mDelegate.onMotionEvent(ev);
+ }
+ }
+
+ private boolean isValidQuickCaptureGesture() {
+ // Make sure there isn't an app to quick switch to on our right
+ boolean atRightMostApp = (mRecentsView == null || mRecentsView.getRunningTaskIndex() <= 0);
+
+ // Check if the gesture is within our angle threshold of horizontal
+ float deltaY = Math.abs(mLastPos.y - mDownPos.y);
+ float deltaX = mDownPos.x - mLastPos.x; // Positive if this is a gesture to the left
+ boolean angleInBounds = Math.toDegrees(Math.atan2(deltaY, deltaX)) < ANGLE_THRESHOLD;
+
+ return atRightMostApp && angleInBounds;
+ }
+
+ 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 = DEVICE_STATE_LAUNCHER;
+ } else if ((consumerType & InputConsumer.TYPE_OTHER_ACTIVITY) > 0) {
+ deviceState = DEVICE_STATE_APP;
+ } else if (((consumerType & InputConsumer.TYPE_RESET_GESTURE) > 0)
+ || ((consumerType & InputConsumer.TYPE_DEVICE_LOCKED) > 0)) {
+ deviceState = DEVICE_STATE_LOCKED;
+ }
+
+ // 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 8eede81..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
@@ -17,6 +17,7 @@
import android.view.MotionEvent;
+import com.android.quickstep.InputConsumer;
import com.android.quickstep.SwipeSharedState;
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index a0e20f2..d5ed321 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -16,16 +16,15 @@
package com.android.quickstep.inputconsumers;
import android.content.Context;
-import android.os.RemoteException;
-import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
-import com.android.quickstep.ActivityControlHelper;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
import com.android.quickstep.util.MotionPauseDetector;
-import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.quickstep.SystemUiProxy;
/**
* An input consumer that detects swipe up and hold to exit screen pinning mode.
@@ -39,25 +38,21 @@
private float mTouchDownY;
- public ScreenPinnedInputConsumer(Context context, ISystemUiProxy sysuiProxy,
- ActivityControlHelper activityControl) {
+ public ScreenPinnedInputConsumer(Context context, GestureState gestureState) {
mMotionPauseMinDisplacement = context.getResources().getDimension(
R.dimen.motion_pause_detector_min_displacement_from_app);
mMotionPauseDetector = new MotionPauseDetector(context, true /* makePauseHarderToTrigger*/);
mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
if (isPaused) {
- try {
- sysuiProxy.stopScreenPinning();
- BaseDraggingActivity launcherActivity = activityControl.getCreatedActivity();
- if (launcherActivity != null) {
- launcherActivity.getRootView().performHapticFeedback(
- HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- }
- mMotionPauseDetector.clear();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to stop screen pinning ", e);
+ SystemUiProxy.INSTANCE.get(context).stopScreenPinning();
+ BaseDraggingActivity launcherActivity = gestureState.getActivityInterface()
+ .getCreatedActivity();
+ if (launcherActivity != null) {
+ launcherActivity.getRootView().performHapticFeedback(
+ HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
+ mMotionPauseDetector.clear();
}
});
}
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
new file mode 100644
index 0000000..9a3bb76
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -0,0 +1,39 @@
+/*
+ * 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.content.Context;
+
+import com.android.launcher3.logging.EventLogArray;
+import com.android.launcher3.util.MainThreadInitializedObject;
+
+/**
+ * A log to keep track of the active gesture.
+ */
+public class ActiveGestureLog extends EventLogArray {
+
+ public static final ActiveGestureLog INSTANCE = new ActiveGestureLog();
+
+ /**
+ * NOTE: This value should be kept same as
+ * ActivityTaskManagerService#INTENT_EXTRA_LOG_TRACE_ID in platform
+ */
+ public static final String INTENT_EXTRA_LOG_TRACE_ID = "INTENT_EXTRA_LOG_TRACE_ID";
+
+ public ActiveGestureLog() {
+ super("touch_interaction_log", 40);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
similarity index 87%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 90989fe..24e7f0e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -28,7 +28,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
-import android.os.RemoteException;
import androidx.annotation.Nullable;
@@ -37,12 +36,13 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.utilities.RectFEvaluator;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
@@ -50,13 +50,11 @@
import com.android.systemui.shared.system.TransactionCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import java.util.function.BiFunction;
-
/**
* Utility class to handle window clip animation
*/
@TargetApi(Build.VERSION_CODES.P)
-public class ClipAnimationHelper {
+public class AppWindowAnimationHelper {
// The bounds of the source app in device coordinates
private final Rect mSourceStackBounds = new Rect();
@@ -102,7 +100,7 @@
private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
- public ClipAnimationHelper(Context context) {
+ public AppWindowAnimationHelper(Context context) {
mWindowCornerRadius = getWindowCornerRadius(context.getResources());
mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources());
mTaskCornerRadius = TaskCornerRadius.get(context);
@@ -157,12 +155,80 @@
mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows && !dp.isMultiWindowMode;
}
- public RectF applyTransform(RemoteAnimationTargetSet targetSet, TransformParams params) {
- return applyTransform(targetSet, params, true /* launcherOnTop */);
+ public RectF applyTransform(TransformParams params) {
+ SurfaceParams[] surfaceParams = getSurfaceParams(params);
+ if (surfaceParams == null) {
+ return null;
+ }
+ applySurfaceParams(params.syncTransactionApplier, surfaceParams);
+ return params.currentRect;
}
- public RectF applyTransform(RemoteAnimationTargetSet targetSet, TransformParams params,
- boolean launcherOnTop) {
+ public SurfaceParams[] getSurfaceParams(TransformParams params) {
+ if (params.targetSet == null) {
+ return null;
+ }
+
+ float progress = params.progress;
+ updateCurrentRect(params);
+
+ SurfaceParams[] surfaceParams = new SurfaceParams[params.targetSet.unfilteredApps.length];
+ for (int i = 0; i < params.targetSet.unfilteredApps.length; i++) {
+ RemoteAnimationTargetCompat app = params.targetSet.unfilteredApps[i];
+ mTmpMatrix.setTranslate(app.position.x, app.position.y);
+ Rect crop = mTmpRect;
+ crop.set(app.sourceContainerBounds);
+ crop.offsetTo(0, 0);
+ float alpha;
+ int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
+ float cornerRadius = 0f;
+ float scale = Math.max(params.currentRect.width(), mTargetRect.width()) / crop.width();
+ if (app.mode == params.targetSet.targetMode) {
+ alpha = mTaskAlphaCallback.getAlpha(app, params.targetAlpha);
+ if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ mTmpMatrix.setRectToRect(mSourceRect, params.currentRect, ScaleToFit.FILL);
+ mTmpMatrix.postTranslate(app.position.x, app.position.y);
+ mClipRectF.roundOut(crop);
+ if (mSupportsRoundedCornersOnWindows) {
+ if (params.cornerRadius > -1) {
+ cornerRadius = params.cornerRadius;
+ scale = params.currentRect.width() / crop.width();
+ } else {
+ float windowCornerRadius = mUseRoundedCornersOnWindows
+ ? mWindowCornerRadius : 0;
+ cornerRadius = Utilities.mapRange(progress, windowCornerRadius,
+ mTaskCornerRadius);
+ }
+ mCurrentCornerRadius = cornerRadius;
+ }
+ // Fade out Assistant overlay.
+ if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
+ && app.isNotInRecents) {
+ alpha = 1 - Interpolators.DEACCEL_2_5.getInterpolation(progress);
+ }
+ } else if (params.targetSet.hasRecents) {
+ // If home has a different target then recents, reverse anim the
+ // home target.
+ alpha = 1 - (progress * params.targetAlpha);
+ }
+ } else {
+ alpha = mBaseAlphaCallback.getAlpha(app, progress);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.launcherOnTop) {
+ crop = null;
+ layer = Integer.MAX_VALUE;
+ }
+ }
+
+ // Since radius is in Surface space, but we draw the rounded corners in screen space, we
+ // have to undo the scale.
+ surfaceParams[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer,
+ cornerRadius / scale);
+ }
+ applySurfaceParams(params.syncTransactionApplier, surfaceParams);
+ return surfaceParams;
+ }
+
+ public RectF updateCurrentRect(TransformParams params) {
float progress = params.progress;
if (params.currentRect == null) {
RectF currentRect;
@@ -183,55 +249,6 @@
mSourceStackBounds.height() - (sourceWindowClipInsets.bottom * progress);
params.setCurrentRectAndTargetAlpha(currentRect, 1);
}
-
- SurfaceParams[] surfaceParams = new SurfaceParams[targetSet.unfilteredApps.length];
- for (int i = 0; i < targetSet.unfilteredApps.length; i++) {
- RemoteAnimationTargetCompat app = targetSet.unfilteredApps[i];
- mTmpMatrix.setTranslate(app.position.x, app.position.y);
- Rect crop = mTmpRect;
- crop.set(app.sourceContainerBounds);
- crop.offsetTo(0, 0);
- float alpha;
- int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
- float cornerRadius = 0f;
- float scale = Math.max(params.currentRect.width(), mTargetRect.width()) / crop.width();
- if (app.mode == targetSet.targetMode) {
- alpha = mTaskAlphaCallback.getAlpha(app, params.targetAlpha);
- if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
- mTmpMatrix.setRectToRect(mSourceRect, params.currentRect, ScaleToFit.FILL);
- mTmpMatrix.postTranslate(app.position.x, app.position.y);
- mClipRectF.roundOut(crop);
- if (mSupportsRoundedCornersOnWindows) {
- if (params.cornerRadius > -1) {
- cornerRadius = params.cornerRadius;
- scale = params.currentRect.width() / crop.width();
- } else {
- float windowCornerRadius = mUseRoundedCornersOnWindows
- ? mWindowCornerRadius : 0;
- cornerRadius = Utilities.mapRange(progress, windowCornerRadius,
- mTaskCornerRadius);
- }
- mCurrentCornerRadius = cornerRadius;
- }
- } else if (targetSet.hasRecents) {
- // If home has a different target then recents, reverse anim the
- // home target.
- alpha = 1 - (progress * params.targetAlpha);
- }
- } else {
- alpha = mBaseAlphaCallback.getAlpha(app, progress);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && launcherOnTop) {
- crop = null;
- layer = Integer.MAX_VALUE;
- }
- }
-
- // Since radius is in Surface space, but we draw the rounded corners in screen space, we
- // have to undo the scale.
- surfaceParams[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer,
- cornerRadius / scale);
- }
- applySurfaceParams(params.syncTransactionApplier, surfaceParams);
return params.currentRect;
}
@@ -240,7 +257,7 @@
return mCurrentRectWithInsets;
}
- private void applySurfaceParams(@Nullable SyncRtSurfaceTransactionApplierCompat
+ public static void applySurfaceParams(@Nullable SyncRtSurfaceTransactionApplierCompat
syncTransactionApplier, SurfaceParams[] params) {
if (syncTransactionApplier != null) {
syncTransactionApplier.scheduleApply(params);
@@ -305,7 +322,7 @@
/**
* Compute scale and translation y such that the specified task view fills the screen.
*/
- public ClipAnimationHelper updateForFullscreenOverview(TaskView v) {
+ public AppWindowAnimationHelper updateForFullscreenOverview(TaskView v) {
TaskThumbnailView thumbnailView = v.getThumbnail();
RecentsView recentsView = v.getRecentsView();
fromTaskThumbnailView(thumbnailView, recentsView);
@@ -325,14 +342,10 @@
}
private void updateStackBoundsToMultiWindowTaskSize(BaseDraggingActivity activity) {
- ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
- if (sysUiProxy != null) {
- try {
- mSourceStackBounds.set(sysUiProxy.getNonMinimizedSplitScreenSecondaryBounds());
- return;
- } catch (RemoteException e) {
- // Use half screen size
- }
+ SystemUiProxy proxy = SystemUiProxy.INSTANCE.get(activity);
+ if (proxy.isActive()) {
+ mSourceStackBounds.set(proxy.getNonMinimizedSplitScreenSecondaryBounds());
+ return;
}
// Assume that the task size is half screen size (minus the insets and the divider size)
@@ -375,12 +388,14 @@
float progress;
public float offsetX;
public float offsetScale;
- @Nullable RectF currentRect;
+ public @Nullable RectF currentRect;
float targetAlpha;
boolean forLiveTile;
float cornerRadius;
+ boolean launcherOnTop;
- SyncRtSurfaceTransactionApplierCompat syncTransactionApplier;
+ public RemoteAnimationTargets targetSet;
+ public SyncRtSurfaceTransactionApplierCompat syncTransactionApplier;
public TransformParams() {
progress = 0;
@@ -390,6 +405,7 @@
targetAlpha = 0;
forLiveTile = false;
cornerRadius = -1;
+ launcherOnTop = false;
}
public TransformParams setProgress(float progress) {
@@ -424,6 +440,16 @@
return this;
}
+ public TransformParams setLauncherOnTop(boolean launcherOnTop) {
+ this.launcherOnTop = launcherOnTop;
+ return this;
+ }
+
+ public TransformParams setTargetSet(RemoteAnimationTargets targetSet) {
+ this.targetSet = targetSet;
+ return this;
+ }
+
public TransformParams setSyncTransactionApplier(
SyncRtSurfaceTransactionApplierCompat applier) {
this.syncTransactionApplier = applier;
@@ -431,4 +457,3 @@
}
}
}
-
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
index 3ce341d..bbb318a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
@@ -21,9 +21,9 @@
import android.content.Context;
import android.view.Surface;
-import android.view.WindowManager;
import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.SysUINavigationMode;
/**
@@ -36,8 +36,7 @@
public NavBarPosition(Context context) {
mMode = SysUINavigationMode.getMode(context);
- mDisplayRotation = context.getSystemService(WindowManager.class)
- .getDefaultDisplay().getRotation();
+ mDisplayRotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
}
public boolean isRightEdge() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
deleted file mode 100644
index 14083dd..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
+++ /dev/null
@@ -1,115 +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.TouchInteractionService.MAIN_THREAD_EXECUTOR;
-
-import android.graphics.Rect;
-import android.util.ArraySet;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-import java.util.Set;
-import java.util.function.Consumer;
-
-import androidx.annotation.UiThread;
-
-/**
- * Wrapper around {@link RecentsAnimationListener} which delegates callbacks to multiple listeners
- * on the main thread
- */
-public class RecentsAnimationListenerSet implements RecentsAnimationListener {
-
- // The actual app surface is replaced by a screenshot upon recents animation cancelation when
- // the thumbnailData exists. Launcher takes the responsibility to clean up this screenshot
- // after app transition is finished. This delay is introduced to cover the app transition
- // period of time.
- private final int TRANSITION_DELAY = 100;
-
- private final Set<SwipeAnimationListener> mListeners = new ArraySet<>();
- private final boolean mShouldMinimizeSplitScreen;
- private final Consumer<SwipeAnimationTargetSet> mOnFinishListener;
- private RecentsAnimationControllerCompat mController;
-
- private boolean mCancelled;
-
- public RecentsAnimationListenerSet(boolean shouldMinimizeSplitScreen,
- Consumer<SwipeAnimationTargetSet> onFinishListener) {
- mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
- mOnFinishListener = onFinishListener;
- }
-
- @UiThread
- public void addListener(SwipeAnimationListener listener) {
- Preconditions.assertUIThread();
- mListeners.add(listener);
- }
-
- @UiThread
- public void removeListener(SwipeAnimationListener listener) {
- Preconditions.assertUIThread();
- mListeners.remove(listener);
- }
-
- @Override
- public final void onAnimationStart(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
- Rect minimizedHomeBounds) {
- mController = controller;
- SwipeAnimationTargetSet targetSet = new SwipeAnimationTargetSet(controller, targets,
- homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
- mOnFinishListener);
-
- if (mCancelled) {
- targetSet.cancelAnimation();
- } else {
- Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> {
- for (SwipeAnimationListener listener : getListeners()) {
- listener.onRecentsAnimationStart(targetSet);
- }
- });
- }
- }
-
- @Override
- public final void onAnimationCanceled(ThumbnailData thumbnailData) {
- Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> {
- for (SwipeAnimationListener listener : getListeners()) {
- listener.onRecentsAnimationCanceled();
- }
- });
- // TODO: handle the transition better instead of simply using a transition delay.
- if (thumbnailData != null) {
- MAIN_THREAD_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(),
- TRANSITION_DELAY);
- }
- }
-
- private SwipeAnimationListener[] getListeners() {
- return mListeners.toArray(new SwipeAnimationListener[mListeners.size()]);
- }
-
- public void cancelListener() {
- mCancelled = true;
- onAnimationCanceled(null);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 1aa5365..d644fd6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -21,15 +21,14 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
-import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -40,14 +39,10 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.anim.SpringObjectAnimator;
-import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.views.IconLabelDotView;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Creates an animation where all the workspace items are moved into their final location,
* staggered row by row from the bottom up.
@@ -69,7 +64,7 @@
// The original view of the {@link FloatingIconView}.
private final View mOriginalView;
- private final List<Animator> mAnimators = new ArrayList<>();
+ private final AnimatorSet mAnimators = new AnimatorSet();
/**
* @param floatingViewOriginalView The FloatingIconView's original view.
@@ -136,16 +131,9 @@
addScrimAnimationForState(launcher, BACKGROUND_APP, 0);
addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
- AnimatorListener resetClipListener = new AnimatorListenerAdapter() {
- int numAnimations = mAnimators.size();
-
+ mAnimators.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- numAnimations--;
- if (numAnimations > 0) {
- return;
- }
-
workspace.setClipChildren(workspaceClipChildren);
workspace.setClipToPadding(workspaceClipToPadding);
cellLayout.setClipChildren(cellLayoutClipChildren);
@@ -153,24 +141,14 @@
hotseat.setClipChildren(hotseatClipChildren);
hotseat.setClipToPadding(hotseatClipToPadding);
}
- };
-
- for (Animator a : mAnimators) {
- a.addListener(resetClipListener);
- }
+ });
}
/**
* Starts the animation.
*/
public void start() {
- for (Animator a : mAnimators) {
- if (a instanceof SpringObjectAnimator) {
- ((SpringObjectAnimator) a).startSpring(1f, mVelocity, null);
- } else {
- a.start();
- }
- }
+ mAnimators.start();
}
/**
@@ -187,10 +165,16 @@
long startDelay = (long) ((invertedRow + 1) * APP_CLOSE_ROW_START_DELAY_MS);
v.setTranslationY(mSpringTransY);
- SpringObjectAnimator springTransY = new SpringObjectAnimator<>(v, VIEW_TRANSLATE_Y,
- 1f, DAMPING_RATIO, STIFFNESS, mSpringTransY, 0);
+
+ ObjectAnimator springTransY = new SpringAnimationBuilder<>(v, VIEW_TRANSLATE_Y)
+ .setStiffness(STIFFNESS)
+ .setDampingRatio(DAMPING_RATIO)
+ .setMinimumVisibleChange(1f)
+ .setEndValue(0)
+ .setStartVelocity(mVelocity)
+ .build(v.getContext());
springTransY.setStartDelay(startDelay);
- mAnimators.add(springTransY);
+ mAnimators.play(springTransY);
ObjectAnimator alpha = getAlphaAnimator(v, startDelay);
if (v == mOriginalView) {
@@ -211,7 +195,7 @@
}
v.setAlpha(0);
- mAnimators.add(alpha);
+ mAnimators.play(alpha);
}
private ObjectAnimator getAlphaAnimator(View v, long startDelay) {
@@ -229,11 +213,11 @@
scrimAnimConfig.duration = duration;
PropertySetter scrimPropertySetter = scrimAnimConfig.getPropertySetter(scrimAnimBuilder);
launcher.getWorkspace().getStateTransitionAnimation().setScrim(scrimPropertySetter, state);
- mAnimators.add(scrimAnimBuilder.build());
+ mAnimators.play(scrimAnimBuilder.build());
Animator fadeOverviewScrim = ObjectAnimator.ofFloat(
launcher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS,
state.getOverviewScrimAlpha(launcher));
fadeOverviewScrim.setDuration(duration);
- mAnimators.add(fadeOverviewScrim);
+ mAnimators.play(fadeOverviewScrim);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
deleted file mode 100644
index 381c27a..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
+++ /dev/null
@@ -1,119 +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.TouchInteractionService.BACKGROUND_EXECUTOR;
-import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-
-import android.graphics.Rect;
-
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-import java.util.function.Consumer;
-
-/**
- * Extension of {@link RemoteAnimationTargetSet} with additional information about swipe
- * up animation
- */
-public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet {
-
- private final boolean mShouldMinimizeSplitScreen;
- private final Consumer<SwipeAnimationTargetSet> mOnFinishListener;
-
- public final RecentsAnimationControllerCompat controller;
- public final Rect homeContentInsets;
- public final Rect minimizedHomeBounds;
-
- public SwipeAnimationTargetSet(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
- Rect minimizedHomeBounds, boolean shouldMinimizeSplitScreen,
- Consumer<SwipeAnimationTargetSet> onFinishListener) {
- super(targets, MODE_CLOSING);
- this.controller = controller;
- this.homeContentInsets = homeContentInsets;
- this.minimizedHomeBounds = minimizedHomeBounds;
- this.mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
- this.mOnFinishListener = onFinishListener;
- }
-
- 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 SwipeAnimationTargetSet cloneWithoutTargets() {
- return new SwipeAnimationTargetSet(controller, new RemoteAnimationTargetCompat[0],
- homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
- mOnFinishListener);
- }
-
- public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
- mOnFinishListener.accept(this);
- BACKGROUND_EXECUTOR.execute(() -> {
- controller.setInputConsumerEnabled(false);
- controller.finish(toRecents, sendUserLeaveHint);
-
- if (callback != null) {
- MAIN_THREAD_EXECUTOR.execute(callback);
- }
- });
- }
-
- public void enableInputConsumer() {
- BACKGROUND_EXECUTOR.submit(() -> {
- controller.hideCurrentInputMethod();
- controller.setInputConsumerEnabled(true);
- });
- }
-
- public void setWindowThresholdCrossed(boolean thresholdCrossed) {
- BACKGROUND_EXECUTOR.execute(() -> {
- controller.setAnimationTargetsBehindSystemBars(!thresholdCrossed);
- if (mShouldMinimizeSplitScreen && thresholdCrossed) {
- // NOTE: As a workaround for conflicting animations (Launcher animating the task
- // leash, and SystemUI resizing the docked stack, which resizes the task), we
- // currently only set the minimized mode, and not the inverse.
- // TODO: Synchronize the minimize animation with the launcher animation
- controller.setSplitScreenMinimized(thresholdCrossed);
- }
- });
- }
-
- public ThumbnailData screenshotTask(int taskId) {
- return controller != null ? controller.screenshotTask(taskId) : null;
- }
-
- public void cancelAnimation() {
- finishController(false /* toRecents */, null, false /* sendUserLeaveHint */);
- }
-
- public void finishAnimation() {
- finishController(true /* toRecents */, null, false /* sendUserLeaveHint */);
- }
-
- public interface SwipeAnimationListener {
-
- void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet);
-
- void onRecentsAnimationCanceled();
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 7fac813..b06d4bc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -19,6 +19,7 @@
import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
import static com.android.launcher3.Utilities.prefixTextWithIcon;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
@@ -41,7 +42,6 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.systemui.shared.recents.model.Task;
@@ -117,7 +117,7 @@
return;
}
- Utilities.THREAD_POOL_EXECUTOR.execute(() -> {
+ THREAD_POOL_EXECUTOR.execute(() -> {
final AppUsageLimit usageLimit = mLauncherApps.getAppUsageLimit(
task.getTopComponent().getPackageName(),
UserHandle.of(task.key.userId));
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 03441c8..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
@@ -35,6 +35,7 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.widget.FrameLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Hotseat;
@@ -45,11 +46,15 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
+import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
import com.android.quickstep.util.LayoutUtils;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.RecentsExtraCard;
/**
* {@link RecentsView} used in Launcher activity
@@ -57,8 +62,29 @@
@TargetApi(Build.VERSION_CODES.O)
public class LauncherRecentsView extends RecentsView<Launcher> implements StateListener {
+ private static final Rect sTempRect = new Rect();
+
private final TransformParams mTransformParams = new TransformParams();
+ private RecentsExtraCard mRecentsExtraCardPlugin;
+ private RecentsExtraViewContainer mRecentsExtraViewContainer;
+ private PluginListener<RecentsExtraCard> mRecentsExtraCardPluginListener =
+ new PluginListener<RecentsExtraCard>() {
+ @Override
+ public void onPluginConnected(RecentsExtraCard recentsExtraCard, Context context) {
+ createRecentsExtraCard();
+ mRecentsExtraCardPlugin = recentsExtraCard;
+ mRecentsExtraCardPlugin.setupView(context, mRecentsExtraViewContainer, mActivity);
+ }
+
+ @Override
+ public void onPluginDisconnected(RecentsExtraCard plugin) {
+ removeView(mRecentsExtraViewContainer);
+ mRecentsExtraCardPlugin = null;
+ mRecentsExtraViewContainer = null;
+ }
+ };
+
public LauncherRecentsView(Context context) {
this(context, null);
}
@@ -75,7 +101,12 @@
@Override
public void startHome() {
- mActivity.getStateManager().goToState(NORMAL);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ switchToScreenshot(() -> finishRecentsAnimation(true /* toRecents */,
+ () -> mActivity.getStateManager().goToState(NORMAL)));
+ } else {
+ mActivity.getStateManager().goToState(NORMAL);
+ }
}
@Override
@@ -112,7 +143,7 @@
*/
@Override
public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv,
- ClipAnimationHelper helper) {
+ AppWindowAnimationHelper helper) {
AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv, helper);
if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
@@ -144,15 +175,33 @@
LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
}
+ /**
+ * @return The translationX to apply to this view so that the first task is just offscreen.
+ */
+ public float getOffscreenTranslationX(float recentsScale) {
+ float offscreenX = NORMAL.getOverviewScaleAndTranslation(mActivity).translationX;
+ // Offset since scale pushes tasks outwards.
+ getTaskSize(sTempRect);
+ int taskWidth = sTempRect.width();
+ offscreenX += taskWidth * (recentsScale - 1) / 2;
+ if (mRunningTaskTileHidden) {
+ // The first task is hidden, so offset by its width.
+ offscreenX -= (taskWidth + getPageSpacing()) * recentsScale;
+ }
+ if (isRtl()) {
+ offscreenX = -offscreenX;
+ }
+ return offscreenX;
+ }
+
@Override
protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (mRecentsAnimationWrapper.targetSet != null && tv.isRunningTask()) {
+ if (tv.isRunningTask()) {
mTransformParams.setProgress(1 - progress)
.setSyncTransactionApplier(mSyncTransactionApplier)
.setForLiveTile(true);
- mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
- mTransformParams);
+ mAppWindowAnimationHelper.applyTransform(mTransformParams);
} else {
redrawLiveTile(true);
}
@@ -172,7 +221,7 @@
@Override
public boolean shouldUseMultiWindowTaskSizeStrategy() {
- return mActivity.isInMultiWindowMode();
+ return TraceHelper.whitelistIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode);
}
@Override
@@ -185,9 +234,18 @@
@Override
public void redrawLiveTile(boolean mightNeedToRefill) {
- if (!mEnableDrawingLiveTile || mRecentsAnimationWrapper == null
- || mClipAnimationHelper == null) {
- return;
+ AppWindowAnimationHelper.TransformParams transformParams = getLiveTileParams(mightNeedToRefill);
+ if (transformParams != null) {
+ mAppWindowAnimationHelper.applyTransform(transformParams);
+ }
+ }
+
+ @Override
+ public AppWindowAnimationHelper.TransformParams getLiveTileParams(
+ boolean mightNeedToRefill) {
+ if (!mEnableDrawingLiveTile || mRecentsAnimationController == null
+ || mRecentsAnimationTargets == null || mAppWindowAnimationHelper == null) {
+ return null;
}
TaskView taskView = getRunningTaskView();
if (taskView != null) {
@@ -209,12 +267,11 @@
mTempRectF.set(mTempRect);
mTransformParams.setProgress(1f)
.setCurrentRectAndTargetAlpha(mTempRectF, taskView.getAlpha())
- .setSyncTransactionApplier(mSyncTransactionApplier);
- if (mRecentsAnimationWrapper.targetSet != null) {
- mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
- mTransformParams);
- }
+ .setSyncTransactionApplier(mSyncTransactionApplier)
+ .setTargetSet(mRecentsAnimationTargets)
+ .setLauncherOnTop(true);
}
+ return mTransformParams;
}
@Override
@@ -264,4 +321,66 @@
}
return super.shouldStealTouchFromSiblingsBelow(ev);
}
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ PluginManagerWrapper.INSTANCE.get(getContext())
+ .addPluginListener(mRecentsExtraCardPluginListener, RecentsExtraCard.class);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(
+ mRecentsExtraCardPluginListener);
+ }
+
+ @Override
+ protected int computeMinScrollX() {
+ if (canComputeScrollX() && !mIsRtl) {
+ return computeScrollX();
+ }
+ return super.computeMinScrollX();
+ }
+
+ @Override
+ protected int computeMaxScrollX() {
+ if (canComputeScrollX() && mIsRtl) {
+ return computeScrollX();
+ }
+ return super.computeMaxScrollX();
+ }
+
+ private boolean canComputeScrollX() {
+ return mRecentsExtraCardPlugin != null && getTaskViewCount() > 0
+ && !mDisallowScrollToClearAll;
+ }
+
+ private int computeScrollX() {
+ int scrollIndex = getTaskViewStartIndex() - 1;
+ while (scrollIndex >= 0 && getChildAt(scrollIndex) instanceof RecentsExtraViewContainer
+ && ((RecentsExtraViewContainer) getChildAt(scrollIndex)).isScrollable()) {
+ scrollIndex--;
+ }
+ return getScrollForPage(scrollIndex + 1);
+ }
+
+ private void createRecentsExtraCard() {
+ mRecentsExtraViewContainer = new RecentsExtraViewContainer(getContext());
+ FrameLayout.LayoutParams helpCardParams =
+ new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT);
+ mRecentsExtraViewContainer.setLayoutParams(helpCardParams);
+ mRecentsExtraViewContainer.setScrollable(true);
+ addView(mRecentsExtraViewContainer, 0);
+ }
+
+ @Override
+ public void resetTaskVisuals() {
+ super.resetTaskVisuals();
+ if (mRecentsExtraViewContainer != null) {
+ mRecentsExtraViewContainer.setAlpha(mContentAlpha);
+ }
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java
new file mode 100644
index 0000000..1ea6d4a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.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.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * Empty view to house recents overview extra card
+ */
+public class RecentsExtraViewContainer extends FrameLayout implements RecentsView.PageCallbacks {
+
+ private boolean mScrollable = false;
+
+ public RecentsExtraViewContainer(Context context) {
+ super(context);
+ }
+
+ public RecentsExtraViewContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public RecentsExtraViewContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ /**
+ * Determine whether the view should be scrolled to in the recents overview, similar to the
+ * taskviews.
+ * @return true if viewed should be scrolled to, false if not
+ */
+ public boolean isScrollable() {
+ return mScrollable;
+ }
+
+ public void setScrollable(boolean scrollable) {
+ this.mScrollable = scrollable;
+ }
+}
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 ef54d3f..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
@@ -34,9 +34,9 @@
import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -101,12 +101,14 @@
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
-import com.android.quickstep.RecentsAnimationWrapper;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
-import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.ViewUtils;
+import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -154,8 +156,9 @@
}
};
- protected RecentsAnimationWrapper mRecentsAnimationWrapper;
- protected ClipAnimationHelper mClipAnimationHelper;
+ protected RecentsAnimationController mRecentsAnimationController;
+ protected RecentsAnimationTargets mRecentsAnimationTargets;
+ protected AppWindowAnimationHelper mAppWindowAnimationHelper;
protected SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
protected int mTaskWidth;
protected int mTaskHeight;
@@ -175,7 +178,7 @@
private final ClearAllButton mClearAllButton;
private final Rect mClearAllButtonDeadZoneRect = new Rect();
private final Rect mTaskViewDeadZoneRect = new Rect();
- protected final ClipAnimationHelper mTempClipAnimationHelper;
+ protected final AppWindowAnimationHelper mTempAppWindowAnimationHelper;
private final ScrollState mScrollState = new ScrollState();
// Keeps track of the previously known visible tasks for purposes of loading/unloading task data
@@ -186,7 +189,7 @@
private final ViewPool<TaskView> mTaskViewPool;
private boolean mDwbToastShown;
- private boolean mDisallowScrollToClearAll;
+ protected boolean mDisallowScrollToClearAll;
private boolean mOverlayEnabled;
private boolean mFreezeViewVisibility;
@@ -227,7 +230,7 @@
return;
}
- BACKGROUND_EXECUTOR.execute(() -> {
+ UI_HELPER_EXECUTOR.execute(() -> {
TaskView taskView = getTaskView(taskId);
if (taskView == null) {
return;
@@ -271,7 +274,7 @@
// Only valid until the launcher state changes to NORMAL
protected int mRunningTaskId = -1;
- private boolean mRunningTaskTileHidden;
+ protected boolean mRunningTaskTileHidden;
private Task mTmpRunningTask;
private boolean mRunningTaskIconScaledDown = false;
@@ -288,7 +291,7 @@
private LayoutTransition mLayoutTransition;
@ViewDebug.ExportedProperty(category = "launcher")
- private float mContentAlpha = 1;
+ protected float mContentAlpha = 1;
@ViewDebug.ExportedProperty(category = "launcher")
protected float mFullscreenProgress = 0;
@@ -305,6 +308,9 @@
private Layout mEmptyTextLayout;
private LiveTileOverlay mLiveTileOverlay;
+ // Keeps track of the index where the first TaskView should be
+ private int mTaskViewStartIndex = 0;
+
private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
(inMultiWindowMode) -> {
if (!inMultiWindowMode && mOverviewStateEnabled) {
@@ -323,12 +329,11 @@
mActivity = (T) BaseActivity.fromContext(context);
mModel = RecentsModel.INSTANCE.get(context);
mIdp = InvariantDeviceProfile.INSTANCE.get(context);
- mTempClipAnimationHelper = new ClipAnimationHelper(context);
+ mTempAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
.inflate(R.layout.overview_clear_all_button, this, false);
mClearAllButton.setOnClickListener(this::dismissAllTasks);
-
mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
10 /* initial size */);
@@ -377,14 +382,23 @@
return null;
}
- public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
+ /**
+ * Update the thumbnail of the task.
+ * @param refreshNow Refresh immediately if it's true.
+ */
+ public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData, boolean refreshNow) {
TaskView taskView = getTaskView(taskId);
if (taskView != null) {
- taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
+ taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData, refreshNow);
}
return taskView;
}
+ /** See {@link #updateThumbnail(int, ThumbnailData, boolean)} */
+ public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
+ return updateThumbnail(taskId, thumbnailData, true /* refreshNow */);
+ }
+
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
@@ -429,7 +443,7 @@
super.onViewRemoved(child);
// Clear the task data for the removed child if it was visible
- if (child != mClearAllButton) {
+ if (child instanceof TaskView) {
TaskView taskView = (TaskView) child;
mHasVisibleTaskData.delete(taskView.getTask().key.id);
mTaskViewPool.recycle(taskView);
@@ -443,7 +457,7 @@
public TaskView getTaskView(int taskId) {
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView tv = (TaskView) getChildAt(i);
+ TaskView tv = getTaskViewAt(i);
if (tv.getTask() != null && tv.getTask().key != null && tv.getTask().key.id == taskId) {
return tv;
}
@@ -535,28 +549,26 @@
}
if (tasks == null || tasks.isEmpty()) {
- removeAllViews();
+ removeTasksViewsAndClearAllButton();
onTaskStackUpdated();
return;
}
- int oldChildCount = getChildCount();
-
// Unload existing visible task data
unloadVisibleTaskData();
- TaskView ignoreRestTaskView =
+ TaskView ignoreResetTaskView =
mIgnoreResetTaskId == -1 ? null : getTaskView(mIgnoreResetTaskId);
final int requiredTaskCount = tasks.size();
if (getTaskViewCount() != requiredTaskCount) {
- if (oldChildCount > 0) {
+ if (indexOfChild(mClearAllButton) != -1) {
removeView(mClearAllButton);
}
- for (int i = getChildCount(); i < requiredTaskCount; i++) {
+ for (int i = getTaskViewCount(); i < requiredTaskCount; i++) {
addView(mTaskViewPool.getView());
}
- while (getChildCount() > requiredTaskCount) {
+ while (getTaskViewCount() > requiredTaskCount) {
removeView(getChildAt(getChildCount() - 1));
}
if (requiredTaskCount > 0) {
@@ -566,7 +578,7 @@
// Rebind and reset all task views
for (int i = requiredTaskCount - 1; i >= 0; i--) {
- final int pageIndex = requiredTaskCount - i - 1;
+ final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
final Task task = tasks.get(i);
final TaskView taskView = (TaskView) getChildAt(pageIndex);
taskView.bind(task);
@@ -577,10 +589,12 @@
TaskView runningTaskView = getRunningTaskView();
if (runningTaskView != null) {
setCurrentPage(indexOfChild(runningTaskView));
+ } else if (getTaskViewCount() > 0) {
+ setCurrentPage(indexOfChild(getTaskViewAt(0)));
}
}
- if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreRestTaskView) {
+ if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreResetTaskView) {
// If the taskView mapping is changing, do not preserve the visuals. Since we are
// mostly preserving the first task, and new taskViews are added to the end, it should
// generally map to the same task.
@@ -591,17 +605,28 @@
updateEnabledOverlays();
}
+ private void removeTasksViewsAndClearAllButton() {
+ for (int i = getTaskViewCount() - 1; i >= 0; i--) {
+ removeView(getTaskViewAt(i));
+ }
+ if (indexOfChild(mClearAllButton) != -1) {
+ removeView(mClearAllButton);
+ }
+ }
+
public int getTaskViewCount() {
- // Account for the clear all button.
- int childCount = getChildCount();
- return childCount == 0 ? 0 : childCount - 1;
+ int taskViewCount = getChildCount() - mTaskViewStartIndex;
+ if (indexOfChild(mClearAllButton) != -1) {
+ taskViewCount--;
+ }
+ return taskViewCount;
}
protected void onTaskStackUpdated() { }
public void resetTaskVisuals() {
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
- TaskView taskView = (TaskView) getChildAt(i);
+ TaskView taskView = getTaskViewAt(i);
if (mIgnoreResetTaskId != taskView.getTask().key.id) {
taskView.resetVisualProperties();
taskView.setStableAlpha(mContentAlpha);
@@ -707,7 +732,7 @@
}
/**
- * Iterates through all thet asks, and loads the associated task data for newly visible tasks,
+ * Iterates through all the tasks, and loads the associated task data for newly visible tasks,
* and unloads the associated task data for tasks that are no longer visible.
*/
public void loadVisibleTaskData() {
@@ -718,15 +743,16 @@
}
int centerPageIndex = getPageNearestToCenterOfScreen();
- int numChildren = getTaskViewCount();
+ int numChildren = getChildCount();
int lower = Math.max(0, centerPageIndex - 2);
int upper = Math.min(centerPageIndex + 2, numChildren - 1);
// Update the task data for the in/visible children
- for (int i = 0; i < numChildren; i++) {
- TaskView taskView = (TaskView) getChildAt(i);
+ for (int i = 0; i < getTaskViewCount(); i++) {
+ TaskView taskView = getTaskViewAt(i);
Task task = taskView.getTask();
- boolean visible = lower <= i && i <= upper;
+ int index = indexOfChild(taskView);
+ boolean visible = lower <= index && index <= upper;
if (visible) {
if (task == mTmpRunningTask) {
// Skip loading if this is the task that we are animating into
@@ -783,8 +809,9 @@
mIgnoreResetTaskId = -1;
mTaskListChangeId = -1;
- mRecentsAnimationWrapper = null;
- mClipAnimationHelper = null;
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
+ mAppWindowAnimationHelper = null;
unloadVisibleTaskData();
setCurrentPage(0);
@@ -801,6 +828,10 @@
return tv == null ? -1 : indexOfChild(tv);
}
+ public int getTaskViewStartIndex() {
+ return mTaskViewStartIndex;
+ }
+
/**
* Reloads the view if anything in recents changed.
*/
@@ -843,7 +874,9 @@
setEnableFreeScroll(true);
setEnableDrawingLiveTile(true);
setOnScrollChangeListener(null);
- setRunningTaskViewShowScreenshot(true);
+ if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ setRunningTaskViewShowScreenshot(true);
+ }
setRunningTaskHidden(false);
animateUpRunningTaskIconScale();
}
@@ -856,10 +889,10 @@
*/
public void showCurrentTask(int runningTaskId) {
if (getTaskView(runningTaskId) == null) {
- boolean wasEmpty = getChildCount() == 0;
+ boolean wasEmpty = getTaskViewCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = mTaskViewPool.getView();
- addView(taskView, 0);
+ addView(taskView, mTaskViewStartIndex);
if (wasEmpty) {
addView(mClearAllButton);
}
@@ -925,14 +958,13 @@
if (runningTaskView == null) {
// Launch the first task
if (getTaskViewCount() > 0) {
- getTaskViewAt(0).launchTask(true /* animate */);
+ getTaskViewAt(0).launchTask(true);
}
} else {
- TaskView nextTaskView = getNextTaskView();
- if (nextTaskView != null) {
- nextTaskView.launchTask(true /* animate */);
+ if (getNextTaskView() != null) {
+ getNextTaskView().launchTask(true);
} else {
- runningTaskView.launchTask(true /* animate */);
+ runningTaskView.launchTask(true);
}
}
}
@@ -1196,7 +1228,7 @@
int count = getTaskViewCount();
for (int i = 0; i < count; i++) {
- addDismissedTaskAnimations(getChildAt(i), anim, duration);
+ addDismissedTaskAnimations(getTaskViewAt(i), anim, duration);
}
mPendingAnimation = pendingAnimation;
@@ -1204,7 +1236,7 @@
if (onEndListener.isSuccess) {
// Remove all the task views now
ActivityManagerWrapper.getInstance().removeAllRecentTasks();
- removeAllViews();
+ removeTasksViewsAndClearAllButton();
startHome();
}
mPendingAnimation = null;
@@ -1351,26 +1383,50 @@
child.setAlpha(mContentAlpha);
}
- /**
- * @return The most recent task that is older than the currently running task. If there is
- * currently no running task or there is no task older than it, then return null.
- */
@Nullable
public TaskView getNextTaskView() {
- TaskView runningTaskView = getRunningTaskView();
- if (runningTaskView == null) {
- return null;
- }
- return getTaskViewAt(indexOfChild(runningTaskView) + 1);
+ return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() + 1);
}
+ @Nullable
+ public TaskView getPreviousTaskView() {
+ return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() - 1);
+ }
+
+ @Nullable
+ public TaskView getCurrentPageTaskView() {
+ return getTaskViewAtByAbsoluteIndex(getCurrentPage());
+ }
+
+ @Nullable
+ public TaskView getNextPageTaskView() {
+ return getTaskViewAtByAbsoluteIndex(getNextPage());
+ }
+
+ @Nullable
+ public TaskView getTaskViewNearestToCenterOfScreen() {
+ return getTaskViewAtByAbsoluteIndex(getPageNearestToCenterOfScreen());
+ }
+
+ /**
+ * Returns null instead of indexOutOfBoundsError when index is not in range
+ */
+ @Nullable
public TaskView getTaskViewAt(int index) {
- View child = getChildAt(index);
- return child == mClearAllButton ? null : (TaskView) child;
+ return getTaskViewAtByAbsoluteIndex(index + mTaskViewStartIndex);
+ }
+
+ @Nullable
+ private TaskView getTaskViewAtByAbsoluteIndex(int index) {
+ if (index < getChildCount() && index >= 0) {
+ View child = getChildAt(index);
+ return child instanceof TaskView ? (TaskView) child : null;
+ }
+ return null;
}
public void updateEmptyMessage() {
- boolean isEmpty = getChildCount() == 0;
+ boolean isEmpty = getTaskViewCount() == 0;
boolean hasSizeChanged = mLastMeasureSize.x != getWidth()
|| mLastMeasureSize.y != getHeight();
if (isEmpty == mShowEmptyMessage && !hasSizeChanged) {
@@ -1466,14 +1522,14 @@
* to the right.
*/
public AnimatorSet createAdjacentPageAnimForTaskLaunch(
- TaskView tv, ClipAnimationHelper clipAnimationHelper) {
+ TaskView tv, AppWindowAnimationHelper appWindowAnimationHelper) {
AnimatorSet anim = new AnimatorSet();
int taskIndex = indexOfChild(tv);
int centerTaskIndex = getCurrentPage();
boolean launchingCenterTask = taskIndex == centerTaskIndex;
- LauncherState.ScaleAndTranslation toScaleAndTranslation = clipAnimationHelper
+ LauncherState.ScaleAndTranslation toScaleAndTranslation = appWindowAnimationHelper
.getScaleAndTranslation();
float toScale = toScaleAndTranslation.scale;
float toTranslationY = toScaleAndTranslation.translationY;
@@ -1504,7 +1560,7 @@
throw new IllegalStateException("Another pending animation is still running");
}
- int count = getChildCount();
+ int count = getTaskViewCount();
if (count == 0) {
return new PendingAnimation(new AnimatorSet());
}
@@ -1533,10 +1589,10 @@
}
});
- ClipAnimationHelper clipAnimationHelper = new ClipAnimationHelper(mActivity);
- clipAnimationHelper.fromTaskThumbnailView(tv.getThumbnail(), this);
- clipAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */);
- AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, clipAnimationHelper);
+ AppWindowAnimationHelper appWindowAnimationHelper = new AppWindowAnimationHelper(mActivity);
+ appWindowAnimationHelper.fromTaskThumbnailView(tv.getThumbnail(), this);
+ appWindowAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */);
+ AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, appWindowAnimationHelper);
anim.play(progressAnim);
anim.setDuration(duration);
@@ -1639,12 +1695,16 @@
public void redrawLiveTile(boolean mightNeedToRefill) { }
- public void setRecentsAnimationWrapper(RecentsAnimationWrapper recentsAnimationWrapper) {
- mRecentsAnimationWrapper = recentsAnimationWrapper;
+ // TODO: To be removed in a follow up CL
+ public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
+ RecentsAnimationTargets recentsAnimationTargets) {
+ mRecentsAnimationController = recentsAnimationController;
+ mRecentsAnimationTargets = recentsAnimationTargets;
}
- public void setClipAnimationHelper(ClipAnimationHelper clipAnimationHelper) {
- mClipAnimationHelper = clipAnimationHelper;
+ // TODO: To be removed in a follow up CL
+ public void setAppWindowAnimationHelper(AppWindowAnimationHelper appWindowAnimationHelper) {
+ mAppWindowAnimationHelper = appWindowAnimationHelper;
}
public void setLiveTileOverlay(LiveTileOverlay liveTileOverlay) {
@@ -1658,14 +1718,14 @@
}
public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
- if (mRecentsAnimationWrapper == null) {
+ if (mRecentsAnimationController == null) {
if (onFinishComplete != null) {
onFinishComplete.run();
}
return;
}
- mRecentsAnimationWrapper.finish(toRecents, onFinishComplete);
+ mRecentsAnimationController.finish(toRecents, onFinishComplete);
}
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
@@ -1677,18 +1737,38 @@
@Override
protected int computeMinScrollX() {
- if (mIsRtl && mDisallowScrollToClearAll) {
- // We aren't showing the clear all button, so use the leftmost task as the min scroll.
- return getScrollForPage(getTaskViewCount() - 1);
+ if (getTaskViewCount() > 0) {
+ if (mDisallowScrollToClearAll) {
+ // We aren't showing the clear all button,
+ // so use the leftmost task as the min scroll.
+ if (mIsRtl) {
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
+ }
+ return getScrollForPage(mTaskViewStartIndex);
+ }
+ if (mIsRtl) {
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
+ }
+ return getScrollForPage(mTaskViewStartIndex);
}
return super.computeMinScrollX();
}
@Override
protected int computeMaxScrollX() {
- if (!mIsRtl && mDisallowScrollToClearAll) {
- // We aren't showing the clear all button, so use the rightmost task as the max scroll.
- return getScrollForPage(getTaskViewCount() - 1);
+ if (getTaskViewCount() > 0) {
+ if (mDisallowScrollToClearAll) {
+ // We aren't showing the clear all button,
+ // so use the rightmost task as the min scroll.
+ if (mIsRtl) {
+ return getScrollForPage(mTaskViewStartIndex);
+ }
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
+ }
+ if (mIsRtl) {
+ return getScrollForPage(mTaskViewStartIndex);
+ }
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
}
return super.computeMaxScrollX();
}
@@ -1732,15 +1812,24 @@
}
}
- public ClipAnimationHelper getTempClipAnimationHelper() {
- return mTempClipAnimationHelper;
+ public AppWindowAnimationHelper getClipAnimationHelper() {
+ return mAppWindowAnimationHelper;
+ }
+
+ public AppWindowAnimationHelper getTempAppWindowAnimationHelper() {
+ return mTempAppWindowAnimationHelper;
+ }
+
+ public AppWindowAnimationHelper.TransformParams getLiveTileParams(
+ boolean mightNeedToRefill) {
+ return null;
}
private void updateEnabledOverlays() {
int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
- ((TaskView) getChildAt(i)).setOverlayEnabled(i == overlayEnabledPage);
+ getTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
}
}
@@ -1760,4 +1849,41 @@
final WindowInsets insets = getRootWindowInsets();
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(Runnable onFinishRunnable) {
+ TaskView taskView = getRunningTaskView();
+ if (taskView == null) {
+ if (onFinishRunnable != null) {
+ onFinishRunnable.run();
+ }
+ return;
+ }
+
+ taskView.setShowScreenshot(true);
+ taskView.getThumbnail().refresh();
+ ViewUtils.postDraw(taskView, onFinishRunnable);
+ }
+
+ @Override
+ public void addView(View child, int index) {
+ super.addView(child, index);
+ if (isExtraCardView(child, index)) {
+ mTaskViewStartIndex++;
+ }
+ }
+
+ @Override
+ public void removeView(View view) {
+ if (isExtraCardView(view, indexOfChild(view))) {
+ mTaskViewStartIndex--;
+ }
+ super.removeView(view);
+ }
+
+ private boolean isExtraCardView(View view, int index) {
+ return !(view instanceof TaskView) && !(view instanceof ClearAllButton)
+ && index <= mTaskViewStartIndex;
+ }
}
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 c1f6b82..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
@@ -26,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;
@@ -40,6 +41,7 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.TaskOverlayFactory;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 7f1e898..adeb974 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -39,24 +39,27 @@
import android.util.FloatProperty;
import android.util.Property;
import android.view.View;
+import android.view.ViewGroup;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
import com.android.quickstep.util.TaskCornerRadius;
+import com.android.systemui.plugins.OverviewScreenshotActions;
+import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
/**
* A task in the Recents view.
*/
-public class TaskThumbnailView extends View {
+public class TaskThumbnailView extends View implements PluginListener<OverviewScreenshotActions> {
private final static ColorMatrix COLOR_MATRIX = new ColorMatrix();
private final static ColorMatrix SATURATION_COLOR_MATRIX = new ColorMatrix();
@@ -100,6 +103,7 @@
private boolean mOverlayEnabled;
private boolean mRotated;
+ private OverviewScreenshotActions mOverviewScreenshotActionsPlugin;
public TaskThumbnailView(Context context) {
this(context, null);
@@ -130,16 +134,35 @@
}
/**
- * Updates this thumbnail.
+ * Updates the thumbnail.
+ * @param refreshNow whether the {@code thumbnailData} will be used to redraw immediately.
+ * In most cases, we use the {@link #setThumbnail(Task, ThumbnailData)}
+ * version with {@code refreshNow} is true. The only exception is
+ * in the live tile case that we grab a screenshot when user enters Overview
+ * upon swipe up so that a usable screenshot is accessible immediately when
+ * recents animation needs to be finished / cancelled.
*/
- public void setThumbnail(Task task, ThumbnailData thumbnailData) {
+ public void setThumbnail(Task task, ThumbnailData thumbnailData, boolean refreshNow) {
mTask = task;
- if (thumbnailData != null && thumbnailData.thumbnail != null) {
- Bitmap bm = thumbnailData.thumbnail;
+ mThumbnailData =
+ (thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null;
+ if (refreshNow) {
+ refresh();
+ }
+ }
+
+ /** See {@link #setThumbnail(Task, ThumbnailData, boolean)} */
+ public void setThumbnail(Task task, ThumbnailData thumbnailData) {
+ setThumbnail(task, thumbnailData, true /* refreshNow */);
+ }
+
+ /** Updates the shader, paint, matrix to redraw. */
+ public void refresh() {
+ if (mThumbnailData != null && mThumbnailData.thumbnail != null) {
+ Bitmap bm = mThumbnailData.thumbnail;
bm.prepareToDraw();
mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(mBitmapShader);
- mThumbnailData = thumbnailData;
updateThumbnailMatrix();
} else {
mBitmapShader = null;
@@ -147,6 +170,10 @@
mPaint.setShader(null);
mOverlay.reset();
}
+ if (mOverviewScreenshotActionsPlugin != null) {
+ mOverviewScreenshotActionsPlugin
+ .setupActions((ViewGroup) getTaskView(), getThumbnail(), mActivity);
+ }
updateThumbnailPaintFilter();
}
@@ -211,6 +238,33 @@
canvas.restore();
}
+ @Override
+ public void onPluginConnected(OverviewScreenshotActions overviewScreenshotActions,
+ Context context) {
+ mOverviewScreenshotActionsPlugin = overviewScreenshotActions;
+ mOverviewScreenshotActionsPlugin.setupActions(getTaskView(), getThumbnail(), mActivity);
+ }
+
+ @Override
+ public void onPluginDisconnected(OverviewScreenshotActions plugin) {
+ if (mOverviewScreenshotActionsPlugin != null) {
+ mOverviewScreenshotActionsPlugin = null;
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ PluginManagerWrapper.INSTANCE.get(getContext())
+ .addPluginListener(this, OverviewScreenshotActions.class);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this);
+ }
+
public RectF getInsetsToDrawInFullscreen(boolean isMultiWindowMode) {
// Don't show insets in multi window mode.
return isMultiWindowMode ? EMPTY_RECT_F : mClippedInsets;
@@ -308,8 +362,7 @@
final Configuration configuration =
getContext().getResources().getConfiguration();
// Rotate the screenshot if not in multi-window mode
- isRotated = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION &&
- configuration.orientation != mThumbnailData.orientation &&
+ isRotated = configuration.orientation != mThumbnailData.orientation &&
!mActivity.isInMultiWindowMode() &&
mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
// Scale the screenshot to always fit the width of the card.
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 5c4d6d8..98aaceb 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -16,8 +16,6 @@
<resources>
<string name="task_overlay_factory_class" translatable="false"></string>
- <string name="overview_callbacks_class" translatable="false"></string>
-
<!-- Activity which blocks home gesture -->
<string name="gesture_blocking_activity" translatable="false"></string>
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index a8e2956..0d5d832 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -17,8 +17,7 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
-import static com.android.systemui.shared.recents.utilities.Utilities
- .postAtFrontOfQueueAsynchronously;
+import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -28,12 +27,12 @@
import android.os.Build;
import android.os.Handler;
-import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
+import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
@TargetApi(Build.VERSION_CODES.P)
public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
@@ -50,13 +49,14 @@
mStartAtFrontOfQueue = startAtFrontOfQueue;
}
+ // Called only in R+ platform
@BinderThread
- @Override
- public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, Runnable runnable) {
+ public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
Runnable r = () -> {
finishExistingAnimation();
mAnimationResult = new AnimationResult(runnable);
- onCreateAnimation(targetCompats, mAnimationResult);
+ onCreateAnimation(appTargets, wallpaperTargets, mAnimationResult);
};
if (mStartAtFrontOfQueue) {
postAtFrontOfQueueAsynchronously(mHandler, r);
@@ -65,13 +65,21 @@
}
}
+ // Called only in Q platform
+ @BinderThread
+ @Deprecated
+ public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, Runnable runnable) {
+ onAnimationStart(appTargets, new RemoteAnimationTargetCompat[0], runnable);
+ }
+
/**
* Called on the UI thread when the animation targets are received. The implementation must
* call {@link AnimationResult#setAnimation} with the target animation to be run.
*/
@UiThread
public abstract void onCreateAnimation(
- RemoteAnimationTargetCompat[] targetCompats, AnimationResult result);
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result);
@UiThread
private void finishExistingAnimation() {
@@ -139,4 +147,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index b9ce1ce..663b125 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -19,30 +19,25 @@
import android.content.Context;
import android.content.Intent;
import android.os.Build;
-import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
-import com.android.launcher3.states.InternalStateHandler;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
-import com.android.quickstep.OverviewCallbacks;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import java.util.function.BiPredicate;
@TargetApi(Build.VERSION_CODES.P)
-public class LauncherInitListener extends InternalStateHandler implements ActivityInitListener {
-
- private final BiPredicate<Launcher, Boolean> mOnInitListener;
+public class LauncherInitListener extends ActivityInitListener<Launcher> {
private RemoteAnimationProvider mRemoteAnimationProvider;
public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
- mOnInitListener = onInitListener;
+ super(onInitListener, Launcher.ACTIVITY_TRACKER);
}
@Override
- protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+ public boolean init(Launcher launcher, boolean alreadyOnHome) {
if (mRemoteAnimationProvider != null) {
QuickstepAppTransitionManagerImpl appTransitionManager =
(QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
@@ -50,7 +45,7 @@
// Set a one-time animation provider. After the first call, this will get cleared.
// TODO: Probably also check the intended target id.
CancellationSignal cancellationSignal = new CancellationSignal();
- appTransitionManager.setRemoteAnimationProvider((targets) -> {
+ appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
// On the first call clear the reference.
cancellationSignal.cancel();
@@ -58,34 +53,25 @@
mRemoteAnimationProvider = null;
if (provider != null && launcher.getStateManager().getState().overviewUi) {
- return provider.createWindowAnimation(targets);
+ return provider.createWindowAnimation(appTargets, wallpaperTargets);
}
return null;
}, cancellationSignal);
}
- OverviewCallbacks.get(launcher).onInitOverviewTransition();
- return mOnInitListener.test(launcher, alreadyOnHome);
- }
-
- @Override
- public void register() {
- initWhenReady();
+ launcher.deferOverlayCallbacksUntilNextResumeOrStop();
+ return super.init(launcher, alreadyOnHome);
}
@Override
public void unregister() {
mRemoteAnimationProvider = null;
- clearReference();
+ super.unregister();
}
@Override
public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
Context context, Handler handler, long duration) {
mRemoteAnimationProvider = animProvider;
-
- register();
-
- Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
- context.startActivity(addToIntent(new Intent((intent))), options);
+ super.registerAndStartActivity(intent, animProvider, context, handler, duration);
}
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 991408c..d4db05a 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -57,6 +57,9 @@
import android.util.Pair;
import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.Interpolators;
@@ -68,7 +71,7 @@
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.RemoteAnimationTargets;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
@@ -80,9 +83,6 @@
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
/**
* {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
* home and/or all-apps.
@@ -202,17 +202,19 @@
true /* startAtFrontOfQueue */) {
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
- AnimationResult result) {
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
AnimatorSet anim = new AnimatorSet();
boolean launcherClosing =
- launcherIsATargetWithMode(targetCompats, MODE_CLOSING);
+ launcherIsATargetWithMode(appTargets, MODE_CLOSING);
- if (isLaunchingFromRecents(v, targetCompats)) {
- composeRecentsLaunchAnimator(anim, v, targetCompats, launcherClosing);
+ if (isLaunchingFromRecents(v, appTargets)) {
+ composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
+ launcherClosing);
} else {
- composeIconLaunchAnimator(anim, v, targetCompats, launcherClosing);
+ composeIconLaunchAnimator(anim, v, appTargets, wallpaperTargets,
+ launcherClosing);
}
if (launcherClosing) {
@@ -255,36 +257,39 @@
*
* @param anim the animator set to add to
* @param v the launching view
- * @param targets the apps that are opening/closing
+ * @param appTargets the apps that are opening/closing
* @param launcherClosing true if the launcher app is closing
*/
protected abstract void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing);
+ @NonNull RemoteAnimationTargetCompat[] appTargets,
+ @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing);
/**
* Compose the animations for a launch from the app icon.
*
* @param anim the animation to add to
* @param v the launching view with the icon
- * @param targets the list of opening/closing apps
+ * @param appTargets the list of opening/closing apps
* @param launcherClosing true if launcher is closing
*/
private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+ @NonNull RemoteAnimationTargetCompat[] appTargets,
+ @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+ boolean launcherClosing) {
// Set the state animation first so that any state listeners are called
// before our internal listeners.
mLauncher.getStateManager().setCurrentAnimation(anim);
- Rect windowTargetBounds = getWindowTargetBounds(targets);
+ Rect windowTargetBounds = getWindowTargetBounds(appTargets);
boolean isAllOpeningTargetTrs = true;
- for (int i = 0; i < targets.length; i++) {
- RemoteAnimationTargetCompat target = targets[i];
+ for (int i = 0; i < appTargets.length; i++) {
+ RemoteAnimationTargetCompat target = appTargets[i];
if (target.mode == MODE_OPENING) {
isAllOpeningTargetTrs &= target.isTranslucent;
}
if (!isAllOpeningTargetTrs) break;
}
- anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds,
+ anim.play(getOpeningWindowAnimators(v, appTargets, wallpaperTargets, windowTargetBounds,
!isAllOpeningTargetTrs));
if (launcherClosing) {
Pair<AnimatorSet, Runnable> launcherContentAnimator =
@@ -305,10 +310,10 @@
* In multiwindow mode, we need to get the final size of the opening app window target to help
* figure out where the floating view should animate to.
*/
- private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] targets) {
+ private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] appTargets) {
Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
if (mLauncher.isInMultiWindowMode()) {
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTargetCompat target : appTargets) {
if (target.mode == MODE_OPENING) {
bounds.set(target.sourceContainerBounds);
bounds.offsetTo(target.position.x, target.position.y);
@@ -418,7 +423,9 @@
/**
* @return Animator that controls the window of the opening targets.
*/
- private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
+ private ValueAnimator getOpeningWindowAnimators(View v,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
Rect windowTargetBounds, boolean toggleVisibility) {
RectF bounds = new RectF();
FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
@@ -426,8 +433,8 @@
Rect crop = new Rect();
Matrix matrix = new Matrix();
- RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
- MODE_OPENING);
+ RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
+ wallpaperTargets, MODE_OPENING);
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(floatingView);
openingTargets.addDependentTransactionApplier(surfaceApplier);
@@ -551,9 +558,9 @@
float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale;
float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale;
- SurfaceParams[] params = new SurfaceParams[targets.length];
- for (int i = targets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = targets[i];
+ SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ for (int i = appTargets.length - 1; i >= 0; i--) {
+ RemoteAnimationTargetCompat target = appTargets[i];
Rect targetCrop;
final float alpha;
final float cornerRadius;
@@ -619,7 +626,8 @@
/**
* Animator that controls the transformations of the windows when unlocking the device.
*/
- private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] targets) {
+ private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
@@ -629,9 +637,9 @@
unlockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- SurfaceParams[] params = new SurfaceParams[targets.length];
- for (int i = targets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = targets[i];
+ SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ for (int i = appTargets.length - 1; i >= 0; i--) {
+ RemoteAnimationTargetCompat target = appTargets[i];
params[i] = new SurfaceParams(target.leash, 1f, null,
target.sourceContainerBounds,
RemoteAnimationProvider.getLayer(target, MODE_OPENING), cornerRadius);
@@ -645,7 +653,8 @@
/**
* Animator that controls the transformations of the windows the targets that are closing.
*/
- private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
+ private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
Matrix matrix = new Matrix();
@@ -661,9 +670,9 @@
@Override
public void onUpdate(float percent) {
- SurfaceParams[] params = new SurfaceParams[targets.length];
- for (int i = targets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = targets[i];
+ SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ for (int i = appTargets.length - 1; i >= 0; i--) {
+ RemoteAnimationTargetCompat target = appTargets[i];
final float alpha;
final float cornerRadius;
if (target.mode == MODE_CLOSING) {
@@ -764,13 +773,21 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
LauncherAnimationRunner.AnimationResult result) {
+ if (mLauncher.isDestroyed()) {
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(getClosingWindowAnimators(appTargets, wallpaperTargets));
+ result.setAnimation(anim, mLauncher.getApplicationContext());
+ return;
+ }
+
if (!mLauncher.hasBeenResumed()) {
// If launcher is not resumed, wait until new async-frame after resume
mLauncher.addOnResumeCallback(() ->
postAsyncCallback(mHandler, () ->
- onCreateAnimation(targetCompats, result)));
+ onCreateAnimation(appTargets, wallpaperTargets, result)));
return;
}
@@ -782,14 +799,14 @@
AnimatorSet anim = null;
RemoteAnimationProvider provider = mRemoteAnimationProvider;
if (provider != null) {
- anim = provider.createWindowAnimation(targetCompats);
+ anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
}
if (anim == null) {
anim = new AnimatorSet();
anim.play(mFromUnlock
- ? getUnlockWindowAnimator(targetCompats)
- : getClosingWindowAnimators(targetCompats));
+ ? getUnlockWindowAnimator(appTargets, wallpaperTargets)
+ : getClosingWindowAnimators(appTargets, wallpaperTargets));
// Normally, we run the launcher content animation when we are transitioning
// home, but if home is already visible, then we don't want to animate the
@@ -799,7 +816,7 @@
// targets list because it is already visible). In that case, we force
// invisibility on touch down, and only reset it after the animation to home
// is initialized.
- if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)
+ if (launcherIsATargetWithMode(appTargets, MODE_OPENING)
|| mLauncher.isForceInvisible()) {
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
index 693ae60..aa0dfc3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
@@ -24,18 +24,18 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.quickstep.OverviewInteractionState;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SystemUiProxy;
public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
private static final String TAG = "BackButtonAlphaHandler";
private final Launcher mLauncher;
- private final OverviewInteractionState mOverviewInteractionState;
public BackButtonAlphaHandler(Launcher launcher) {
mLauncher = launcher;
- mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(mLauncher);
}
@Override
@@ -49,14 +49,23 @@
if (!config.playNonAtomicComponent()) {
return;
}
- float fromAlpha = mOverviewInteractionState.getBackButtonAlpha();
+
+ if (!SysUINavigationMode.getMode(mLauncher).hasGestures) {
+ // If the nav mode is not gestural, then force back button alpha to be 1
+ UiThreadHelper.setBackButtonAlphaAsync(mLauncher, UiFactory.SET_BACK_BUTTON_ALPHA, 1f,
+ true /* animate */);
+ return;
+ }
+
+ float fromAlpha = SystemUiProxy.INSTANCE.get(mLauncher).getLastBackButtonAlpha();
float toAlpha = toState.hideBackButton ? 0 : 1;
if (Float.compare(fromAlpha, toAlpha) != 0) {
ValueAnimator anim = ValueAnimator.ofFloat(fromAlpha, toAlpha);
anim.setDuration(config.duration);
anim.addUpdateListener(valueAnimator -> {
final float alpha = (float) valueAnimator.getAnimatedValue();
- mOverviewInteractionState.setBackButtonAlpha(alpha, false);
+ UiThreadHelper.setBackButtonAlphaAsync(mLauncher, UiFactory.SET_BACK_BUTTON_ALPHA,
+ alpha, false /* animate */);
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
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/TogglableFlag.java b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
index 853a1c6..27d81ef 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -18,7 +18,8 @@
import android.content.Context;
import android.provider.DeviceConfig;
-import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+
+import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
public class TogglableFlag extends BaseTogglableFlag {
public static final String NAMESPACE_LAUNCHER = "launcher";
@@ -36,14 +37,14 @@
@Override
public void addChangeListener(Context context, Runnable r) {
DeviceConfig.addOnPropertiesChangedListener(
- NAMESPACE_LAUNCHER,
- context.getMainExecutor(),
- (properties) -> {
- if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
- return;
- }
- initialize(context);
- r.run();
- });
+ NAMESPACE_LAUNCHER,
+ context.getMainExecutor(),
+ (properties) -> {
+ if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
+ return;
+ }
+ initialize(context);
+ r.run();
+ });
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index c02df93..17c681b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -51,11 +51,12 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.proxy.ProxyActivityStarter;
import com.android.launcher3.proxy.StartActivityParams;
-import com.android.quickstep.OverviewInteractionState;
+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;
@@ -65,6 +66,15 @@
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();
@@ -88,7 +98,9 @@
* Sets the back button visibility based on the current state/window focus.
*/
public static void onLauncherStateOrFocusChanged(Launcher launcher) {
- boolean shouldBackButtonBeHidden = launcher != null
+ Mode mode = SysUINavigationMode.getMode(launcher);
+ boolean shouldBackButtonBeHidden = mode.hasGestures
+ && launcher != null
&& launcher.getStateManager().getState().hideBackButton
&& launcher.hasWindowFocus();
if (shouldBackButtonBeHidden) {
@@ -96,8 +108,8 @@
shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher,
TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
}
- OverviewInteractionState.INSTANCE.get(launcher)
- .setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
+ UiThreadHelper.setBackButtonAlphaAsync(launcher, UiFactory.SET_BACK_BUTTON_ALPHA,
+ shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
if (launcher != null && launcher.getDragLayer() != null) {
launcher.getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
}
@@ -165,13 +177,14 @@
CancellationSignal cancellationSignal) {
QuickstepAppTransitionManagerImpl appTransitionManager =
(QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
- appTransitionManager.setRemoteAnimationProvider((targets) -> {
+ appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
// On the first call clear the reference.
cancellationSignal.cancel();
ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
- fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(targets));
+ fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
+ wallpaperTargets));
AnimatorSet anim = new AnimatorSet();
anim.play(fadeAnimation);
return anim;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
index 910fa0d..7beb9db 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
@@ -14,16 +14,17 @@
package com.android.launcher3.uioverrides.plugins;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.content.Context;
import android.os.Looper;
-import com.android.launcher3.LauncherModel;
import com.android.systemui.shared.plugins.PluginInitializer;
public class PluginInitializerImpl implements PluginInitializer {
@Override
public Looper getBgLooper() {
- return LauncherModel.getWorkerLooper();
+ return MODEL_EXECUTOR.getLooper();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index b81edfa..ef6a5e2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -47,8 +47,8 @@
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.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.LayoutUtils;
@@ -137,8 +137,7 @@
} else if (fromState == OVERVIEW) {
return isDragTowardPositive ? ALL_APPS : NORMAL;
} else if (fromState == NORMAL && isDragTowardPositive) {
- int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher)
- .getSystemUiStateFlags();
+ int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
return mAllowDragToOverview && TouchInteractionService.isConnected()
&& (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0
? OVERVIEW : ALL_APPS;
@@ -177,6 +176,20 @@
return builder;
}
+ private AnimatorSetBuilder getNormalToAllAppsAnimation() {
+ AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
+ 0, ALL_APPS_CONTENT_FADE_THRESHOLD));
+ return builder;
+ }
+
+ private AnimatorSetBuilder getAllAppsToNormalAnimation() {
+ AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
+ 1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
+ return builder;
+ }
+
@Override
protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
LauncherState toState) {
@@ -187,6 +200,10 @@
builder = getOverviewToAllAppsAnimation();
} else if (fromState == ALL_APPS && toState == OVERVIEW) {
builder = getAllAppsToOverviewAnimation();
+ } else if (fromState == NORMAL && toState == ALL_APPS) {
+ builder = getNormalToAllAppsAnimation();
+ } else if (fromState == ALL_APPS && toState == NORMAL) {
+ builder = getAllAppsToNormalAnimation();
}
return builder;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 11a8043..16bd9ed 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -21,8 +21,6 @@
import static android.view.MotionEvent.ACTION_CANCEL;
import android.graphics.PointF;
-import android.os.RemoteException;
-import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -37,9 +35,8 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TouchController;
-import com.android.quickstep.RecentsModel;
-import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
/**
@@ -62,9 +59,9 @@
*/
private static final int FLAG_SLIPPERY = 0x20000000;
- protected final Launcher mLauncher;
+ private final Launcher mLauncher;
+ private final SystemUiProxy mSystemUiProxy;
private final float mTouchSlop;
- private ISystemUiProxy mSysUiProxy;
private int mLastAction;
private final SparseArray<PointF> mDownEvents;
@@ -73,6 +70,7 @@
public StatusBarTouchController(Launcher l) {
mLauncher = l;
+ mSystemUiProxy = SystemUiProxy.INSTANCE.get(mLauncher);
// Guard against TAPs by increasing the touch slop.
mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop();
mDownEvents = new SparseArray<>();
@@ -82,17 +80,14 @@
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "mCanIntercept:" + mCanIntercept);
writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction));
- writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null));
+ writer.println(prefix + "mSysUiProxy available:"
+ + SystemUiProxy.INSTANCE.get(mLauncher).isActive());
}
private void dispatchTouchEvent(MotionEvent ev) {
- try {
- if (mSysUiProxy != null) {
- mLastAction = ev.getActionMasked();
- mSysUiProxy.onStatusBarMotionEvent(ev);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Remote exception on sysUiProxy.", e);
+ if (mSystemUiProxy.isActive()) {
+ mLastAction = ev.getActionMasked();
+ mSystemUiProxy.onStatusBarMotionEvent(ev);
}
}
@@ -170,7 +165,6 @@
return false;
}
}
- mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy();
- return mSysUiProxy != null;
+ return SystemUiProxy.INSTANCE.get(mLauncher).isActive();
}
}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
similarity index 87%
rename from quickstep/src/com/android/quickstep/ActivityControlHelper.java
rename to quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 5c9c7d4..409bec6 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -17,12 +17,9 @@
import android.annotation.TargetApi;
import android.content.Context;
-import android.content.Intent;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.Region;
import android.os.Build;
-import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -34,8 +31,8 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.ActivityInitListener;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.BiPredicate;
@@ -45,7 +42,7 @@
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
*/
@TargetApi(Build.VERSION_CODES.P)
-public interface ActivityControlHelper<T extends BaseDraggingActivity> {
+public interface BaseActivityInterface<T extends BaseDraggingActivity> {
void onTransitionCancelled(T activity, boolean activityVisible);
@@ -82,7 +79,7 @@
boolean shouldMinimizeSplitScreen();
- default boolean deferStartingActivity(Region activeNavBarRegion, MotionEvent ev) {
+ default boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
return true;
}
@@ -97,15 +94,9 @@
void onLaunchTaskSuccess(T activity);
- interface ActivityInitListener {
+ default void closeOverlay() { }
- void register();
-
- void unregister();
-
- void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
- Context context, Handler handler, long duration);
- }
+ default void switchToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {}
interface AnimationFactory {
@@ -119,9 +110,9 @@
public final boolean shouldPreformHaptic;
}
- default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { }
+ default void onRemoteAnimationReceived(RemoteAnimationTargets targets) { }
- void createActivityController(long transitionLength);
+ void createActivityInterface(long transitionLength);
default void adjustActivityControllerInterpolators() { }
diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
index c840132..71833ad 100644
--- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
@@ -28,6 +28,7 @@
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;
@@ -43,6 +44,7 @@
*/
public abstract class BaseRecentsActivity extends BaseDraggingActivity {
+ public static ActivityTracker<BaseRecentsActivity> ACTIVITY_TRACKER = new ActivityTracker<>();
private Configuration mOldConfig;
@Override
@@ -55,7 +57,7 @@
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
- RecentsActivityTracker.onRecentsActivityCreate(this);
+ ACTIVITY_TRACKER.handleCreate((RecentsActivity) this);
}
/**
@@ -132,13 +134,13 @@
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
- RecentsActivityTracker.onRecentsActivityNewIntent(this);
+ ACTIVITY_TRACKER.handleNewIntent(this, intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
- RecentsActivityTracker.onRecentsActivityDestroy(this);
+ ACTIVITY_TRACKER.onActivityDestroyed(this);
}
@Override
@@ -157,6 +159,6 @@
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
writer.println(prefix + "Misc:");
- dumpMisc(writer);
+ dumpMisc(prefix + "\t", writer);
}
}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
new file mode 100644
index 0000000..de64227
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.android.launcher3.BaseDraggingActivity;
+
+/**
+ * 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 {
+
+ // Needed to interact with the current activity
+ private BaseActivityInterface mActivityInterface;
+
+ public GestureState(BaseActivityInterface activityInterface) {
+ mActivityInterface = activityInterface;
+ }
+
+ public <T extends BaseDraggingActivity> BaseActivityInterface<T> getActivityInterface() {
+ return mActivityInterface;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java
rename to quickstep/src/com/android/quickstep/InputConsumer.java
index a1e5d47..62c0ded 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep.inputconsumers;
+package com.android.quickstep;
import android.annotation.TargetApi;
import android.os.Build;
@@ -33,6 +33,7 @@
int TYPE_SCREEN_PINNED = 1 << 6;
int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
int TYPE_RESET_GESTURE = 1 << 8;
+ int TYPE_QUICK_CAPTURE = 1 << 9;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -44,6 +45,7 @@
"TYPE_SCREEN_PINNED", // 6
"TYPE_OVERVIEW_WITHOUT_FOCUS", // 7
"TYPE_RESET_GESTURE", // 8
+ "TYPE_QUICK_CAPTURE", // 9
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
diff --git a/quickstep/src/com/android/quickstep/OverviewCallbacks.java b/quickstep/src/com/android/quickstep/OverviewCallbacks.java
deleted file mode 100644
index f5573ba..0000000
--- a/quickstep/src/com/android/quickstep/OverviewCallbacks.java
+++ /dev/null
@@ -1,43 +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 android.content.Context;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-/**
- * Callbacks related to overview/quicksteps.
- */
-public class OverviewCallbacks implements ResourceBasedOverride {
-
- private static OverviewCallbacks sInstance;
-
- public static OverviewCallbacks get(Context context) {
- Preconditions.assertUIThread();
- if (sInstance == null) {
- sInstance = Overrides.getObject(OverviewCallbacks.class,
- context.getApplicationContext(), R.string.overview_callbacks_class);
- }
- return sInstance;
- }
-
- public void onInitOverviewTransition() { }
-
- public void closeAllWindows() { }
-}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 88a4eb6..73b78db 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -57,19 +56,21 @@
}
};
private final Context mContext;
+ private final RecentsAnimationDeviceState mDeviceState;
private final Intent mCurrentHomeIntent;
private final Intent mMyHomeIntent;
private final Intent mFallbackIntent;
private final SparseIntArray mConfigChangesMap = new SparseIntArray();
private String mUpdateRegisteredPackage;
- private ActivityControlHelper mActivityControlHelper;
+ private BaseActivityInterface mActivityInterface;
private Intent mOverviewIntent;
- private int mSystemUiStateFlags;
private boolean mIsHomeAndOverviewSame;
private boolean mIsDefaultHome;
+ private boolean mIsHomeDisabled;
- public OverviewComponentObserver(Context context) {
+ public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
mContext = context;
+ mDeviceState = deviceState;
mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
@@ -98,36 +99,33 @@
updateOverviewTargets();
}
- public void onSystemUiStateChanged(int stateFlags) {
- boolean homeDisabledChanged = (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED)
- != (stateFlags & SYSUI_STATE_HOME_DISABLED);
- mSystemUiStateFlags = stateFlags;
- if (homeDisabledChanged) {
+ public void onSystemUiStateChanged() {
+ if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
updateOverviewTargets();
}
}
/**
- * Update overview intent and {@link ActivityControlHelper} based off the current launcher home
+ * Update overview intent and {@link BaseActivityInterface} based off the current launcher home
* component.
*/
private void updateOverviewTargets() {
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
+ mIsHomeDisabled = mDeviceState.isHomeDisabled();
mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
// Set assistant visibility to 0 from launcher's perspective, ensures any elements that
// launcher made invisible become visible again before the new activity control helper
// becomes active.
- if (mActivityControlHelper != null) {
- mActivityControlHelper.onAssistantVisibilityChanged(0.f);
+ if (mActivityInterface != null) {
+ mActivityInterface.onAssistantVisibilityChanged(0.f);
}
- if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
- && (defaultHome == null || mIsDefaultHome)) {
+ if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
- mActivityControlHelper = new LauncherActivityControllerHelper();
+ mActivityInterface = new LauncherActivityInterface();
mIsHomeAndOverviewSame = true;
mOverviewIntent = mMyHomeIntent;
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
@@ -140,7 +138,7 @@
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- mActivityControlHelper = new FallbackActivityControllerHelper();
+ mActivityInterface = new FallbackActivityInterface();
mIsHomeAndOverviewSame = false;
mOverviewIntent = mFallbackIntent;
mCurrentHomeIntent.setComponent(defaultHome);
@@ -232,7 +230,7 @@
*
* @return the current activity control helper
*/
- public ActivityControlHelper getActivityControlHelper() {
- return mActivityControlHelper;
+ public BaseActivityInterface getActivityInterface() {
+ return mActivityInterface;
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
deleted file mode 100644
index 78b48d7..0000000
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ /dev/null
@@ -1,145 +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 android.content.Context;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.systemui.shared.recents.ISystemUiProxy;
-
-import androidx.annotation.WorkerThread;
-
-/**
- * Sets alpha for the back button
- */
-public class OverviewInteractionState {
-
- private static final String TAG = "OverviewFlags";
-
- private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
-
- // We do not need any synchronization for this variable as its only written on UI thread.
- public static final MainThreadInitializedObject<OverviewInteractionState> INSTANCE =
- new MainThreadInitializedObject<>(OverviewInteractionState::new);
-
- private static final int MSG_SET_PROXY = 200;
- private static final int MSG_SET_BACK_BUTTON_ALPHA = 201;
-
- private final Context mContext;
- private final Handler mUiHandler;
- private final Handler mBgHandler;
-
- // These are updated on the background thread
- private ISystemUiProxy mISystemUiProxy;
- private float mBackButtonAlpha = 1;
-
- private int mSystemUiStateFlags;
-
- private OverviewInteractionState(Context context) {
- mContext = context;
-
- // Data posted to the uihandler will be sent to the bghandler. Data is sent to uihandler
- // because of its high send frequency and data may be very different than the previous value
- // For example, send back alpha on uihandler to avoid flickering when setting its visibility
- mUiHandler = new Handler(this::handleUiMessage);
- mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage);
-
- onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(context)
- .addModeChangeListener(this::onNavigationModeChanged));
- }
-
- public float getBackButtonAlpha() {
- return mBackButtonAlpha;
- }
-
- public void setBackButtonAlpha(float alpha, boolean animate) {
- if (!modeSupportsGestures()) {
- alpha = 1;
- }
- mUiHandler.removeMessages(MSG_SET_BACK_BUTTON_ALPHA);
- mUiHandler.obtainMessage(MSG_SET_BACK_BUTTON_ALPHA, animate ? 1 : 0, 0, alpha)
- .sendToTarget();
- }
-
- public void setSystemUiProxy(ISystemUiProxy proxy) {
- mBgHandler.obtainMessage(MSG_SET_PROXY, proxy).sendToTarget();
- }
-
- public void setSystemUiStateFlags(int stateFlags) {
- mSystemUiStateFlags = stateFlags;
- }
-
- public int getSystemUiStateFlags() {
- return mSystemUiStateFlags;
- }
-
- private boolean handleUiMessage(Message msg) {
- if (msg.what == MSG_SET_BACK_BUTTON_ALPHA) {
- mBackButtonAlpha = (float) msg.obj;
- }
- mBgHandler.obtainMessage(msg.what, msg.arg1, msg.arg2, msg.obj).sendToTarget();
- return true;
- }
-
- private boolean handleBgMessage(Message msg) {
- switch (msg.what) {
- case MSG_SET_PROXY:
- mISystemUiProxy = (ISystemUiProxy) msg.obj;
- break;
- case MSG_SET_BACK_BUTTON_ALPHA:
- applyBackButtonAlpha((float) msg.obj, msg.arg1 == 1);
- return true;
- }
- return true;
- }
-
- @WorkerThread
- private void applyBackButtonAlpha(float alpha, boolean animate) {
- if (mISystemUiProxy == null) {
- return;
- }
- try {
- mISystemUiProxy.setBackButtonAlpha(alpha, animate);
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to update overview back button alpha", e);
- }
- }
-
- private void onNavigationModeChanged(SysUINavigationMode.Mode mode) {
- resetHomeBounceSeenOnQuickstepEnabledFirstTime();
- }
-
- private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
- if (modeSupportsGestures() && !Utilities.getPrefs(mContext).getBoolean(
- HAS_ENABLED_QUICKSTEP_ONCE, true)) {
- Utilities.getPrefs(mContext).edit()
- .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
- .putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
- .apply();
- }
- }
-
- private boolean modeSupportsGestures() {
- return SysUINavigationMode.getMode(mContext).hasGestures;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index e41dba9..10f9feb 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -16,19 +16,22 @@
package com.android.quickstep;
-import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.annotation.TargetApi;
import android.app.ActivityManager;
-import android.content.Context;
import android.os.Build;
import android.os.Process;
import android.util.SparseBooleanArray;
-import com.android.launcher3.MainThreadExecutor;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.launcher3.util.LooperExecutor;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -41,7 +44,8 @@
public class RecentTasksList extends TaskStackChangeListener {
private final KeyguardManagerCompat mKeyguardManager;
- private final MainThreadExecutor mMainThreadExecutor;
+ private final LooperExecutor mMainThreadExecutor;
+ private final ActivityManagerWrapper mActivityManagerWrapper;
// The list change id, increments as the task list changes in the system
private int mChangeId;
@@ -52,11 +56,13 @@
ArrayList<Task> mTasks = new ArrayList<>();
- public RecentTasksList(Context context) {
- mMainThreadExecutor = new MainThreadExecutor();
- mKeyguardManager = new KeyguardManagerCompat(context);
+ public RecentTasksList(LooperExecutor mainThreadExecutor,
+ KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) {
+ mMainThreadExecutor = mainThreadExecutor;
+ mKeyguardManager = keyguardManager;
mChangeId = 1;
- ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
+ mActivityManagerWrapper = activityManagerWrapper;
+ mActivityManagerWrapper.registerTaskStackListener(this);
}
/**
@@ -64,7 +70,7 @@
*/
public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
// Kick off task loading in the background
- BACKGROUND_EXECUTOR.execute(() -> {
+ UI_HELPER_EXECUTOR.execute(() -> {
ArrayList<Task> tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */);
mMainThreadExecutor.execute(() -> callback.accept(tasks));
});
@@ -86,12 +92,12 @@
if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
// The list is up to date, send the callback on the next frame,
// so that requestID can be returned first.
- mMainThreadExecutor.getHandler().post(resultCallback);
+ mMainThreadExecutor.post(resultCallback);
return requestLoadId;
}
// Kick off task loading in the background
- BACKGROUND_EXECUTOR.execute(() -> {
+ UI_HELPER_EXECUTOR.execute(() -> {
ArrayList<Task> tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly);
mMainThreadExecutor.execute(() -> {
@@ -136,12 +142,13 @@
/**
* Loads and creates a list of all the recent tasks.
*/
- private ArrayList<Task> loadTasksInBackground(int numTasks,
+ @VisibleForTesting
+ ArrayList<Task> loadTasksInBackground(int numTasks,
boolean loadKeysOnly) {
int currentUserId = Process.myUserHandle().getIdentifier();
ArrayList<Task> allTasks = new ArrayList<>();
List<ActivityManager.RecentTaskInfo> rawTasks =
- ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, currentUserId);
+ mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId);
// The raw tasks are given in most-recent to least-recent order, we need to reverse it
Collections.reverse(rawTasks);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
deleted file mode 100644
index f9d2f11..0000000
--- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
+++ /dev/null
@@ -1,133 +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 android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-
-import com.android.launcher3.MainThreadExecutor;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
-import com.android.quickstep.util.RemoteAnimationProvider;
-
-import java.lang.ref.WeakReference;
-import java.util.function.BiPredicate;
-
-/**
- * Utility class to track create/destroy for some {@link BaseRecentsActivity}.
- */
-@TargetApi(Build.VERSION_CODES.P)
-public class RecentsActivityTracker<T extends BaseRecentsActivity> implements ActivityInitListener {
-
- private static WeakReference<BaseRecentsActivity> sCurrentActivity =
- new WeakReference<>(null);
- private static final Scheduler sScheduler = new Scheduler();
-
- private final BiPredicate<T, Boolean> mOnInitListener;
-
- public RecentsActivityTracker(BiPredicate<T, Boolean> onInitListener) {
- mOnInitListener = onInitListener;
- }
-
- @Override
- public void register() {
- sScheduler.schedule(this);
- }
-
- @Override
- public void unregister() {
- sScheduler.clearReference(this);
- }
-
- private boolean init(T activity, boolean visible) {
- return mOnInitListener.test(activity, visible);
- }
-
- public static <T extends BaseRecentsActivity> T getCurrentActivity() {
- return (T) sCurrentActivity.get();
- }
-
- @Override
- public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
- Context context, Handler handler, long duration) {
- register();
-
- Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
- context.startActivity(intent, options);
- }
-
- public static void onRecentsActivityCreate(BaseRecentsActivity activity) {
- sCurrentActivity = new WeakReference<>(activity);
- sScheduler.initIfPending(activity, false);
- }
-
-
- public static void onRecentsActivityNewIntent(BaseRecentsActivity activity) {
- sScheduler.initIfPending(activity, activity.isStarted());
- }
-
- public static void onRecentsActivityDestroy(BaseRecentsActivity activity) {
- if (sCurrentActivity.get() == activity) {
- sCurrentActivity.clear();
- }
- }
-
-
- private static class Scheduler implements Runnable {
-
- private WeakReference<RecentsActivityTracker> mPendingTracker = new WeakReference<>(null);
- private MainThreadExecutor mMainThreadExecutor;
-
- public synchronized void schedule(RecentsActivityTracker tracker) {
- mPendingTracker = new WeakReference<>(tracker);
- if (mMainThreadExecutor == null) {
- mMainThreadExecutor = new MainThreadExecutor();
- }
- mMainThreadExecutor.execute(this);
- }
-
- @Override
- public void run() {
- BaseRecentsActivity activity = sCurrentActivity.get();
- if (activity != null) {
- initIfPending(activity, activity.isStarted());
- }
- }
-
- public synchronized boolean initIfPending(BaseRecentsActivity activity,
- boolean alreadyOnHome) {
- RecentsActivityTracker tracker = mPendingTracker.get();
- if (tracker != null) {
- if (!tracker.init(activity, alreadyOnHome)) {
- mPendingTracker.clear();
- }
- return true;
- }
- return false;
- }
-
- public synchronized boolean clearReference(RecentsActivityTracker tracker) {
- if (mPendingTracker.get() == tracker) {
- mPendingTracker.clear();
- return true;
- }
- return false;
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
new file mode 100644
index 0000000..2918879
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -0,0 +1,140 @@
+/*
+ * 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.graphics.Rect;
+import android.util.ArraySet;
+
+import androidx.annotation.BinderThread;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Preconditions;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.Set;
+
+/**
+ * Wrapper around {@link com.android.systemui.shared.system.RecentsAnimationListener} which
+ * delegates callbacks to multiple listeners on the main thread
+ */
+public class RecentsAnimationCallbacks implements
+ com.android.systemui.shared.system.RecentsAnimationListener {
+
+ private final Set<RecentsAnimationListener> mListeners = new ArraySet<>();
+ private final boolean mShouldMinimizeSplitScreen;
+
+ // TODO(141886704): Remove these references when they are no longer needed
+ private RecentsAnimationController mController;
+
+ private boolean mCancelled;
+
+ public RecentsAnimationCallbacks(boolean shouldMinimizeSplitScreen) {
+ mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+ }
+
+ @UiThread
+ public void addListener(RecentsAnimationListener listener) {
+ Preconditions.assertUIThread();
+ mListeners.add(listener);
+ }
+
+ @UiThread
+ public void removeListener(RecentsAnimationListener listener) {
+ Preconditions.assertUIThread();
+ mListeners.remove(listener);
+ }
+
+ public void notifyAnimationCanceled() {
+ mCancelled = true;
+ onAnimationCanceled(null);
+ }
+
+ // Called only in Q platform
+ @BinderThread
+ @Deprecated
+ public final void onAnimationStart(RecentsAnimationControllerCompat controller,
+ RemoteAnimationTargetCompat[] appTargets, Rect homeContentInsets,
+ Rect minimizedHomeBounds) {
+ onAnimationStart(controller, appTargets, new RemoteAnimationTargetCompat[0],
+ homeContentInsets, minimizedHomeBounds);
+ }
+
+ // Called only in R+ platform
+ @BinderThread
+ public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ Rect homeContentInsets, Rect minimizedHomeBounds) {
+ RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
+ wallpaperTargets, homeContentInsets, minimizedHomeBounds);
+ mController = new RecentsAnimationController(animationController,
+ mShouldMinimizeSplitScreen, this::onAnimationFinished);
+
+ if (mCancelled) {
+ Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
+ mController::finishAnimationToApp);
+ } else {
+ Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ for (RecentsAnimationListener listener : getListeners()) {
+ listener.onRecentsAnimationStart(mController, targets);
+ }
+ });
+ }
+ }
+
+ @BinderThread
+ @Override
+ public final void onAnimationCanceled(ThumbnailData thumbnailData) {
+ Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ for (RecentsAnimationListener listener : getListeners()) {
+ listener.onRecentsAnimationCanceled(thumbnailData);
+ }
+ });
+ }
+
+ private final void onAnimationFinished(RecentsAnimationController controller) {
+ Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ for (RecentsAnimationListener listener : getListeners()) {
+ listener.onRecentsAnimationFinished(controller);
+ }
+ });
+ }
+
+ private RecentsAnimationListener[] getListeners() {
+ return mListeners.toArray(new RecentsAnimationListener[mListeners.size()]);
+ }
+
+ /**
+ * Listener for the recents animation callbacks.
+ */
+ public interface RecentsAnimationListener {
+ default void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targetSet) {}
+
+ /**
+ * Callback from the system when the recents animation is canceled. {@param thumbnailData}
+ * is passed back for rendering screenshot to replace live tile.
+ */
+ default void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {}
+
+ default void onRecentsAnimationFinished(RecentsAnimationController controller) {}
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
new file mode 100644
index 0000000..d938dc5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -0,0 +1,249 @@
+/*
+ * 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.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;
+
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.util.Preconditions;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Wrapper around RecentsAnimationController to help with some synchronization
+ */
+public class RecentsAnimationController {
+
+ private static final String TAG = "RecentsAnimationController";
+
+ private final RecentsAnimationControllerCompat mController;
+ 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 mTouchInProgress;
+ private boolean mFinishPending;
+
+ public RecentsAnimationController(RecentsAnimationControllerCompat controller,
+ boolean shouldMinimizeSplitScreen,
+ Consumer<RecentsAnimationController> onFinishedListener) {
+ mController = controller;
+ mOnFinishedListener = onFinishedListener;
+ mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+
+ setWindowThresholdCrossed(mWindowThresholdCrossed);
+ }
+
+ /**
+ * Synchronously takes a screenshot of the task with the given {@param taskId} if the task is
+ * currently being animated.
+ */
+ public ThumbnailData screenshotTask(int taskId) {
+ return mController != null ? mController.screenshotTask(taskId) : null;
+ }
+
+ /**
+ * Indicates that the gesture has crossed the window boundary threshold and system UI can be
+ * update the represent the window behind
+ */
+ public void setWindowThresholdCrossed(boolean windowThresholdCrossed) {
+ if (mWindowThresholdCrossed != windowThresholdCrossed) {
+ mWindowThresholdCrossed = windowThresholdCrossed;
+ UI_HELPER_EXECUTOR.execute(() -> {
+ mController.setAnimationTargetsBehindSystemBars(!windowThresholdCrossed);
+ if (mShouldMinimizeSplitScreen && windowThresholdCrossed) {
+ // NOTE: As a workaround for conflicting animations (Launcher animating the task
+ // leash, and SystemUI resizing the docked stack, which resizes the task), we
+ // currently only set the minimized mode, and not the inverse.
+ // TODO: Synchronize the minimize animation with the launcher animation
+ mController.setSplitScreenMinimized(windowThresholdCrossed);
+ }
+ });
+ }
+ }
+
+ /**
+ * Notifies the controller that we want to defer cancel until the next app transition starts.
+ * If {@param screenshot} is set, then we will receive a screenshot on the next
+ * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call
+ * {@link #cleanupScreenshot()} when that screenshot is no longer used.
+ */
+ public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
+ mController.setDeferCancelUntilNextTransition(defer, screenshot);
+ }
+
+ /**
+ * Cleans up the screenshot previously returned from
+ * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}.
+ */
+ public void cleanupScreenshot() {
+ UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
+ }
+
+ @UiThread
+ public void finishAnimationToHome() {
+ finishAndClear(true /* toRecents */, null, false /* sendUserLeaveHint */);
+ }
+
+ @UiThread
+ public void finishAnimationToApp() {
+ finishAndClear(false /* toRecents */, null, false /* sendUserLeaveHint */);
+ }
+
+ /** See {@link #finish(boolean, Runnable, boolean)} */
+ @UiThread
+ public void finish(boolean toRecents, Runnable onFinishComplete) {
+ finish(toRecents, onFinishComplete, false /* sendUserLeaveHint */);
+ }
+
+ /**
+ * @param onFinishComplete A callback that runs on the main thread after the animation
+ * controller has finished on the background thread.
+ * @param sendUserLeaveHint Determines whether userLeaveHint flag will be set on the pausing
+ * activity. If userLeaveHint is true, the activity will enter into
+ * picture-in-picture mode upon being paused.
+ */
+ @UiThread
+ public void finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) {
+ Preconditions.assertUIThread();
+ if (!toRecents) {
+ finishAndClear(false, onFinishComplete, sendUserLeaveHint);
+ } else {
+ if (mTouchInProgress) {
+ mFinishPending = true;
+ // Execute the callback
+ if (onFinishComplete != null) {
+ onFinishComplete.run();
+ }
+ } else {
+ finishAndClear(true, onFinishComplete, sendUserLeaveHint);
+ }
+ }
+ }
+
+ private void finishAndClear(boolean toRecents, Runnable onFinishComplete,
+ boolean sendUserLeaveHint) {
+ disableInputProxy();
+ finishController(toRecents, onFinishComplete, sendUserLeaveHint);
+ }
+
+ @UiThread
+ public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
+ mOnFinishedListener.accept(this);
+ UI_HELPER_EXECUTOR.execute(() -> {
+ mController.setInputConsumerEnabled(false);
+ mController.finish(toRecents, sendUserLeaveHint);
+ if (callback != null) {
+ MAIN_EXECUTOR.execute(callback);
+ }
+ });
+ }
+
+ /**
+ * Enables the input consumer to start intercepting touches in the app window.
+ */
+ public void enableInputConsumer() {
+ UI_HELPER_EXECUTOR.submit(() -> {
+ mController.hideCurrentInputMethod();
+ mController.setInputConsumerEnabled(true);
+ });
+ }
+
+ public void enableInputProxy(InputConsumerController inputConsumerController,
+ Supplier<InputConsumer> inputProxySupplier) {
+ mInputProxySupplier = inputProxySupplier;
+ mInputConsumerController = inputConsumerController;
+ mInputConsumerController.setInputListener(this::onInputConsumerEvent);
+ }
+
+ private void disableInputProxy() {
+ if (mInputConsumer != null && mTouchInProgress) {
+ long now = SystemClock.uptimeMillis();
+ MotionEvent dummyCancel = MotionEvent.obtain(now, now, ACTION_CANCEL, 0, 0, 0);
+ mInputConsumer.onMotionEvent(dummyCancel);
+ dummyCancel.recycle();
+ }
+ if (mInputConsumerController != null) {
+ mInputConsumerController.setInputListener(null);
+ }
+ }
+
+ private boolean onInputConsumerEvent(InputEvent ev) {
+ if (ev instanceof MotionEvent) {
+ onInputConsumerMotionEvent((MotionEvent) ev);
+ } else if (ev instanceof KeyEvent) {
+ if (mInputConsumer == null) {
+ mInputConsumer = mInputProxySupplier.get();
+ }
+ mInputConsumer.onKeyEvent((KeyEvent) ev);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean onInputConsumerMotionEvent(MotionEvent ev) {
+ int action = ev.getAction();
+
+ // Just to be safe, verify that ACTION_DOWN comes before any other action,
+ // and ignore any ACTION_DOWN after the first one (though that should not happen).
+ if (!mTouchInProgress && action != ACTION_DOWN) {
+ Log.w(TAG, "Received non-down motion before down motion: " + action);
+ return false;
+ }
+ if (mTouchInProgress && action == ACTION_DOWN) {
+ Log.w(TAG, "Received down motion while touch was already in progress");
+ return false;
+ }
+
+ if (action == ACTION_DOWN) {
+ mTouchInProgress = true;
+ if (mInputConsumer == null) {
+ mInputConsumer = mInputProxySupplier.get();
+ }
+ } else if (action == ACTION_CANCEL || action == ACTION_UP) {
+ // Finish any pending actions
+ mTouchInProgress = false;
+ if (mFinishPending) {
+ mFinishPending = false;
+ finishAndClear(true /* toRecents */, null, false /* sendUserLeaveHint */);
+ }
+ }
+ if (mInputConsumer != null) {
+ mInputConsumer.onMotionEvent(ev);
+ }
+
+ return true;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
new file mode 100644
index 0000000..9b094f6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -0,0 +1,426 @@
+/*
+ * 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 android.content.Intent.ACTION_USER_UNLOCKED;
+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.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.SysUINavigationMode.Mode.THREE_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;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.Process;
+import android.text.TextUtils;
+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.util.DefaultDisplay;
+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;
+
+/**
+ * Manages the state of the system during a swipe up gesture.
+ */
+public class RecentsAnimationDeviceState implements
+ SysUINavigationMode.NavigationModeChangeListener,
+ DefaultDisplay.DisplayInfoChangeListener {
+
+ 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 final RectF mSwipeUpTouchRegion = new RectF();
+ private final Region mDeferredGestureRegion = new Region();
+ private final RectF mAssistantLeftRegion = new RectF();
+ private final RectF mAssistantRightRegion = new RectF();
+ private boolean mAssistantAvailable;
+ private float mAssistantVisibility;
+
+ private boolean mIsUserUnlocked;
+ private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
+ private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+ mIsUserUnlocked = true;
+ notifyUserUnlocked();
+ }
+ }
+ };
+
+ private Region mExclusionRegion;
+ private SystemGestureExclusionListenerCompat mExclusionListener;
+
+ private ComponentName mGestureBlockedActivity;
+
+ public RecentsAnimationDeviceState(Context context) {
+ mContext = context;
+ mUserManager = UserManagerCompat.getInstance(context);
+ mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
+ mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
+ mDisplayId = mDefaultDisplay.getInfo().id;
+
+ // Register for user unlocked if necessary
+ mIsUserUnlocked = mUserManager.isUserUnlocked(Process.myUserHandle());
+ if (!mIsUserUnlocked) {
+ mContext.registerReceiver(mUserUnlockedReceiver,
+ new IntentFilter(ACTION_USER_UNLOCKED));
+ }
+
+ // Register for exclusion updates
+ mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
+ @Override
+ @BinderThread
+ public void onExclusionChanged(Region region) {
+ // Assignments are atomic, it should be safe on binder thread
+ mExclusionRegion = region;
+ }
+ };
+ onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
+
+ // Add any blocked activities
+ String blockingActivity = context.getString(R.string.gesture_blocking_activity);
+ if (!TextUtils.isEmpty(blockingActivity)) {
+ mGestureBlockedActivity = ComponentName.unflattenFromString(blockingActivity);
+ }
+ }
+
+ /**
+ * Cleans up all the registered listeners and receivers.
+ */
+ public void destroy() {
+ Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver);
+ mSysUiNavMode.removeModeChangeListener(this);
+ mDefaultDisplay.removeChangeListener(this);
+ mExclusionListener.unregister();
+ }
+
+ @Override
+ public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
+ mDefaultDisplay.removeChangeListener(this);
+ if (newMode.hasGestures) {
+ mDefaultDisplay.addChangeListener(this);
+ }
+
+ if (mMode == NO_BUTTON) {
+ mExclusionListener.register();
+ } else {
+ mExclusionListener.unregister();
+ }
+ mMode = newMode;
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
+ if (info.id != getDisplayId()) {
+ return;
+ }
+
+ updateGestureTouchRegions();
+ }
+
+ /**
+ * @return the display id for the display that Launcher is running on.
+ */
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ /**
+ * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener
+ * will be called back immediately.
+ */
+ public void runOnUserUnlocked(Runnable action) {
+ if (mIsUserUnlocked) {
+ action.run();
+ } else {
+ mUserUnlockedActions.add(action);
+ }
+ }
+
+ /**
+ * @return whether the user is unlocked.
+ */
+ public boolean isUserUnlocked() {
+ return mIsUserUnlocked;
+ }
+
+ private void notifyUserUnlocked() {
+ for (Runnable action : mUserUnlockedActions) {
+ action.run();
+ }
+ mUserUnlockedActions.clear();
+ Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver);
+ }
+
+ /**
+ * @return whether the given running task info matches the gesture-blocked activity.
+ */
+ public boolean isGestureBlockedActivity(ActivityManager.RunningTaskInfo runningTaskInfo) {
+ return runningTaskInfo != null
+ && mGestureBlockedActivity.equals(runningTaskInfo.topActivity);
+ }
+
+ /**
+ * @return the package of the gesture-blocked activity or {@code null} if there is none.
+ */
+ public String getGestureBlockedActivityPackage() {
+ return (mGestureBlockedActivity != null)
+ ? mGestureBlockedActivity.getPackageName()
+ : null;
+ }
+
+ /**
+ * Updates the system ui state flags from SystemUI.
+ */
+ public void setSystemUiFlags(int stateFlags) {
+ mSystemUiStateFlags = stateFlags;
+ }
+
+ /**
+ * @return the system ui state flags.
+ */
+ // TODO(141886704): See if we can remove this
+ public @SystemUiStateFlags int getSystemUiStateFlags() {
+ return mSystemUiStateFlags;
+ }
+
+ /**
+ * @return whether SystemUI is in a state where we can start a system gesture.
+ */
+ public boolean canStartSystemGesture() {
+ return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+ && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
+ && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
+ && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
+ || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
+ }
+
+ /**
+ * @return whether the keyguard is showing and is occluded by an app showing above the keyguard
+ * (like camera or maps)
+ */
+ public boolean isKeyguardShowingOccluded() {
+ return (mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0;
+ }
+
+ /**
+ * @return whether screen pinning is enabled and active
+ */
+ public boolean isScreenPinningActive() {
+ return (mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
+ }
+
+ /**
+ * @return whether lock-task mode is active
+ */
+ public boolean isLockToAppActive() {
+ return ActivityManagerWrapper.getInstance().isLockToAppActive();
+ }
+
+ /**
+ * @return whether the accessibility menu is available.
+ */
+ public boolean isAccessibilityMenuAvailable() {
+ return (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+ }
+
+ /**
+ * @return whether the accessibility menu shortcut is available.
+ */
+ public boolean isAccessibilityMenuShortcutAvailable() {
+ return (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+ }
+
+ /**
+ * @return whether home is disabled (either by SUW/SysUI/device policy)
+ */
+ public boolean isHomeDisabled() {
+ return (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
+ }
+
+ /**
+ * @return whether overview is disabled (either by SUW/SysUI/device policy)
+ */
+ public boolean isOverviewDisabled() {
+ return (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
+ }
+
+ /**
+ * Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
+ */
+ public void updateGestureTouchRegions() {
+ if (!mMode.hasGestures) {
+ return;
+ }
+
+ Resources res = mContext.getResources();
+ DefaultDisplay.Info displayInfo = mDefaultDisplay.getInfo();
+ Point realSize = new Point(displayInfo.realSize);
+ mSwipeUpTouchRegion.set(0, 0, realSize.x, realSize.y);
+ if (mMode == NO_BUTTON) {
+ int touchHeight = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE, res);
+ mSwipeUpTouchRegion.top = mSwipeUpTouchRegion.bottom - touchHeight;
+
+ final int assistantWidth = res.getDimensionPixelSize(R.dimen.gestures_assistant_width);
+ final float assistantHeight = Math.max(touchHeight,
+ QuickStepContract.getWindowCornerRadius(res));
+ mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mSwipeUpTouchRegion.bottom;
+ mAssistantLeftRegion.top = mAssistantRightRegion.top =
+ mSwipeUpTouchRegion.bottom - assistantHeight;
+
+ mAssistantLeftRegion.left = 0;
+ mAssistantLeftRegion.right = assistantWidth;
+
+ mAssistantRightRegion.right = mSwipeUpTouchRegion.right;
+ mAssistantRightRegion.left = mSwipeUpTouchRegion.right - assistantWidth;
+ } else {
+ mAssistantLeftRegion.setEmpty();
+ mAssistantRightRegion.setEmpty();
+ switch (displayInfo.rotation) {
+ case Surface.ROTATION_90:
+ mSwipeUpTouchRegion.left = mSwipeUpTouchRegion.right
+ - ResourceUtils.getNavbarSize(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, res);
+ break;
+ case Surface.ROTATION_270:
+ mSwipeUpTouchRegion.right = mSwipeUpTouchRegion.left
+ + ResourceUtils.getNavbarSize(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, res);
+ break;
+ default:
+ mSwipeUpTouchRegion.top = mSwipeUpTouchRegion.bottom
+ - ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE, res);
+ }
+ }
+ }
+
+ /**
+ * @return whether the coordinates of the {@param event} is in the swipe up gesture region.
+ */
+ public boolean isInSwipeUpTouchRegion(MotionEvent event) {
+ return mSwipeUpTouchRegion.contains(event.getX(), event.getY());
+ }
+
+ /**
+ * @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
+ * is in the swipe up gesture region.
+ */
+ public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
+ return mSwipeUpTouchRegion.contains(event.getX(pointerIndex), event.getY(pointerIndex));
+ }
+
+ /**
+ * Sets the region in screen space where the gestures should be deferred (ie. due to specific
+ * nav bar ui).
+ */
+ public void setDeferredGestureRegion(Region deferredGestureRegion) {
+ mDeferredGestureRegion.set(deferredGestureRegion);
+ }
+
+ /**
+ * @return whether the given {@param event} is in the deferred gesture region indicating that
+ * the Launcher should not immediately start the recents animation until the gesture
+ * passes a certain threshold.
+ */
+ public boolean isInDeferredGestureRegion(MotionEvent event) {
+ return mDeferredGestureRegion.contains((int) event.getX(), (int) event.getY());
+ }
+
+ /**
+ * @return whether the given {@param event} is in the app-requested gesture-exclusion region.
+ * This is only used for quickswitch, and not swipe up.
+ */
+ public boolean isInExclusionRegion(MotionEvent event) {
+ // mExclusionRegion can change on binder thread, use a local instance here.
+ Region exclusionRegion = mExclusionRegion;
+ return mMode == NO_BUTTON && exclusionRegion != null
+ && exclusionRegion.contains((int) event.getX(), (int) event.getY());
+ }
+
+ /**
+ * Sets whether the assistant is available.
+ */
+ public void setAssistantAvailable(boolean assistantAvailable) {
+ mAssistantAvailable = assistantAvailable;
+ }
+
+ /**
+ * Sets the visibility fraction of the assistant.
+ */
+ public void setAssistantVisibility(float visibility) {
+ mAssistantVisibility = visibility;
+ }
+
+ /**
+ * @return the visibility fraction of the assistant.
+ */
+ public float getAssistantVisibility() {
+ return mAssistantVisibility;
+ }
+
+ /**
+ * @param ev An ACTION_DOWN motion event
+ * @return whether the given motion event can trigger the assistant.
+ */
+ public boolean canTriggerAssistantAction(MotionEvent ev) {
+ return mAssistantAvailable
+ && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
+ && (mAssistantLeftRegion.contains(ev.getX(), ev.getY())
+ || mAssistantRightRegion.contains(ev.getX(), ev.getY()))
+ && !isLockToAppActive();
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("DeviceState:");
+ pw.println(" canStartSystemGesture=" + canStartSystemGesture());
+ pw.println(" systemUiFlags=" + mSystemUiStateFlags);
+ pw.println(" systemUiFlagsDesc="
+ + QuickStepContract.getSystemUiStateString(mSystemUiStateFlags));
+ pw.println(" assistantAvailable=" + mAssistantAvailable);
+ pw.println(" assistantDisabled="
+ + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
new file mode 100644
index 0000000..9353759
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -0,0 +1,53 @@
+/*
+ * 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.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+
+import android.graphics.Rect;
+
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Extension of {@link RemoteAnimationTargets} with additional information about swipe
+ * up animation
+ */
+public class RecentsAnimationTargets extends RemoteAnimationTargets {
+
+ public final Rect homeContentInsets;
+ public final Rect minimizedHomeBounds;
+
+ public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps,
+ RemoteAnimationTargetCompat[] wallpapers, Rect homeContentInsets,
+ Rect minimizedHomeBounds) {
+ super(apps, wallpapers, MODE_CLOSING);
+ this.homeContentInsets = homeContentInsets;
+ this.minimizedHomeBounds = minimizedHomeBounds;
+ }
+
+ 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 dfab434..465d464 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -15,23 +15,27 @@
*/
package com.android.quickstep;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.createAndStartNewLooper;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
+import android.content.pm.LauncherApps;
import android.os.Build;
-import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Process;
-import android.os.RemoteException;
-import android.util.Log;
+import android.os.UserHandle;
import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
@@ -53,21 +57,20 @@
private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
private final Context mContext;
- private ISystemUiProxy mSystemUiProxy;
-
private final RecentTasksList mTaskList;
private final TaskIconCache mIconCache;
private final TaskThumbnailCache mThumbnailCache;
private RecentsModel(Context context) {
mContext = context;
- HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
- Process.THREAD_PRIORITY_BACKGROUND);
- loaderThread.start();
- mTaskList = new RecentTasksList(context);
- mIconCache = new TaskIconCache(context, loaderThread.getLooper());
- mThumbnailCache = new TaskThumbnailCache(context, loaderThread.getLooper());
+ Looper looper =
+ createAndStartNewLooper("TaskThumbnailIconCache", THREAD_PRIORITY_BACKGROUND);
+ mTaskList = new RecentTasksList(MAIN_EXECUTOR,
+ new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
+ mIconCache = new TaskIconCache(context, looper);
+ mThumbnailCache = new TaskThumbnailCache(context, looper);
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
+ setupPackageListener();
}
public TaskIconCache getIconCache() {
@@ -166,14 +169,7 @@
public void onTaskRemoved(int taskId) {
Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
mThumbnailCache.remove(dummyKey);
- }
-
- public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
- mSystemUiProxy = systemUiProxy;
- }
-
- public ISystemUiProxy getSystemUiProxy() {
- return mSystemUiProxy;
+ mIconCache.onTaskRemoved(dummyKey);
}
public void onTrimMemory(int level) {
@@ -188,16 +184,32 @@
}
public void onOverviewShown(boolean fromHome, String tag) {
- if (mSystemUiProxy == null) {
- return;
- }
- try {
- mSystemUiProxy.onOverviewShown(fromHome);
- } catch (RemoteException e) {
- Log.w(tag,
- "Failed to notify SysUI of overview shown from " + (fromHome ? "home" : "app")
- + ": ", e);
- }
+ SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(fromHome, tag);
+ }
+
+ 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) {
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
similarity index 86%
rename from quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
rename to quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 1229293..5fa6bc7 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep.util;
+package com.android.quickstep;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
@@ -25,17 +25,19 @@
/**
* Holds a collection of RemoteAnimationTargets, filtered by different properties.
*/
-public class RemoteAnimationTargetSet {
+public class RemoteAnimationTargets {
private final Queue<SyncRtSurfaceTransactionApplierCompat> mDependentTransactionAppliers =
new ArrayDeque<>(1);
public final RemoteAnimationTargetCompat[] unfilteredApps;
public final RemoteAnimationTargetCompat[] apps;
+ public final RemoteAnimationTargetCompat[] wallpapers;
public final int targetMode;
public final boolean hasRecents;
- public RemoteAnimationTargetSet(RemoteAnimationTargetCompat[] apps, int targetMode) {
+ public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps,
+ RemoteAnimationTargetCompat[] wallpapers, int targetMode) {
ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
boolean hasRecents = false;
if (apps != null) {
@@ -51,6 +53,7 @@
this.unfilteredApps = apps;
this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
+ this.wallpapers = wallpapers;
this.targetMode = targetMode;
this.hasRecents = hasRecents;
}
@@ -83,6 +86,9 @@
for (RemoteAnimationTargetCompat target : unfilteredApps) {
target.release();
}
+ for (RemoteAnimationTargetCompat target : wallpapers) {
+ target.release();
+ }
} else {
applier.addAfterApplyCallback(this::release);
}
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index b67c6f8..5902672 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -57,7 +57,7 @@
private static final String TAG = "SysUINavigationMode";
- private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
+ private static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
private static final String NAV_BAR_INTERACTION_MODE_RES_NAME =
"config_navBarInteractionMode";
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
new file mode 100644
index 0000000..5539b3e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -0,0 +1,285 @@
+/*
+ * 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.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.MotionEvent;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.quickstep.util.SharedApiCompat;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+
+/**
+ * Holds the reference to SystemUI.
+ */
+public class SystemUiProxy implements ISystemUiProxy {
+ private static final String TAG = SystemUiProxy.class.getSimpleName();
+
+ public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
+ new MainThreadInitializedObject<>(SystemUiProxy::new);
+
+ private ISystemUiProxy mSystemUiProxy;
+ private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
+ MAIN_EXECUTOR.execute(() -> setProxy(null));
+ };
+
+ // Used to dedupe calls to SystemUI
+ private int mLastShelfHeight;
+ private boolean mLastShelfVisible;
+ private float mLastBackButtonAlpha;
+ private boolean mLastBackButtonAnimate;
+
+ // TODO(141886704): Find a way to remove this
+ private int mLastSystemUiStateFlags;
+
+ public SystemUiProxy(Context context) {
+ // Do nothing
+ }
+
+ @Override
+ public IBinder asBinder() {
+ // Do nothing
+ return null;
+ }
+
+ public void setProxy(ISystemUiProxy proxy) {
+ unlinkToDeath();
+ mSystemUiProxy = proxy;
+ linkToDeath();
+ }
+
+ // TODO(141886704): Find a way to remove this
+ public void setLastSystemUiStateFlags(int stateFlags) {
+ mLastSystemUiStateFlags = stateFlags;
+ }
+
+ // TODO(141886704): Find a way to remove this
+ public int getLastSystemUiStateFlags() {
+ return mLastSystemUiStateFlags;
+ }
+
+ public boolean isActive() {
+ return mSystemUiProxy != null;
+ }
+
+ private void linkToDeath() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.asBinder().linkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to link sysui proxy death recipient");
+ }
+ }
+ }
+
+ private void unlinkToDeath() {
+ if (mSystemUiProxy != null) {
+ mSystemUiProxy.asBinder().unlinkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
+ }
+ }
+
+ @Override
+ public void startScreenPinning(int taskId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startScreenPinning(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startScreenPinning", e);
+ }
+ }
+ }
+
+ @Override
+ public void onSplitScreenInvoked() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onSplitScreenInvoked();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onSplitScreenInvoked", e);
+ }
+ }
+ }
+
+ @Override
+ public void onOverviewShown(boolean fromHome) {
+ onOverviewShown(fromHome, TAG);
+ }
+
+ public void onOverviewShown(boolean fromHome, String tag) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onOverviewShown(fromHome);
+ } catch (RemoteException e) {
+ Log.w(tag, "Failed call onOverviewShown from: " + (fromHome ? "home" : "app"), e);
+ }
+ }
+ }
+
+ @Override
+ public Rect getNonMinimizedSplitScreenSecondaryBounds() {
+ if (mSystemUiProxy != null) {
+ try {
+ return mSystemUiProxy.getNonMinimizedSplitScreenSecondaryBounds();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call getNonMinimizedSplitScreenSecondaryBounds", e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setBackButtonAlpha(float alpha, boolean animate) {
+ boolean changed = Float.compare(alpha, mLastBackButtonAlpha) != 0
+ || animate != mLastBackButtonAnimate;
+ if (mSystemUiProxy != null && changed) {
+ mLastBackButtonAlpha = alpha;
+ mLastBackButtonAnimate = animate;
+ try {
+ mSystemUiProxy.setBackButtonAlpha(alpha, animate);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setBackButtonAlpha", e);
+ }
+ }
+ }
+
+ public float getLastBackButtonAlpha() {
+ return mLastBackButtonAlpha;
+ }
+
+ @Override
+ public void setNavBarButtonAlpha(float alpha, boolean animate) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.setNavBarButtonAlpha(alpha, animate);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setNavBarButtonAlpha", e);
+ }
+ }
+ }
+
+ @Override
+ public void onStatusBarMotionEvent(MotionEvent event) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onStatusBarMotionEvent(event);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onStatusBarMotionEvent", e);
+ }
+ }
+ }
+
+ @Override
+ public void onAssistantProgress(float progress) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onAssistantProgress(progress);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onAssistantProgress with progress: " + progress, e);
+ }
+ }
+ }
+
+ @Override
+ public void onAssistantGestureCompletion(float velocity) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onAssistantGestureCompletion(velocity);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onAssistantGestureCompletion", e);
+ }
+ }
+ }
+
+ @Override
+ public void startAssistant(Bundle args) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startAssistant(args);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startAssistant", e);
+ }
+ }
+ }
+
+ @Override
+ public Bundle monitorGestureInput(String name, int displayId) {
+ if (mSystemUiProxy != null) {
+ try {
+ return mSystemUiProxy.monitorGestureInput(name, displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call monitorGestureInput: " + name, e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void notifyAccessibilityButtonClicked(int displayId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.notifyAccessibilityButtonClicked(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call notifyAccessibilityButtonClicked", e);
+ }
+ }
+ }
+
+ @Override
+ public void notifyAccessibilityButtonLongClicked() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.notifyAccessibilityButtonLongClicked();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call notifyAccessibilityButtonLongClicked", e);
+ }
+ }
+ }
+
+ @Override
+ public void stopScreenPinning() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.stopScreenPinning();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stopScreenPinning", e);
+ }
+ }
+ }
+
+ /**
+ * See SharedApiCompat#setShelfHeight()
+ */
+ public void setShelfHeight(boolean visible, int shelfHeight) {
+ boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
+ if (mSystemUiProxy != null && changed) {
+ mLastShelfVisible = visible;
+ mLastShelfHeight = shelfHeight;
+ try {
+ SharedApiCompat.setShelfHeight(mSystemUiProxy, visible, shelfHeight);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setShelfHeight visible: " + visible
+ + " height: " + shelfHeight, e);
+ }
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 07af9b3..289a129 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.ComponentName;
import android.content.Context;
@@ -27,26 +28,25 @@
import android.util.LruCache;
import android.view.accessibility.AccessibilityManager;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.HandlerRunnable;
-import com.android.launcher3.uioverrides.RecentsUiFactory;
import com.android.launcher3.util.Preconditions;
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 java.util.Map;
import java.util.function.Consumer;
/**
* Manages the caching of task icons and related data.
- * TODO: This class should later be merged into IconCache.
+ * TODO(b/138944598): This class should later be merged into IconCache.
*/
public class TaskIconCache {
private final Handler mBackgroundHandler;
- private final MainThreadExecutor mMainThreadExecutor;
private final AccessibilityManager mAccessibilityManager;
private final NormalizedIconLoader mIconLoader;
@@ -67,7 +67,6 @@
public TaskIconCache(Context context, Looper backgroundLooper) {
mBackgroundHandler = new Handler(backgroundLooper);
- mMainThreadExecutor = new MainThreadExecutor();
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Resources res = context.getResources();
@@ -103,7 +102,7 @@
// We don't call back to the provided callback in this case
return;
}
- mMainThreadExecutor.execute(() -> {
+ MAIN_EXECUTOR.execute(() -> {
task.icon = icon;
task.titleDescription = contentDescription;
callback.accept(task);
@@ -149,6 +148,21 @@
return label;
}
+
+ void onTaskRemoved(TaskKey taskKey) {
+ mIconCache.remove(taskKey);
+ }
+
+ 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);
+ }
+ }
+ }
+
public static abstract class IconLoadRequest extends HandlerRunnable {
IconLoadRequest(Handler handler) {
super(handler, null);
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 57c5a27..3b50c26 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -15,12 +15,14 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
-import com.android.launcher3.MainThreadExecutor;
+
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.HandlerRunnable;
@@ -30,13 +32,13 @@
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+
import java.util.ArrayList;
import java.util.function.Consumer;
public class TaskThumbnailCache {
private final Handler mBackgroundHandler;
- private final MainThreadExecutor mMainThreadExecutor;
private final int mCacheSize;
private final ThumbnailCache mCache;
@@ -94,7 +96,6 @@
public TaskThumbnailCache(Context context, Looper backgroundLooper) {
mBackgroundHandler = new Handler(backgroundLooper);
- mMainThreadExecutor = new MainThreadExecutor();
mHighResLoadingState = new HighResLoadingState(context);
Resources res = context.getResources();
@@ -168,7 +169,7 @@
// We don't call back to the provided callback in this case
return;
}
- mMainThreadExecutor.execute(() -> {
+ MAIN_EXECUTOR.execute(() -> {
mCache.put(key, thumbnail);
callback.accept(thumbnail);
onEnd();
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index 5f76ca7..230a22a 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -23,9 +23,9 @@
import android.os.UserHandle;
import android.util.Log;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -44,15 +44,14 @@
* TODO: remove this once we switch to getting the icon and label from IconCache.
*/
public static CharSequence getTitle(Context context, Task task) {
- LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(context);
- PackageManager packageManager = context.getPackageManager();
UserHandle user = UserHandle.of(task.key.userId);
- ApplicationInfo applicationInfo = launcherAppsCompat.getApplicationInfo(
- task.getTopComponent().getPackageName(), 0, user);
+ ApplicationInfo applicationInfo = new PackageManagerHelper(context)
+ .getApplicationInfo(task.getTopComponent().getPackageName(), user, 0);
if (applicationInfo == null) {
Log.e(TAG, "Failed to get title for task " + task);
return "";
}
+ PackageManager packageManager = context.getPackageManager();
return packageManager.getUserBadgedLabel(
applicationInfo.loadLabel(packageManager), user);
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index bf3cd8a..8e5ed1a 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -29,12 +29,16 @@
import android.stats.launcher.nano.Launcher;
import android.stats.launcher.nano.LauncherExtension;
import android.stats.launcher.nano.LauncherTarget;
+import android.util.Log;
import android.view.View;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.util.ComponentKey;
import com.android.systemui.shared.system.StatsLogCompat;
import com.google.protobuf.nano.MessageNano;
@@ -50,6 +54,8 @@
public class StatsLogCompatManager extends StatsLogManager {
private static final int SUPPORTED_TARGET_DEPTH = 2;
+ private static final String TAG = "StatsLogCompatManager";
+ private static final boolean DEBUG = false;
public StatsLogCompatManager(Context context) { }
@@ -59,6 +65,9 @@
ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
int srcState = mStateProvider.getCurrentState();
fillInLauncherExtension(v, ext);
+ if (ext.srcTarget[0] != null) {
+ ext.srcTarget[0].item = LauncherTarget.APP_ICON;
+ }
StatsLogCompat.write(LAUNCH_APP, srcState, BACKGROUND /* dstState */,
MessageNano.toByteArray(ext), true);
}
@@ -95,28 +104,132 @@
}
public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
+ if (DEBUG) {
+ Log.d(TAG, "fillInLauncherExtension");
+ }
+
StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
+ if (DEBUG) {
+ Log.d(TAG, "View or provider is null, or view doesn't have an ItemInfo tag.");
+ }
+
return false;
}
ItemInfo itemInfo = (ItemInfo) v.getTag();
Target child = new Target();
Target parent = new Target();
provider.fillInLogContainerData(v, itemInfo, child, parent);
+ extension.srcTarget[0] = new LauncherTarget();
+ extension.srcTarget[1] = new LauncherTarget();
copy(child, extension.srcTarget[0]);
copy(parent, extension.srcTarget[1]);
return true;
}
public static boolean fillInLauncherExtensionWithPageId(LauncherExtension ext, int pageId) {
+ if (DEBUG) {
+ Log.d(TAG, "fillInLauncherExtensionWithPageId, pageId = " + pageId);
+ }
+
Target target = new Target();
target.pageIndex = pageId;
+ ext.srcTarget[0] = new LauncherTarget();
copy(target, ext.srcTarget[0]);
return true;
}
private static void copy(Target src, LauncherTarget dst) {
- // fill in
+ if (DEBUG) {
+ Log.d(TAG, "copy target information from clearcut Target to LauncherTarget.");
+ }
+
+ // Fill in type
+ switch (src.type) {
+ case Target.Type.ITEM:
+ dst.type = LauncherTarget.ITEM_TYPE;
+ break;
+ case Target.Type.CONTROL:
+ dst.type = LauncherTarget.CONTROL_TYPE;
+ break;
+ case Target.Type.CONTAINER:
+ dst.type = LauncherTarget.CONTAINER_TYPE;
+ break;
+ default:
+ dst.type = LauncherTarget.NONE;
+ break;
+ }
+
+ // Fill in item
+ switch (src.itemType) {
+ case ItemType.APP_ICON:
+ dst.item = LauncherTarget.APP_ICON;
+ break;
+ case ItemType.SHORTCUT:
+ dst.item = LauncherTarget.SHORTCUT;
+ break;
+ case ItemType.WIDGET:
+ dst.item = LauncherTarget.WIDGET;
+ break;
+ case ItemType.FOLDER_ICON:
+ dst.item = LauncherTarget.FOLDER_ICON;
+ break;
+ case ItemType.DEEPSHORTCUT:
+ dst.item = LauncherTarget.DEEPSHORTCUT;
+ break;
+ case ItemType.SEARCHBOX:
+ dst.item = LauncherTarget.SEARCHBOX;
+ break;
+ case ItemType.EDITTEXT:
+ dst.item = LauncherTarget.EDITTEXT;
+ break;
+ case ItemType.NOTIFICATION:
+ dst.item = LauncherTarget.NOTIFICATION;
+ break;
+ case ItemType.TASK:
+ dst.item = LauncherTarget.TASK;
+ break;
+ default:
+ dst.item = LauncherTarget.DEFAULT_ITEM;
+ break;
+ }
+
+ // Fill in container
+ switch (src.containerType) {
+ case ContainerType.HOTSEAT:
+ dst.container = LauncherTarget.HOTSEAT;
+ break;
+ case ContainerType.FOLDER:
+ dst.container = LauncherTarget.FOLDER;
+ break;
+ case ContainerType.PREDICTION:
+ dst.container = LauncherTarget.PREDICTION;
+ break;
+ case ContainerType.SEARCHRESULT:
+ dst.container = LauncherTarget.SEARCHRESULT;
+ break;
+ default:
+ dst.container = LauncherTarget.DEFAULT_CONTAINER;
+ break;
+ }
+
+ // Fill in control
+ switch (src.controlType) {
+ case ControlType.UNINSTALL_TARGET:
+ dst.control = LauncherTarget.UNINSTALL;
+ break;
+ case ControlType.REMOVE_TARGET:
+ dst.control = LauncherTarget.REMOVE;
+ break;
+ default:
+ dst.control = LauncherTarget.DEFAULT_CONTROL;
+ break;
+ }
+
+ // Fill in other fields
+ dst.pageId = src.pageIndex;
+ dst.gridX = src.gridX;
+ dst.gridY = src.gridY;
}
@Override
diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
new file mode 100644
index 0000000..fe37d60
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -0,0 +1,60 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.util.ActivityTracker;
+import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
+
+import java.util.function.BiPredicate;
+
+public class ActivityInitListener<T extends BaseActivity> implements SchedulerCallback<T> {
+
+ private final BiPredicate<T, Boolean> mOnInitListener;
+ private final ActivityTracker<T> mActivityTracker;
+
+ public ActivityInitListener(BiPredicate<T, Boolean> onInitListener,
+ ActivityTracker<T> tracker) {
+ mOnInitListener = onInitListener;
+ mActivityTracker = tracker;
+ }
+
+ @Override
+ public boolean init(T activity, boolean alreadyOnHome) {
+ return mOnInitListener.test(activity, alreadyOnHome);
+ }
+
+ public void register() {
+ mActivityTracker.schedule(this);
+ }
+
+ public void unregister() {
+ mActivityTracker.clearReference(this);
+ }
+
+ public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
+ Context context, Handler handler, long duration) {
+ register();
+
+ Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
+ context.startActivity(addToIntent(new Intent((intent))), options);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 4503a43..6210fc2 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -31,16 +31,17 @@
static final int Z_BOOST_BASE = 800570000;
- AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targets);
+ AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets);
default ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
false /* startAtFrontOfQueue */) {
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
- AnimationResult result) {
- result.setAnimation(createWindowAnimation(targetCompats), context);
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
}
};
return ActivityOptionsCompat.makeRemoteAnimation(
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index 40dd74b..fa2d338 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -21,6 +21,7 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import com.android.quickstep.RemoteAnimationTargets;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;
@@ -29,11 +30,12 @@
*/
public class RemoteFadeOutAnimationListener implements AnimatorUpdateListener {
- private final RemoteAnimationTargetSet mTarget;
+ private final RemoteAnimationTargets mTarget;
private boolean mFirstFrame = true;
- public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] targets) {
- mTarget = new RemoteAnimationTargetSet(targets, MODE_CLOSING);
+ public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
+ mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_CLOSING);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index dc6b56e..26e9eaf 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -161,7 +161,7 @@
mMidProgress = OVERVIEW.getVerticalProgress(mLauncher);
Rect hotseatPadding = dp.getHotseatLayoutPadding();
int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
- - hotseatPadding.bottom - hotseatPadding.top;
+ + hotseatPadding.bottom + hotseatPadding.top;
float dragHandleTop =
Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(context, dp));
mDragHandleProgress = 1 - (dragHandleTop / mShiftRange);
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
index d0956d1..8e4762d 100644
--- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -24,6 +24,7 @@
import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.os.Process;
import android.view.View;
@@ -35,7 +36,6 @@
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.model.AppLaunchTracker;
import org.junit.After;
@@ -60,7 +60,7 @@
public void setUp() throws Exception {
super.setUp();
- List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(mTargetContext)
+ List<LauncherActivityInfo> activities = mTargetContext.getSystemService(LauncherApps.class)
.getActivityList(null, Process.myUserHandle());
mSampleApp1 = activities.get(0);
mSampleApp2 = activities.get(1);
@@ -150,10 +150,10 @@
List<AppTarget> targets = new ArrayList<>(activities.length);
for (LauncherActivityInfo info : activities) {
ComponentName cn = info.getComponentName();
- AppTarget target =
- new AppTarget.Builder(new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser())
- .setClassName(cn.getClassName())
- .build();
+ AppTarget target = new AppTarget.Builder(
+ new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser())
+ .setClassName(cn.getClassName())
+ .build();
targets.add(target);
}
mCallback.onTargetsAvailable(targets);
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index e5f949b..aa5fce1 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -22,14 +22,22 @@
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
+import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_ACTIVITY_TIMEOUT;
+import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT;
import static com.android.launcher3.ui.AbstractLauncherUiTest.resolveSystemApp;
+import static com.android.launcher3.ui.AbstractLauncherUiTest.startAppFast;
+import static com.android.launcher3.ui.AbstractLauncherUiTest.startTestActivity;
+import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+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.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -41,10 +49,16 @@
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.tapl.BaseOverview;
import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.OverviewTask;
+import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
+import com.android.quickstep.views.RecentsView;
import org.junit.Rule;
import org.junit.Test;
@@ -53,11 +67,11 @@
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
@LargeTest
@RunWith(AndroidJUnit4.class)
-/**
- * TODO: Fix fallback when quickstep is enabled
- */
public class FallbackRecentsTest {
private final UiDevice mDevice;
@@ -78,11 +92,14 @@
Context context = instrumentation.getContext();
mDevice = UiDevice.getInstance(instrumentation);
mDevice.setOrientationNatural();
- mLauncher = new LauncherInstrumentation(instrumentation);
+ mLauncher = new LauncherInstrumentation();
- mOrderSensitiveRules = RuleChain.
- outerRule(new NavigationModeSwitchRule(mLauncher)).
- around(new FailureWatcher(mDevice));
+ if (TestHelpers.isInLauncherProcess()) {
+ Utilities.enableRunningInTestHarnessForTests();
+ }
+
+ mOrderSensitiveRules = RuleChain.outerRule(new NavigationModeSwitchRule(mLauncher))
+ .around(new FailureWatcher(mDevice));
mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
getHomeIntentInPackage(context),
@@ -103,9 +120,14 @@
}
}
};
+ if (TestHelpers.isInLauncherProcess()) {
+ mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand(
+ TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()).
+ getString("result"));
+ }
}
- @NavigationModeSwitch(mode = THREE_BUTTON)
+ @NavigationModeSwitch
@Test
public void goToOverviewFromHome() {
mDevice.pressHome();
@@ -115,23 +137,115 @@
mLauncher.getBackground().switchToOverview();
}
- @NavigationModeSwitch(mode = THREE_BUTTON)
+ @NavigationModeSwitch
@Test
public void goToOverviewFromApp() {
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
mLauncher.getBackground().switchToOverview();
}
- private void startAppFast(String packageName) {
- final Instrumentation instrumentation = getInstrumentation();
- final Intent intent = instrumentation.getContext().getPackageManager().
- getLaunchIntentForPackage(packageName);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- instrumentation.getTargetContext().startActivity(intent);
- assertTrue(packageName + " didn't start",
- mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), WAIT_TIME_MS));
+ protected void executeOnRecents(Consumer<RecentsActivity> f) {
+ getFromRecents(r -> {
+ f.accept(r);
+ return true;
+ });
}
+ protected <T> T getFromRecents(Function<RecentsActivity, T> f) {
+ if (!TestHelpers.isInLauncherProcess()) return null;
+ Object[] result = new Object[1];
+ Wait.atMost("Failed to get from recents", () -> MAIN_EXECUTOR.submit(() -> {
+ RecentsActivity activity = RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
+ if (activity == null) {
+ return false;
+ }
+ result[0] = f.apply(activity);
+ return true;
+ }).get(), DEFAULT_UI_TIMEOUT);
+ return (T) result[0];
+ }
+
+ private BaseOverview pressHomeAndGoToOverview() {
+ mDevice.pressHome();
+ return mLauncher.getBackground().switchToOverview();
+ }
+
+ @NavigationModeSwitch
+ @Test
+ public void testOverview() {
+ 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);
+
+ BaseOverview overview = mLauncher.getBackground().switchToOverview();
+ executeOnRecents(recents ->
+ assertTrue("Don't have at least 3 tasks", getTaskCount(recents) >= 3));
+
+ // Test flinging forward and backward.
+ overview.flingForward();
+ final Integer currentTaskAfterFlingForward = getFromRecents(this::getCurrentOverviewPage);
+ executeOnRecents(recents -> assertTrue("Current task in Overview is still 0",
+ currentTaskAfterFlingForward > 0));
+
+ overview.flingBackward();
+ executeOnRecents(recents -> assertTrue("Flinging back in Overview did nothing",
+ getCurrentOverviewPage(recents) < currentTaskAfterFlingForward));
+
+ // Test opening a task.
+ overview = pressHomeAndGoToOverview();
+
+ OverviewTask task = overview.getCurrentTask();
+ assertNotNull("overview.getCurrentTask() returned null (1)", task);
+ assertNotNull("OverviewTask.open returned null", task.open());
+ assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
+ By.pkg(getAppPackageName()).text("TestActivity2")),
+ DEFAULT_UI_TIMEOUT));
+
+
+ // Test dismissing a task.
+ overview = pressHomeAndGoToOverview();
+ final Integer numTasks = getFromRecents(this::getTaskCount);
+ task = overview.getCurrentTask();
+ assertNotNull("overview.getCurrentTask() returned null (2)", task);
+ task.dismiss();
+ executeOnRecents(
+ recents -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
+ numTasks - 1, getTaskCount(recents)));
+
+ // Test dismissing all tasks.
+ pressHomeAndGoToOverview().dismissAllTasks();
+ assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
+ mOtherLauncherActivity.packageName)), WAIT_TIME_MS));
+ }
+
+ private int getCurrentOverviewPage(RecentsActivity recents) {
+ return recents.<RecentsView>getOverviewPanel().getCurrentPage();
+ }
+
+ private int getTaskCount(RecentsActivity recents) {
+ return recents.<RecentsView>getOverviewPanel().getTaskViewCount();
+ }
+
+ /**
+ * Workaround for b/141580748, there was an issue where the recent task is only updated when the
+ * activity starting the task is resumed. In this case, we should wait until the task is in
+ * the recents task list before continuing.
+ */
+ private void startAppFastAndWaitForRecentTask(String packageName) {
+ startAppFast(packageName);
+ Wait.atMost("Expected app in task list",
+ () -> containsRecentTaskWithPackage(packageName), DEFAULT_ACTIVITY_TIMEOUT);
+ }
+
+ private boolean containsRecentTaskWithPackage(String packageName) {
+ for (ComponentName cn : mLauncher.getRecentTasks()) {
+ if (cn.getPackageName().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index e295527..c2197ab 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -181,7 +181,7 @@
SysUINavigationMode.INSTANCE.get(targetContext);
targetContext.getMainExecutor().execute(() ->
sysUINavigationMode.addModeChangeListener(listener));
- latch.await(10, TimeUnit.SECONDS);
+ latch.await(60, TimeUnit.SECONDS);
targetContext.getMainExecutor().execute(() ->
sysUINavigationMode.removeModeChangeListener(listener));
assertTrue("Navigation mode didn't change to " + expectedMode,
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
new file mode 100644
index 0000000..34eb7f8
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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 junit.framework.TestCase.assertNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.util.LooperExecutor;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.KeyguardManagerCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+public class RecentTasksListTest {
+
+ private ActivityManagerWrapper mockActivityManagerWrapper;
+
+ // Class under test
+ private RecentTasksList mRecentTasksList;
+
+ @Before
+ public void setup() {
+ LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class);
+ KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class);
+ mockActivityManagerWrapper = mock(ActivityManagerWrapper.class);
+ mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat,
+ mockActivityManagerWrapper);
+ }
+
+ @Test
+ public void onTaskRemoved_reloadsAllTasks() {
+ mRecentTasksList.onTaskRemoved(0);
+ verify(mockActivityManagerWrapper, times(1))
+ .getRecentTasks(anyInt(), anyInt());
+ }
+
+ @Test
+ public void onTaskStackChanged_doesNotFetchTasks() {
+ mRecentTasksList.onTaskStackChanged();
+ verify(mockActivityManagerWrapper, times(0))
+ .getRecentTasks(anyInt(), anyInt());
+ }
+
+ @Test
+ public void loadTasksInBackground_onlyKeys_noValidTaskDescription() {
+ ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
+ when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(recentTaskInfo));
+
+ List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, true);
+
+ assertEquals(1, taskList.size());
+ assertNull(taskList.get(0).taskDescription.getLabel());
+ }
+
+ @Test
+ public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() {
+ String taskDescription = "Wheeee!";
+ ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
+ recentTaskInfo.taskDescription = new ActivityManager.TaskDescription(taskDescription);
+ when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(recentTaskInfo));
+
+ List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, false);
+
+ assertEquals(1, taskList.size());
+ assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel());
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index c5b560c..f5b9b7e 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -16,8 +16,8 @@
package com.android.quickstep;
-import static com.android.launcher3.util.RaceConditionTracker.enterEvt;
-import static com.android.launcher3.util.RaceConditionTracker.exitEvt;
+import static com.android.launcher3.util.RaceConditionReproducer.enterEvt;
+import static com.android.launcher3.util.RaceConditionReproducer.exitEvt;
import android.content.Intent;
@@ -25,7 +25,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
-import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.util.RaceConditionReproducer;
import com.android.quickstep.NavigationModeSwitchRule.Mode;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -80,8 +79,6 @@
@Test
@NavigationModeSwitch
public void testStressPressHome() {
- if (LauncherInstrumentation.isAvd()) return; // b/136278866
-
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
// Destroy Launcher activity.
closeLauncherActivity();
@@ -94,8 +91,6 @@
@Test
@NavigationModeSwitch
public void testStressSwipeToOverview() {
- if (LauncherInstrumentation.isAvd()) return; // b/136278866
-
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
// Destroy Launcher activity.
closeLauncherActivity();
@@ -104,4 +99,4 @@
mLauncher.getBackground().switchToOverview();
}
}
-}
\ No newline at end of file
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 885fdbf..d270d76 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -17,6 +17,7 @@
package com.android.quickstep;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -34,6 +35,7 @@
import com.android.launcher3.tapl.AllApps;
import com.android.launcher3.tapl.AllAppsFromOverview;
import com.android.launcher3.tapl.Background;
+import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.tapl.TestHelpers;
@@ -210,16 +212,21 @@
@PortraitLandscape
public void testBackground() throws Exception {
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ final Background background = getAndAssertBackground();
+
+ assertNotNull("Background.switchToOverview() returned null", background.switchToOverview());
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+ }
+
+ private Background getAndAssertBackground() {
final Background background = mLauncher.getBackground();
assertNotNull("Launcher.getBackground() returned null", background);
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
+ "one",
isInBackground(launcher)));
-
- assertNotNull("Background.switchToOverview() returned null", background.switchToOverview());
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
+ return background;
}
@Test
@@ -237,4 +244,48 @@
assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
assertNotNull("getHome returned null", mLauncher.getWorkspace());
}
+
+ @Test
+ @NavigationModeSwitch
+ @PortraitLandscape
+ @Ignore("Temporarily disabled b/140252765")
+ public void testQuickSwitchFromApp() throws Exception {
+ startAppFast(getAppPackageName());
+ startTestActivity(2);
+ 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("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.",
+ 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("Test Pin Item"));
+ }
+ getAndAssertBackground();
+ }
+
+ private boolean isTestActivityRunning(String activityLabel) {
+ return mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text(activityLabel)),
+ DEFAULT_UI_TIMEOUT);
+ }
+
+ @Test
+ @NavigationModeSwitch
+ @PortraitLandscape
+ public void testQuickSwitchFromHome() throws Exception {
+ startTestActivity(2);
+ mLauncher.pressHome().quickSwitchToPreviousApp();
+ assertTrue("The most recent task is not running after quick switching from home",
+ isTestActivityRunning("TestActivity2"));
+ getAndAssertBackground();
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
new file mode 100644
index 0000000..f8f22a1
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -0,0 +1,280 @@
+/*
+ * 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 androidx.test.InstrumentationRegistry.getContext;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER;
+import static com.android.launcher3.ui.widget.BindWidgetTest.createWidgetInfo;
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+
+import android.appwidget.AppWidgetManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.RemoteViews;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.tapl.Background;
+import com.android.launcher3.testcomponent.ListViewService;
+import com.android.launcher3.testcomponent.ListViewService.SimpleViewsFactory;
+import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.ui.TestViewHelpers;
+import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.lang.reflect.Field;
+import java.util.function.IntConsumer;
+
+/**
+ * Test to verify view inflation does not happen during swipe up.
+ * To verify view inflation, we setup a dummy ViewConfiguration and check if any call to that class
+ * does from a View.init method or not.
+ *
+ * Alternative approaches considered:
+ * Overriding LayoutInflater: This does not cover views initialized
+ * directly (ex: new LinearLayout)
+ * Using ExtendedMockito: Mocking static methods from platform classes (loaded in zygote) makes
+ * the main thread extremely slow and untestable
+ *
+ * Suppressed until b/141579810 is resolved
+ */
+@Suppress
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest {
+
+ private ContentResolver mResolver;
+ private SparseArray<ViewConfiguration> mConfigMap;
+ private InitTracker mInitTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // Workaround for b/142351228, when there are no activities, the system may not destroy the
+ // activity correctly for activities under instrumentation, which can leave two concurrent
+ // activities, which changes the order in which the activities are cleaned up (overlapping
+ // stop and start) leading to all sort of issues. To workaround this, ensure that the test
+ // is started only after starting another app.
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+
+ TaplTestsLauncher3.initialize(this);
+
+ mResolver = mTargetContext.getContentResolver();
+ LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+
+ // Get static configuration map
+ Field field = ViewConfiguration.class.getDeclaredField("sConfigurations");
+ field.setAccessible(true);
+ mConfigMap = (SparseArray<ViewConfiguration>) field.get(null);
+
+ mInitTracker = new InitTracker();
+ }
+
+ @Test
+ @NavigationModeSwitch(mode = ZERO_BUTTON)
+ public void testSwipeUpFromApp() throws Exception {
+ try {
+ // Go to overview once so that all views are initialized and cached
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ mLauncher.getBackground().switchToOverview().dismissAllTasks();
+
+ // Track view creations
+ mInitTracker.startTracking();
+
+ startTestActivity(2);
+ mLauncher.getBackground().switchToOverview();
+
+ assertEquals("Views inflated during swipe up", 0, mInitTracker.viewInitCount);
+ } finally {
+ mConfigMap.clear();
+ }
+ }
+
+ @Test
+ @NavigationModeSwitch(mode = ZERO_BUTTON)
+ public void testSwipeUpFromApp_widget_update() {
+ String dummyText = "Some random dummy text";
+
+ executeSwipeUpTestWithWidget(
+ widgetId -> { },
+ widgetId -> AppWidgetManager.getInstance(getContext())
+ .updateAppWidget(widgetId, createMainWidgetViews(dummyText)),
+ dummyText);
+ }
+
+ @Test
+ @NavigationModeSwitch(mode = ZERO_BUTTON)
+ public void testSwipeUp_with_list_widgets() {
+ SimpleViewsFactory viewFactory = new SimpleViewsFactory();
+ viewFactory.viewCount = 1;
+ Bundle args = new Bundle();
+ args.putBinder(EXTRA_VALUE, viewFactory.toBinder());
+ TestCommandReceiver.callCommand(SET_LIST_VIEW_SERVICE_BINDER, null, args);
+
+ try {
+ executeSwipeUpTestWithWidget(
+ widgetId -> {
+ // Initialize widget
+ RemoteViews views = createMainWidgetViews("List widget title");
+ views.setRemoteAdapter(android.R.id.list,
+ new Intent(getContext(), ListViewService.class));
+ AppWidgetManager.getInstance(getContext()).updateAppWidget(widgetId, views);
+ verifyWidget(viewFactory.getLabel(0));
+ },
+ widgetId -> {
+ // Update widget
+ viewFactory.viewCount = 2;
+ AppWidgetManager.getInstance(getContext())
+ .notifyAppWidgetViewDataChanged(widgetId, android.R.id.list);
+ },
+ viewFactory.getLabel(1)
+ );
+ } finally {
+ TestCommandReceiver.callCommand(SET_LIST_VIEW_SERVICE_BINDER, null, new Bundle());
+ }
+ }
+
+ private void executeSwipeUpTestWithWidget(IntConsumer widgetIdCreationCallback,
+ IntConsumer updateBeforeSwipeUp, String finalWidgetText) {
+ try {
+ // Clear all existing data
+ LauncherSettings.Settings.call(mResolver,
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+ LauncherSettings.Settings.call(mResolver,
+ LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
+ LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
+ LauncherAppWidgetInfo item = createWidgetInfo(info, true);
+
+ addItemToScreen(item);
+ assertTrue("Widget is not present",
+ mLauncher.pressHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
+ int widgetId = item.appWidgetId;
+
+ // Verify widget id
+ widgetIdCreationCallback.accept(widgetId);
+
+ // Go to overview once so that all views are initialized and cached
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ mLauncher.getBackground().switchToOverview().dismissAllTasks();
+
+ // Track view creations
+ mInitTracker.startTracking();
+
+ startTestActivity(2);
+ Background background = mLauncher.getBackground();
+
+ // Update widget
+ updateBeforeSwipeUp.accept(widgetId);
+
+ background.switchToOverview();
+ assertEquals("Views inflated during swipe up", 0, mInitTracker.viewInitCount);
+
+ // Widget is updated when going home
+ mInitTracker.disableLog();
+ mLauncher.pressHome();
+ verifyWidget(finalWidgetText);
+ assertNotEquals(1, mInitTracker.viewInitCount);
+ } finally {
+ mConfigMap.clear();
+ }
+ }
+
+ private void verifyWidget(String text) {
+ assertNotNull("Widget not updated",
+ UiDevice.getInstance(getInstrumentation())
+ .wait(Until.findObject(By.text(text)), DEFAULT_UI_TIMEOUT));
+ }
+
+ private RemoteViews createMainWidgetViews(String title) {
+ Context c = getContext();
+ int layoutId = c.getResources().getIdentifier(
+ "test_layout_widget_list", "layout", c.getPackageName());
+ RemoteViews views = new RemoteViews(c.getPackageName(), layoutId);
+ views.setTextViewText(android.R.id.text1, title);
+ return views;
+ }
+
+ private class InitTracker implements Answer {
+
+ public int viewInitCount = 0;
+
+ public boolean log = true;
+
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Exception ex = new Exception();
+
+ boolean found = false;
+ for (StackTraceElement ste : ex.getStackTrace()) {
+ if ("<init>".equals(ste.getMethodName())
+ && View.class.getName().equals(ste.getClassName())) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ viewInitCount++;
+ if (log) {
+ Log.d("InitTracker", "New view inflated", ex);
+ }
+
+ }
+ return invocation.callRealMethod();
+ }
+
+ public void disableLog() {
+ log = false;
+ }
+
+ public void startTracking() {
+ ViewConfiguration vc = ViewConfiguration.get(mTargetContext);
+ ViewConfiguration spyVC = spy(vc);
+ mConfigMap.put(mConfigMap.keyAt(mConfigMap.indexOfValue(vc)), spyVC);
+ doAnswer(this).when(spyVC).getScaledTouchSlop();
+ }
+ }
+}
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/layout/folder_application.xml b/res/layout/folder_application.xml
index c156e11..32a5419 100644
--- a/res/layout/folder_application.xml
+++ b/res/layout/folder_application.xml
@@ -20,4 +20,5 @@
style="@style/BaseIcon"
android:textColor="?attr/folderTextColor"
android:includeFontPadding="false"
+ android:hapticFeedbackEnabled="false"
launcher:iconDisplay="folder" />
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index d942e9e..0396df6 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"ስራ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"መተግበሪያ አልተጫነም።"</string>
<string name="activity_not_available" msgid="7456344436509528827">"መተግበሪያ አይገኝም"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 5528a19..7c1ce84 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"İş"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Tətbiq quraşdırılmayıb."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Tətbiq əlçatmazdır"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 775885c..09608b5 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"কাজ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"অ্যাপ্লিকেশান ইনস্টল করা নেই৷"</string>
<string name="activity_not_available" msgid="7456344436509528827">"অ্যাপ্লিকেশান অনুপলব্ধ"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 5650457..65ad91e 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Posao"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija nije instalirana."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 50f92da..4ef9ec3 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Feina"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'aplicació no s\'ha instal·lat."</string>
<string name="activity_not_available" msgid="7456344436509528827">"L\'aplicació no està disponible."</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 2363e61..d80e905 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Εργασία"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Η εφαρμογή δεν έχει εγκατασταθεί."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Η εφαρμογή δεν είναι διαθέσιμη"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 0c48b5d..7adc218 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 0c48b5d..7adc218 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 0c48b5d..7adc218 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 9ba46c1..10aebe7 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Lana"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikazioa instalatu gabe dago."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Ez dago erabilgarri aplikazioa"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 7b4bcb1..65db47e 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Android Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Application indisponible"</string>
@@ -136,7 +135,7 @@
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Retrouvez ici vos applications professionnelles"</string>
<string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Les applications professionnelles sont accompagnées d\'un badge et sont sécurisées par votre organisation. Vous pouvez les déplacer vers votre écran d\'accueil pour y accéder plus facilement."</string>
<string name="work_mode_on_label" msgid="4781128097185272916">"Géré par votre organisation"</string>
- <string name="work_mode_off_label" msgid="3194894777601421047">"Les notifications et les applications sont désactivées"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications et applications désactivées"</string>
<string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fermer"</string>
<string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fermé"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index e115a72..ca5ba3c 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Traballo"</string>
<string name="activity_not_found" msgid="8071924732094499514">"A aplicación non está instalada"</string>
<string name="activity_not_available" msgid="7456344436509528827">"A aplicación non está dispoñible"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 7def3ac..3228dea 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"કાર્યાલય"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ઍપ્લિકેશન ઇન્સ્ટોલ થઈ નથી."</string>
<string name="activity_not_available" msgid="7456344436509528827">"ઍપ્લિકેશન ઉપલબ્ધ નથી"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 3b0432e..8fa02c0 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"कार्यस्थल"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ऐप्लिकेशन इंस्टॉल नहीं है."</string>
<string name="activity_not_available" msgid="7456344436509528827">"ऐप्लिकेशन उपलब्ध नहीं है"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 1caa27f..b738bc0 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Posao"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija nije instalirana."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index fbb657e..93404e6 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Աշխատանքային"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Ծրագիրը տեղադրված չէ:"</string>
<string name="activity_not_available" msgid="7456344436509528827">"Հավելվածը հասանելի չէ"</string>
@@ -87,7 +86,7 @@
<string name="allow_rotation_desc" msgid="8662546029078692509">"Հեռախոսը պտտելու դեպքում"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Ծանուցումների կետիկներ"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Միացված է"</string>
- <string name="notification_dots_desc_off" msgid="1760796511504341095">"Անջատած է"</string>
+ <string name="notification_dots_desc_off" msgid="1760796511504341095">"Անջատված է"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Անհրաժեշտ է ծանուցման թույլտվություն"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Ծանուցումների կետիկները ցուցադրելու համար միացրեք ծանուցումները <xliff:g id="NAME">%1$s</xliff:g>-ի համար"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Փոխել կարգավորումները"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 211f735..92c7acc 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Kantor"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikasi tidak dipasang."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikasi tidak tersedia"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index f4a328d..9738042 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"ការងារ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"មិនបានដំឡើងកម្មវិធី។"</string>
<string name="activity_not_available" msgid="7456344436509528827">"មិនមានកម្មវិធី"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 14c8300..67ea6a8 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"ಕೆಲಸ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"</string>
<string name="activity_not_available" msgid="7456344436509528827">"ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 046a662..d52446c 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Жумуш"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Колдонмо орнотулган эмес."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Колдонмо жеткиликтүү эмес"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 9550a5f..ab14e89 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Стартер3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Работа"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Апликацијата не е инсталирана."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Апликацијата не е достапна"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index fd1f7d9..78856f6 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"ဖွင့်တင်စက်၃"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"အလုပ်"</string>
<string name="activity_not_found" msgid="8071924732094499514">"အက်ပ်မထည့်သွင်းထားပါ"</string>
<string name="activity_not_available" msgid="7456344436509528827">"အက်ပ်လက်လှမ်း မမှီပါ"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 071b71e..e0d2c74 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabalho"</string>
<string name="activity_not_found" msgid="8071924732094499514">"A aplicação não está instalada."</string>
<string name="activity_not_available" msgid="7456344436509528827">"A aplicação não está disponível"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index e88ff18..bb4834f 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Tela de início 3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabalho"</string>
<string name="activity_not_found" msgid="8071924732094499514">"O app não está instalado."</string>
<string name="activity_not_available" msgid="7456344436509528827">"O app não está disponível"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 1251d7e..f8a61f1 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplicația nu este instalată."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplicația nu este disponibilă"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 340a128..5bbf7c3 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Pracovné"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikácia nie je nainštalovaná."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikácia nie je k dispozícii"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 48ae26e..7f2567c 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Nisësi3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Puna"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacioni nuk është i instaluar."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacioni nuk mundësohet"</string>
@@ -41,8 +40,8 @@
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Kërko për më shumë aplikacione"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacioni"</string>
<string name="notifications_header" msgid="1404149926117359025">"Njoftimet"</string>
- <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Prek dhe mbaj prekur për të zgjedhur një shkurtore."</string>
- <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara."</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Prek dhe mbaj të shtypur për të zgjedhur një shkurtore."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Prek dy herë dhe mbaj të shtypur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara."</string>
<string name="out_of_space" msgid="4691004494942118364">"Nuk ka më hapësirë në këtë ekran bazë."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nuk ka më hapësirë në tabakanë \"Të preferuarat\""</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista e aplikacioneve"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 6f33c4f..445e382 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Kazini"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Programu haijasakinishwa."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Programu haipatikani"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index c926bc1..6e933a1 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"பணியிடம்"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ஆப்ஸ் நிறுவப்படவில்லை."</string>
<string name="activity_not_available" msgid="7456344436509528827">"ஆப்ஸ் இல்லை"</string>
@@ -36,7 +35,7 @@
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"நீங்களே சேர்க்க, தொட்டுப் பிடித்திருக்கவும்"</string>
<string name="place_automatically" msgid="8064208734425456485">"தானாகவே சேர்"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"பயன்பாடுகளில் தேடுக"</string>
- <string name="all_apps_loading_message" msgid="5813968043155271636">"பயன்பாடுகளை ஏற்றுகிறது…"</string>
+ <string name="all_apps_loading_message" msgid="5813968043155271636">"ஆப்ஸை ஏற்றுகிறது…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"கூடுதல் பயன்பாடுகளைத் தேடு"</string>
<string name="label_application" msgid="8531721983832654978">"ஆப்ஸ்"</string>
@@ -45,7 +44,7 @@
<string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ஷார்ட்கட்டைச் சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் (அ) தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
<string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
- <string name="all_apps_button_label" msgid="8130441508702294465">"பயன்பாடுகளின் பட்டியல்"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"ஆப்ஸின் பட்டியல்"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"தனிப்பட்ட ஆப்ஸ் பட்டியல்"</string>
<string name="all_apps_button_work_label" msgid="7270707118948892488">"பணி ஆப்ஸ் பட்டியல்"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"முகப்பு"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index c8f70fe..1f8d3b8 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"కార్యాలయం"</string>
<string name="activity_not_found" msgid="8071924732094499514">"యాప్ ఇన్స్టాల్ చేయబడలేదు."</string>
<string name="activity_not_available" msgid="7456344436509528827">"యాప్ అందుబాటులో లేదు"</string>
@@ -53,7 +52,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"అన్ఇన్స్టాల్ చేయి"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"యాప్ సమాచారం"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ఇన్స్టాల్ చేయండి"</string>
- <string name="permlab_install_shortcut" msgid="5632423390354674437">"సత్వరమార్గాలను ఇన్స్టాల్ చేయడం"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"షార్ట్కట్లను ఇన్స్టాల్ చేయడం"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"హోమ్ సెట్టింగ్లు మరియు సత్వరమార్గాలను చదవడం"</string>
<string name="permdesc_read_settings" msgid="5833423719057558387">"హోమ్లో సెట్టింగ్లు మరియు సత్వరమార్గాలను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 03c02ad..5ab9b86 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"งาน"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ไม่ได้ติดตั้งแอป"</string>
<string name="activity_not_available" msgid="7456344436509528827">"แอปไม่พร้อมใช้งาน"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 83178fe..b937764 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Isiqalisi3"</string>
- <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Umsebenzi"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Uhlelo lokusebenza alufakiwe."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Uhlelo lokusebenza alutholakali"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 69b8c8a..7be584e 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -115,7 +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>
@@ -131,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">
@@ -152,21 +159,6 @@
<attr name="canThumbDetach" format="boolean" />
</declare-styleable>
- <declare-styleable name="CustomAppWidgetProviderInfo">
- <attr name="providerId" format="integer" />
-
- <attr name="android:label" />
- <attr name="android:initialLayout" />
- <attr name="android:icon" />
- <attr name="android:previewImage" />
- <attr name="android:resizeMode" />
-
- <attr name="numRows" />
- <attr name="numColumns" />
- <attr name="numMinRows" format="integer" />
- <attr name="numMinColumns" format="integer" />
- </declare-styleable>
-
<declare-styleable name="PreviewFragment">
<attr name="android:name" />
<attr name="android:id" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 13e096c..9d9c2e8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -23,8 +23,6 @@
<!-- Application name -->
<string name="app_name">Launcher3</string>
- <!-- Default folder name -->
- <string name="folder_name"></string>
<!-- Work folder name -->
<string name="work_folder_name">Work</string>
<!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
@@ -105,6 +103,9 @@
<!-- 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>
+
<!-- Permissions: -->
<skip />
<!-- Permission short label -->
diff --git a/res/xml/custom_widgets.xml b/res/xml/custom_widgets.xml
deleted file mode 100644
index 4b54386..0000000
--- a/res/xml/custom_widgets.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<widgets>
- <!-- Sample widget definition
- <widget
- android:label="My custom widget"
- android:initialLayout="@layout/sample_widget_layout"
- android:icon="@drawable/ic_launcher_home"
- android:resizeMode="horizontal|vertical"
- launcher:numRows="2"
- launcher:numColumns="3"
- launcher:numMinRows="1"
- launcher:numMinColumns="2"
- launcher:providerId="1" />
- -->
-</widgets>
\ No newline at end of file
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
index a3d1216..4bb9a53 100644
--- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
@@ -1,12 +1,12 @@
package com.android.launcher3.config;
-import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
import com.android.launcher3.uioverrides.TogglableFlag;
+
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
-import org.robolectric.RuntimeEnvironment;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
@@ -14,6 +14,10 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
/**
* Test rule that makes overriding flags in Robolectric tests easier. This rule clears all flags
@@ -51,68 +55,48 @@
boolean value();
}
- private boolean ruleInProgress;
-
@Override
public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- FeatureFlags.initialize(RuntimeEnvironment.application.getApplicationContext());
- ruleInProgress = true;
- try {
- clearOverrides();
- applyAnnotationOverrides(description);
- base.evaluate();
- } finally {
- ruleInProgress = false;
- clearOverrides();
+ return new MyStatement(base, description);
+ }
+
+ private class MyStatement extends Statement {
+
+ private final Statement mBase;
+ private final Description mDescription;
+
+
+ MyStatement(Statement base, Description description) {
+ mBase = base;
+ mDescription = description;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ Map<String, BaseTogglableFlag> allFlags = FeatureFlags.getTogglableFlags().stream()
+ .collect(Collectors.toMap(TogglableFlag::getKey, Function.identity()));
+
+ HashMap<BaseTogglableFlag, Boolean> changedValues = new HashMap<>();
+ FlagOverride[] overrides = new FlagOverride[0];
+ try {
+ for (Annotation annotation : mDescription.getAnnotations()) {
+ if (annotation.annotationType() == FlagOverride.class) {
+ overrides = new FlagOverride[] { (FlagOverride) annotation };
+ } else if (annotation.annotationType() == FlagOverrides.class) {
+ // Note: this branch is hit if the annotation is repeated
+ overrides = ((FlagOverrides) annotation).value();
+ }
}
- }
- };
- }
-
- private void override(BaseTogglableFlag flag, boolean newValue) {
- if (!ruleInProgress) {
- throw new IllegalStateException(
- "Rule isn't in progress. Did you remember to mark it with @Rule?");
- }
- flag.setForTests(newValue);
- }
-
- private void applyAnnotationOverrides(Description description) {
- for (Annotation annotation : description.getAnnotations()) {
- if (annotation.annotationType() == FlagOverride.class) {
- applyAnnotation((FlagOverride) annotation);
- } else if (annotation.annotationType() == FlagOverrides.class) {
- // Note: this branch is hit if the annotation is repeated
- for (FlagOverride flagOverride : ((FlagOverrides) annotation).value()) {
- applyAnnotation(flagOverride);
+ for (FlagOverride override : overrides) {
+ BaseTogglableFlag flag = allFlags.get(override.key());
+ changedValues.put(flag, flag.get());
+ flag.setForTests(override.value());
}
+ mBase.evaluate();
+ } finally {
+ // Clear the values
+ changedValues.forEach(BaseTogglableFlag::setForTests);
}
}
}
-
- private void applyAnnotation(FlagOverride flagOverride) {
- boolean found = false;
- for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
- if (flag.getKey().equals(flagOverride.key())) {
- override(flag, flagOverride.value());
- found = true;
- break;
- }
- }
- if (!found) {
- throw new IllegalStateException("Flag " + flagOverride.key() + " not found");
- }
- }
-
- /**
- * Resets all flags to their default values.
- */
- private void clearOverrides() {
- for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
- flag.setForTests(flag.getDefaultValue());
- }
- }
}
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
index 1351348..31a037b 100644
--- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
@@ -1,5 +1,8 @@
package com.android.launcher3.config;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import com.android.launcher3.config.FlagOverrideRule.FlagOverride;
import org.junit.Rule;
@@ -7,9 +10,6 @@
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
/**
* Sample Robolectric test that demonstrates flag-overriding.
*/
@@ -21,16 +21,21 @@
@Rule
public final FlagOverrideRule flags = new FlagOverrideRule();
- @FlagOverride(key = "EXAMPLE_FLAG", value = true)
+ /**
+ * Test if flag can be overriden to true via annoation.
+ */
+ @FlagOverride(key = "FAKE_LANDSCAPE_UI", value = true)
@Test
public void withFlagOn() {
- assertTrue(FeatureFlags.EXAMPLE_FLAG.get());
+ assertTrue(FeatureFlags.FAKE_LANDSCAPE_UI.get());
}
-
- @FlagOverride(key = "EXAMPLE_FLAG", value = false)
+ /**
+ * Test if flag can be overriden to false via annoation.
+ */
+ @FlagOverride(key = "FAKE_LANDSCAPE_UI", value = false)
@Test
public void withFlagOff() {
- assertFalse(FeatureFlags.EXAMPLE_FLAG.get());
+ assertFalse(FeatureFlags.FAKE_LANDSCAPE_UI.get());
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
index 096db57..410a077 100644
--- a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -1,20 +1,22 @@
package com.android.launcher3.logging;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.util.Scheduler;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Calendar;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
/**
* Tests for {@link FileLog}
*/
@@ -22,9 +24,10 @@
public class FileLogTest {
private File mTempDir;
+ private boolean mTestActive;
@Before
- public void setUp() throws Exception {
+ public void setUp() {
int count = 0;
do {
mTempDir = new File(RuntimeEnvironment.application.getCacheDir(),
@@ -32,14 +35,24 @@
} while (!mTempDir.mkdir());
FileLog.setDir(mTempDir);
+
+ mTestActive = true;
+ Scheduler scheduler = Shadows.shadowOf(FileLog.getHandler().getLooper()).getScheduler();
+ new Thread(() -> {
+ while (mTestActive) {
+ scheduler.advanceToLastPostedRunnable();
+ }
+ }).start();
}
@After
- public void tearDown() throws Exception {
+ public void tearDown() {
// Clear existing logs
new File(mTempDir, "log-0").delete();
new File(mTempDir, "log-1").delete();
mTempDir.delete();
+
+ mTestActive = false;
}
@Test
@@ -49,12 +62,12 @@
}
FileLog.print("Testing", "hoolalala");
StringWriter writer = new StringWriter();
- FileLog.flushAll(new PrintWriter(writer));
+ assertTrue(FileLog.flushAll(new PrintWriter(writer)));
assertTrue(writer.toString().contains("hoolalala"));
FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
writer = new StringWriter();
- FileLog.flushAll(new PrintWriter(writer));
+ assertTrue(FileLog.flushAll(new PrintWriter(writer)));
assertTrue(writer.toString().contains("abracadabra"));
// Exception is also printed
assertTrue(writer.toString().contains("cat! cat!"));
@@ -70,7 +83,7 @@
}
FileLog.print("Testing", "hoolalala");
StringWriter writer = new StringWriter();
- FileLog.flushAll(new PrintWriter(writer));
+ assertTrue(FileLog.flushAll(new PrintWriter(writer)));
assertTrue(writer.toString().contains("hoolalala"));
Calendar threeDaysAgo = Calendar.getInstance();
@@ -80,7 +93,7 @@
FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
writer = new StringWriter();
- FileLog.flushAll(new PrintWriter(writer));
+ assertTrue(FileLog.flushAll(new PrintWriter(writer)));
assertTrue(writer.toString().contains("abracadabra"));
// Exception is also printed
assertTrue(writer.toString().contains("cat! cat!"));
diff --git a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index ab39274..32eb2ec 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -15,18 +15,20 @@
import android.os.Process;
import android.os.UserHandle;
-import com.android.launcher3.AllAppsList;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.AppFilter;
import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.TestLauncherProvider;
@@ -45,8 +47,6 @@
import java.util.concurrent.Executor;
import java.util.function.Supplier;
-import androidx.annotation.NonNull;
-
/**
* Base class for writing tests for Model update tasks.
*/
@@ -80,6 +80,7 @@
model = mock(LauncherModel.class);
modelWriter = mock(ModelWriter.class);
+ LauncherAppState.INSTANCE.initializeForTesting(appState);
when(appState.getModel()).thenReturn(model);
when(model.getWriter(anyBoolean(), anyBoolean())).thenReturn(modelWriter);
when(model.getCallback()).thenReturn(callbacks);
@@ -217,5 +218,10 @@
public Bitmap newIcon() {
return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
}
+
+ @Override
+ public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
+ return BitmapInfo.fromBitmap(newIcon());
+ }
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 42848f4..81b9043 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -11,7 +11,6 @@
import com.android.launcher3.WorkspaceItemInfo;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -41,7 +40,6 @@
}
@Test
- @Ignore("This test fails with resource errors") // b/131115553
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) {
@@ -66,7 +64,6 @@
}
@Test
- @Ignore("This test fails with resource errors") // b/131115553
public void testSessionUpdate_ignores_normal_apps() throws Exception {
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
@@ -75,7 +72,6 @@
}
@Test
- @Ignore("This test fails with resource errors") // b/131115553
public void testSessionUpdate_updates_pending_apps() throws Exception {
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
diff --git a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
index a46617e..b7340cf 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -130,7 +130,7 @@
}
helper.close();
- helper = new DatabaseHelper(mContext, null, DB_FILE) {
+ helper = new DatabaseHelper(mContext, DB_FILE) {
@Override
public void onOpen(SQLiteDatabase db) { }
};
@@ -161,7 +161,7 @@
DbDowngradeHelper.updateSchemaFile(mSchemaFile, LauncherProvider.SCHEMA_VERSION, mContext);
- DatabaseHelper dbHelper = new DatabaseHelper(mContext, null, DB_FILE) {
+ DatabaseHelper dbHelper = new DatabaseHelper(mContext, DB_FILE) {
@Override
public void onOpen(SQLiteDatabase db) { }
};
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index 42a4f5c..a1a4561 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -5,8 +5,7 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import com.android.launcher3.pm.PackageInstallInfo;
import org.junit.Before;
import org.junit.Test;
@@ -28,7 +27,7 @@
}
private PackageInstallStateChangedTask newTask(String pkg, int progress) {
- int state = PackageInstallerCompat.STATUS_INSTALLING;
+ int state = PackageInstallInfo.STATUS_INSTALLING;
PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress,
android.os.Process.myUserHandle());
return new PackageInstallStateChangedTask(installInfo);
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
new file mode 100644
index 0000000..c08e198
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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 com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Robolectric unit tests for {@link IntArray}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class IntArrayTest {
+
+ @Test
+ public void concatAndParseString() {
+ int[] array = new int[] {0, 2, 3, 9};
+ String concat = IntArray.wrap(array).toConcatString();
+
+ int[] parsed = IntArray.fromConcatString(concat).toArray();
+ assertThat(array).isEqualTo(parsed);
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
index f846de5..8513353 100644
--- a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
+++ b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
@@ -21,7 +21,6 @@
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
index 31e303e..a9c1a7c 100644
--- a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -2,7 +2,6 @@
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
import com.android.launcher3.LauncherProvider;
@@ -28,12 +27,9 @@
return mOpenHelper.getWritableDatabase();
}
- @Override
- protected void notifyListeners() { }
-
private static class MyDatabaseHelper extends DatabaseHelper {
public MyDatabaseHelper(Context context) {
- super(context, null, null);
+ super(context, null);
initIds();
}
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 65f9d6b..af2cdc3 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
@@ -30,6 +29,7 @@
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.LinearLayout;
import androidx.annotation.IntDef;
@@ -86,7 +86,7 @@
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
- | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
+ | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
// Usually we show the back button when a floating view is open. Instead, hide for these types.
public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
@@ -171,7 +171,7 @@
targetInfo.first, TYPE_WINDOW_STATE_CHANGED, targetInfo.second);
if (mIsOpen) {
- sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
+ performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
}
ActivityContext.lookupContext(getContext()).getDragLayer()
.sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index d884049..c8e7619 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -29,11 +29,19 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
+import java.util.Comparator;
+
/**
* Represents an app in AllAppsView.
*/
public class AppInfo extends ItemInfoWithIcon {
+ public static AppInfo[] EMPTY_ARRAY = new AppInfo[0];
+ public static Comparator<AppInfo> COMPONENT_KEY_COMPARATOR = (a, b) -> {
+ int uc = a.user.hashCode() - b.user.hashCode();
+ return uc != 0 ? uc : a.componentName.compareTo(b.componentName);
+ };
+
/**
* The intent used to start the application.
*/
@@ -41,6 +49,9 @@
public ComponentName componentName;
+ // Section name used for indexing.
+ public String sectionName = "";
+
public AppInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
}
@@ -74,6 +85,8 @@
componentName = info.componentName;
title = Utilities.trim(info.title);
intent = new Intent(info.intent);
+ user = info.user;
+ runtimeStatusFlags = info.runtimeStatusFlags;
}
@Override
@@ -116,4 +129,9 @@
info.runtimeStatusFlags |= FLAG_ADAPTIVE_ICON;
}
}
+
+ @Override
+ public AppInfo clone() {
+ return new AppInfo(this);
+ }
}
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index e3ef5d6..5f1be94 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -1,5 +1,7 @@
package com.android.launcher3;
+import static android.os.Process.myUserHandle;
+
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -12,15 +14,13 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ContentWriter;
import androidx.annotation.WorkerThread;
-import static android.os.Process.myUserHandle;
-
public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
private static final String TAG = "AWRestoredReceiver";
@@ -50,7 +50,7 @@
@WorkerThread
public static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
AppWidgetHost appWidgetHost = new LauncherAppWidgetHost(context);
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
Log.e(TAG, "Skipping widget ID remap as widgets not supported");
appWidgetHost.deleteHost();
return;
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 9724869..ac43967 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -38,11 +38,15 @@
import android.util.Patterns;
import android.util.Xml;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherProvider.SqlArguments;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
@@ -72,7 +76,7 @@
static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback) {
- Pair<String, Resources> customizationApkInfo = Utilities.findSystemApk(
+ Pair<String, Resources> customizationApkInfo = PackageManagerHelper.findSystemApk(
ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager());
if (customizationApkInfo == null) {
return null;
@@ -83,7 +87,7 @@
// Try with grid size and hotseat count
String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
- grid.numColumns, grid.numRows, grid.numHotseatIcons);
+ grid.numColumns, grid.numRows, grid.numHotseatIcons);
int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
// Try with only grid size
@@ -91,7 +95,7 @@
Log.d(TAG, "Formatted layout: " + layoutName
+ " not found. Trying layout without hosteat");
layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES,
- grid.numColumns, grid.numRows);
+ grid.numColumns, grid.numRows);
layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
}
@@ -116,6 +120,7 @@
private static final String TAG_AUTO_INSTALL = "autoinstall";
private static final String TAG_FOLDER = "folder";
private static final String TAG_APPWIDGET = "appwidget";
+ protected static final String TAG_SEARCH_WIDGET = "searchwidget";
private static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_EXTRA = "extra";
@@ -147,8 +152,10 @@
private static final String HOTSEAT_CONTAINER_NAME =
Favorites.containerToString(Favorites.CONTAINER_HOTSEAT);
- @Thunk final Context mContext;
- @Thunk final AppWidgetHost mAppWidgetHost;
+ @Thunk
+ final Context mContext;
+ @Thunk
+ final AppWidgetHost mAppWidgetHost;
protected final LayoutParserCallback mCallback;
protected final PackageManager mPackageManager;
@@ -160,7 +167,8 @@
private final int mColumnCount;
private final int[] mTemp = new int[2];
- @Thunk final ContentValues mValues;
+ @Thunk
+ final ContentValues mValues;
protected final String mRootTag;
protected SQLiteDatabase mDb;
@@ -244,7 +252,7 @@
*/
protected int parseAndAddNode(
XmlPullParser parser, ArrayMap<String, TagParser> tagParserMap, IntArray screenIds)
- throws XmlPullParserException, IOException {
+ throws XmlPullParserException, IOException {
if (TAG_INCLUDE.equals(parser.getName())) {
final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
@@ -315,6 +323,7 @@
parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
parsers.put(TAG_FOLDER, new FolderParser());
parsers.put(TAG_APPWIDGET, new PendingWidgetParser());
+ parsers.put(TAG_SEARCH_WIDGET, new SearchWidgetParser());
parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes));
return parsers;
}
@@ -347,15 +356,15 @@
info = mPackageManager.getActivityInfo(cn, 0);
} catch (PackageManager.NameNotFoundException nnfe) {
String[] packages = mPackageManager.currentToCanonicalPackageNames(
- new String[] { packageName });
+ new String[]{packageName});
cn = new ComponentName(packages[0], className);
info = mPackageManager.getActivityInfo(cn, 0);
}
final Intent intent = new Intent(Intent.ACTION_MAIN, null)
- .addCategory(Intent.CATEGORY_LAUNCHER)
- .setComponent(cn)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setComponent(cn)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
return addShortcut(info.loadLabel(mPackageManager).toString(),
intent, Favorites.ITEM_TYPE_APPLICATION);
@@ -393,10 +402,10 @@
mValues.put(Favorites.RESTORED, WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON);
final Intent intent = new Intent(Intent.ACTION_MAIN, null)
- .addCategory(Intent.CATEGORY_LAUNCHER)
- .setComponent(new ComponentName(packageName, className))
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setComponent(new ComponentName(packageName, className))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
return addShortcut(mContext.getString(R.string.package_state_unknown), intent,
Favorites.ITEM_TYPE_APPLICATION);
}
@@ -444,7 +453,7 @@
mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
return addShortcut(mSourceRes.getString(titleResId),
intent, Favorites.ITEM_TYPE_SHORTCUT);
}
@@ -469,12 +478,22 @@
*/
protected class PendingWidgetParser implements TagParser {
- @Override
- public int parseAndAdd(XmlPullParser parser)
- throws XmlPullParserException, IOException {
+ @Nullable
+ public ComponentName getComponentName(XmlPullParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
+ return null;
+ }
+ return new ComponentName(packageName, className);
+ }
+
+
+ @Override
+ public int parseAndAdd(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ ComponentName cn = getComponentName(parser);
+ if (cn == null) {
if (LOGD) Log.d(TAG, "Skipping invalid <appwidget> with no component");
return -1;
}
@@ -505,16 +524,15 @@
throw new RuntimeException("Widgets can contain only extras");
}
}
-
- return verifyAndInsert(new ComponentName(packageName, className), extras);
+ return verifyAndInsert(cn, extras);
}
protected int verifyAndInsert(ComponentName cn, Bundle extras) {
mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
mValues.put(Favorites.RESTORED,
- LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
- LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
- LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
+ LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
+ | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY
+ | LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
mValues.put(Favorites._ID, mCallback.generateNewItemId());
if (!extras.isEmpty()) {
mValues.put(Favorites.INTENT, new Intent().putExtras(extras).toUri(0));
@@ -529,6 +547,23 @@
}
}
+ protected class SearchWidgetParser extends PendingWidgetParser {
+ @Override
+ @Nullable
+ public ComponentName getComponentName(XmlPullParser parser) {
+ return QsbContainerView.getSearchComponentName(mContext);
+ }
+
+ @Override
+ protected int verifyAndInsert(ComponentName cn, Bundle extras) {
+ mValues.put(Favorites.OPTIONS, LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET);
+ int flags = mValues.getAsInteger(Favorites.RESTORED)
+ | WorkspaceItemInfo.FLAG_RESTORE_STARTED;
+ mValues.put(Favorites.RESTORED, flags);
+ return super.verifyAndInsert(cn, extras);
+ }
+ }
+
protected class FolderParser implements TagParser {
private final ArrayMap<String, TagParser> mFolderElements;
@@ -548,7 +583,7 @@
if (titleResId != 0) {
title = mSourceRes.getString(titleResId);
} else {
- title = mContext.getResources().getString(R.string.folder_name);
+ title = "";
}
mValues.put(Favorites.TITLE, title);
@@ -680,7 +715,8 @@
int insertAndCheck(SQLiteDatabase db, ContentValues values);
}
- @Thunk static void copyInteger(ContentValues from, ContentValues to, String key) {
+ @Thunk
+ static void copyInteger(ContentValues from, ContentValues to, String key) {
to.put(key, from.getAsInteger(key));
}
}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 6455056..b28077f 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -27,6 +27,8 @@
import android.content.res.Configuration;
import android.view.ContextThemeWrapper;
+import androidx.annotation.IntDef;
+
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogUtils;
@@ -44,8 +46,6 @@
import java.lang.annotation.Retention;
import java.util.ArrayList;
-import androidx.annotation.IntDef;
-
public abstract class BaseActivity extends Activity
implements UserEventDelegate, LogStateProvider, ActivityContext {
@@ -265,12 +265,13 @@
}
}
- protected void dumpMisc(PrintWriter writer) {
- writer.println(" deviceProfile isTransposed=" + getDeviceProfile().isVerticalBarLayout());
- writer.println(" orientation=" + getResources().getConfiguration().orientation);
- writer.println(" mSystemUiController: " + mSystemUiController);
- writer.println(" mActivityFlags: " + mActivityFlags);
- writer.println(" mForceInvisible: " + mForceInvisible);
+ protected void dumpMisc(String prefix, PrintWriter writer) {
+ writer.println(prefix + "deviceProfile isTransposed="
+ + getDeviceProfile().isVerticalBarLayout());
+ writer.println(prefix + "orientation=" + getResources().getConfiguration().orientation);
+ writer.println(prefix + "mSystemUiController: " + mSystemUiController);
+ writer.println(prefix + "mActivityFlags: " + mActivityFlags);
+ writer.println(prefix + "mForceInvisible: " + mForceInvisible);
}
public static <T extends BaseActivity> T fromContext(Context context) {
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index f0b3afd..d24de8e 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -19,6 +19,7 @@
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Intent;
+import android.content.pm.LauncherApps;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
@@ -30,16 +31,16 @@
import android.view.View;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Themes;
-
-import androidx.annotation.Nullable;
+import com.android.launcher3.util.TraceHelper;
/**
* Extension of BaseActivity allowing support for drag-n-drop
@@ -65,7 +66,10 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mIsSafeModeEnabled = getPackageManager().isSafeMode();
+
+
+ mIsSafeModeEnabled = TraceHelper.whitelistIpcs("isSafeMode",
+ () -> getPackageManager().isSafeMode());
mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged);
// Update theme
@@ -120,6 +124,10 @@
public abstract View getRootView();
+ public void returnToHomescreen() {
+ // no-op
+ }
+
public Rect getViewBounds(View v) {
int[] pos = new int[2];
v.getLocationOnScreen(pos);
@@ -135,7 +143,7 @@
public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
@Nullable String sourceContainer) {
- if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
+ if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
}
@@ -162,7 +170,7 @@
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
Process.myUserHandle(), sourceContainer);
} else {
- LauncherAppsCompat.getInstance(this).startActivityForProfile(
+ getSystemService(LauncherApps.class).startMainActivity(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
sourceContainer);
@@ -237,14 +245,14 @@
protected void onDeviceProfileInitiated() {
if (mDeviceProfile.isVerticalBarLayout()) {
mRotationListener.enable();
- mDeviceProfile.updateIsSeascape(getWindowManager());
+ mDeviceProfile.updateIsSeascape(this);
} else {
mRotationListener.disable();
}
}
private void onDeviceRotationChanged() {
- if (mDeviceProfile.updateIsSeascape(getWindowManager())) {
+ if (mDeviceProfile.updateIsSeascape(this)) {
reapplyUi();
}
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index b113249..7adb6a4 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -104,6 +104,8 @@
private Drawable mIcon;
private final boolean mCenterVertically;
+ private final int mDisplay;
+
private final CheckLongPressHelper mLongPressHelper;
private final StylusEventHelper mStylusEventHelper;
private final float mSlop;
@@ -133,6 +135,9 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDisableRelayout = false;
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private final boolean mIgnorePaddingTouch;
+
private IconLoadRequest mIconLoadRequest;
public BubbleTextView(Context context) {
@@ -152,26 +157,32 @@
R.styleable.BubbleTextView, defStyle, 0);
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
- int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
+ mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
final int defaultIconSize;
- if (display == DISPLAY_WORKSPACE) {
+ if (mDisplay == DISPLAY_WORKSPACE) {
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
defaultIconSize = grid.iconSizePx;
- } else if (display == DISPLAY_ALL_APPS) {
+ mIgnorePaddingTouch = true;
+ } else if (mDisplay == DISPLAY_ALL_APPS) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
defaultIconSize = grid.allAppsIconSizePx;
- } else if (display == DISPLAY_FOLDER) {
+ mIgnorePaddingTouch = true;
+ } else if (mDisplay == DISPLAY_FOLDER) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
defaultIconSize = grid.folderChildIconSizePx;
+ mIgnorePaddingTouch = true;
} else {
+ // widget_selection or shortcut_popup
defaultIconSize = mActivity.getDeviceProfile().iconSizePx;
+ mIgnorePaddingTouch = false;
}
+
mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,
@@ -319,6 +330,15 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
+ // ignore events if they happen in padding area
+ if (event.getAction() == MotionEvent.ACTION_DOWN && mIgnorePaddingTouch
+ && (event.getY() < getPaddingTop()
+ || event.getX() < getPaddingLeft()
+ || event.getY() > getHeight() - getPaddingBottom()
+ || event.getX() > getWidth() - getPaddingRight())) {
+ return false;
+ }
+
// Call the superclass onTouchEvent first, because sometimes it changes the state to
// isPressed() on an ACTION_UP
boolean result = super.onTouchEvent(event);
@@ -564,7 +584,11 @@
mDotInfo = mActivity.getDotInfoForItem(itemInfo);
boolean isDotted = mDotInfo != null;
float newDotScale = isDotted ? 1f : 0;
- mDotRenderer = mActivity.getDeviceProfile().mDotRenderer;
+ if (mDisplay == DISPLAY_ALL_APPS) {
+ mDotRenderer = mActivity.getDeviceProfile().mDotRendererAllApps;
+ } else {
+ mDotRenderer = mActivity.getDeviceProfile().mDotRendererWorkSpace;
+ }
if (wasDotted || isDotted) {
// Animate when a dot is first added or when it is removed.
if (animate && (wasDotted ^ isDotted) && isShown()) {
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 09fb244..976ccd5 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2702,6 +2702,14 @@
}
}
+ /**
+ * Sets the position to the provided point
+ */
+ public void setXY(Point point) {
+ cellX = point.x;
+ cellY = point.y;
+ }
+
public String toString() {
return "(" + this.cellX + ", " + this.cellY + ")";
}
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 75297f6..af85594 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -14,13 +14,16 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.util.Thunk;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
/**
* Implements the layout parser with rules for internal layouts and partner layouts.
@@ -55,7 +58,8 @@
return getFolderElementsMap(mSourceRes);
}
- @Thunk ArrayMap<String, TagParser> getFolderElementsMap(Resources res) {
+ @Thunk
+ ArrayMap<String, TagParser> getFolderElementsMap(Resources res) {
ArrayMap<String, TagParser> parsers = new ArrayMap<>();
parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
parsers.put(TAG_SHORTCUT, new UriShortcutParser(res));
@@ -67,6 +71,7 @@
ArrayMap<String, TagParser> parsers = new ArrayMap<>();
parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
parsers.put(TAG_APPWIDGET, new AppWidgetParser());
+ parsers.put(TAG_SEARCH_WIDGET, new SearchWidgetParser());
parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
parsers.put(TAG_RESOLVE, new ResolveParser());
parsers.put(TAG_FOLDER, new MyFolderParser());
@@ -229,7 +234,8 @@
/**
* A parser which adds a folder whose contents come from partner apk.
*/
- @Thunk class PartnerFolderParser implements TagParser {
+ @Thunk
+ class PartnerFolderParser implements TagParser {
@Override
public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
@@ -255,7 +261,8 @@
/**
* An extension of FolderParser which allows adding items from a different xml.
*/
- @Thunk class MyFolderParser extends FolderParser {
+ @Thunk
+ class MyFolderParser extends FolderParser {
@Override
public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
@@ -281,7 +288,7 @@
mPackageManager.getReceiverInfo(cn, 0);
} catch (Exception e) {
String[] packages = mPackageManager.currentToCanonicalPackageNames(
- new String[] { cn.getPackageName() });
+ new String[]{cn.getPackageName()});
cn = new ComponentName(packages[0], cn.getClassName());
try {
mPackageManager.getReceiverInfo(cn, 0);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 883e8c6..736142f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -24,12 +24,12 @@
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Surface;
-import android.view.WindowManager;
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
+import com.android.launcher3.util.DefaultDisplay;
public class DeviceProfile {
@@ -111,6 +111,7 @@
// All apps
public int allAppsCellHeightPx;
+ public int allAppsCellWidthPx;
public int allAppsIconSizePx;
public int allAppsIconDrawablePaddingPx;
public float allAppsIconTextSizePx;
@@ -129,7 +130,8 @@
private boolean mIsSeascape;
// Notification dots
- public DotRenderer mDotRenderer;
+ public DotRenderer mDotRendererWorkSpace;
+ public DotRenderer mDotRendererAllApps;
public DeviceProfile(Context context, InvariantDeviceProfile inv,
Point minSize, Point maxSize,
@@ -230,8 +232,11 @@
updateWorkspacePadding();
// This is done last, after iconSizePx is calculated above.
- mDotRenderer = new DotRenderer(iconSizePx, IconShape.getShapePath(),
+ mDotRendererWorkSpace = new DotRenderer(iconSizePx, IconShape.getShapePath(),
IconShape.DEFAULT_PATH_SIZE);
+ mDotRendererAllApps = iconSizePx == allAppsIconSizePx ? mDotRendererWorkSpace :
+ new DotRenderer(allAppsIconSizePx, IconShape.getShapePath(),
+ IconShape.DEFAULT_PATH_SIZE);
}
public DeviceProfile copy(Context context) {
@@ -307,11 +312,16 @@
updateAvailableFolderCellDimensions(dm, res);
}
+ /**
+ * Updating the iconSize affects many aspects of the launcher layout, such as: iconSizePx,
+ * iconTextSizePx, iconDrawablePaddingPx, cellWidth/Height, allApps* variants,
+ * hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and folderIconOffsetYPx.
+ */
private void updateIconSize(float scale, Resources res, DisplayMetrics dm) {
// Workspace
final boolean isVerticalLayout = isVerticalBarLayout();
- float invIconSizePx = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
- iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizePx, dm) * scale));
+ float invIconSizeDp = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
+ iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizeDp, dm) * scale));
iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
@@ -329,12 +339,20 @@
cellWidthPx = iconSizePx + iconDrawablePaddingPx;
// All apps
- allAppsIconTextSizePx = iconTextSizePx;
- allAppsIconSizePx = iconSizePx;
- allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
- allAppsCellHeightPx = getCellSize().y;
+ 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 (isVerticalLayout) {
+ if (isVerticalBarLayout()) {
// Always hide the Workspace text with vertical bar layout.
adjustToHideWorkspaceLabels();
}
@@ -419,14 +437,18 @@
}
public Point getCellSize() {
+ return getCellSize(inv.numColumns, inv.numRows);
+ }
+
+ private Point getCellSize(int numColumns, int numRows) {
Point result = new Point();
// Since we are only concerned with the overall padding, layout direction does
// not matter.
Point padding = getTotalWorkspacePadding();
result.x = calculateCellWidth(availableWidthPx - padding.x
- - cellLayoutPaddingLeftRightPx * 2, inv.numColumns);
+ - cellLayoutPaddingLeftRightPx * 2, numColumns);
result.y = calculateCellHeight(availableHeightPx - padding.y
- - cellLayoutBottomPaddingPx, inv.numRows);
+ - cellLayoutBottomPaddingPx, numRows);
return result;
}
@@ -542,11 +564,19 @@
}
/**
+ * Returns true when the number of workspace columns and all apps columns differs.
+ */
+ private boolean allAppsHasDifferentNumColumns() {
+ return inv.numAllAppsColumns != inv.numColumns;
+ }
+
+ /**
* Updates orientation information and returns true if it has changed from the previous value.
*/
- public boolean updateIsSeascape(WindowManager wm) {
+ public boolean updateIsSeascape(Context context) {
if (isVerticalBarLayout()) {
- boolean isSeascape = wm.getDefaultDisplay().getRotation() == Surface.ROTATION_270;
+ boolean isSeascape = DefaultDisplay.INSTANCE.get(context).getInfo().rotation
+ == Surface.ROTATION_270;
if (mIsSeascape != isSeascape) {
mIsSeascape = isSeascape;
return true;
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index b747d62..e2b7b68 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -50,9 +50,9 @@
/**
* The apps and shortcuts
*/
- public ArrayList<WorkspaceItemInfo> contents = new ArrayList<WorkspaceItemInfo>();
+ public ArrayList<WorkspaceItemInfo> contents = new ArrayList<>();
- ArrayList<FolderListener> listeners = new ArrayList<FolderListener>();
+ private ArrayList<FolderListener> mListeners = new ArrayList<>();
public FolderInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
@@ -72,10 +72,10 @@
* Add an app or shortcut for a specified rank.
*/
public void add(WorkspaceItemInfo item, int rank, boolean animate) {
- rank = Utilities.boundToRange(rank, 0, contents.size());
+ rank = Utilities.boundToRange(rank, 0, contents.size() + 1);
contents.add(rank, item);
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onAdd(item, rank);
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onAdd(item, rank);
}
itemsChanged(animate);
}
@@ -87,53 +87,37 @@
*/
public void remove(WorkspaceItemInfo item, boolean animate) {
contents.remove(item);
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onRemove(item);
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onRemove(item);
}
itemsChanged(animate);
}
- public void setTitle(CharSequence title) {
- this.title = title;
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onTitleChanged(title);
- }
- }
-
@Override
public void onAddToDatabase(ContentWriter writer) {
super.onAddToDatabase(writer);
writer.put(LauncherSettings.Favorites.TITLE, title)
.put(LauncherSettings.Favorites.OPTIONS, options);
-
}
public void addListener(FolderListener listener) {
- listeners.add(listener);
+ mListeners.add(listener);
}
public void removeListener(FolderListener listener) {
- listeners.remove(listener);
+ mListeners.remove(listener);
}
public void itemsChanged(boolean animate) {
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onItemsChanged(animate);
- }
- }
-
- public void prepareAutoUpdate() {
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).prepareAutoUpdate();
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onItemsChanged(animate);
}
}
public interface FolderListener {
public void onAdd(WorkspaceItemInfo item, int rank);
public void onRemove(WorkspaceItemInfo item);
- public void onTitleChanged(CharSequence title);
public void onItemsChanged(boolean animate);
- public void prepareAutoUpdate();
}
public boolean hasOption(int optionFlag) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 00acdcd..03ee707 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -105,8 +105,7 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- // Don't let if follow through to workspace
- return true;
+ return event.getY() > getCellHeight();
}
@Override
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index f19c602..0b79dd2 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -16,21 +16,22 @@
package com.android.launcher3;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.ShortcutUtil.fetchAndUpdateShortcutIconAsync;
+
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;
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.os.Handler;
-import android.os.Message;
import android.os.Parcelable;
import android.os.Process;
import android.os.UserHandle;
@@ -39,7 +40,8 @@
import android.util.Log;
import android.util.Pair;
-import com.android.launcher3.compat.LauncherAppsCompat;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
@@ -65,13 +67,9 @@
public class InstallShortcutReceiver extends BroadcastReceiver {
- private static final int MSG_ADD_TO_QUEUE = 1;
- private static final int MSG_FLUSH_QUEUE = 2;
-
public static final int FLAG_ACTIVITY_PAUSED = 1;
public static final int FLAG_LOADER_RUNNING = 2;
public static final int FLAG_DRAG_AND_DROP = 4;
- public static final int FLAG_BULK_ADD = 4;
// Determines whether to defer installing shortcuts immediately until
// processAllPendingInstalls() is called.
@@ -100,66 +98,57 @@
public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
- private static final Handler sHandler = new Handler(LauncherModel.getWorkerLooper()) {
+ @WorkerThread
+ private static void addToQueue(Context context, PendingInstallShortcutInfo info) {
+ String encoded = info.encodeToString();
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
+ strings = (strings != null) ? new HashSet<>(strings) : new HashSet<>(1);
+ strings.add(encoded);
+ prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
+ }
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ADD_TO_QUEUE: {
- Pair<Context, PendingInstallShortcutInfo> pair =
- (Pair<Context, PendingInstallShortcutInfo>) msg.obj;
- String encoded = pair.second.encodeToString();
- SharedPreferences prefs = Utilities.getPrefs(pair.first);
- Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
- strings = (strings != null) ? new HashSet<>(strings) : new HashSet<String>(1);
- strings.add(encoded);
- prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
- return;
- }
- case MSG_FLUSH_QUEUE: {
- Context context = (Context) msg.obj;
- LauncherModel model = LauncherAppState.getInstance(context).getModel();
- if (model.getCallback() == null) {
- // Launcher not loaded
- return;
- }
-
- ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
- SharedPreferences prefs = Utilities.getPrefs(context);
- Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
- if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
- if (strings == null) {
- return;
- }
-
- LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
- for (String encoded : strings) {
- PendingInstallShortcutInfo info = decode(encoded, context);
- if (info == null) {
- continue;
- }
-
- String pkg = getIntentPackage(info.launchIntent);
- if (!TextUtils.isEmpty(pkg)
- && !launcherApps.isPackageEnabledForProfile(pkg, info.user)
- && !info.isActivity) {
- if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
- + info.launchIntent);
- continue;
- }
-
- // Generate a shortcut info to add into the model
- installQueue.add(info.getItemInfo());
- }
- prefs.edit().remove(APPS_PENDING_INSTALL).apply();
- if (!installQueue.isEmpty()) {
- model.addAndBindAddedWorkspaceItems(installQueue);
- }
- return;
- }
- }
+ @WorkerThread
+ private static void flushQueueInBackground(Context context) {
+ if (Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null) {
+ // Launcher not loaded
+ return;
}
- };
+
+ ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
+ if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
+ if (strings == null) {
+ return;
+ }
+
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ for (String encoded : strings) {
+ PendingInstallShortcutInfo info = decode(encoded, context);
+ if (info == null) {
+ continue;
+ }
+
+ String pkg = getIntentPackage(info.launchIntent);
+ if (!TextUtils.isEmpty(pkg)
+ && !launcherApps.isPackageEnabled(pkg, info.user)
+ && !info.isActivity) {
+ if (DBG) {
+ Log.d(TAG, "Ignoring shortcut for absent package: " + info.launchIntent);
+ }
+ continue;
+ }
+
+ // Generate a shortcut info to add into the model
+ installQueue.add(info.getItemInfo());
+ }
+ prefs.edit().remove(APPS_PENDING_INSTALL).apply();
+ if (!installQueue.isEmpty()) {
+ LauncherAppState.getInstance(context).getModel()
+ .addAndBindAddedWorkspaceItems(installQueue);
+ }
+ }
public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
UserHandle user) {
@@ -291,7 +280,7 @@
private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
// Queue the item up for adding if launcher has not loaded properly yet
- Message.obtain(sHandler, MSG_ADD_TO_QUEUE, Pair.create(context, info)).sendToTarget();
+ MODEL_EXECUTOR.post(() -> addToQueue(context, info));
flushInstallQueue(context);
}
@@ -307,7 +296,7 @@
if (sInstallQueueDisabledFlags != 0) {
return;
}
- Message.obtain(sHandler, MSG_FLUSH_QUEUE, context.getApplicationContext()).sendToTarget();
+ MODEL_EXECUTOR.post(() -> flushQueueInBackground(context));
}
/**
@@ -494,9 +483,7 @@
return Pair.create(si, null);
} else if (shortcutInfo != null) {
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
- LauncherIcons li = LauncherIcons.obtain(mContext);
- itemInfo.applyFrom(li.createShortcutIcon(shortcutInfo));
- li.recycle();
+ fetchAndUpdateShortcutIconAsync(mContext, itemInfo, shortcutInfo, true);
return Pair.create(itemInfo, shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
@@ -531,7 +518,7 @@
try {
Decoder decoder = new Decoder(encoded, context);
if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
- LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
+ LauncherActivityInfo info = context.getSystemService(LauncherApps.class)
.resolveActivity(decoder.launcherIntent, decoder.user);
if (info != null) {
return new PendingInstallShortcutInfo(info, context);
@@ -617,11 +604,11 @@
// Already an activity target
return original;
}
- if (!Utilities.isLauncherAppTarget(original.launchIntent)) {
+ if (!PackageManagerHelper.isLauncherAppTarget(original.launchIntent)) {
return original;
}
- LauncherActivityInfo info = LauncherAppsCompat.getInstance(original.mContext)
+ LauncherActivityInfo info = original.mContext.getSystemService(LauncherApps.class)
.resolveActivity(original.launchIntent, original.user);
if (info == null) {
return original;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index bde87cb..310a9e9 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.settings.SettingsActivity.GRID_OPTIONS_PREFERENCE_KEY;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import android.annotation.TargetApi;
@@ -39,11 +41,13 @@
import android.util.SparseArray;
import android.util.TypedValue;
import android.util.Xml;
-import android.view.Display;
-import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.util.ConfigMonitor;
+import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Themes;
@@ -55,9 +59,6 @@
import java.util.ArrayList;
import java.util.Collections;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
public class InvariantDeviceProfile {
public static final String TAG = "IDP";
@@ -102,6 +103,8 @@
public int iconBitmapSize;
public int fillResIconDpi;
public float iconTextSize;
+ public float allAppsIconSize;
+ public float allAppsIconTextSize;
private SparseArray<TypedValue> mExtraAttrs;
@@ -110,6 +113,11 @@
*/
public int numHotseatIcons;
+ /**
+ * Number of columns in the all apps list.
+ */
+ public int numAllAppsColumns;
+
public int defaultLayoutId;
int demoModeLayoutId;
@@ -136,6 +144,9 @@
landscapeIconSize = p.landscapeIconSize;
iconTextSize = p.iconTextSize;
numHotseatIcons = p.numHotseatIcons;
+ numAllAppsColumns = p.numAllAppsColumns;
+ allAppsIconSize = p.allAppsIconSize;
+ allAppsIconTextSize = p.allAppsIconTextSize;
defaultLayoutId = p.defaultLayoutId;
demoModeLayoutId = p.demoModeLayoutId;
mExtraAttrs = p.mExtraAttrs;
@@ -144,7 +155,10 @@
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
- initGrid(context, Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null));
+ String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
+ ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
+ : null;
+ initGrid(context, gridName);
mConfigMonitor = new ConfigMonitor(context,
APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
mOverlayMonitor = new OverlayMonitor(context);
@@ -172,19 +186,17 @@
}
private String initGrid(Context context, String gridName) {
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- DisplayMetrics dm = new DisplayMetrics();
- display.getMetrics(dm);
+ DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(context).getInfo();
- Point smallestSize = new Point();
- Point largestSize = new Point();
- display.getCurrentSizeRange(smallestSize, largestSize);
+ 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), dm);
- float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
+ 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),
@@ -200,6 +212,8 @@
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
+ numAllAppsColumns = closestProfile.numAllAppsColumns;
+
mExtraAttrs = closestProfile.extraAttrs;
if (!closestProfile.name.equals(gridName)) {
@@ -210,16 +224,23 @@
iconSize = interpolatedDisplayOption.iconSize;
iconShapePath = getIconShapePath(context);
landscapeIconSize = interpolatedDisplayOption.landscapeIconSize;
- iconBitmapSize = ResourceUtils.pxFromDp(iconSize, dm);
+ 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, dm);
+ applyPartnerDeviceProfileOverrides(context, displayInfo.metrics);
- Point realSize = new Point();
- display.getRealSize(realSize);
+ Point realSize = new Point(displayInfo.realSize);
// The real size never changes. smallSide and largeSide will remain the
// same in any orientation.
int smallSide = Math.min(realSize.x, realSize.y);
@@ -280,7 +301,7 @@
public void setCurrentGrid(Context context, String gridName) {
Context appContext = context.getApplicationContext();
Utilities.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply();
- new MainThreadExecutor().execute(() -> onConfigChanged(appContext));
+ MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext));
}
private void onConfigChanged(Context context) {
@@ -288,9 +309,10 @@
InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
// Re-init grid
- // TODO(b/131867841): We pass in null here so that we can calculate the closest profile
- // without the bias of the grid name.
- initGrid(context, null);
+ String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
+ ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
+ : null;
+ initGrid(context, gridName);
int changeFlags = 0;
if (numRows != oldProfile.numRows ||
@@ -492,6 +514,8 @@
private final int numHotseatIcons;
+ private final int numAllAppsColumns;
+
private final int defaultLayoutId;
private final int demoModeLayoutId;
@@ -514,6 +538,9 @@
R.styleable.GridDisplayOption_numFolderRows, numRows);
numFolderColumns = a.getInt(
R.styleable.GridDisplayOption_numFolderColumns, numColumns);
+ numAllAppsColumns = a.getInt(
+ R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
+
a.recycle();
extraAttrs = Themes.createValueMap(context, attrs,
@@ -530,8 +557,10 @@
private final boolean canBeDefault;
private float iconSize;
- private float landscapeIconSize;
private float iconTextSize;
+ private float landscapeIconSize;
+ private float allAppsIconSize;
+ private float allAppsIconTextSize;
DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
this.grid = grid;
@@ -549,6 +578,11 @@
landscapeIconSize = a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
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();
}
@@ -563,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 e29f927..1550bb0 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -27,6 +27,8 @@
*/
public abstract class ItemInfoWithIcon extends ItemInfo {
+ public static final String TAG = "ItemInfoDebug";
+
/**
* A bitmap version of the application icon.
*/
@@ -126,4 +128,8 @@
iconColor = info.color;
}
+ /**
+ * @return a copy of this
+ */
+ public abstract ItemInfoWithIcon clone();
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 257f0df..4b4d793 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -16,22 +16,23 @@
package com.android.launcher3;
-import static android.content.pm.ActivityInfo.CONFIG_LOCALE;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
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.LauncherState.OVERVIEW_PEEK;
+import static com.android.launcher3.Utilities.postAsyncCallback;
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.states.RotationHelper.REQUEST_NONE;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT;
+import static com.android.launcher3.testing.TestProtocol.CRASH_ADD_CUSTOM_SHORTCUT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -77,6 +78,9 @@
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
@@ -85,15 +89,13 @@
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.CustomActionsPopup;
@@ -103,21 +105,24 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.states.InternalStateHandler;
+import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.states.RotationHelper;
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;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ActivityResultInfo;
+import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -126,7 +131,6 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
-import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
@@ -145,7 +149,13 @@
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetListRowEntry;
import com.android.launcher3.widget.WidgetsFullSheet;
-import com.android.launcher3.widget.custom.CustomWidgetParser;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
+import com.android.systemui.plugins.OverlayPlugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.shared.LauncherExterns;
+import com.android.systemui.plugins.shared.LauncherOverlayManager;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -155,17 +165,18 @@
import java.util.HashSet;
import java.util.List;
import java.util.function.Predicate;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
+import java.util.function.Supplier;
/**
* Default launcher application.
*/
public class Launcher extends BaseDraggingActivity implements LauncherExterns,
- LauncherModel.Callbacks, LauncherProviderChangeListener, UserEventDelegate,
- InvariantDeviceProfile.OnIDPChangeListener {
+ Callbacks, UserEventDelegate,
+ InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
public static final String TAG = "Launcher";
+
+ public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
+
static final boolean LOGD = false;
static final boolean DEBUG_STRICT_MODE = false;
@@ -201,9 +212,11 @@
private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
// Type: SparseArray<Parcelable>
private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
+
public static final String ON_CREATE_EVT = "Launcher.onCreate";
- private static final String ON_START_EVT = "Launcher.onStart";
- private static final String ON_RESUME_EVT = "Launcher.onResume";
+ public static final String ON_START_EVT = "Launcher.onStart";
+ public static final String ON_RESUME_EVT = "Launcher.onResume";
+ public static final String ON_NEW_INTENT_EVT = "Launcher.onNewIntent";
private LauncherStateManager mStateManager;
@@ -220,9 +233,11 @@
private LauncherAppTransitionManager mAppTransitionManager;
private Configuration mOldConfig;
- @Thunk Workspace mWorkspace;
+ @Thunk
+ Workspace mWorkspace;
private View mLauncherView;
- @Thunk DragLayer mDragLayer;
+ @Thunk
+ DragLayer mDragLayer;
private DragController mDragController;
private AppWidgetManagerCompat mAppWidgetManager;
@@ -230,21 +245,25 @@
private final int[] mTmpAddItemCellCoordinates = new int[2];
- @Thunk Hotseat mHotseat;
+ @Thunk
+ Hotseat mHotseat;
private DropTargetBar mDropTargetBar;
// Main container view for the all apps screen.
- @Thunk AllAppsContainerView mAppsView;
+ @Thunk
+ AllAppsContainerView mAppsView;
AllAppsTransitionController mAllAppsController;
// Scrim view for the all apps and overview state.
- @Thunk ScrimView mScrimView;
+ @Thunk
+ ScrimView mScrimView;
// UI and state for the overview panel
private View mOverviewPanel;
- @Thunk boolean mWorkspaceLoading = true;
+ @Thunk
+ boolean mWorkspaceLoading = true;
private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
@@ -287,9 +306,15 @@
private DeviceProfile mStableDeviceProfile;
private RotationMode mRotationMode = RotationMode.NORMAL;
+ protected LauncherOverlayManager mOverlayManager;
+ // If true, overlay callbacks are deferred
+ private boolean mDeferOverlayCallbacks;
+ private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
- RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
+ Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
+ TraceHelper.FLAG_UI_EVENT);
if (DEBUG_STRICT_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
@@ -304,10 +329,8 @@
.penaltyDeath()
.build());
}
- TraceHelper.beginSection("Launcher-onCreate");
super.onCreate(savedInstanceState);
- TraceHelper.partitionSection("Launcher-onCreate", "super call");
LauncherAppState app = LauncherAppState.getInstance(this);
mOldConfig = new Configuration(getResources().getConfiguration());
@@ -326,8 +349,8 @@
UiFactory.onCreate(this);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
-
- mAppWidgetHost = new LauncherAppWidgetHost(this);
+ mAppWidgetHost = new LauncherAppWidgetHost(this,
+ appWidgetId -> getWorkspace().removeWidget(appWidgetId));
mAppWidgetHost.startListening();
mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
@@ -337,7 +360,7 @@
mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
- boolean internalStateHandled = InternalStateHandler.handleCreate(this, getIntent());
+ boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
if (internalStateHandled) {
if (savedInstanceState != null) {
// InternalStateHandler has already set the appropriate state.
@@ -383,13 +406,16 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
+ mOverlayManager = getDefaultOverlay();
+ PluginManagerWrapper.INSTANCE.get(this).addPluginListener(this,
+ OverlayPlugin.class, false /* allowedMultiple */);
+
mRotationHelper.initialize();
- TraceHelper.endSection("Launcher-onCreate");
- RaceConditionTracker.onEvent(ON_CREATE_EVT, EXIT);
mStateManager.addStateListener(new LauncherStateManager.StateListener() {
@Override
- public void onStateTransitionStart(LauncherState toState) {}
+ public void onStateTransitionStart(LauncherState toState) {
+ }
@Override
public void onStateTransitionComplete(LauncherState finalState) {
@@ -405,6 +431,40 @@
}
}
});
+
+ TraceHelper.INSTANCE.endSection(traceToken);
+ }
+
+ protected LauncherOverlayManager getDefaultOverlay() {
+ return new LauncherOverlayManager() { };
+ }
+
+ @Override
+ public void onPluginConnected(OverlayPlugin overlayManager, Context context) {
+ switchOverlay(() -> overlayManager.createOverlayManager(this, this));
+ }
+
+ @Override
+ public void onPluginDisconnected(OverlayPlugin plugin) {
+ switchOverlay(this::getDefaultOverlay);
+ }
+
+ private void switchOverlay(Supplier<LauncherOverlayManager> overlaySupplier) {
+ if (mOverlayManager != null) {
+ mOverlayManager.onActivityDestroyed(this);
+ }
+ mOverlayManager = overlaySupplier.get();
+ if (getRootView().isAttachedToWindow()) {
+ mOverlayManager.onAttachedToWindow();
+ }
+ mDeferOverlayCallbacks = true;
+ checkIfOverlayStillDeferred();
+ }
+
+ @Override
+ protected void dispatchDeviceProfileChanged() {
+ super.dispatchDeviceProfileChanged();
+ mOverlayManager.onDeviceProvideChanged();
}
@Override
@@ -419,10 +479,6 @@
public void onConfigurationChanged(Configuration newConfig) {
int diff = newConfig.diff(mOldConfig);
- if ((diff & CONFIG_LOCALE) != 0) {
- Folder.setLocaleDependentFields(getResources(), true /* force */);
- }
-
if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
onIdpChanged(mDeviceProfile.inv);
}
@@ -497,6 +553,8 @@
// Load configuration-specific DeviceProfile
mDeviceProfile = idp.getDeviceProfile(this);
if (isInMultiWindowMode()) {
+ // Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return
+ // the app window size
Display display = getWindowManager().getDefaultDisplay();
Point mwSize = new Point();
display.getSize(mwSize);
@@ -553,18 +611,12 @@
return mLauncherView.findViewById(id);
}
- @Override
- public void onAppWidgetHostReset() {
- if (mAppWidgetHost != null) {
- mAppWidgetHost.startListening();
- }
- }
-
private LauncherCallbacks mLauncherCallbacks;
/**
* Call this after onCreate to set or clear overlay.
*/
+ @Override
public void setLauncherOverlay(LauncherOverlay overlay) {
if (overlay != null) {
overlay.setOverlayCallbacks(new LauncherOverlayCallbacksImpl());
@@ -572,18 +624,16 @@
mWorkspace.setLauncherOverlay(overlay);
}
+ @Override
+ public void runOnOverlayHidden(Runnable runnable) {
+ getWorkspace().runOnOverlayHidden(runnable);
+ }
+
public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
mLauncherCallbacks = callbacks;
return true;
}
- @Override
- public void onLauncherProviderChanged() {
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onLauncherProviderChange();
- }
- }
-
public boolean isDraggingEnabled() {
// We prevent dragging when we are loading the workspace as it is possible to pick up a view
// that is subsequently removed from the workspace in startBinding().
@@ -604,10 +654,9 @@
if (info.container >= 0) {
View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container);
if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) {
- FolderIconPreviewVerifier verifier =
- new FolderIconPreviewVerifier(getDeviceProfile().inv);
- verifier.setFolderInfo((FolderInfo) folderIcon.getTag());
- if (verifier.isItemInPreview(info.rank)) {
+ if (new FolderGridOrganizer(getDeviceProfile().inv)
+ .setFolderInfo((FolderInfo) folderIcon.getTag())
+ .isItemInPreview(info.rank)) {
folderIcon.invalidate();
}
}
@@ -647,7 +696,8 @@
.getLauncherAppWidgetInfo(widgetId);
if (provider != null) {
new WidgetAddFlowHandler(provider)
- .startConfigActivity(this, widgetInfo, REQUEST_RECONFIGURE_APPWIDGET);
+ .startConfigActivity(this, widgetInfo,
+ REQUEST_RECONFIGURE_APPWIDGET);
}
}
break;
@@ -782,9 +832,6 @@
final int requestCode, final int resultCode, final Intent data) {
mPendingActivityRequestCode = -1;
handleActivityResult(requestCode, resultCode, data);
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onActivityResult(requestCode, resultCode, data);
- }
}
@Override
@@ -811,10 +858,6 @@
getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show();
}
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onRequestPermissionsResult(requestCode, permissions,
- grantResults);
- }
}
/**
@@ -835,7 +878,8 @@
}
}
- @Thunk void completeTwoStageWidgetDrop(
+ @Thunk
+ void completeTwoStageWidgetDrop(
final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) {
CellLayout cellLayout = mWorkspace.getScreenWithId(requestArgs.screenId);
Runnable onCompleteRunnable = null;
@@ -872,9 +916,12 @@
protected void onStop() {
super.onStop();
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onStop();
+ if (mDeferOverlayCallbacks) {
+ checkIfOverlayStillDeferred();
+ } else {
+ mOverlayManager.onActivityStopped(this);
}
+
logStopAndResume(Action.Command.STOP);
mAppWidgetHost.setListenIfResumed(false);
@@ -890,13 +937,15 @@
@Override
protected void onStart() {
- RaceConditionTracker.onEvent(ON_START_EVT, ENTER);
+ Object traceToken = TraceHelper.INSTANCE.beginSection(ON_START_EVT,
+ TraceHelper.FLAG_UI_EVENT);
super.onStart();
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onStart();
+ if (!mDeferOverlayCallbacks) {
+ mOverlayManager.onActivityStarted(this);
}
+
mAppWidgetHost.setListenIfResumed(true);
- RaceConditionTracker.onEvent(ON_START_EVT, EXIT);
+ TraceHelper.INSTANCE.endSection(traceToken);
}
private void handleDeferredResume() {
@@ -932,29 +981,68 @@
int containerType = mStateManager.getState().containerType;
if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
getUserEventDispatcher().logActionCommand(command,
- containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0);
+ containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0);
} else {
getUserEventDispatcher().logActionCommand(command, containerType, -1);
}
-
}
- protected void onStateSet(LauncherState state) {
- getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+ private void scheduleDeferredCheck() {
+ mHandler.removeCallbacks(mDeferredOverlayCallbacks);
+ postAsyncCallback(mHandler, mDeferredOverlayCallbacks);
+ }
+
+ private void checkIfOverlayStillDeferred() {
+ if (!mDeferOverlayCallbacks) {
+ return;
+ }
+ if (isStarted() && (!hasBeenResumed() || mStateManager.getState().disableInteraction)) {
+ return;
+ }
+ mDeferOverlayCallbacks = false;
+
+ // Move the client to the correct state. Calling the same method twice is no-op.
+ if (isStarted()) {
+ mOverlayManager.onActivityStarted(this);
+ }
+ if (hasBeenResumed()) {
+ mOverlayManager.onActivityResumed(this);
+ } else {
+ mOverlayManager.onActivityPaused(this);
+ }
+ if (!isStarted()) {
+ mOverlayManager.onActivityStopped(this);
+ }
+ }
+
+ public void deferOverlayCallbacksUntilNextResumeOrStop() {
+ mDeferOverlayCallbacks = true;
+ }
+
+ public LauncherOverlayManager getOverlayManager() {
+ return mOverlayManager;
+ }
+
+ public void onStateSetStart(LauncherState state) {
if (mDeferredResumePending) {
handleDeferredResume();
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onStateChanged();
+ if (mDeferOverlayCallbacks) {
+ scheduleDeferredCheck();
}
}
+ public void onStateSetEnd(LauncherState state) {
+ getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+ getWorkspace().setClipChildren(!state.disablePageClipping);
+ finishAutoCancelActionMode();
+ }
+
@Override
protected void onResume() {
- RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER);
- TraceHelper.beginSection("ON_RESUME");
+ Object traceToken = TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT,
+ TraceHelper.FLAG_UI_EVENT);
super.onResume();
- TraceHelper.partitionSection("ON_RESUME", "superCall");
mHandler.removeCallbacks(mHandleDeferredResume);
Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
@@ -968,12 +1056,13 @@
resumeCallbacks.clear();
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onResume();
+ if (mDeferOverlayCallbacks) {
+ scheduleDeferredCheck();
+ } else {
+ mOverlayManager.onActivityResumed(this);
}
- TraceHelper.endSection("ON_RESUME");
- RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT);
+ TraceHelper.INSTANCE.endSection(traceToken);
}
@Override
@@ -985,8 +1074,9 @@
mDragController.cancelDrag();
mDragController.resetLastGestureUpTime();
mDropTargetBar.animateToVisibility(false);
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onPause();
+
+ if (!mDeferOverlayCallbacks) {
+ mOverlayManager.onActivityPaused(this);
}
}
@@ -1002,35 +1092,6 @@
mStateManager.onWindowFocusChanged();
}
- public interface LauncherOverlay {
-
- /**
- * Touch interaction leading to overscroll has begun
- */
- void onScrollInteractionBegin();
-
- /**
- * Touch interaction related to overscroll has ended
- */
- void onScrollInteractionEnd();
-
- /**
- * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
- * screen (or in the case of RTL, the rightmost screen).
- */
- void onScrollChange(float progress, boolean rtl);
-
- /**
- * Called when the launcher is ready to use the overlay
- * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
- */
- void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
- }
-
- public interface LauncherOverlayCallbacks {
- void onScrollChanged(float progress);
- }
-
class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
public void onScrollChanged(float progress) {
@@ -1061,7 +1122,8 @@
mStateManager.goToState(state, false /* animated */);
}
- PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS);
+ PendingRequestArgs requestArgs = savedState.getParcelable(
+ RUNTIME_STATE_PENDING_REQUEST_ARGS);
if (requestArgs != null) {
setWaitingForResult(requestArgs);
}
@@ -1131,8 +1193,7 @@
* Creates a view representing a shortcut inflated from the specified resource.
*
* @param parent The group the shortcut belongs to.
- * @param info The data structure describing the shortcut.
- *
+ * @param info The data structure describing the shortcut.
* @return A View inflated from layoutResId.
*/
public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) {
@@ -1151,8 +1212,13 @@
*/
private void completeAddShortcut(Intent data, int container, int screenId, int cellX,
int cellY, PendingRequestArgs args) {
- if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
+ if (data == null
+ || args.getRequestCode() != REQUEST_CREATE_SHORTCUT
|| args.getPendingIntent().getComponent() == null) {
+ if (data == null && TestProtocol.sDebugTracing) {
+ Log.d(CRASH_ADD_CUSTOM_SHORTCUT,
+ "Failed to add custom shortcut: Intent is null, args = " + args);
+ }
return;
}
@@ -1161,8 +1227,8 @@
WorkspaceItemInfo info = null;
if (Utilities.ATLEAST_OREO) {
- info = LauncherAppsCompatVO.createWorkspaceItemFromPinItemRequest(
- this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
+ info = PinRequestHelper.createWorkspaceItemFromPinItemRequest(
+ this, PinRequestHelper.getPinItemRequest(data), 0);
}
if (info == null) {
@@ -1234,7 +1300,8 @@
*
* @param appWidgetId The app widget id
*/
- @Thunk void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
+ @Thunk
+ void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
if (appWidgetInfo == null) {
@@ -1287,19 +1354,14 @@
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
-
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onAttachedToWindow();
- }
+ mOverlayManager.onAttachedToWindow();
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
-
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onDetachedFromWindow();
- }
+ mOverlayManager.onDetachedFromWindow();
+ closeContextMenu();
}
public AllAppsTransitionController getAllAppsController() {
@@ -1348,15 +1410,23 @@
return mModelWriter;
}
+ @Override
public SharedPreferences getSharedPrefs() {
return mSharedPrefs;
}
- public int getOrientation() { return mOldConfig.orientation; }
+ @Override
+ public SharedPreferences getDevicePrefs() {
+ return Utilities.getDevicePrefs(this);
+ }
+
+ public int getOrientation() {
+ return mOldConfig.orientation;
+ }
@Override
protected void onNewIntent(Intent intent) {
- TraceHelper.beginSection("NEW_INTENT");
+ Object traceToken = TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT);
super.onNewIntent(intent);
boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags() &
@@ -1367,8 +1437,7 @@
boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL)
&& AbstractFloatingView.getTopOpenView(this) == null;
boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
- boolean internalStateHandled = InternalStateHandler
- .handleNewIntent(this, intent, isStarted());
+ boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this, intent);
if (isActionMain) {
if (!internalStateHandled) {
@@ -1406,9 +1475,10 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onHomeIntent(internalStateHandled);
}
+ mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
}
- TraceHelper.endSection("NEW_INTENT");
+ TraceHelper.INSTANCE.endSection(traceToken);
}
@Override
@@ -1436,9 +1506,10 @@
outState.remove(RUNTIME_STATE_WIDGET_PANEL);
}
- // We close any open folders and shortcut containers since they will not be re-opened,
+ // We close any open folders and shortcut containers that are not safe for rebind,
// and we need to make sure this state is reflected.
- AbstractFloatingView.closeAllOpenViews(this, false);
+ AbstractFloatingView.closeOpenViews(this, false, TYPE_ALL & ~TYPE_REBIND_SAFE);
+ finishAutoCancelActionMode();
if (mPendingRequestArgs != null) {
outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
@@ -1450,18 +1521,17 @@
}
super.onSaveInstanceState(outState);
-
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onSaveInstanceState(outState);
- }
+ mOverlayManager.onActivitySaveInstanceState(this, outState);
}
@Override
public void onDestroy() {
super.onDestroy();
+ ACTIVITY_TRACKER.onActivityDestroyed(this);
unregisterReceiver(mScreenOffReceiver);
mWorkspace.removeFolderListeners();
+ PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this);
if (mCancelTouchController != null) {
mCancelTouchController.run();
@@ -1486,9 +1556,8 @@
TextKeyListener.getInstance().release();
clearPendingBinds();
LauncherAppState.getIDP(this).removeOnChangeListener(this);
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onDestroy();
- }
+
+ mOverlayManager.onActivityDestroyed(this);
}
public LauncherAccessibilityDelegate getAccessibilityDelegate() {
@@ -1575,7 +1644,8 @@
void addAppWidgetImpl(int appWidgetId, ItemInfo info,
AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay) {
- if (!addFlowHandler.startConfigActivity(this, appWidgetId, info, REQUEST_CREATE_APPWIDGET)) {
+ if (!addFlowHandler.startConfigActivity(this, appWidgetId, info,
+ REQUEST_CREATE_APPWIDGET)) {
// If the configuration flow was not started, add the widget
Runnable onComplete = new Runnable() {
@@ -1585,7 +1655,8 @@
mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
}
};
- completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this));
+ completeAddAppWidget(appWidgetId, info, boundWidget,
+ addFlowHandler.getProviderInfo(this));
mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
}
}
@@ -1611,7 +1682,7 @@
break;
default:
throw new IllegalStateException("Unknown item type: " + info.itemType);
- }
+ }
}
/**
@@ -1647,10 +1718,9 @@
} else {
// In this case, we either need to start an activity to get permission to bind
// the widget, or we need to start an activity to configure the widget, or both.
- if (FeatureFlags.ENABLE_CUSTOM_WIDGETS &&
- info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) {
- appWidgetId = CustomWidgetParser.getWidgetIdForCustomProvider(
- this, info.componentName);
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) {
+ appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider(
+ info.componentName);
} else {
appWidgetId = getAppWidgetHost().allocateAppWidgetId();
}
@@ -1669,7 +1739,7 @@
FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX,
int cellY) {
final FolderInfo folderInfo = new FolderInfo();
- folderInfo.title = getText(R.string.folder_name);
+ folderInfo.title = "";
// Update the model
getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
@@ -1723,8 +1793,6 @@
return true;
}
-
-
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
@@ -1735,9 +1803,6 @@
if (finishAutoCancelActionMode()) {
return;
}
- if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) {
- return;
- }
if (mDragController.isDragging()) {
mDragController.cancelDrag();
@@ -1788,7 +1853,7 @@
@Override
public int getCurrentState() {
- if(mStateManager.getState() == LauncherState.ALL_APPS) {
+ if (mStateManager.getState() == LauncherState.ALL_APPS) {
return StatsLogUtils.LAUNCHER_STATE_ALLAPPS;
} else if (mStateManager.getState() == OVERVIEW) {
return StatsLogUtils.LAUNCHER_STATE_OVERVIEW;
@@ -1821,7 +1886,7 @@
// recents animation into launcher. Defer launching the activity until Launcher is
// next resumed.
addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
- UiFactory.clearSwipeSharedState(true /* finishAnimation */);
+ UiFactory.clearSwipeSharedState(this, true /* finishAnimation */);
return true;
}
@@ -1862,9 +1927,6 @@
// This clears all widget bitmaps from the widget tray
// TODO(hyunyoungs)
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onTrimMemory(level);
- }
UiFactory.onTrimMemory(this, level);
}
@@ -1919,12 +1981,11 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
public void startBinding() {
- TraceHelper.beginSection("startBinding");
+ Object traceToken = TraceHelper.INSTANCE.beginSection("startBinding");
// Floating panels (except the full widget sheet) are associated with individual icons. If
// we are starting a fresh bind, close all such panels as all the icons are about
// to go away.
- AbstractFloatingView.closeOpenViews(this, true,
- AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+ AbstractFloatingView.closeOpenViews(this, true, TYPE_ALL & ~TYPE_REBIND_SAFE);
setWorkspaceLoading(true);
@@ -1938,7 +1999,7 @@
if (mHotseat != null) {
mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout());
}
- TraceHelper.endSection("startBinding");
+ TraceHelper.INSTANCE.endSection(traceToken);
}
@Override
@@ -2052,7 +2113,7 @@
throw new RuntimeException("Invalid Item Type");
}
- /*
+ /*
* Remove colliding items.
*/
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
@@ -2124,110 +2185,120 @@
}
private View inflateAppWidget(LauncherAppWidgetInfo item) {
+ if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) {
+ item.providerName = QsbContainerView.getSearchComponentName(this);
+ if (item.providerName == null) {
+ getModelWriter().deleteItemFromDatabase(item);
+ return null;
+ }
+ }
+ final AppWidgetHostView view;
if (mIsSafeModeEnabled) {
- PendingAppWidgetHostView view =
- new PendingAppWidgetHostView(this, item, mIconCache, true);
+ view = new PendingAppWidgetHostView(this, item, mIconCache, true);
prepareAppWidget(view, item);
return view;
}
- TraceHelper.beginSection("BIND_WIDGET");
+ Object traceToken = TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId);
- final LauncherAppWidgetProviderInfo appWidgetInfo;
+ try {
+ final LauncherAppWidgetProviderInfo appWidgetInfo;
- if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
- // If the provider is not ready, bind as a pending widget.
- appWidgetInfo = null;
- } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
- // The widget id is not valid. Try to find the widget based on the provider info.
- appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
- } else {
- appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
- }
-
- // If the provider is ready, but the width is not yet restored, try to restore it.
- if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) &&
- (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
- if (appWidgetInfo == null) {
- Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
- + " belongs to component " + item.providerName
- + ", as the provider is null");
- getModelWriter().deleteItemFromDatabase(item);
- return null;
+ if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+ // If the provider is not ready, bind as a pending widget.
+ appWidgetInfo = null;
+ } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+ // The widget id is not valid. Try to find the widget based on the provider info.
+ appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
+ } else {
+ appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
}
- // If we do not have a valid id, try to bind an id.
- if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
- if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
- // Id has not been allocated yet. Allocate a new id.
- item.appWidgetId = mAppWidgetHost.allocateAppWidgetId();
- item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
+ // If the provider is ready, but the width is not yet restored, try to restore it.
+ if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+ && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
+ if (appWidgetInfo == null) {
+ Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ + " belongs to component " + item.providerName
+ + ", as the provider is null");
+ getModelWriter().deleteItemFromDatabase(item);
+ return null;
+ }
- // Also try to bind the widget. If the bind fails, the user will be shown
- // a click to setup UI, which will ask for the bind permission.
- PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo);
- pendingInfo.spanX = item.spanX;
- pendingInfo.spanY = item.spanY;
- pendingInfo.minSpanX = item.minSpanX;
- pendingInfo.minSpanY = item.minSpanY;
- Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+ // If we do not have a valid id, try to bind an id.
+ if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+ if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
+ // Id has not been allocated yet. Allocate a new id.
+ item.appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
- boolean isDirectConfig =
- item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
- if (isDirectConfig && item.bindOptions != null) {
- Bundle newOptions = item.bindOptions.getExtras();
- if (options != null) {
- newOptions.putAll(options);
+ // Also try to bind the widget. If the bind fails, the user will be shown
+ // a click to setup UI, which will ask for the bind permission.
+ PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo);
+ pendingInfo.spanX = item.spanX;
+ pendingInfo.spanY = item.spanY;
+ pendingInfo.minSpanX = item.minSpanX;
+ pendingInfo.minSpanY = item.minSpanY;
+ Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this,
+ pendingInfo);
+
+ boolean isDirectConfig =
+ item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
+ if (isDirectConfig && item.bindOptions != null) {
+ Bundle newOptions = item.bindOptions.getExtras();
+ if (options != null) {
+ newOptions.putAll(options);
+ }
+ options = newOptions;
}
- options = newOptions;
+ boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+ item.appWidgetId, appWidgetInfo, options);
+
+ // We tried to bind once. If we were not able to bind, we would need to
+ // go through the permission dialog, which means we cannot skip the config
+ // activity.
+ item.bindOptions = null;
+ item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG;
+
+ // Bind succeeded
+ if (success) {
+ // If the widget has a configure activity, it is still needs to set it
+ // up, otherwise the widget is ready to go.
+ item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig
+ ? LauncherAppWidgetInfo.RESTORE_COMPLETED
+ : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+ }
+
+ getModelWriter().updateItemInDatabase(item);
}
- boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
- item.appWidgetId, appWidgetInfo, options);
-
- // We tried to bind once. If we were not able to bind, we would need to
- // go through the permission dialog, which means we cannot skip the config
- // activity.
- item.bindOptions = null;
- item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG;
-
- // Bind succeeded
- if (success) {
- // If the widget has a configure activity, it is still needs to set it up,
- // otherwise the widget is ready to go.
- item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig
- ? LauncherAppWidgetInfo.RESTORE_COMPLETED
- : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
- }
-
+ } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
+ && (appWidgetInfo.configure == null)) {
+ // The widget was marked as UI not ready, but there is no configure activity to
+ // update the UI.
+ item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
getModelWriter().updateItemInDatabase(item);
}
- } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
- && (appWidgetInfo.configure == null)) {
- // The widget was marked as UI not ready, but there is no configure activity to
- // update the UI.
- item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
- getModelWriter().updateItemInDatabase(item);
- }
- }
-
- final AppWidgetHostView view;
- if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
- // Verify that we own the widget
- if (appWidgetInfo == null) {
- FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
- getModelWriter().deleteWidgetInfo(item, getAppWidgetHost());
- return null;
}
- item.minSpanX = appWidgetInfo.minSpanX;
- item.minSpanY = appWidgetInfo.minSpanY;
- view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
- } else {
- view = new PendingAppWidgetHostView(this, item, mIconCache, false);
- }
- prepareAppWidget(view, item);
+ if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+ // Verify that we own the widget
+ if (appWidgetInfo == null) {
+ FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
+ getModelWriter().deleteWidgetInfo(item, getAppWidgetHost());
+ return null;
+ }
- TraceHelper.endSection("BIND_WIDGET", "id=" + item.appWidgetId);
+ item.minSpanX = appWidgetInfo.minSpanX;
+ item.minSpanY = appWidgetInfo.minSpanY;
+ view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
+ } else {
+ view = new PendingAppWidgetHostView(this, item, mIconCache, false);
+ }
+ prepareAppWidget(view, item);
+ } finally {
+ TraceHelper.INSTANCE.endSection(traceToken);
+ }
+
return view;
}
@@ -2305,7 +2376,7 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
public void finishBindingItems(int pageBoundFirst) {
- TraceHelper.beginSection("finishBindingItems");
+ Object traceToken = TraceHelper.INSTANCE.beginSection("finishBindingItems");
mWorkspace.restoreInstanceStateForRemainingPages();
setWorkspaceLoading(false);
@@ -2324,7 +2395,12 @@
// override the previous page so we don't log the page switch.
mWorkspace.setCurrentPage(pageBoundFirst, pageBoundFirst /* overridePrevPage */);
- TraceHelper.endSection("finishBindingItems");
+ // Cache one page worth of icons
+ getViewCache().setCacheSize(R.layout.folder_application,
+ mDeviceProfile.inv.numFolderColumns * mDeviceProfile.inv.numFolderRows);
+ getViewCache().setCacheSize(R.layout.folder_page, 2);
+
+ TraceHelper.INSTANCE.endSection(traceToken);
}
private boolean canRunNewAppsAnimation() {
@@ -2345,7 +2421,7 @@
*
* Implementation of the method from LauncherModel.Callbacks.
*/
- public void bindAllApplications(ArrayList<AppInfo> apps) {
+ public void bindAllApplications(AppInfo[] apps) {
mAppsView.getAppsStore().setApps(apps);
}
@@ -2358,16 +2434,6 @@
mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
}
- /**
- * A package was updated.
- *
- * Implementation of the method from LauncherModel.Callbacks.
- */
- @Override
- public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps) {
- mAppsView.getAppsStore().addOrUpdateApps(apps);
- }
-
@Override
public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
mAppsView.getAppsStore().updatePromiseAppProgress(app);
@@ -2415,11 +2481,6 @@
}
@Override
- public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
- mAppsView.getAppsStore().removeApps(appInfos);
- }
-
- @Override
public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
mPopupDataProvider.setAllWidgets(allWidgets);
}
@@ -2464,14 +2525,16 @@
}
writer.println(prefix + "Misc:");
- writer.print(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading);
- writer.print(" mPendingRequestArgs=" + mPendingRequestArgs);
- writer.println(" mPendingActivityResult=" + mPendingActivityResult);
- writer.println(" mRotationHelper: " + mRotationHelper);
+ dumpMisc(prefix + "\t", writer);
+ writer.println(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading);
+ writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs
+ + " mPendingActivityResult=" + mPendingActivityResult);
+ writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
+ writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());
+
// Extra logging for b/116853349
mDragLayer.dump(prefix, writer);
mStateManager.dump(prefix, writer);
- dumpMisc(writer);
try {
FileLog.flushAll(writer);
@@ -2484,6 +2547,7 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.dump(prefix, fd, writer, args);
}
+ mOverlayManager.dump(prefix, writer);
}
@Override
@@ -2532,8 +2596,8 @@
if (focusedView instanceof BubbleTextView
&& focusedView.getTag() instanceof ItemInfo
&& mAccessibilityDelegate.performAction(focusedView,
- (ItemInfo) focusedView.getTag(),
- LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
+ (ItemInfo) focusedView.getTag(),
+ LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
PopupContainerWithArrow.getOpen(this).requestFocus();
return true;
}
@@ -2578,6 +2642,12 @@
return (Launcher) fromContext(context);
}
+ @Override
+ public void returnToHomescreen() {
+ super.returnToHomescreen();
+ getStateManager().goToState(LauncherState.NORMAL);
+ }
+
/**
* 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 b4a2216..c717d1a 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,33 +17,35 @@
package com.android.launcher3;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
import android.content.ComponentName;
-import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.pm.LauncherApps;
import android.os.Handler;
import android.util.Log;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
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.SecureSettingsObserver;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
public class LauncherAppState {
public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
// We do not need any synchronization for this variable as its only written on UI thread.
- private static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
+ public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
new MainThreadInitializedObject<>(LauncherAppState::new);
private final Context mContext;
@@ -53,6 +55,9 @@
private final InvariantDeviceProfile mInvariantDeviceProfile;
private final SecureSettingsObserver mNotificationDotsObserver;
+ private final InstallSessionTracker mInstallSessionTracker;
+ private final SimpleBroadcastReceiver mModelChangeReceiver;
+
public static LauncherAppState getInstance(final Context context) {
return INSTANCE.get(context);
}
@@ -66,10 +71,6 @@
}
private LauncherAppState(Context context) {
- if (getLocalProvider(context) == null) {
- throw new RuntimeException(
- "Initializing LauncherAppState in the absence of LauncherProvider");
- }
Log.v(Launcher.TAG, "LauncherAppState initiated");
Preconditions.assertUIThread();
mContext = context;
@@ -79,28 +80,30 @@
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
- LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
+ mModelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
- // Register intent receivers
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- // For handling managed profiles
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
-
+ mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
+ mModelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
+ Intent.ACTION_MANAGED_PROFILE_ADDED,
+ Intent.ACTION_MANAGED_PROFILE_REMOVED,
+ Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
+ Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
if (FeatureFlags.IS_DOGFOOD_BUILD) {
- filter.addAction(ACTION_FORCE_ROLOAD);
+ mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
}
+ // TODO: remove listener on terminate
FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
+ CustomWidgetManager.INSTANCE.get(mContext)
+ .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
- mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged);
new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
+ mInstallSessionTracker = PackageInstallerCompat.getInstance(context)
+ .registerInstallTracker(mModel, MODEL_EXECUTOR);
+
if (!mContext.getResources().getBoolean(R.bool.notification_dots_enabled)) {
mNotificationDotsObserver = null;
} else {
@@ -137,17 +140,17 @@
* Call from Application.onTerminate(), which is not guaranteed to ever be called.
*/
public void onTerminate() {
- mContext.unregisterReceiver(mModel);
- final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
- launcherApps.removeOnAppsChangedCallback(mModel);
- PackageInstallerCompat.getInstance(mContext).onStop();
+ mContext.unregisterReceiver(mModelChangeReceiver);
+ mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
+ mInstallSessionTracker.unregister();
+ CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
+
if (mNotificationDotsObserver != null) {
mNotificationDotsObserver.unregister();
}
}
LauncherModel setLauncher(Launcher launcher) {
- getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
return mModel;
}
@@ -174,11 +177,4 @@
public static InvariantDeviceProfile getIDP(Context context) {
return InvariantDeviceProfile.INSTANCE.get(context);
}
-
- private static LauncherProvider getLocalProvider(Context context) {
- try (ContentProviderClient cl = context.getContentResolver()
- .acquireContentProviderClient(LauncherProvider.AUTHORITY)) {
- return (LauncherProvider) cl.getLocalContentProvider();
- }
- }
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 7f5ac52..4e29a95 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -27,14 +27,15 @@
import android.content.Intent;
import android.os.Handler;
import android.util.SparseArray;
-import android.view.LayoutInflater;
import android.widget.Toast;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.widget.DeferredAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
+import java.util.function.IntConsumer;
/**
@@ -56,9 +57,17 @@
private final Context mContext;
private int mFlags = FLAG_RESUMED;
+ private IntConsumer mAppWidgetRemovedCallback = null;
+
public LauncherAppWidgetHost(Context context) {
+ this(context, null);
+ }
+
+ public LauncherAppWidgetHost(Context context,
+ IntConsumer appWidgetRemovedCallback) {
super(context, APPWIDGET_HOST_ID);
mContext = context;
+ mAppWidgetRemovedCallback = appWidgetRemovedCallback;
}
@Override
@@ -71,7 +80,7 @@
@Override
public void startListening() {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return;
}
mFlags |= FLAG_LISTENING;
@@ -98,13 +107,17 @@
@Override
public void stopListening() {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return;
}
mFlags &= ~FLAG_LISTENING;
super.stopListening();
}
+ public boolean isListening() {
+ return (mFlags & FLAG_LISTENING) != 0;
+ }
+
/**
* Updates the resumed state of the host.
* When a host is not resumed, it defers calls to startListening until host is resumed again.
@@ -153,7 +166,7 @@
@Override
public int allocateAppWidgetId() {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return AppWidgetManager.INVALID_APPWIDGET_ID;
}
@@ -180,10 +193,8 @@
LauncherAppWidgetProviderInfo appWidget) {
if (appWidget.isCustomWidget()) {
LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
- LayoutInflater inflater = (LayoutInflater)
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(appWidget.initialLayout, lahv);
lahv.setAppWidget(0, appWidget);
+ CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
return lahv;
} else if ((mFlags & FLAG_LISTENING) == 0) {
DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
@@ -207,7 +218,7 @@
}
view.setAppWidget(appWidgetId, appWidget);
view.switchToErrorView();
- return view;
+ return view;
}
}
}
@@ -225,6 +236,18 @@
info.initSpans(mContext);
}
+ /**
+ * Called on an appWidget is removed for a widgetId
+ * @param appWidgetId
+ * TODO: make this override when SDK is updated
+ */
+ public void onAppWidgetRemoved(int appWidgetId) {
+ if (mAppWidgetRemovedCallback == null) {
+ return;
+ }
+ mAppWidgetRemovedCallback.accept(appWidgetId);
+ }
+
@Override
public void deleteAppWidgetId(int appWidgetId) {
super.deleteAppWidgetId(appWidgetId);
@@ -240,7 +263,7 @@
public void startBindFlow(BaseActivity activity,
int appWidgetId, AppWidgetProviderInfo info, int requestCode) {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
sendActionCancelled(activity, requestCode);
return;
}
@@ -256,7 +279,7 @@
public void startConfigActivity(BaseActivity activity, int widgetId, int requestCode) {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
sendActionCancelled(activity, requestCode);
return;
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 051846c..b824301 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -29,6 +29,9 @@
*/
public class LauncherAppWidgetInfo extends ItemInfo {
+ public static final int OPTION_SEARCH_WIDGET = 1;
+
+
public static final int RESTORE_COMPLETED = 0;
/**
@@ -97,6 +100,11 @@
public Intent bindOptions;
/**
+ * Widget options
+ */
+ public int options;
+
+ /**
* Nonnull for pending widgets. We use this to get the icon and title for the widget.
*/
public PackageItemInfo pendingItemInfo;
@@ -137,6 +145,7 @@
writer.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId)
.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString())
.put(LauncherSettings.Favorites.RESTORED, restoreStatus)
+ .put(LauncherSettings.Favorites.OPTIONS, options)
.put(LauncherSettings.Favorites.INTENT, bindOptions);
}
@@ -164,4 +173,13 @@
public final boolean hasRestoreFlag(int flag) {
return (restoreStatus & flag) == flag;
}
+
+ /**
+ * returns if widget options include an option or not
+ * @param option
+ * @return
+ */
+ public final boolean hasOptionFlag(int option) {
+ return (options & option) != 0;
+ }
}
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index dfe75ec..0e529bd 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import android.content.Intent;
import android.os.Bundle;
import java.io.FileDescriptor;
@@ -36,31 +35,8 @@
* the code in the corresponding Launcher method is executed.
*/
void onCreate(Bundle savedInstanceState);
- void onResume();
- void onStart();
- void onStop();
- void onPause();
- void onDestroy();
- void onSaveInstanceState(Bundle outState);
- void onActivityResult(int requestCode, int resultCode, Intent data);
- void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults);
- void onAttachedToWindow();
- void onDetachedFromWindow();
void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
void onHomeIntent(boolean internalStateHandled);
- boolean handleBackPressed();
- void onTrimMemory(int level);
-
- /**
- * Called when the launcher state changed
- */
- default void onStateChanged() { }
-
- /*
- * Extension points for providing custom behavior on certain user interactions.
- */
- void onLauncherProviderChange();
/**
* Starts a search with {@param initialQuery}. Return false if search was not started.
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index a041489..fc2e953 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -18,29 +18,30 @@
import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageInstaller;
import android.content.pm.ShortcutInfo;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.AddWorkspaceItemsTask;
+import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.CacheDataUpdatedTask;
import com.android.launcher3.model.LoaderResults;
import com.android.launcher3.model.LoaderTask;
@@ -49,56 +50,42 @@
import com.android.launcher3.model.PackageUpdatedTask;
import com.android.launcher3.model.ShortcutsChangedTask;
import com.android.launcher3.model.UserLockStateChangedTask;
+import com.android.launcher3.pm.InstallSessionTracker;
+import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntArray;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.ViewOnDrawExecutor;
-import com.android.launcher3.widget.WidgetListRowEntry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
-import androidx.annotation.Nullable;
-
/**
* Maintains in-memory state of the Launcher. It is expected that there should be only one
* LauncherModel object held in a static. Also provide APIs for updating the database state
* for the Launcher.
*/
-public class LauncherModel extends BroadcastReceiver
- implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
+public class LauncherModel extends LauncherApps.Callback implements InstallSessionTracker.Callback {
private static final boolean DEBUG_RECEIVER = false;
static final String TAG = "Launcher.Model";
- private final MainThreadExecutor mUiExecutor = new MainThreadExecutor();
@Thunk final LauncherAppState mApp;
@Thunk final Object mLock = new Object();
@Thunk
LoaderTask mLoaderTask;
@Thunk boolean mIsLoaderTaskRunning;
- @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
- private static final Looper mWorkerLooper;
- static {
- sWorkerThread.start();
- mWorkerLooper = sWorkerThread.getLooper();
- }
- @Thunk static final Handler sWorker = new Handler(mWorkerLooper);
-
// Indicates whether the current model data is valid or not.
// We start off with everything not loaded. After that, we assume that
// our monitoring of the package manager provides all updates and we never
@@ -106,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;
}
}
@@ -135,52 +126,11 @@
}
};
- public interface Callbacks {
- public void rebindModel();
-
- public int getCurrentWorkspaceScreen();
- public void clearPendingBinds();
- public void startBinding();
- public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons);
- public void bindScreens(IntArray orderedScreenIds);
- public void finishFirstPageBind(ViewOnDrawExecutor executor);
- public void finishBindingItems(int pageBoundFirst);
- public void bindAllApplications(ArrayList<AppInfo> apps);
- public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps);
- public void preAddApps();
- public void bindAppsAdded(IntArray newScreens,
- ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
- public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
- public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
- public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
- public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
- public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
- public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
- public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
- public void onPageBoundSynchronously(int page);
- public void executeOnNextDraw(ViewOnDrawExecutor executor);
- public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
- }
-
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
}
- public void setPackageState(PackageInstallInfo installInfo) {
- enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
- }
-
- /**
- * Updates the icons and label of all pending icons for the provided package name.
- */
- public void updateSessionDisplayInfo(final String packageName) {
- HashSet<String> packages = new HashSet<>();
- packages.add(packageName);
- enqueueModelUpdateTask(new CacheDataUpdatedTask(
- CacheDataUpdatedTask.OP_SESSION_UPDATE, Process.myUserHandle(), packages));
- }
-
/**
* Adds the provided items to the workspace.
*/
@@ -213,30 +163,6 @@
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
- public void onSessionFailure(String packageName, UserHandle user) {
- enqueueModelUpdateTask(new BaseModelUpdateTask() {
- @Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>();
- synchronized (dataModel) {
- for (ItemInfo info : dataModel.itemsIdMap) {
- if (info instanceof WorkspaceItemInfo
- && ((WorkspaceItemInfo) info).hasPromiseIconUi()
- && user.equals(info.user)
- && info.getIntent() != null
- && TextUtils.equals(packageName, info.getIntent().getPackage())) {
- removedIds.put(info.id, true /* remove */);
- }
- }
- }
-
- if (!removedIds.isEmpty()) {
- deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
- }
- }
- });
- }
-
@Override
public void onPackageRemoved(String packageName, UserHandle user) {
onPackagesRemoved(user, packageName);
@@ -244,6 +170,7 @@
public void onPackagesRemoved(UserHandle user, String... packages) {
int op = PackageUpdatedTask.OP_REMOVE;
+ FileLog.d(TAG, "package removed received " + String.join("," + packages));
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages));
}
@@ -292,21 +219,15 @@
enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
}
- /**
- * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
- * ACTION_PACKAGE_CHANGED.
- */
- @Override
- public void onReceive(Context context, Intent intent) {
+ public void onBroadcastIntent(Intent intent) {
if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
-
final String action = intent.getAction();
if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
// If we have changed locale we need to clear out the labels in all apps/workspace.
forceReload();
} else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
|| Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
- UserManagerCompat.getInstance(context).enableAndResetCache();
+ UserManagerCompat.getInstance(mApp.getContext()).enableAndResetCache();
forceReload();
} else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
@@ -375,7 +296,7 @@
if (mCallbacks != null && mCallbacks.get() != null) {
final Callbacks oldCallbacks = mCallbacks.get();
// Clear any pending bind-runnables from the synchronized load process.
- mUiExecutor.execute(oldCallbacks::clearPendingBinds);
+ MAIN_EXECUTOR.execute(oldCallbacks::clearPendingBinds);
// If there is already one running, tell it to stop.
stopLoader();
@@ -405,6 +326,9 @@
public void stopLoader() {
synchronized (mLock) {
LoaderTask oldTask = mLoaderTask;
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "LauncherModel.stopLoader");
+ }
mLoaderTask = null;
if (oldTask != null) {
oldTask.stopLocked();
@@ -416,10 +340,14 @@
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "LauncherModel.startLoaderForResults " + mLoaderTask);
+ }
// Always post the loader task, instead of running directly (even on same thread) so
// that we exit any nested synchronized blocks
- sWorker.post(mLoaderTask);
+ MODEL_EXECUTOR.post(mLoaderTask);
}
}
@@ -432,25 +360,65 @@
}
}
+ @Override
public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
+ if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
+ enqueueModelUpdateTask(new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ apps.addPromiseApp(app.getContext(), sessionInfo);
+ bindApplicationsIfNeeded();
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onSessionFailure(String packageName, UserHandle user) {
+ if (!FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
+ return;
+ }
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- apps.addPromiseApp(app.getContext(), sessionInfo);
- if (!apps.added.isEmpty()) {
- final ArrayList<AppInfo> arrayList = new ArrayList<>(apps.added);
- apps.added.clear();
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAppsAddedOrUpdated(arrayList);
+ final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>();
+ synchronized (dataModel) {
+ for (ItemInfo info : dataModel.itemsIdMap) {
+ if (info instanceof WorkspaceItemInfo
+ && ((WorkspaceItemInfo) info).hasPromiseIconUi()
+ && user.equals(info.user)
+ && info.getIntent() != null
+ && TextUtils.equals(packageName, info.getIntent().getPackage())) {
+ removedIds.put(info.id, true /* remove */);
}
- });
+ }
+ }
+
+ if (!removedIds.isEmpty()) {
+ deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
}
}
});
}
+ @Override
+ public void onPackageStateChanged(PackageInstallInfo installInfo) {
+ enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
+ }
+
+ /**
+ * Updates the icons and label of all pending icons for the provided package name.
+ */
+ @Override
+ public void onUpdateSessionDisplay(PackageUserKey key, PackageInstaller.SessionInfo info) {
+ mApp.getIconCache().updateSessionCache(key, info);
+
+ HashSet<String> packages = new HashSet<>();
+ packages.add(key.mPackageName);
+ enqueueModelUpdateTask(new CacheDataUpdatedTask(
+ CacheDataUpdatedTask.OP_SESSION_UPDATE, key.mUser, packages));
+ }
+
public class LoaderTransaction implements AutoCloseable {
private final LoaderTask mTask;
@@ -477,6 +445,10 @@
public void close() {
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "LauncherModel.close " + mLoaderTask + ", " + mTask);
+ }
if (mLoaderTask == mTask) {
mLoaderTask = null;
}
@@ -495,8 +467,8 @@
* use partial updates similar to {@link UserManagerCompat}
*/
public void refreshShortcutsIfRequired() {
- sWorker.removeCallbacks(mShortcutPermissionCheckRunnable);
- sWorker.post(mShortcutPermissionCheckRunnable);
+ MODEL_EXECUTOR.getHandler().removeCallbacks(mShortcutPermissionCheckRunnable);
+ MODEL_EXECUTOR.post(mShortcutPermissionCheckRunnable);
}
/**
@@ -523,14 +495,8 @@
}
public void enqueueModelUpdateTask(ModelUpdateTask task) {
- task.init(mApp, this, sBgDataModel, mBgAllAppsList, mUiExecutor);
-
- if (sWorkerThread.getThreadId() == Process.myTid()) {
- task.run();
- } else {
- // If we are not on the worker thread, then post to the worker handler
- sWorker.post(task);
- }
+ task.init(mApp, this, sBgDataModel, mBgAllAppsList, MAIN_EXECUTOR);
+ MODEL_EXECUTOR.execute(task);
}
/**
@@ -605,15 +571,4 @@
public Callbacks getCallback() {
return mCallbacks != null ? mCallbacks.get() : null;
}
-
- /**
- * @return the looper for the worker thread which can be used to start background tasks.
- */
- public static Looper getWorkerLooper() {
- return mWorkerLooper;
- }
-
- public static void setWorkerPriority(final int priority) {
- Process.setThreadPriority(sWorkerThread.getThreadId(), priority);
- }
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 6ad5c36..42927ea 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -33,7 +33,6 @@
import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
import android.database.Cursor;
@@ -46,11 +45,8 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.BaseColumns;
import android.provider.Settings;
import android.text.TextUtils;
@@ -70,14 +66,13 @@
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.NoLocaleSQLiteHelper;
-import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
import java.io.File;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringReader;
@@ -102,9 +97,6 @@
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
- private final ChangeListenerWrapper mListenerWrapper = new ChangeListenerWrapper();
- private Handler mListenerHandler;
-
protected DatabaseHelper mOpenHelper;
/**
@@ -124,7 +116,6 @@
if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.d(TAG, "Launcher process started");
}
- mListenerHandler = new Handler(mListenerWrapper);
// The content provider exists for the entire duration of the launcher main process and
// is the first component to get created.
@@ -132,14 +123,6 @@
return true;
}
- /**
- * Sets a provider listener.
- */
- public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {
- Preconditions.assertUIThread();
- mListenerWrapper.mListener = listener;
- }
-
@Override
public String getType(Uri uri) {
SqlArguments args = new SqlArguments(uri, null, null);
@@ -155,7 +138,7 @@
*/
protected synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
- mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
+ mOpenHelper = new DatabaseHelper(getContext());
if (RestoreDbTask.isPending(getContext())) {
if (!RestoreDbTask.performRestore(getContext(), mOpenHelper,
@@ -225,7 +208,6 @@
mOpenHelper.onAddOrDeleteOp(db);
uri = ContentUris.withAppendedId(uri, rowId);
- notifyListeners();
reloadLauncherIfExternal();
return uri;
}
@@ -285,7 +267,6 @@
t.commit();
}
- notifyListeners();
reloadLauncherIfExternal();
return values.length;
}
@@ -331,7 +312,6 @@
int count = db.delete(args.table, args.where, args.args);
if (count > 0) {
mOpenHelper.onAddOrDeleteOp(db);
- notifyListeners();
reloadLauncherIfExternal();
}
return count;
@@ -345,8 +325,6 @@
addModifiedTime(values);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count = db.update(args.table, values, args.where, args.args);
- if (count > 0) notifyListeners();
-
reloadLauncherIfExternal();
return count;
}
@@ -440,13 +418,6 @@
}
}
- /**
- * Overridden in tests
- */
- protected void notifyListeners() {
- mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_LAUNCHER_PROVIDER_CHANGED);
- }
-
@Thunk static void addModifiedTime(ContentValues values) {
values.put(LauncherSettings.Favorites.MODIFIED, System.currentTimeMillis());
}
@@ -566,15 +537,13 @@
* The class is subclassed in tests to create an in-memory db.
*/
public static class DatabaseHelper extends NoLocaleSQLiteHelper implements LayoutParserCallback {
- private final BackupManager mBackupManager;
- private final Handler mWidgetHostResetHandler;
private final Context mContext;
private int mMaxItemId = -1;
private int mMaxScreenId = -1;
private boolean mBackupTableExists;
- DatabaseHelper(Context context, Handler widgetHostResetHandler) {
- this(context, widgetHostResetHandler, LauncherFiles.LAUNCHER_DB);
+ DatabaseHelper(Context context) {
+ this(context, LauncherFiles.LAUNCHER_DB);
// Table creation sometimes fails silently, which leads to a crash loop.
// This way, we will try to create a table every time after crash, so the device
// would eventually be able to recover.
@@ -591,12 +560,9 @@
/**
* Constructor used in tests and for restore.
*/
- public DatabaseHelper(
- Context context, Handler widgetHostResetHandler, String tableName) {
+ public DatabaseHelper(Context context, String tableName) {
super(context, tableName, SCHEMA_VERSION);
mContext = context;
- mWidgetHostResetHandler = widgetHostResetHandler;
- mBackupManager = new BackupManager(mContext);
}
protected void initIds() {
@@ -635,13 +601,6 @@
* Overriden in tests.
*/
protected void onEmptyDbCreated() {
- // Database was just created, so wipe any previous widgets
- if (mWidgetHostResetHandler != null) {
- newLauncherWidgetHost().deleteHost();
- mWidgetHostResetHandler.sendEmptyMessage(
- ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET);
- }
-
// Set the flag for empty DB
Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
}
@@ -873,7 +832,7 @@
continue;
}
- if (!Utilities.isLauncherAppTarget(intent)) {
+ if (!PackageManagerHelper.isLauncherAppTarget(intent)) {
continue;
}
@@ -1041,27 +1000,4 @@
}
}
}
-
- private static class ChangeListenerWrapper implements Handler.Callback {
-
- private static final int MSG_LAUNCHER_PROVIDER_CHANGED = 1;
- private static final int MSG_APP_WIDGET_HOST_RESET = 2;
-
- private LauncherProviderChangeListener mListener;
-
- @Override
- public boolean handleMessage(Message msg) {
- if (mListener != null) {
- switch (msg.what) {
- case MSG_LAUNCHER_PROVIDER_CHANGED:
- mListener.onLauncherProviderChanged();
- break;
- case MSG_APP_WIDGET_HOST_RESET:
- mListener.onAppWidgetHostReset();
- break;
- }
- }
- return true;
- }
- }
}
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
deleted file mode 100644
index 0243088..0000000
--- a/src/com/android/launcher3/LauncherProviderChangeListener.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.android.launcher3;
-
-/**
- * This class is a listener for {@link LauncherProvider} changes. It gets notified in the
- * sendNotify method. This listener is needed because by default the Launcher suppresses
- * standard data change callbacks.
- */
-public interface LauncherProviderChangeListener {
-
- void onLauncherProviderChanged();
-
- void onAppWidgetHostReset();
-}
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index f964b8d..ce1795a 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -39,6 +39,8 @@
private WindowStateListener mWindowStateListener;
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDisallowBackGesture;
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private boolean mForceHideBackArrow;
public LauncherRootView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -176,12 +178,18 @@
}
@TargetApi(Build.VERSION_CODES.Q)
+ public void setForceHideBackArrow(boolean forceHideBackArrow) {
+ this.mForceHideBackArrow = forceHideBackArrow;
+ setDisallowBackGesture(mDisallowBackGesture);
+ }
+
+ @TargetApi(Build.VERSION_CODES.Q)
public void setDisallowBackGesture(boolean disallowBackGesture) {
if (!Utilities.ATLEAST_Q) {
return;
}
mDisallowBackGesture = disallowBackGesture;
- setSystemGestureExclusionRects(mDisallowBackGesture
+ setSystemGestureExclusionRects((mForceHideBackArrow || mDisallowBackGesture)
? SYSTEM_GESTURE_EXCLUSION_RECT
: Collections.emptyList());
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index e248ba0..c509680 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -59,6 +59,10 @@
public static final String ITEM_TYPE = "itemType";
/**
+ * The gesture is a package
+ */
+ public static final int ITEM_TYPE_NON_ACTIONABLE = -1;
+ /**
* The gesture is an application
*/
public static final int ITEM_TYPE_APPLICATION = 0;
@@ -122,11 +126,13 @@
*/
public static final int CONTAINER_DESKTOP = -100;
public static final int CONTAINER_HOTSEAT = -101;
+ public static final int CONTAINER_PREDICTION = -102;
static final String containerToString(int container) {
switch (container) {
case CONTAINER_DESKTOP: return "desktop";
case CONTAINER_HOTSEAT: return "hotseat";
+ case CONTAINER_PREDICTION: return "prediction";
default: return String.valueOf(container);
}
}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 63914b0..848e19f 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -136,7 +136,7 @@
}
public void dump(String prefix, PrintWriter writer) {
- writer.println(prefix + "LauncherState");
+ writer.println(prefix + "LauncherState:");
writer.println(prefix + "\tmLastStableState:" + mLastStableState);
writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
writer.println(prefix + "\tmState:" + mState);
@@ -227,11 +227,6 @@
private void goToState(LauncherState state, boolean animated, long delay,
final Runnable onCompleteRunnable) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " +
- state.getClass().getSimpleName() +
- " @ " + Log.getStackTraceString(new Throwable()));
- }
animated &= Utilities.areAnimationsEnabled(mLauncher);
if (mLauncher.isInState(state)) {
if (mConfig.mCurrentAnimation == null) {
@@ -412,13 +407,8 @@
mState.onStateDisabled(mLauncher);
}
mState = state;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.STABLE_STATE_MISMATCH, "onStateTransitionStart: " +
- state.getClass().getSimpleName() +
- " @ " + Log.getStackTraceString(new Throwable()));
- }
mState.onStateEnabled(mLauncher);
- mLauncher.onStateSet(mState);
+ mLauncher.onStateSetStart(mState);
if (state.disablePageClipping) {
// Only disable clipping if needed, otherwise leave it as previous value.
@@ -436,16 +426,10 @@
if (state != mCurrentStableState) {
mLastStableState = state.getHistoryForState(mCurrentStableState);
mCurrentStableState = state;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " +
- state.getClass().getSimpleName() +
- " @ " + Log.getStackTraceString(new Throwable()));
- }
}
state.onStateTransitionEnd(mLauncher);
- mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
- mLauncher.finishAutoCancelActionMode();
+ mLauncher.onStateSetEnd(state);
if (state == NORMAL) {
setRestState(null);
diff --git a/src/com/android/launcher3/MainThreadExecutor.java b/src/com/android/launcher3/MainThreadExecutor.java
deleted file mode 100644
index 5094682..0000000
--- a/src/com/android/launcher3/MainThreadExecutor.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2014 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 android.os.Looper;
-
-import com.android.launcher3.util.LooperExecutor;
-
-/**
- * An executor service that executes its tasks on the main thread.
- *
- * Shutting down this executor is not supported.
- */
-public class MainThreadExecutor extends LooperExecutor {
-
- public MainThreadExecutor() {
- super(Looper.getMainLooper());
- }
-}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 79d6ca7..ff2b400 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1296,6 +1296,10 @@
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL: {
+ Launcher launcher = Launcher.getLauncher(getContext());
+ if (launcher != null) {
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ }
// Handle mouse (or ext. device) by shifting the page depending on the scroll
final float vscroll;
final float hscroll;
@@ -1306,6 +1310,9 @@
vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
}
+ if (Math.abs(vscroll) > Math.abs(hscroll) && !isVerticalScrollable()) {
+ return true;
+ }
if (hscroll != 0 || vscroll != 0) {
boolean isForwardScroll = mIsRtl ? (hscroll < 0 || vscroll < 0)
: (hscroll > 0 || vscroll > 0);
@@ -1322,6 +1329,10 @@
return super.onGenericMotionEvent(event);
}
+ protected boolean isVerticalScrollable() {
+ return true;
+ }
+
private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
@@ -1539,7 +1550,7 @@
snapToPage(getNextPage() - 1);
return true;
}
- return false;
+ return onOverscroll(-getMeasuredWidth());
}
public boolean scrollRight() {
@@ -1547,7 +1558,15 @@
snapToPage(getNextPage() + 1);
return true;
}
- return false;
+ return onOverscroll(getMeasuredWidth());
+ }
+
+ protected boolean onOverscroll(int amount) {
+ if (!mAllowOverScroll) return false;
+ onScrollInteractionBegin();
+ overScroll(amount);
+ onScrollInteractionEnd();
+ return true;
}
@Override
@@ -1567,8 +1586,9 @@
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
final boolean pagesFlipped = isPageOrderFlipped();
- info.setScrollable(getPageCount() > 1);
- if (getCurrentPage() < getPageCount() - 1) {
+ int offset = (mAllowOverScroll ? 0 : 1);
+ info.setScrollable(getPageCount() > offset);
+ if (getCurrentPage() < getPageCount() - offset) {
info.addAction(pagesFlipped ?
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD
: AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
@@ -1576,7 +1596,7 @@
AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT
: AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);
}
- if (getCurrentPage() > 0) {
+ if (getCurrentPage() >= offset) {
info.addAction(pagesFlipped ?
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD
: AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
@@ -1584,7 +1604,6 @@
AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT
: AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);
}
-
// Accessibility-wise, PagedView doesn't support long click, so disabling it.
// Besides disabling the accessibility long-click, this also prevents this view from getting
// accessibility focus.
@@ -1603,7 +1622,7 @@
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
- event.setScrollable(getPageCount() > 1);
+ event.setScrollable(mAllowOverScroll || getPageCount() > 1);
}
@Override
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
index 380078b..af5402a 100644
--- a/src/com/android/launcher3/Partner.java
+++ b/src/com/android/launcher3/Partner.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.util.PackageManagerHelper.findSystemApk;
+
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.util.DisplayMetrics;
@@ -59,7 +61,7 @@
*/
public static synchronized Partner get(PackageManager pm) {
if (!sSearched) {
- Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
+ Pair<String, Resources> apkInfo = findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
if (apkInfo != null) {
sPartner = new Partner(apkInfo.first, apkInfo.second);
}
diff --git a/src/com/android/launcher3/PromiseAppInfo.java b/src/com/android/launcher3/PromiseAppInfo.java
index 4ad0b3d..e55e4bd 100644
--- a/src/com/android/launcher3/PromiseAppInfo.java
+++ b/src/com/android/launcher3/PromiseAppInfo.java
@@ -19,16 +19,16 @@
import android.content.Context;
import android.content.Intent;
-import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.util.PackageManagerHelper;
-
import androidx.annotation.NonNull;
+import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.util.PackageManagerHelper;
+
public class PromiseAppInfo extends AppInfo {
public int level = 0;
- public PromiseAppInfo(@NonNull PackageInstallerCompat.PackageInstallInfo installInfo) {
+ public PromiseAppInfo(@NonNull PackageInstallInfo installInfo) {
componentName = installInfo.componentName;
intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 55cb6f2..3c2ed72 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -2,6 +2,7 @@
import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE;
+
import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
@@ -15,6 +16,7 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
@@ -27,11 +29,12 @@
import android.widget.Toast;
import com.android.launcher3.Launcher.OnResumeCallback;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Themes;
import java.net.URISyntaxException;
@@ -155,7 +158,7 @@
user = item.user;
}
if (intent != null) {
- LauncherActivityInfo info = LauncherAppsCompat.getInstance(mLauncher)
+ LauncherActivityInfo info = mLauncher.getSystemService(LauncherApps.class)
.resolveActivity(intent, user);
if (info != null
&& (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -240,6 +243,7 @@
.setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
.putExtra(Intent.EXTRA_USER, info.user);
mLauncher.startActivity(i);
+ FileLog.d(TAG, "start uninstall activity " + cn.getPackageName());
return cn;
} catch (URISyntaxException e) {
Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
@@ -284,9 +288,8 @@
@Override
public void onLauncherResume() {
// We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
- if (LauncherAppsCompat.getInstance(mContext)
- .getApplicationInfo(mPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
- mDragObject.dragInfo.user) == null) {
+ if (new PackageManagerHelper(mContext).getApplicationInfo(mPackageName,
+ mDragObject.dragInfo.user, PackageManager.MATCH_UNINSTALLED_PACKAGES) == null) {
mDragObject.dragSource = mOriginal;
mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
} else {
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index b4078ee..8dedc6c 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -16,14 +16,16 @@
package com.android.launcher3;
+import static com.android.launcher3.pm.PackageInstallerCompat.getUserHandle;
+
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
@@ -38,13 +40,11 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.pm.PackageInstallerCompat;
+import com.android.launcher3.util.Executors;
import java.util.List;
-import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle;
-
/**
* BroadcastReceiver to handle session commit intent.
*/
@@ -85,9 +85,8 @@
public static void queuePromiseAppIconAddition(Context context, SessionInfo sessionInfo) {
String packageName = sessionInfo.getAppPackageName();
- List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
- .getActivityList(packageName, getUserHandle(sessionInfo));
- if (activities == null || activities.isEmpty()) {
+ if (context.getSystemService(LauncherApps.class)
+ .getActivityList(packageName, getUserHandle(sessionInfo)).isEmpty()) {
// Ensure application isn't already installed.
queueAppIconAddition(context, packageName, sessionInfo.getAppLabel(),
sessionInfo.getAppIcon(), getUserHandle(sessionInfo));
@@ -95,9 +94,9 @@
}
public static void queueAppIconAddition(Context context, String packageName, UserHandle user) {
- List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
+ List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
.getActivityList(packageName, user);
- if (activities == null || activities.isEmpty()) {
+ if (activities.isEmpty()) {
// no activity found
return;
}
@@ -133,7 +132,7 @@
// grid.
prefs.edit().putBoolean(ADD_ICON_PREFERENCE_KEY, true).apply();
} else if (!prefs.contains(ADD_ICON_PREFERENCE_INITIALIZED_KEY)) {
- new PrefInitTask(context).executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
+ new PrefInitTask(context).executeOnExecutor(Executors.THREAD_POOL_EXECUTOR);
}
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ba122f9..92f8069 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -24,15 +24,10 @@
import android.app.Person;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.LauncherApps;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
@@ -47,7 +42,6 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.os.Build;
-import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.Message;
@@ -60,20 +54,17 @@
import android.text.style.TtsSpan;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Pair;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IntArray;
@@ -81,17 +72,10 @@
import com.android.launcher3.views.Transposable;
import com.android.launcher3.widget.PendingAddShortcutInfo;
-import java.io.Closeable;
-import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
-import java.util.StringTokenizer;
-import java.util.concurrent.Executor;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -124,8 +108,6 @@
public static final boolean ATLEAST_OREO =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
- public static final int SINGLE_FRAME_MS = 16;
-
/**
* Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
*/
@@ -148,18 +130,6 @@
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
- // These values are same as that in {@link AsyncTask}.
- private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
- private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
- private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
- private static final int KEEP_ALIVE = 1;
- /**
- * An {@link Executor} to be used with async task with no limit on the queue size.
- */
- public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
- CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
- TimeUnit.SECONDS, new LinkedBlockingQueue<>());
-
public static boolean IS_RUNNING_IN_TEST_HARNESS =
ActivityManager.isRunningInTestHarness();
@@ -247,7 +217,6 @@
return scale;
}
-
/**
* Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, float[], boolean)}.
*/
@@ -383,53 +352,6 @@
return min + (value * (max - min));
}
- public static boolean isSystemApp(Context context, Intent intent) {
- PackageManager pm = context.getPackageManager();
- ComponentName cn = intent.getComponent();
- String packageName = null;
- if (cn == null) {
- ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
- if ((info != null) && (info.activityInfo != null)) {
- packageName = info.activityInfo.packageName;
- }
- } else {
- packageName = cn.getPackageName();
- }
- if (packageName != null) {
- try {
- PackageInfo info = pm.getPackageInfo(packageName, 0);
- return (info != null) && (info.applicationInfo != null) &&
- ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
- } catch (NameNotFoundException e) {
- return false;
- }
- } else {
- return false;
- }
- }
-
- /*
- * Finds a system apk which had a broadcast receiver listening to a particular action.
- * @param action intent action used to find the apk
- * @return a pair of apk package name and the resources.
- */
- static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
- final Intent intent = new Intent(action);
- for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
- if (info.activityInfo != null &&
- (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- final String packageName = info.activityInfo.packageName;
- try {
- final Resources res = pm.getResourcesForApplication(packageName);
- return Pair.create(packageName, res);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Failed to find resources for " + packageName);
- }
- }
- }
- return null;
- }
-
/**
* Trims the string, removing all whitespace at the beginning and end of the string.
* Non-breaking whitespaces are also removed.
@@ -454,51 +376,10 @@
return (int) Math.ceil(fm.bottom - fm.top);
}
- /**
- * Convenience println with multiple args.
- */
- public static void println(String key, Object... args) {
- StringBuilder b = new StringBuilder();
- b.append(key);
- b.append(": ");
- boolean isFirstArgument = true;
- for (Object arg : args) {
- if (isFirstArgument) {
- isFirstArgument = false;
- } else {
- b.append(", ");
- }
- b.append(arg);
- }
- System.out.println(b.toString());
- }
-
public static boolean isRtl(Resources res) {
return res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
- /**
- * Returns true if the intent is a valid launch intent for a launcher activity of an app.
- * This is used to identify shortcuts which are different from the ones exposed by the
- * applications' manifest file.
- *
- * @param launchIntent The intent that will be launched when the shortcut is clicked.
- */
- public static boolean isLauncherAppTarget(Intent launchIntent) {
- if (launchIntent != null
- && Intent.ACTION_MAIN.equals(launchIntent.getAction())
- && launchIntent.getComponent() != null
- && launchIntent.getCategories() != null
- && launchIntent.getCategories().size() == 1
- && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
- && TextUtils.isEmpty(launchIntent.getDataString())) {
- // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE.
- Bundle extras = launchIntent.getExtras();
- return extras == null || extras.keySet().isEmpty();
- }
- return false;
- }
-
public static float dpiFromPx(int size, DisplayMetrics metrics){
float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
return (size / densityRatio);
@@ -601,18 +482,6 @@
return context.getSystemService(WallpaperManager.class).isSetWallpaperAllowed();
}
- public static void closeSilently(Closeable c) {
- if (c != null) {
- try {
- c.close();
- } catch (IOException e) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
- Log.d(TAG, "Error closing", e);
- }
- }
- }
- }
-
public static boolean isBinderSizeError(Exception e) {
return e.getCause() instanceof TransactionTooLargeException
|| e.getCause() instanceof DeadObjectException;
@@ -657,7 +526,7 @@
boolean flattenDrawable, Object[] outObj) {
LauncherAppState appState = LauncherAppState.getInstance(launcher);
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(launcher)
+ LauncherActivityInfo activityInfo = launcher.getSystemService(LauncherApps.class)
.resolveActivity(info.getIntent(), info.user);
outObj[0] = activityInfo;
return (activityInfo != null) ? appState.getIconCache()
@@ -715,7 +584,7 @@
LauncherIcons li = LauncherIcons.obtain(appState.getContext());
Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
li.recycle();
- float badgeSize = launcher.getResources().getDimension(R.dimen.profile_badge_size);
+ float badgeSize = iconSize * LauncherIcons.getBadgeSizeForIconSize(iconSize);
float insetFraction = (iconSize - badgeSize) / iconSize;
return new InsetDrawable(new FastBitmapDrawable(badge),
insetFraction, insetFraction, 0, 0);
@@ -727,25 +596,6 @@
}
}
- public static int[] getIntArrayFromString(String tokenized) {
- StringTokenizer tokenizer = new StringTokenizer(tokenized, ",");
- int[] array = new int[tokenizer.countTokens()];
- int count = 0;
- while (tokenizer.hasMoreTokens()) {
- array[count] = Integer.parseInt(tokenizer.nextToken().trim());
- count++;
- }
- return array;
- }
-
- public static String getStringFromIntArray(int[] array) {
- StringBuilder str = new StringBuilder();
- for (int value : array) {
- str.append(value).append(",");
- }
- return str.toString();
- }
-
public static float squaredHypot(float x, float y) {
return x * x + y * y;
}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 6d1bc1a..c5e74ef 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -1,5 +1,8 @@
package com.android.launcher3;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
@@ -23,21 +26,24 @@
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.CancellationSignal;
-import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.Pair;
+
+import androidx.annotation.Nullable;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.icons.GraphicsUtils;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShadowGenerator;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Executors;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SQLiteCacheHelper;
@@ -50,11 +56,8 @@
import java.util.HashSet;
import java.util.Set;
import java.util.WeakHashMap;
-import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
-import androidx.annotation.Nullable;
-
public class WidgetPreviewLoader {
private static final String TAG = "WidgetPreviewLoader";
@@ -68,23 +71,18 @@
* Note: synchronized block used for this variable is expensive and the block should always
* be posted to a background thread.
*/
- @Thunk final Set<Bitmap> mUnusedBitmaps =
- Collections.newSetFromMap(new WeakHashMap<Bitmap, Boolean>());
+ @Thunk final Set<Bitmap> mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap<>());
private final Context mContext;
private final IconCache mIconCache;
private final UserManagerCompat mUserManager;
private final CacheDb mDb;
- private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
- @Thunk final Handler mWorkerHandler;
-
public WidgetPreviewLoader(Context context, IconCache iconCache) {
mContext = context;
mIconCache = iconCache;
mUserManager = UserManagerCompat.getInstance(context);
mDb = new CacheDb(context);
- mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
}
/**
@@ -99,7 +97,7 @@
WidgetCacheKey key = new WidgetCacheKey(item.componentName, item.user, size);
PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller);
- task.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
+ task.executeOnExecutor(Executors.THREAD_POOL_EXECUTOR);
CancellationSignal signal = new CancellationSignal();
signal.setOnCancelListener(task);
@@ -292,14 +290,25 @@
return null;
}
- private Bitmap generatePreview(BaseActivity launcher, WidgetItem item, Bitmap recycle,
+ /**
+ * Returns generatedPreview for a widget and if the preview should be saved in persistent
+ * storage.
+ * @param launcher
+ * @param item
+ * @param recycle
+ * @param previewWidth
+ * @param previewHeight
+ * @return Pair<Bitmap, Boolean>
+ */
+ private Pair<Bitmap, Boolean> generatePreview(BaseActivity launcher, WidgetItem item,
+ Bitmap recycle,
int previewWidth, int previewHeight) {
if (item.widgetInfo != null) {
return generateWidgetPreview(launcher, item.widgetInfo,
previewWidth, recycle, null);
} else {
- return generateShortcutPreview(launcher, item.activityInfo,
- previewWidth, previewHeight, recycle);
+ return new Pair<>(generateShortcutPreview(launcher, item.activityInfo,
+ previewWidth, previewHeight, recycle), false);
}
}
@@ -312,9 +321,10 @@
* @param maxPreviewWidth width of the preview on either workspace or tray
* @param preview bitmap that can be recycled
* @param preScaledWidthOut return the width of the returned bitmap
- * @return
+ * @return Pair<Bitmap (the preview) , Boolean (should be stored in db)>
*/
- public Bitmap generateWidgetPreview(BaseActivity launcher, LauncherAppWidgetProviderInfo info,
+ public Pair<Bitmap, Boolean> generateWidgetPreview(BaseActivity launcher,
+ LauncherAppWidgetProviderInfo info,
int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) {
// Load the preview image if possible
if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
@@ -344,6 +354,8 @@
int previewWidth;
int previewHeight;
+ boolean savePreviewImage = widgetPreviewExists || info.previewImage == 0;
+
if (widgetPreviewExists && drawable.getIntrinsicWidth() > 0
&& drawable.getIntrinsicHeight() > 0) {
previewWidth = drawable.getIntrinsicWidth();
@@ -430,10 +442,12 @@
icon.setBounds(hoffset, yoffset, hoffset + iconSize, yoffset + iconSize);
icon.draw(c);
}
- } catch (Resources.NotFoundException e) { }
+ } catch (Resources.NotFoundException e) {
+ savePreviewImage = false;
+ }
c.setBitmap(null);
}
- return preview;
+ return new Pair<>(preview, savePreviewImage);
}
private RectF drawBoxWithShadow(Canvas c, int width, int height) {
@@ -453,7 +467,7 @@
private Bitmap generateShortcutPreview(BaseActivity launcher, ShortcutConfigActivityInfo info,
int maxWidth, int maxHeight, Bitmap preview) {
- int iconSize = launcher.getDeviceProfile().iconSizePx;
+ int iconSize = launcher.getDeviceProfile().allAppsIconSizePx;
int padding = launcher.getResources()
.getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
@@ -494,12 +508,7 @@
private Drawable mutateOnMainThread(final Drawable drawable) {
try {
- return mMainThreadExecutor.submit(new Callable<Drawable>() {
- @Override
- public Drawable call() throws Exception {
- return drawable.mutate();
- }
- }).get();
+ return MAIN_EXECUTOR.submit(drawable::mutate).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
@@ -541,6 +550,8 @@
@Thunk long[] mVersions;
@Thunk Bitmap mBitmapToRecycle;
+ private boolean mSaveToDB = false;
+
PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth,
int previewHeight, WidgetCell caller) {
mKey = key;
@@ -596,7 +607,10 @@
: null;
// it's not in the db... we need to generate it
- preview = generatePreview(mActivity, mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight);
+ Pair<Bitmap, Boolean> pair = generatePreview(mActivity, mInfo, unusedBitmap,
+ mPreviewWidth, mPreviewHeight);
+ preview = pair.first;
+ this.mSaveToDB = pair.second;
}
return preview;
}
@@ -607,10 +621,10 @@
// Write the generated preview to the DB in the worker thread
if (mVersions != null) {
- mWorkerHandler.post(new Runnable() {
+ MODEL_EXECUTOR.post(new Runnable() {
@Override
public void run() {
- if (!isCancelled()) {
+ if (!isCancelled() && mSaveToDB) {
// If we are still using this preview, then write it to the DB and then
// let the normal clear mechanism recycle the bitmap
writeToDb(mKey, mVersions, preview);
@@ -637,7 +651,7 @@
// recycled set immediately. Otherwise, it will be recycled after the preview is written
// to disk.
if (preview != null) {
- mWorkerHandler.post(new Runnable() {
+ MODEL_EXECUTOR.post(new Runnable() {
@Override
public void run() {
synchronized (mUnusedBitmaps) {
@@ -658,7 +672,7 @@
// in the tasks's onCancelled() call, and if cancelled while the task is writing to
// disk, it will be cancelled in the task's onPostExecute() call.
if (mBitmapToRecycle != null) {
- mWorkerHandler.post(new Runnable() {
+ MODEL_EXECUTOR.post(new Runnable() {
@Override
public void run() {
synchronized (mUnusedBitmaps) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 6612662..eca5d12 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -60,16 +60,15 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;
-import com.android.launcher3.Launcher.LauncherOverlay;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -90,8 +89,8 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.Executors;
import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageUserKey;
@@ -101,6 +100,7 @@
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.PendingAppWidgetHostView;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
import java.util.ArrayList;
import java.util.HashSet;
@@ -133,9 +133,6 @@
private static final int DEFAULT_PAGE = 0;
- public static final boolean MAP_NO_RECURSE = false;
- public static final boolean MAP_RECURSE = true;
-
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
@@ -968,6 +965,9 @@
onOverlayScrollChanged(0);
}
+ public boolean hasOverlay() {
+ return mLauncherOverlay != null;
+ }
private boolean isScrollingOverlay() {
return mLauncherOverlay != null &&
@@ -1038,6 +1038,13 @@
}
@Override
+ protected boolean onOverscroll(int amount) {
+ // Enforce overscroll on -1 direction
+ if ((amount > 0 && !mIsRtl) || (amount < 0 && mIsRtl)) return false;
+ return super.onOverscroll(amount);
+ }
+
+ @Override
protected boolean shouldFlingForVelocity(int velocityX) {
// When the overlay is moving, the fling or settle transition is controlled by the overlay.
return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 &&
@@ -1161,7 +1168,7 @@
}
protected void setWallpaperDimension() {
- Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+ Executors.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
final Point size = LauncherAppState.getIDP(getContext()).defaultWallpaperSize;
@@ -1464,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();
@@ -1489,7 +1499,7 @@
Rect dragRect = null;
if (child instanceof BubbleTextView) {
dragRect = new Rect();
- ((BubbleTextView) child).getIconBounds(dragRect);
+ BubbleTextView.getIconBounds(child, dragRect, grid.iconSizePx);
dragLayerY += dragRect.top;
// Note: The dragRect is used to calculate drag layer offsets, but the
// dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
@@ -2532,22 +2542,22 @@
View view;
switch (info.itemType) {
- case ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- if (info.container == NO_ID && info instanceof AppInfo) {
- // Came from all apps -- make a copy
- info = ((AppInfo) info).makeWorkspaceItem();
- d.dragInfo = info;
- }
- view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info);
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
- (FolderInfo) info);
- break;
- default:
- throw new IllegalStateException("Unknown item type: " + info.itemType);
+ case ITEM_TYPE_APPLICATION:
+ case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ if (info instanceof AppInfo) {
+ // Came from all apps -- make a copy
+ info = ((AppInfo) info).makeWorkspaceItem();
+ d.dragInfo = info;
+ }
+ view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info);
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+ view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
+ (FolderInfo) info);
+ break;
+ default:
+ throw new IllegalStateException("Unknown item type: " + info.itemType);
}
// First we find the cell nearest to point at which the item is
@@ -2805,10 +2815,27 @@
}
/**
+ * Removed widget from workspace by appWidgetId
+ * @param appWidgetId
+ */
+ public void removeWidget(int appWidgetId) {
+ mapOverItems((info, view) -> {
+ if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo appWidgetInfo = (LauncherAppWidgetInfo) info;
+ if (appWidgetInfo.appWidgetId == appWidgetId) {
+ mLauncher.removeItem(view, appWidgetInfo, true);
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+
+ /**
* Removes all folder listeners
*/
public void removeFolderListeners() {
- mapOverItems(false, new ItemOperator() {
+ mapOverItems(new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View view) {
if (view instanceof FolderIcon) {
@@ -2960,7 +2987,7 @@
public View getFirstMatch(final ItemOperator operator) {
final View[] value = new View[1];
- mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+ mapOverItems(new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View v) {
if (operator.evaluate(info, v)) {
@@ -2983,7 +3010,7 @@
final View[] matches = new View[operators.length];
// For efficiency, the outer loop should be CellLayout.
for (CellLayout cellLayout : cellLayouts) {
- mapOverCellLayout(MAP_NO_RECURSE, cellLayout, (info, v) -> {
+ mapOverCellLayout(cellLayout, (info, v) -> {
for (int i = 0; i < operators.length; ++i) {
if (matches[i] == null && operators[i].evaluate(info, v)) {
matches[i] = v;
@@ -3008,7 +3035,7 @@
}
void clearDropTargets() {
- mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+ mapOverItems(new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View v) {
if (v instanceof DropTarget) {
@@ -3053,10 +3080,12 @@
} else if (itemToRemove.container >= 0) {
// The item may belong to a folder.
View parent = idToViewMap.get(itemToRemove.container);
- if (parent != null) {
+ if (parent instanceof FolderIcon) {
FolderInfo folderInfo = (FolderInfo) parent.getTag();
- folderInfo.prepareAutoUpdate();
folderInfo.remove((WorkspaceItemInfo) itemToRemove, false);
+ if (((FolderIcon) parent).getFolder().isOpen()) {
+ ((FolderIcon) parent).getFolder().close(false /* animate */);
+ }
}
}
}
@@ -3080,18 +3109,17 @@
/**
* Map the operator over the shortcuts and widgets, return the first-non-null value.
*
- * @param recurse true: iterate over folder children. false: op get the folders themselves.
* @param op the operator to map over the shortcuts
*/
- public void mapOverItems(boolean recurse, ItemOperator op) {
+ public void mapOverItems(ItemOperator op) {
for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
- if (mapOverCellLayout(recurse, layout, op)) {
+ if (mapOverCellLayout(layout, op)) {
return;
}
}
}
- private boolean mapOverCellLayout(boolean recurse, CellLayout layout, ItemOperator op) {
+ private boolean mapOverCellLayout(CellLayout layout, ItemOperator op) {
// TODO(b/128460496) Potential race condition where layout is not yet loaded
if (layout == null) {
return false;
@@ -3101,103 +3129,68 @@
final int itemCount = container.getChildCount();
for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
View item = container.getChildAt(itemIdx);
- ItemInfo info = (ItemInfo) item.getTag();
- if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
- FolderIcon folder = (FolderIcon) item;
- ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
- // map over all the children in the folder
- final int childCount = folderChildren.size();
- for (int childIdx = 0; childIdx < childCount; childIdx++) {
- View child = folderChildren.get(childIdx);
- info = (ItemInfo) child.getTag();
- if (op.evaluate(info, child)) {
- return true;
- }
- }
- } else {
- if (op.evaluate(info, item)) {
- return true;
- }
+ if (op.evaluate((ItemInfo) item.getTag(), item)) {
+ return true;
}
}
return false;
}
void updateShortcuts(ArrayList<WorkspaceItemInfo> shortcuts) {
- int total = shortcuts.size();
- final HashSet<WorkspaceItemInfo> updates = new HashSet<>(total);
- final IntSet folderIds = new IntSet();
+ final HashSet<WorkspaceItemInfo> updates = new HashSet<>(shortcuts);
+ ItemOperator op = (info, v) -> {
+ if (v instanceof BubbleTextView && updates.contains(info)) {
+ WorkspaceItemInfo si = (WorkspaceItemInfo) info;
+ BubbleTextView shortcut = (BubbleTextView) v;
+ Drawable oldIcon = shortcut.getIcon();
+ boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
+ && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
+ shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState);
+ } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
+ ((FolderIcon) v).updatePreviewItems(updates::contains);
+ }
- for (int i = 0; i < total; i++) {
- WorkspaceItemInfo s = shortcuts.get(i);
- updates.add(s);
- folderIds.add(s.container);
+ // Iterate all items
+ return false;
+ };
+
+ mapOverItems(op);
+ Folder openFolder = Folder.getOpen(mLauncher);
+ if (openFolder != null) {
+ openFolder.iterateOverItems(op);
}
-
- mapOverItems(MAP_RECURSE, new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View v) {
- if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView &&
- updates.contains(info)) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) info;
- BubbleTextView shortcut = (BubbleTextView) v;
- Drawable oldIcon = shortcut.getIcon();
- boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
- && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
- shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState);
- }
- // process all the shortcuts
- return false;
- }
- });
-
- // Update folder icons
- mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View v) {
- if (info instanceof FolderInfo && folderIds.contains(info.id)) {
- ((FolderInfo) info).itemsChanged(false);
- }
- // process all the shortcuts
- return false;
- }
- });
}
public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
final PackageUserKey packageUserKey = new PackageUserKey(null, null);
- final IntSet folderIds = new IntSet();
- mapOverItems(MAP_RECURSE, new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View v) {
- if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) {
- if (!packageUserKey.updateFromItemInfo(info)
- || updatedDots.test(packageUserKey)) {
- ((BubbleTextView) v).applyDotState(info, true /* animate */);
- folderIds.add(info.container);
- }
- }
- // process all the shortcuts
- return false;
- }
- });
+ Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
+ || updatedDots.test(packageUserKey);
- // Update folder icons
- mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View v) {
- if (info instanceof FolderInfo && folderIds.contains(info.id)
- && v instanceof FolderIcon) {
+ ItemOperator op = (info, v) -> {
+ if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) {
+ if (matcher.test(info)) {
+ ((BubbleTextView) v).applyDotState(info, true /* animate */);
+ }
+ } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
+ FolderInfo fi = (FolderInfo) info;
+ if (fi.contents.stream().anyMatch(matcher)) {
FolderDotInfo folderDotInfo = new FolderDotInfo();
- for (WorkspaceItemInfo si : ((FolderInfo) info).contents) {
+ for (WorkspaceItemInfo si : fi.contents) {
folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si));
}
((FolderIcon) v).setDotInfo(folderDotInfo);
}
- // process all the shortcuts
- return false;
}
- });
+
+ // process all the shortcuts
+ return false;
+ };
+
+ mapOverItems(op);
+ Folder folder = Folder.getOpen(mLauncher);
+ if (folder != null) {
+ folder.iterateOverItems(op);
+ }
}
public void removeAbandonedPromise(String packageName, UserHandle user) {
@@ -3209,21 +3202,25 @@
}
public void updateRestoreItems(final HashSet<ItemInfo> updates) {
- mapOverItems(MAP_RECURSE, new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View v) {
- if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
- && updates.contains(info)) {
- ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
- } else if (v instanceof PendingAppWidgetHostView
- && info instanceof LauncherAppWidgetInfo
- && updates.contains(info)) {
- ((PendingAppWidgetHostView) v).applyState();
- }
- // process all the shortcuts
- return false;
+ ItemOperator op = (info, v) -> {
+ if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
+ && updates.contains(info)) {
+ ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
+ } else if (v instanceof PendingAppWidgetHostView
+ && info instanceof LauncherAppWidgetInfo
+ && updates.contains(info)) {
+ ((PendingAppWidgetHostView) v).applyState();
+ } else if (v instanceof FolderIcon && info instanceof FolderInfo) {
+ ((FolderIcon) v).updatePreviewItems(updates::contains);
}
- });
+ // process all the shortcuts
+ return false;
+ };
+ mapOverItems(op);
+ Folder folder = Folder.getOpen(mLauncher);
+ if (folder != null) {
+ folder.iterateOverItems(op);
+ }
}
public void widgetsRestored(final ArrayList<LauncherAppWidgetInfo> changedInfo) {
@@ -3247,7 +3244,7 @@
} else {
// widgetRefresh will automatically run when the packages are updated.
// For now just update the progress bars
- mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+ mapOverItems(new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View view) {
if (view instanceof PendingAppWidgetHostView
@@ -3371,7 +3368,7 @@
mRefreshPending = false;
ArrayList<PendingAppWidgetHostView> views = new ArrayList<>(mInfos.size());
- mapOverItems(MAP_NO_RECURSE, (info, view) -> {
+ mapOverItems((info, view) -> {
if (view instanceof PendingAppWidgetHostView && mInfos.contains(info)) {
views.add((PendingAppWidgetHostView) view);
}
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index 050a8be..23795c5 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -212,7 +212,7 @@
public ComponentName getTargetComponent() {
ComponentName cn = super.getTargetComponent();
if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT
- || hasStatusFlag(FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON))) {
+ || hasStatusFlag(FLAG_SUPPORTS_WEB_UI|FLAG_AUTOINSTALL_ICON|FLAG_RESTORED_ICON))) {
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
// a packageName. In that case create a dummy componentName instead of adding additional
// check everywhere.
@@ -221,4 +221,9 @@
}
return cn;
}
+
+ @Override
+ public ItemInfoWithIcon clone() {
+ return new WorkspaceItemInfo(this);
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 293b867..13b7b54 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -21,12 +21,10 @@
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Bundle;
import android.os.Process;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -34,6 +32,13 @@
import android.view.ViewGroup;
import android.view.WindowInsets;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.launcher3.AppInfo;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
@@ -45,10 +50,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -60,13 +62,6 @@
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
/**
* The all apps view container.
*/
@@ -163,16 +158,14 @@
}
private void onAppsUpdated() {
- if (FeatureFlags.ALL_APPS_TABS_ENABLED) {
- boolean hasWorkApps = false;
- for (AppInfo app : mAllAppsStore.getApps()) {
- if (mWorkMatcher.matches(app, null)) {
- hasWorkApps = true;
- break;
- }
+ boolean hasWorkApps = false;
+ for (AppInfo app : mAllAppsStore.getApps()) {
+ if (mWorkMatcher.matches(app, null)) {
+ hasWorkApps = true;
+ break;
}
- rebindAdapters(hasWorkApps);
}
+ rebindAdapters(hasWorkApps);
}
/**
@@ -312,6 +305,7 @@
+ grid.cellLayoutPaddingLeftRightPx;
for (int i = 0; i < mAH.length; i++) {
+ mAH[i].adapter.setAppsPerRow(grid.inv.numAllAppsColumns);
mAH[i].padding.bottom = insets.bottom;
mAH[i].padding.left = mAH[i].padding.right = leftRightPadding;
mAH[i].applyPadding();
@@ -615,17 +609,4 @@
&& verticalFadingEdge);
}
}
-
- @Override
- public boolean performAccessibilityAction(int action, Bundle arguments) {
- if (AccessibilityManagerCompat.processTestRequest(
- mLauncher, TestProtocol.GET_SCROLL_MESSAGE, action, arguments,
- response ->
- response.putInt(TestProtocol.SCROLL_Y_FIELD,
- getActiveRecyclerView().getCurrentScrollY()))) {
- return true;
- }
-
- return super.performAccessibilityAction(action, arguments);
- }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 3cfa0b1..bb21268 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -180,7 +180,7 @@
private final GridLayoutManager mGridLayoutMgr;
private final GridSpanSizer mGridSizer;
- private final int mAppsPerRow;
+ private int mAppsPerRow;
private BindViewCallback mBindViewCallback;
private OnFocusChangeListener mIconFocusListener;
@@ -200,7 +200,11 @@
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
mLayoutInflater = LayoutInflater.from(launcher);
- mAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
+ setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
+ }
+
+ public void setAppsPerRow(int appsPerRow) {
+ mAppsPerRow = appsPerRow;
mGridLayoutMgr.setSpanCount(mAppsPerRow);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
index 69068c6..5b73940 100644
--- a/src/com/android/launcher3/allapps/AllAppsPagedView.java
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -81,4 +81,9 @@
public boolean hasOverlappingRendering() {
return false;
}
+
+ @Override
+ protected boolean isVerticalScrollable() {
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index ca8dbeb..c4b2f68 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
+import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
+
import android.view.View;
import android.view.ViewGroup;
@@ -26,8 +29,7 @@
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
+import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -43,27 +45,33 @@
public static final int DEFER_UPDATES_TEST = 1 << 1;
private PackageUserKey mTempKey = new PackageUserKey(null, null);
- private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
+ private AppInfo mTempInfo = new AppInfo();
+
+ private AppInfo[] mApps = EMPTY_ARRAY;
+
private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
private int mDeferUpdatesFlags = 0;
private boolean mUpdatePending = false;
- public Collection<AppInfo> getApps() {
- return mComponentToAppMap.values();
+ public AppInfo[] getApps() {
+ return mApps;
}
/**
* Sets the current set of apps.
*/
- public void setApps(List<AppInfo> apps) {
- mComponentToAppMap.clear();
- addOrUpdateApps(apps);
+ public void setApps(AppInfo[] apps) {
+ mApps = apps;
+ notifyUpdate();
}
public AppInfo getApp(ComponentKey key) {
- return mComponentToAppMap.get(key);
+ mTempInfo.componentName = key.componentName;
+ mTempInfo.user = key.user;
+ int index = Arrays.binarySearch(mApps, mTempInfo, COMPONENT_KEY_COMPARATOR);
+ return index < 0 ? null : mApps[index];
}
public void enableDeferUpdates(int flag) {
@@ -86,27 +94,6 @@
return mDeferUpdatesFlags;
}
- /**
- * Adds or updates existing apps in the list
- */
- public void addOrUpdateApps(List<AppInfo> apps) {
- for (AppInfo app : apps) {
- mComponentToAppMap.put(app.toComponentKey(), app);
- }
- notifyUpdate();
- }
-
- /**
- * Removes some apps from the list.
- */
- public void removeApps(List<AppInfo> apps) {
- for (AppInfo app : apps) {
- mComponentToAppMap.remove(app.toComponentKey());
- }
- notifyUpdate();
- }
-
-
private void notifyUpdate() {
if (mDeferUpdatesFlags != 0) {
mUpdatePending = true;
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 1369441..0c4be62 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -21,16 +21,13 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LabelComparator;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -145,9 +142,7 @@
// The of ordered component names as a result of a search query
private ArrayList<ComponentKey> mSearchResults;
- private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
private AllAppsGridAdapter mAdapter;
- private AlphabeticIndexCompat mIndexer;
private AppInfoComparator mAppNameComparator;
private final int mNumAppsPerRow;
private int mNumAppRowsInAdapter;
@@ -156,7 +151,6 @@
public AlphabeticalAppsList(Context context, AllAppsStore appsStore, boolean isWork) {
mAllAppsStore = appsStore;
mLauncher = Launcher.getLauncher(context);
- mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppInfoComparator(context);
mIsWork = isWork;
mNumAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
@@ -263,7 +257,7 @@
TreeMap<String, ArrayList<AppInfo>> sectionMap = new TreeMap<>(new LabelComparator());
for (AppInfo info : mApps) {
// Add the section to the cache
- String sectionName = getAndUpdateCachedSectionName(info.title);
+ String sectionName = info.sectionName;
// Add it to the mapping
ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
@@ -279,12 +273,6 @@
for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
mApps.addAll(entry.getValue());
}
- } else {
- // Just compute the section headers for use below
- for (AppInfo info : mApps) {
- // Add the section to the cache
- getAndUpdateCachedSectionName(info.title);
- }
}
// Recompose the set of adapter items from the current set of apps
@@ -320,7 +308,7 @@
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
// ordered set of sections
for (AppInfo info : getFiltersAppInfos()) {
- String sectionName = getAndUpdateCachedSectionName(info.title);
+ String sectionName = info.sectionName;
// Create a new section if the section names do not match
if (!sectionName.equals(lastSectionName)) {
@@ -428,18 +416,4 @@
}
return result;
}
-
- /**
- * Returns the cached section name for the given title, recomputing and updating the cache if
- * the title has no cached section name.
- */
- private String getAndUpdateCachedSectionName(CharSequence title) {
- String sectionName = mCachedSectionNames.get(title);
- if (sectionName == null) {
- sectionName = mIndexer.computeSectionName(title);
- mCachedSectionNames.put(title, sectionName);
- }
- return sectionName;
- }
-
}
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 8c59626..dc2f7bd 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -31,12 +31,10 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.states.InternalStateHandler;
/**
* Abstract base class of floating view responsible for showing discovery bounce animation
@@ -181,7 +179,7 @@
if (withDelay) {
new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false), DELAY_MS);
return;
- } else if (InternalStateHandler.hasPending()
+ } else if (Launcher.ACTIVITY_TRACKER.hasPending()
|| AbstractFloatingView.getTopOpenView(launcher) != null) {
// TODO: Move these checks to the top and call this method after invalidate handler.
return;
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 81c95cb..d47a40e 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -21,12 +21,9 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.Utilities;
-
-import java.util.function.Consumer;
+import com.android.launcher3.testing.TestProtocol;
public class AccessibilityManagerCompat {
@@ -103,24 +100,6 @@
return accessibilityManager;
}
- public static boolean processTestRequest(Context context, String eventTag, int action,
- Bundle request, Consumer<Bundle> responseFiller) {
- final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
- if (accessibilityManager == null) return false;
-
- // The test sends a request via a ACTION_SET_TEXT.
- if (action == AccessibilityNodeInfo.ACTION_SET_TEXT &&
- eventTag.equals(request.getCharSequence(
- AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE))) {
- final Bundle response = new Bundle();
- responseFiller.accept(response);
- AccessibilityManagerCompat.sendEventToTest(
- accessibilityManager, eventTag + TestProtocol.RESPONSE_MESSAGE_POSTFIX, response);
- return true;
- }
- return false;
- }
-
public static int getRecommendedTimeoutMillis(Context context, int originalTimeout, int flags) {
if (Utilities.ATLEAST_Q) {
return getManager(context).getRecommendedTimeoutMillis(originalTimeout, flags);
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index dfdcc70..46c9006 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -3,7 +3,6 @@
import android.content.Context;
import android.icu.text.AlphabeticIndex;
import android.os.LocaleList;
-import android.util.Log;
import com.android.launcher3.Utilities;
@@ -12,28 +11,32 @@
import androidx.annotation.NonNull;
public class AlphabeticIndexCompat {
- private static final String TAG = "AlphabeticIndexCompat";
private static final String MID_DOT = "\u2219";
- private final BaseIndex mBaseIndex;
private final String mDefaultMiscLabel;
+ private final AlphabeticIndex.ImmutableIndex mBaseIndex;
+
public AlphabeticIndexCompat(Context context) {
- BaseIndex index = null;
+ this(context.getResources().getConfiguration().getLocales());
+ }
- try {
- index = new AlphabeticIndexVN(context);
- } catch (Exception e) {
- Log.d(TAG, "Unable to load the system index", e);
+ public AlphabeticIndexCompat(LocaleList locales) {
+ int localeCount = locales.size();
+
+ Locale primaryLocale = localeCount == 0 ? Locale.ENGLISH : locales.get(0);
+ AlphabeticIndex indexBuilder = new AlphabeticIndex(primaryLocale);
+ for (int i = 1; i < localeCount; i++) {
+ indexBuilder.addLabels(locales.get(i));
}
+ indexBuilder.addLabels(Locale.ENGLISH);
+ mBaseIndex = indexBuilder.buildImmutableIndex();
- mBaseIndex = index == null ? new BaseIndex() : index;
-
- if (context.getResources().getConfiguration().locale
- .getLanguage().equals(Locale.JAPANESE.getLanguage())) {
+ if (primaryLocale.getLanguage().equals(Locale.JAPANESE.getLanguage())) {
// Japanese character 他 ("misc")
mDefaultMiscLabel = "\u4ed6";
- // TODO(winsonc, omakoto): We need to handle Japanese sections better, especially the kanji
+ // TODO(winsonc, omakoto): We need to handle Japanese sections better,
+ // especially the kanji
} else {
// Dot
mDefaultMiscLabel = MID_DOT;
@@ -45,7 +48,7 @@
*/
public String computeSectionName(@NonNull CharSequence cs) {
String s = Utilities.trim(cs);
- String sectionName = mBaseIndex.getBucketLabel(mBaseIndex.getBucketIndex(s));
+ String sectionName = mBaseIndex.getBucket(mBaseIndex.getBucketIndex(s)).getLabel();
if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) {
int c = s.codePointAt(0);
boolean startsWithDigit = Character.isDigit(c);
@@ -66,71 +69,4 @@
}
return sectionName;
}
-
- /**
- * Base class to support Alphabetic indexing if not supported by the framework.
- * TODO(winsonc): disable for non-english locales
- */
- private static class BaseIndex {
-
- private static final String BUCKETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-";
- private static final int UNKNOWN_BUCKET_INDEX = BUCKETS.length() - 1;
-
- /**
- * Returns the index of the bucket in which the given string should appear.
- */
- protected int getBucketIndex(@NonNull String s) {
- if (s.isEmpty()) {
- return UNKNOWN_BUCKET_INDEX;
- }
- int index = BUCKETS.indexOf(s.substring(0, 1).toUpperCase());
- if (index != -1) {
- return index;
- }
- return UNKNOWN_BUCKET_INDEX;
- }
-
- /**
- * Returns the label for the bucket at the given index (as returned by getBucketIndex).
- */
- protected String getBucketLabel(int index) {
- return BUCKETS.substring(index, index + 1);
- }
- }
-
- /**
- * Implementation based on {@link AlphabeticIndex}.
- */
- private static class AlphabeticIndexVN extends BaseIndex {
-
- private final AlphabeticIndex.ImmutableIndex mAlphabeticIndex;
-
- public AlphabeticIndexVN(Context context) {
- LocaleList locales = context.getResources().getConfiguration().getLocales();
- int localeCount = locales.size();
-
- Locale primaryLocale = localeCount == 0 ? Locale.ENGLISH : locales.get(0);
- AlphabeticIndex indexBuilder = new AlphabeticIndex(primaryLocale);
- for (int i = 1; i < localeCount; i++) {
- indexBuilder.addLabels(locales.get(i));
- }
- indexBuilder.addLabels(Locale.ENGLISH);
-
- mAlphabeticIndex = indexBuilder.buildImmutableIndex();
- }
-
- /**
- * Returns the index of the bucket in which {@param s} should appear.
- */
- protected int getBucketIndex(String s) {
- return mAlphabeticIndex.getBucketIndex(s);
- }
-
- /**
- * Returns the label for the bucket at the given index
- */
- protected String getBucketLabel(int index) {
- return mAlphabeticIndex.getBucket(index).getLabel();
- }
- }
}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index 3243256..fc5d11c 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -23,19 +23,18 @@
import android.os.Bundle;
import android.os.UserHandle;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.custom.CustomWidgetParser;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.HashMap;
import java.util.List;
-import androidx.annotation.Nullable;
-
public abstract class AppWidgetManagerCompat {
private static final Object sInstanceLock = new Object();
@@ -63,11 +62,9 @@
}
public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) {
- if (FeatureFlags.ENABLE_CUSTOM_WIDGETS
- && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
- return CustomWidgetParser.getWidgetProvider(mContext, appWidgetId);
+ if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
+ return CustomWidgetManager.INSTANCE.get(mContext).getWidgetProvider(appWidgetId);
}
-
AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index 1065748..8f6500b 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -24,12 +24,15 @@
import android.os.UserHandle;
import android.os.UserManager;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.custom.CustomWidgetParser;
+import com.android.launcher3.widget.custom.CustomAppWidgetProviderInfo;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -37,8 +40,6 @@
import java.util.Iterator;
import java.util.List;
-import androidx.annotation.Nullable;
-
class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
private final UserManager mUserManager;
@@ -50,18 +51,15 @@
@Override
public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return Collections.emptyList();
}
if (packageUser == null) {
- ArrayList<AppWidgetProviderInfo> providers = new ArrayList<AppWidgetProviderInfo>();
+ ArrayList<AppWidgetProviderInfo> providers = new ArrayList<>();
for (UserHandle user : mUserManager.getUserProfiles()) {
providers.addAll(mAppWidgetManager.getInstalledProvidersForProfile(user));
}
-
- if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) {
- providers.addAll(CustomWidgetParser.getCustomWidgets(mContext));
- }
+ providers.addAll(getCustomWidgets());
return providers;
}
// Only get providers for the given package/user.
@@ -74,9 +72,9 @@
}
}
- if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(packageUser.mUser)
+ if (Process.myUserHandle().equals(packageUser.mUser)
&& mContext.getPackageName().equals(packageUser.mPackageName)) {
- providers.addAll(CustomWidgetParser.getCustomWidgets(mContext));
+ providers.addAll(getCustomWidgets());
}
return providers;
}
@@ -84,12 +82,10 @@
@Override
public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
Bundle options) {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return false;
}
-
- if (FeatureFlags.ENABLE_CUSTOM_WIDGETS
- && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
+ if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
return true;
}
return mAppWidgetManager.bindAppWidgetIdIfAllowed(
@@ -98,7 +94,7 @@
@Override
public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandle user) {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return null;
}
for (AppWidgetProviderInfo info :
@@ -108,9 +104,8 @@
}
}
- if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(user)) {
- for (LauncherAppWidgetProviderInfo info :
- CustomWidgetParser.getCustomWidgets(mContext)) {
+ if (Process.myUserHandle().equals(user)) {
+ for (LauncherAppWidgetProviderInfo info : getCustomWidgets()) {
if (info.provider.equals(provider)) {
return info;
}
@@ -122,7 +117,7 @@
@Override
public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return result;
}
for (UserHandle user : mUserManager.getUserProfiles()) {
@@ -131,13 +126,13 @@
result.put(new ComponentKey(info.provider, user), info);
}
}
-
- if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) {
- for (LauncherAppWidgetProviderInfo info :
- CustomWidgetParser.getCustomWidgets(mContext)) {
- result.put(new ComponentKey(info.provider, info.getProfile()), info);
- }
+ for (LauncherAppWidgetProviderInfo info : getCustomWidgets()) {
+ result.put(new ComponentKey(info.provider, info.getProfile()), info);
}
return result;
}
+
+ List<CustomAppWidgetProviderInfo> getCustomWidgets() {
+ return CustomWidgetManager.INSTANCE.get(mContext).getCustomWidgets();
+ }
}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
index b7b0563..2814afc 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
@@ -19,14 +19,14 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import com.android.launcher3.config.FeatureFlags;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.PackageUserKey;
import java.util.Collections;
import java.util.List;
-import androidx.annotation.Nullable;
-
class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL {
AppWidgetManagerCompatVO(Context context) {
@@ -35,7 +35,7 @@
@Override
public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return Collections.emptyList();
}
if (packageUser == null) {
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
deleted file mode 100644
index 58fc73d..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2014 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.compat;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.UserHandle;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.PackageUserKey;
-
-import java.util.List;
-
-import androidx.annotation.Nullable;
-
-public abstract class LauncherAppsCompat {
-
- public interface OnAppsChangedCallbackCompat {
- void onPackageRemoved(String packageName, UserHandle user);
- void onPackageAdded(String packageName, UserHandle user);
- void onPackageChanged(String packageName, UserHandle user);
- void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing);
- void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing);
- void onPackagesSuspended(String[] packageNames, UserHandle user);
- void onPackagesUnsuspended(String[] packageNames, UserHandle user);
- void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
- UserHandle user);
- }
-
- protected LauncherAppsCompat() {
- }
-
- private static LauncherAppsCompat sInstance;
- private static final Object sInstanceLock = new Object();
-
- public static LauncherAppsCompat getInstance(Context context) {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- if (Utilities.ATLEAST_Q) {
- sInstance = new LauncherAppsCompatVQ(context.getApplicationContext());
- } else if (Utilities.ATLEAST_OREO) {
- sInstance = new LauncherAppsCompatVO(context.getApplicationContext());
- } else {
- sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
- }
- }
- return sInstance;
- }
- }
-
- public abstract List<LauncherActivityInfo> getActivityList(String packageName,
- UserHandle user);
- public abstract LauncherActivityInfo resolveActivity(Intent intent,
- UserHandle user);
- public abstract void startActivityForProfile(ComponentName component, UserHandle user,
- Rect sourceBounds, Bundle opts);
- public abstract ApplicationInfo getApplicationInfo(
- String packageName, int flags, UserHandle user);
- public abstract void showAppDetailsForProfile(ComponentName component, UserHandle user,
- Rect sourceBounds, Bundle opts);
- public abstract void addOnAppsChangedCallback(OnAppsChangedCallbackCompat listener);
- public abstract void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat listener);
- public abstract boolean isPackageEnabledForProfile(String packageName, UserHandle user);
- public abstract boolean isActivityEnabledForProfile(ComponentName component,
- UserHandle user);
- public abstract List<ShortcutConfigActivityInfo> getCustomShortcutActivityList(
- @Nullable PackageUserKey packageUser);
-
- public abstract List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
-}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
deleted file mode 100644
index 1885d8f..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2014 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.compat;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.PackageUserKey;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public class LauncherAppsCompatVL extends LauncherAppsCompat {
-
- protected final LauncherApps mLauncherApps;
- protected final Context mContext;
-
- private final ArrayMap<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks =
- new ArrayMap<>();
-
- LauncherAppsCompatVL(Context context) {
- mContext = context;
- mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
- }
-
- @Override
- public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
- return mLauncherApps.getActivityList(packageName, user);
- }
-
- @Override
- public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
- return mLauncherApps.resolveActivity(intent, user);
- }
-
- @Override
- public void startActivityForProfile(ComponentName component, UserHandle user,
- Rect sourceBounds, Bundle opts) {
- mLauncherApps.startMainActivity(component, user, sourceBounds, opts);
- }
-
- @Override
- public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user) {
- final boolean isPrimaryUser = Process.myUserHandle().equals(user);
- if (!isPrimaryUser && (flags == 0)) {
- // We are looking for an installed app on a secondary profile. Prior to O, the only
- // entry point for work profiles is through the LauncherActivity.
- List<LauncherActivityInfo> activityList =
- mLauncherApps.getActivityList(packageName, user);
- return activityList.size() > 0 ? activityList.get(0).getApplicationInfo() : null;
- }
- try {
- ApplicationInfo info =
- mContext.getPackageManager().getApplicationInfo(packageName, flags);
- // There is no way to check if the app is installed for managed profile. But for
- // primary profile, we can still have this check.
- if (isPrimaryUser && ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0)
- || !info.enabled) {
- return null;
- }
- return info;
- } catch (PackageManager.NameNotFoundException e) {
- // Package not found
- return null;
- }
- }
-
- @Override
- public void showAppDetailsForProfile(ComponentName component, UserHandle user,
- Rect sourceBounds, Bundle opts) {
- mLauncherApps.startAppDetailsActivity(component, user, sourceBounds, opts);
- }
-
- @Override
- public void addOnAppsChangedCallback(LauncherAppsCompat.OnAppsChangedCallbackCompat callback) {
- WrappedCallback wrappedCallback = new WrappedCallback(callback);
- synchronized (mCallbacks) {
- mCallbacks.put(callback, wrappedCallback);
- }
- mLauncherApps.registerCallback(wrappedCallback);
- }
-
- @Override
- public void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat callback) {
- final WrappedCallback wrappedCallback;
- synchronized (mCallbacks) {
- wrappedCallback = mCallbacks.remove(callback);
- }
- if (wrappedCallback != null) {
- mLauncherApps.unregisterCallback(wrappedCallback);
- }
- }
-
- @Override
- public boolean isPackageEnabledForProfile(String packageName, UserHandle user) {
- return mLauncherApps.isPackageEnabled(packageName, user);
- }
-
- @Override
- public boolean isActivityEnabledForProfile(ComponentName component, UserHandle user) {
- return mLauncherApps.isActivityEnabled(component, user);
- }
-
- private static class WrappedCallback extends LauncherApps.Callback {
- private final LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
-
- public WrappedCallback(LauncherAppsCompat.OnAppsChangedCallbackCompat callback) {
- mCallback = callback;
- }
-
- @Override
- public void onPackageRemoved(String packageName, UserHandle user) {
- mCallback.onPackageRemoved(packageName, user);
- }
-
- @Override
- public void onPackageAdded(String packageName, UserHandle user) {
- mCallback.onPackageAdded(packageName, user);
- }
-
- @Override
- public void onPackageChanged(String packageName, UserHandle user) {
- mCallback.onPackageChanged(packageName, user);
- }
-
- @Override
- public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
- mCallback.onPackagesAvailable(packageNames, user, replacing);
- }
-
- @Override
- public void onPackagesUnavailable(String[] packageNames, UserHandle user,
- boolean replacing) {
- mCallback.onPackagesUnavailable(packageNames, user, replacing);
- }
-
- @Override
- public void onPackagesSuspended(String[] packageNames, UserHandle user) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.APP_NOT_DISABLED, "onPackagesSuspended: " +
- Arrays.toString(packageNames));
- }
- mCallback.onPackagesSuspended(packageNames, user);
- }
-
- @Override
- public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
- mCallback.onPackagesUnsuspended(packageNames, user);
- }
-
- @Override
- public void onShortcutsChanged(@NonNull String packageName,
- @NonNull List<ShortcutInfo> shortcuts,
- @NonNull UserHandle user) {
- mCallback.onShortcutsChanged(packageName, shortcuts, user);
- }
- }
-
- @Override
- public List<ShortcutConfigActivityInfo> getCustomShortcutActivityList(
- @Nullable PackageUserKey packageUser) {
- List<ShortcutConfigActivityInfo> result = new ArrayList<>();
- if (packageUser != null && !packageUser.mUser.equals(Process.myUserHandle())) {
- return result;
- }
- PackageManager pm = mContext.getPackageManager();
- for (ResolveInfo info :
- pm.queryIntentActivities(new Intent(Intent.ACTION_CREATE_SHORTCUT), 0)) {
- if (packageUser == null || packageUser.mPackageName
- .equals(info.activityInfo.packageName)) {
- result.add(new ShortcutConfigActivityInfoVL(info.activityInfo));
- }
- }
- return result;
- }
-
- @Override
- public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
- return mContext.getPackageManager().getPackageInstaller().getAllSessions();
- }
-}
-
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
deleted file mode 100644
index 6e7a1bd..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ /dev/null
@@ -1,156 +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.compat;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.LauncherApps.PinItemRequest;
-import android.content.pm.PackageManager;
-import android.content.pm.ShortcutInfo;
-import android.os.Build;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.UserHandle;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO;
-import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.PackageUserKey;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.annotation.Nullable;
-
-@TargetApi(26)
-public class LauncherAppsCompatVO extends LauncherAppsCompatVL {
-
- LauncherAppsCompatVO(Context context) {
- super(context);
- }
-
- @Override
- public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user) {
- try {
- ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
- return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
- ? null : info;
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
- }
-
- @Override
- public List<ShortcutConfigActivityInfo> getCustomShortcutActivityList(
- @Nullable PackageUserKey packageUser) {
- List<ShortcutConfigActivityInfo> result = new ArrayList<>();
- UserHandle myUser = Process.myUserHandle();
-
- final List<UserHandle> users;
- final String packageName;
- if (packageUser == null) {
- users = UserManagerCompat.getInstance(mContext).getUserProfiles();
- packageName = null;
- } else {
- users = new ArrayList<>(1);
- users.add(packageUser.mUser);
- packageName = packageUser.mPackageName;
- }
- for (UserHandle user : users) {
- boolean ignoreTargetSdk = myUser.equals(user);
- List<LauncherActivityInfo> activities =
- mLauncherApps.getShortcutConfigActivityList(packageName, user);
- for (LauncherActivityInfo activityInfo : activities) {
- if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion >=
- Build.VERSION_CODES.O) {
- result.add(new ShortcutConfigActivityInfoVO(activityInfo));
- }
- }
- }
-
- return result;
- }
-
- /**
- * request.accept() will initiate the following flow:
- * -> go-to-system-process for actual processing (a)
- * -> callback-to-launcher on UI thread (b)
- * -> post callback on the worker thread (c)
- * -> Update model and unpin (in system) any shortcut not in out model. (d)
- *
- * Note that (b) will take at-least one frame as it involves posting callback from binder
- * thread to UI thread.
- * If (d) happens before we add this shortcut to our model, we will end up unpinning
- * the shortcut in the system.
- * Here its the caller's responsibility to add the newly created WorkspaceItemInfo immediately
- * to the model (which may involves a single post-to-worker-thread). That will guarantee
- * that (d) happens after model is updated.
- */
- @Nullable
- public static WorkspaceItemInfo createWorkspaceItemFromPinItemRequest(
- Context context, final PinItemRequest request, final long acceptDelay) {
- if (request != null &&
- request.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT &&
- request.isValid()) {
-
- if (acceptDelay <= 0) {
- if (!request.accept()) {
- return null;
- }
- } else {
- // Block the worker thread until the accept() is called.
- new LooperExecutor(LauncherModel.getWorkerLooper()).execute(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(acceptDelay);
- } catch (InterruptedException e) {
- // Ignore
- }
- if (request.isValid()) {
- request.accept();
- }
- }
- });
- }
-
- ShortcutInfo si = request.getShortcutInfo();
- WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
- // Apply the unbadged icon and fetch the actual icon asynchronously.
- LauncherIcons li = LauncherIcons.obtain(context);
- info.applyFrom(li.createShortcutIcon(si, false /* badged */));
- li.recycle();
- LauncherAppState.getInstance(context).getModel()
- .updateAndBindWorkspaceItem(info, si);
- return info;
- } else {
- return null;
- }
- }
-
- public static PinItemRequest getPinItemRequest(Intent intent) {
- Parcelable extra = intent.getParcelableExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST);
- return extra instanceof PinItemRequest ? (PinItemRequest) extra : null;
- }
-}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java b/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java
deleted file mode 100644
index 0a1811e..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.compat;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInstaller;
-
-import java.util.List;
-
-@TargetApi(29)
-public class LauncherAppsCompatVQ extends LauncherAppsCompatVO {
-
- LauncherAppsCompatVQ(Context context) {
- super(context);
- }
-
- public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
- return mLauncherApps.getAllPackageInstallerSessions();
- }
-}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
deleted file mode 100644
index 55df98b..0000000
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2014 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.compat;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.os.Process;
-import android.os.UserHandle;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.PackageUserKey;
-
-public abstract class PackageInstallerCompat {
-
- // Set<String> of session ids of promise icons that have been added to the home screen
- // as FLAG_PROMISE_NEW_INSTALLS.
- protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
-
- public static final int STATUS_INSTALLED = 0;
- public static final int STATUS_INSTALLING = 1;
- public static final int STATUS_FAILED = 2;
-
- private static final Object sInstanceLock = new Object();
- private static PackageInstallerCompat sInstance;
-
- public static PackageInstallerCompat getInstance(Context context) {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new PackageInstallerCompatVL(context);
- }
- return sInstance;
- }
- }
-
- public static UserHandle getUserHandle(SessionInfo info) {
- return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
- }
-
- /**
- * @return a map of active installs to their progress
- */
- public abstract HashMap<PackageUserKey, SessionInfo> updateAndGetActiveSessionCache();
-
- /**
- * @return an active SessionInfo for {@param pkg} or null if none exists.
- */
- public abstract SessionInfo getActiveSessionInfo(UserHandle user, String pkg);
-
- public abstract void onStop();
-
- public static final class PackageInstallInfo {
- public final ComponentName componentName;
- public final String packageName;
- public final int state;
- public final int progress;
- public final UserHandle user;
-
- private PackageInstallInfo(@NonNull SessionInfo info) {
- this.state = STATUS_INSTALLING;
- this.packageName = info.getAppPackageName();
- this.componentName = new ComponentName(packageName, "");
- this.progress = (int) (info.getProgress() * 100f);
- this.user = getUserHandle(info);
- }
-
- public PackageInstallInfo(String packageName, int state, int progress, UserHandle user) {
- this.state = state;
- this.packageName = packageName;
- this.componentName = new ComponentName(packageName, "");
- this.progress = progress;
- this.user = user;
- }
-
- public static PackageInstallInfo fromInstallingState(SessionInfo info) {
- return new PackageInstallInfo(info);
- }
-
- public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
- return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
- }
-
- }
-
- public abstract List<SessionInfo> getAllVerifiedSessions();
-
- /**
- * Returns true if a promise icon was already added to the home screen for {@param sessionId}.
- * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
- */
- public abstract boolean promiseIconAddedForId(int sessionId);
-
- /**
- * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
- */
- public abstract void removePromiseIconId(int sessionId);
-}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
deleted file mode 100644
index 879d963..0000000
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2014 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.compat;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionCallback;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageManager;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.SparseArray;
-
-import com.android.launcher3.SessionCommitReceiver;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.Thunk;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-import static com.android.launcher3.Utilities.getPrefs;
-
-public class PackageInstallerCompatVL extends PackageInstallerCompat {
-
- private static final boolean DEBUG = false;
-
- @Thunk final SparseArray<PackageUserKey> mActiveSessions = new SparseArray<>();
-
- @Thunk final PackageInstaller mInstaller;
- private final IconCache mCache;
- private final Handler mWorker;
- private final Context mAppContext;
- private final HashMap<String,Boolean> mSessionVerifiedMap = new HashMap<>();
- private final LauncherAppsCompat mLauncherApps;
- private final IntSet mPromiseIconIds;
-
- PackageInstallerCompatVL(Context context) {
- mAppContext = context.getApplicationContext();
- mInstaller = context.getPackageManager().getPackageInstaller();
- mCache = LauncherAppState.getInstance(context).getIconCache();
- mWorker = new Handler(LauncherModel.getWorkerLooper());
- mInstaller.registerSessionCallback(mCallback, mWorker);
- mLauncherApps = LauncherAppsCompat.getInstance(context);
- mPromiseIconIds = IntSet.wrap(IntArray.wrap(Utilities.getIntArrayFromString(
- getPrefs(context).getString(PROMISE_ICON_IDS, ""))));
-
- cleanUpPromiseIconIds();
- }
-
- private void cleanUpPromiseIconIds() {
- IntArray existingIds = new IntArray();
- for (SessionInfo info : updateAndGetActiveSessionCache().values()) {
- existingIds.add(info.getSessionId());
- }
- IntArray idsToRemove = new IntArray();
-
- for (int i = mPromiseIconIds.size() - 1; i >= 0; --i) {
- if (!existingIds.contains(mPromiseIconIds.getArray().get(i))) {
- idsToRemove.add(mPromiseIconIds.getArray().get(i));
- }
- }
- for (int i = idsToRemove.size() - 1; i >= 0; --i) {
- mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
- }
- }
-
- @Override
- public HashMap<PackageUserKey, SessionInfo> updateAndGetActiveSessionCache() {
- HashMap<PackageUserKey, SessionInfo> activePackages = new HashMap<>();
- for (SessionInfo info : getAllVerifiedSessions()) {
- addSessionInfoToCache(info, getUserHandle(info));
- if (info.getAppPackageName() != null) {
- activePackages.put(new PackageUserKey(info.getAppPackageName(),
- getUserHandle(info)), info);
- mActiveSessions.put(info.getSessionId(),
- new PackageUserKey(info.getAppPackageName(), getUserHandle(info)));
- }
- }
- return activePackages;
- }
-
- public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
- for (SessionInfo info : getAllVerifiedSessions()) {
- boolean match = pkg.equals(info.getAppPackageName());
- if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) {
- match = false;
- }
- if (match) {
- return info;
- }
- }
- return null;
- }
-
- @Thunk void addSessionInfoToCache(SessionInfo info, UserHandle user) {
- String packageName = info.getAppPackageName();
- if (packageName != null) {
- mCache.cachePackageInstallInfo(packageName, user, info.getAppIcon(),
- info.getAppLabel());
- }
- }
-
- @Override
- public void onStop() {
- mInstaller.unregisterSessionCallback(mCallback);
- }
-
- @Thunk void sendUpdate(PackageInstallInfo info) {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
- app.getModel().setPackageState(info);
- }
- }
-
- /**
- * Add a promise app icon to the workspace iff:
- * - The settings for it are enabled
- * - The user installed the app
- * - There is an app icon and label (For apps with no launching activity, no icon is provided).
- * - The app is not already installed
- * - A promise icon for the session has not already been created
- */
- private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) {
- if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
- && SessionCommitReceiver.isEnabled(mAppContext)
- && verify(sessionInfo) != null
- && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
- && sessionInfo.getAppIcon() != null
- && !TextUtils.isEmpty(sessionInfo.getAppLabel())
- && !mPromiseIconIds.contains(sessionInfo.getSessionId())
- && mLauncherApps.getApplicationInfo(sessionInfo.getAppPackageName(), 0,
- getUserHandle(sessionInfo)) == null) {
- SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
- mPromiseIconIds.add(sessionInfo.getSessionId());
- updatePromiseIconPrefs();
- }
- }
-
- private final SessionCallback mCallback = new SessionCallback() {
-
- @Override
- public void onCreated(int sessionId) {
- SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
- if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS && sessionInfo != null) {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
- app.getModel().onInstallSessionCreated(
- PackageInstallInfo.fromInstallingState(sessionInfo));
- }
- }
-
- tryQueuePromiseAppIcon(sessionInfo);
- }
-
- @Override
- public void onFinished(int sessionId, boolean success) {
- // For a finished session, we can't get the session info. So use the
- // packageName from our local cache.
- PackageUserKey key = mActiveSessions.get(sessionId);
- mActiveSessions.remove(sessionId);
-
- if (key != null && key.mPackageName != null) {
- String packageName = key.mPackageName;
- sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
- packageName, key.mUser));
-
- if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
- && mPromiseIconIds.contains(sessionId)) {
- LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
- if (appState != null) {
- appState.getModel().onSessionFailure(packageName, key.mUser);
- }
- // If it is successful, the id is removed in the the package added flow.
- removePromiseIconId(sessionId);
- }
- }
- }
-
- @Override
- public void onProgressChanged(int sessionId, float progress) {
- SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
- if (session != null && session.getAppPackageName() != null) {
- sendUpdate(PackageInstallInfo.fromInstallingState(session));
- }
- }
-
- @Override
- public void onActiveChanged(int sessionId, boolean active) { }
-
- @Override
- public void onBadgingChanged(int sessionId) {
- SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
- if (sessionInfo != null) {
- tryQueuePromiseAppIcon(sessionInfo);
- }
- }
-
- private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
- SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
- if (session != null && session.getAppPackageName() != null) {
- mActiveSessions.put(session.getSessionId(),
- new PackageUserKey(session.getAppPackageName(), getUserHandle(session)));
- addSessionInfoToCache(session, getUserHandle(session));
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
- app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
- }
- return session;
- }
- return null;
- }
- };
-
- private PackageInstaller.SessionInfo verify(PackageInstaller.SessionInfo sessionInfo) {
- if (sessionInfo == null
- || sessionInfo.getInstallerPackageName() == null
- || TextUtils.isEmpty(sessionInfo.getAppPackageName())) {
- return null;
- }
- String pkg = sessionInfo.getInstallerPackageName();
- synchronized (mSessionVerifiedMap) {
- if (!mSessionVerifiedMap.containsKey(pkg)) {
- LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
- boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
- ApplicationInfo.FLAG_SYSTEM, getUserHandle(sessionInfo)) != null;
- mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
- }
- }
- return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
- }
-
- @Override
- public List<SessionInfo> getAllVerifiedSessions() {
- List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q
- ? mLauncherApps.getAllPackageInstallerSessions()
- : mInstaller.getAllSessions());
- Iterator<SessionInfo> it = list.iterator();
- while (it.hasNext()) {
- if (verify(it.next()) == null) {
- it.remove();
- }
- }
- return list;
- }
-
- @Override
- public boolean promiseIconAddedForId(int sessionId) {
- return mPromiseIconIds.contains(sessionId);
- }
-
- @Override
- public void removePromiseIconId(int sessionId) {
- if (mPromiseIconIds.contains(sessionId)) {
- mPromiseIconIds.getArray().removeValue(sessionId);
- updatePromiseIconPrefs();
- }
- }
-
- private void updatePromiseIconPrefs() {
- getPrefs(mAppContext).edit()
- .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
- .apply();
- }
-}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
similarity index 81%
rename from src/com/android/launcher3/config/BaseFlags.java
rename to src/com/android/launcher3/config/FeatureFlags.java
index 025087b..4abdbef 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -23,11 +23,12 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.Keep;
-
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.BuildConfig;
+import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.TogglableFlag;
+
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
@@ -39,7 +40,7 @@
* <p>All the flags should be defined here with appropriate default values.
*/
@Keep
-public abstract class BaseFlags {
+public final class FeatureFlags {
private static final Object sLock = new Object();
@GuardedBy("sLock")
@@ -47,49 +48,47 @@
static final String FLAGS_PREF_NAME = "featureFlags";
- BaseFlags() {
- throw new UnsupportedOperationException("Don't instantiate BaseFlags");
- }
+ private FeatureFlags() { }
public static boolean showFlagTogglerUi(Context context) {
return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context);
}
- public static final boolean IS_DOGFOOD_BUILD = false;
+ public static final boolean IS_DOGFOOD_BUILD = BuildConfig.DEBUG;
+ /**
+ * Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature
+ * and should be modified at a project level.
+ */
+ public static final boolean QSB_ON_FIRST_SCREEN = true;
+
+
+ /**
+ * Feature flag to handle define config changes dynamically instead of killing the process.
+ *
+ *
+ * To add a new flag that can be toggled through the flags UI:
+ *
+ * 1. Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"),
+ * and set a default value for the flag. This will be the default value on Debug builds.
+ *
+ * 2. Add your flag to mTogglableFlags.
+ *
+ * 3. Create a getter method (an 'is' method) for the flag by copying an existing one.
+ *
+ * 4. Create a getter method with the same name in the release flags copy of FeatureFlags.java.
+ * This should returns a constant (true/false). This will be the value of the flag used on
+ * release builds.
+ */
// When enabled the promise icon is visible in all apps while installation an app.
- public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
+ public static final TogglableFlag PROMISE_APPS_IN_ALL_APPS = new TogglableFlag(
+ "PROMISE_APPS_IN_ALL_APPS", false, "Add promise icon in all-apps");
// When enabled a promise icon is added to the home screen when install session is active.
public static final TogglableFlag PROMISE_APPS_NEW_INSTALLS =
new TogglableFlag("PROMISE_APPS_NEW_INSTALLS", true,
"Adds a promise icon to the home screen for new install sessions.");
- // Enable moving the QSB on the 0th screen of the workspace
- public static final boolean QSB_ON_FIRST_SCREEN = true;
-
- public static final TogglableFlag EXAMPLE_FLAG = new TogglableFlag("EXAMPLE_FLAG", true,
- "An example flag that doesn't do anything. Useful for testing");
-
- //Feature flag to enable pulling down navigation shade from workspace.
- public static final boolean PULL_DOWN_STATUS_BAR = true;
-
- // When true, custom widgets are loaded using CustomWidgetParser.
- public static final boolean ENABLE_CUSTOM_WIDGETS = false;
-
- // Features to control Launcher3Go behavior
- public static final boolean GO_DISABLE_WIDGETS = false;
-
- // When enabled shows a work profile tab in all apps
- public static final boolean ALL_APPS_TABS_ENABLED = true;
-
- // When true, overview shows screenshots in the orientation they were taken rather than
- // trying to make them fit the orientation the device is in.
- public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
-
- /**
- * Feature flag to handle define config changes dynamically instead of killing the process.
- */
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
"APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
@@ -115,6 +114,12 @@
"APP_SEARCH_IMPROVEMENTS", false,
"Adds localized title and keyword search and ranking");
+ public static final TogglableFlag ENABLE_PREDICTION_DISMISS = new TogglableFlag(
+ "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
+
+ 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 void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
@@ -129,13 +134,13 @@
static List<TogglableFlag> getTogglableFlags() {
// By Java Language Spec 12.4.2
// https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2, the
- // TogglableFlag instances on BaseFlags will be created before those on the FeatureFlags
+ // TogglableFlag instances on FeatureFlags will be created before those on the FeatureFlags
// subclass. This code handles flags that are redeclared in FeatureFlags, ensuring the
// FeatureFlags one takes priority.
SortedMap<String, TogglableFlag> flagsByKey = new TreeMap<>();
synchronized (sLock) {
for (TogglableFlag flag : sFlags) {
- flagsByKey.put(((BaseTogglableFlag) flag).getKey(), flag);
+ flagsByKey.put(flag.getKey(), flag);
}
}
return new ArrayList<>(flagsByKey.values());
@@ -235,14 +240,7 @@
@Override
public int hashCode() {
- int h$ = 1;
- h$ *= 1000003;
- h$ ^= key.hashCode();
- h$ *= 1000003;
- h$ ^= getDefaultValue() ? 1231 : 1237;
- h$ *= 1000003;
- h$ ^= description.hashCode();
- return h$;
+ return key.hashCode();
}
}
}
diff --git a/src/com/android/launcher3/config/FlagTogglerPrefUi.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
index 54e5322..200938d 100644
--- a/src/com/android/launcher3/config/FlagTogglerPrefUi.java
+++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
@@ -26,13 +26,13 @@
import android.widget.Toast;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
+import com.android.launcher3.uioverrides.TogglableFlag;
import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceFragment;
import androidx.preference.PreferenceGroup;
import androidx.preference.SwitchPreference;
-import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
-import com.android.launcher3.uioverrides.TogglableFlag;
/**
* Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index a2dcbf8..1b0567f 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
@@ -44,18 +45,17 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.InstantAppResolver;
-import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -93,7 +93,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mRequest = LauncherAppsCompatVO.getPinItemRequest(getIntent());
+ mRequest = PinRequestHelper.getPinItemRequest(getIntent());
if (mRequest == null) {
finish();
return;
@@ -177,7 +177,7 @@
.setPackage(getPackageName())
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- listener.initWhenReady();
+ Launcher.ACTIVITY_TRACKER.schedule(listener);
startActivity(homeIntent,
ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
mFinishOnPause = true;
@@ -234,7 +234,7 @@
mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
mWidgetCell.ensurePreview();
}
- }.executeOnExecutor(new LooperExecutor(LauncherModel.getWorkerLooper()));
+ }.executeOnExecutor(MODEL_EXECUTOR);
// TODO: Create a worker looper executor and reuse that everywhere.
}
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 1b08723..75693c6 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -36,8 +36,7 @@
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.states.InternalStateHandler;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
import com.android.launcher3.widget.PendingItemDragHelper;
import java.util.UUID;
@@ -45,8 +44,8 @@
/**
* {@link DragSource} for handling drop from a different window.
*/
-public abstract class BaseItemDragListener extends InternalStateHandler implements
- View.OnDragListener, DragSource, DragOptions.PreDragCondition {
+public abstract class BaseItemDragListener implements View.OnDragListener, DragSource,
+ DragOptions.PreDragCondition, SchedulerCallback<Launcher> {
private static final String TAG = "BaseItemDragListener";
@@ -165,7 +164,7 @@
}
protected void postCleanup() {
- clearReference();
+ Launcher.ACTIVITY_TRACKER.clearReference(this);
if (mLauncher != null) {
// Remove any drag params from the launcher intent since the drag operation is complete.
Intent newIntent = new Intent(mLauncher.getIntent());
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index b59164a..cdc7061 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -284,7 +284,8 @@
// The child may be scaled (always about the center of the view) so to account for it,
// we have to offset the position by the scaled size. Once we do that, we can center
// the drag view about the scaled child view.
- toY += Math.round(toScale * tv.getPaddingTop());
+ // padding will remain constant (does not scale with size)
+ toY += tv.getPaddingTop();
toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2;
if (dragView.getDragVisualizeOffset() != null) {
toY -= Math.round(toScale * dragView.getDragVisualizeOffset().y);
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 09c5e5b..f66d07e 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -17,6 +17,7 @@
package com.android.launcher3.dragndrop;
import static com.android.launcher3.Utilities.getBadge;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -41,16 +42,19 @@
import android.os.Looper;
import android.view.View;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.FirstFrameAnimatorHelper;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.FirstFrameAnimatorHelper;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.util.Themes;
@@ -58,10 +62,6 @@
import java.util.Arrays;
-import androidx.dynamicanimation.animation.FloatPropertyCompat;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
public class DragView extends View implements LauncherStateManager.StateListener {
private static final ColorMatrix sTempMatrix1 = new ColorMatrix();
private static final ColorMatrix sTempMatrix2 = new ColorMatrix();
@@ -210,7 +210,7 @@
return;
}
// Load the adaptive icon on a background thread and add the view in ui thread.
- new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(new Runnable() {
+ MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
Object[] outObj = new Object[1];
diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
index 589ad25..06b5c40 100644
--- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
+++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
@@ -106,6 +106,7 @@
* @return the vector at which the item was flung, or null if no fling was detected.
*/
private PointF isFlingingToDelete() {
+ if (mVelocityTracker == null) return null;
if (mDropTarget == null) {
mDropTarget = (ButtonDropTarget) mLauncher.findViewById(R.id.delete_target_text);
}
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index d8a1f99..0bb3fba 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -16,6 +16,8 @@
package com.android.launcher3.dragndrop;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -31,7 +33,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.Launcher;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
@@ -85,7 +86,7 @@
// Create the actual drawable on the UI thread to avoid race conditions with
// FolderIcon draw pass
try {
- return new MainThreadExecutor().submit(() -> {
+ return MAIN_EXECUTOR.submit(() -> {
FolderIcon icon = launcher.findFolderIcon(folderId);
return icon == null ? null : createDrawableOnUiThread(icon, badge, dragViewSize);
}).get();
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index 91a31aa..09062a4 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -29,14 +29,14 @@
import android.os.Process;
import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.compat.LauncherAppsCompatVO;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.pm.PinRequestHelper;
+import com.android.launcher3.pm.ShortcutConfigActivityInfo;
/**
* Extension of ShortcutConfigActivityInfo to be used in the confirmation prompt for pin item
@@ -88,7 +88,7 @@
LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY +
LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
// Delay the actual accept() call until the drop animation is complete.
- return LauncherAppsCompatVO.createWorkspaceItemFromPinItemRequest(
+ return PinRequestHelper.createWorkspaceItemFromPinItemRequest(
mContext, mRequest, duration);
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index f22b533..65d593c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -26,12 +26,12 @@
import android.annotation.SuppressLint;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
import android.text.InputType;
import android.text.Selection;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
@@ -126,9 +126,6 @@
private static final Rect sTempRect = new Rect();
private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10;
- private static String sDefaultFolderName;
- private static String sHintText;
-
private final Alarm mReorderAlarm = new Alarm();
private final Alarm mOnExitAlarm = new Alarm();
private final Alarm mOnScrollHintAlarm = new Alarm();
@@ -173,8 +170,6 @@
private boolean mDeleteFolderOnDropCompleted = false;
private boolean mSuppressFolderDeletion = false;
private boolean mItemAddedBackToSelfViaIcon = false;
- @Thunk float mFolderIconPivotX;
- @Thunk float mFolderIconPivotY;
private boolean mIsEditingName = false;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -196,8 +191,6 @@
super(context, attrs);
setAlwaysDrawnWithCacheEnabled(false);
- setLocaleDependentFields(getResources(), false /* force */);
-
mLauncher = Launcher.getLauncher(context);
// We need this view to be focusable in touch mode so that when text editing of the folder
// name is complete, we have something to focus on, thus hiding the cursor and giving
@@ -315,10 +308,15 @@
// Convert to a string here to ensure that no other state associated with the text field
// gets saved.
String newTitle = mFolderName.getText().toString();
- mInfo.setTitle(newTitle);
+ mInfo.title = newTitle;
+ mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
- mFolderName.setHint(sDefaultFolderName.contentEquals(newTitle) ? sHintText : null);
+ if (TextUtils.isEmpty(mInfo.title)) {
+ mFolderName.setHint(R.string.folder_hint_text);
+ } else {
+ mFolderName.setHint(null);
+ }
sendCustomAccessibilityEvent(
this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -385,7 +383,7 @@
mInfo = info;
ArrayList<WorkspaceItemInfo> children = info.contents;
Collections.sort(children, ITEM_POS_COMPARATOR);
- mContent.bindItems(children);
+ updateItemLocationsInDatabaseBatch();
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
if (lp == null) {
@@ -393,26 +391,21 @@
lp.customPosition = true;
setLayoutParams(lp);
}
- centerAboutIcon();
-
mItemsInvalidated = true;
- updateTextViewFocus();
mInfo.addListener(this);
- if (!sDefaultFolderName.contentEquals(mInfo.title)) {
+ if (!TextUtils.isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
mFolderName.setHint(null);
} else {
mFolderName.setText("");
- mFolderName.setHint(sHintText);
+ mFolderName.setHint(R.string.folder_hint_text);
}
// In case any children didn't come across during loading, clean up the folder accordingly
- mFolderIcon.post(new Runnable() {
- public void run() {
- if (getItemCount() <= 1) {
- replaceFolderWithFinalItem();
- }
+ mFolderIcon.post(() -> {
+ if (getItemCount() <= 1) {
+ replaceFolderWithFinalItem();
}
});
}
@@ -473,17 +466,49 @@
}
/**
+ * Opens the folder as part of a drag operation
+ */
+ public void beginExternalDrag() {
+ mIsExternalDrag = true;
+ mDragInProgress = true;
+
+ // Since this folder opened by another controller, it might not get onDrop or
+ // onDropComplete. Perform cleanup once drag-n-drop ends.
+ mDragController.addDragListener(this);
+
+ ArrayList<WorkspaceItemInfo> items = new ArrayList<>(mInfo.contents);
+ mEmptyCellRank = items.size();
+ items.add(null); // Add an empty spot at the end
+
+ animateOpen(items, mEmptyCellRank / mContent.itemsPerPage());
+ }
+
+ /**
* Opens the user folder described by the specified tag. The opening of the folder
* is animated relative to the specified View. If the View is null, no animation
* is played.
*/
public void animateOpen() {
+ animateOpen(mInfo.contents, 0);
+ }
+
+ /**
+ * Opens the user folder described by the specified tag. The opening of the folder
+ * is animated relative to the specified View. If the View is null, no animation
+ * is played.
+ */
+ private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) {
Folder openFolder = getOpen(mLauncher);
if (openFolder != null && openFolder != this) {
// Close any open folder before opening a folder.
openFolder.close(true);
}
+ mContent.bindItems(items);
+ centerAboutIcon();
+ mItemsInvalidated = true;
+ updateTextViewFocus();
+
mIsOpen = true;
DragLayer dragLayer = mLauncher.getDragLayer();
@@ -500,10 +525,7 @@
}
mContent.completePendingPageChanges();
- if (!mDragInProgress) {
- // Open on the first page.
- mContent.snapToPageImmediately(0);
- }
+ mContent.snapToPageImmediately(pageNo);
// This is set to true in close(), but isn't reset to false until onDropCompleted(). This
// leads to an inconsistent state if you drag out of the folder and drag back in without
@@ -574,16 +596,6 @@
mContent.verifyVisibleHighResIcons(mContent.getNextPage());
}
- public void beginExternalDrag() {
- mEmptyCellRank = mContent.allocateRankForNewItem();
- mIsExternalDrag = true;
- mDragInProgress = true;
-
- // Since this folder opened by another controller, it might not get onDrop or
- // onDropComplete. Perform cleanup once drag-n-drop ends.
- mDragController.addDragListener(this);
- }
-
@Override
protected boolean isOfType(int type) {
return (type & TYPE_FOLDER) != 0;
@@ -668,6 +680,8 @@
} else if (mDragInProgress) {
mDeleteFolderOnDropCompleted = true;
}
+ } else if (!mDragInProgress) {
+ mContent.unbindItems();
}
mSuppressFolderDeletion = false;
clearDragInfo();
@@ -822,9 +836,9 @@
}
}
+ @Override
public void onDropCompleted(final View target, final DragObject d,
final boolean success) {
-
if (success) {
if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) {
replaceFolderWithFinalItem();
@@ -834,9 +848,9 @@
WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo;
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
? mCurrentDragView : mContent.createNewView(info);
- ArrayList<View> views = getItemsInReadingOrder();
+ ArrayList<View> views = getIconsInReadingOrder();
views.add(info.rank, icon);
- mContent.arrangeChildren(views, views.size());
+ mContent.arrangeChildren(views);
mItemsInvalidated = true;
try (SuppressInfoChanges s = new SuppressInfoChanges()) {
@@ -874,16 +888,21 @@
}
private void updateItemLocationsInDatabaseBatch() {
- ArrayList<View> list = getItemsInReadingOrder();
- ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
- for (int i = 0; i < list.size(); i++) {
- View v = list.get(i);
- ItemInfo info = (ItemInfo) v.getTag();
- info.rank = i;
- items.add(info);
+ FolderGridOrganizer verifier = new FolderGridOrganizer(
+ mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+
+ ArrayList<ItemInfo> items = new ArrayList<>();
+ int total = mInfo.contents.size();
+ for (int i = 0; i < total; i++) {
+ WorkspaceItemInfo itemInfo = mInfo.contents.get(i);
+ if (verifier.updateRankAndPos(itemInfo, i)) {
+ items.add(itemInfo);
+ }
}
- mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
+ if (!items.isEmpty()) {
+ mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
+ }
}
public void notifyDrop() {
@@ -948,24 +967,12 @@
setPivotX(folderPivotX);
setPivotY(folderPivotY);
- mFolderIconPivotX = (int) (mFolderIcon.getMeasuredWidth() *
- (1.0f * folderPivotX / width));
- mFolderIconPivotY = (int) (mFolderIcon.getMeasuredHeight() *
- (1.0f * folderPivotY / height));
-
lp.width = width;
lp.height = height;
lp.x = left;
lp.y = top;
}
- public float getPivotXForIconAnimation() {
- return mFolderIconPivotX;
- }
- public float getPivotYForIconAnimation() {
- return mFolderIconPivotY;
- }
-
private int getContentAreaHeight() {
DeviceProfile grid = mLauncher.getDeviceProfile();
int maxContentAreaHeight = grid.availableHeightPx
@@ -1021,22 +1028,12 @@
* Rearranges the children based on their rank.
*/
public void rearrangeChildren() {
- rearrangeChildren(-1);
- }
-
- /**
- * Rearranges the children based on their rank.
- * @param itemCount if greater than the total children count, empty spaces are left at the end,
- * otherwise it is ignored.
- */
- public void rearrangeChildren(int itemCount) {
- ArrayList<View> views = getItemsInReadingOrder();
- mContent.arrangeChildren(views, Math.max(itemCount, views.size()));
+ mContent.arrangeChildren(getIconsInReadingOrder());
mItemsInvalidated = true;
}
public int getItemCount() {
- return mContent.getItemCount();
+ return mInfo.contents.size();
}
@Thunk void replaceFolderWithFinalItem() {
@@ -1044,7 +1041,7 @@
Runnable onCompleteRunnable = new Runnable() {
@Override
public void run() {
- int itemCount = mInfo.contents.size();
+ int itemCount = getItemCount();
if (itemCount <= 1) {
View newIcon = null;
@@ -1121,9 +1118,12 @@
return false;
}
});
+ } else {
+ setOnKeyListener(null);
}
}
+ @Override
public void onDrop(DragObject d, DragOptions options) {
// If the icon was dropped while the page was being scrolled, we need to compute
// the target location again such that the icon is placed of the final page.
@@ -1171,12 +1171,6 @@
// before creating the view, so that WorkspaceItemInfo is updated appropriately.
mLauncher.getModelWriter().addOrMoveItemInDatabase(
si, mInfo.id, 0, si.cellX, si.cellY);
-
- // We only need to update the locations if it doesn't get handled in
- // #onDropCompleted.
- if (d.dragSource != this) {
- updateItemLocationsInDatabaseBatch();
- }
mIsExternalDrag = false;
} else {
currentDragView = mCurrentDragView;
@@ -1203,7 +1197,13 @@
// Temporarily suppress the listener, as we did all the work already here.
try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mInfo.add(si, false);
+ mInfo.add(si, mEmptyCellRank, false);
+ }
+
+ // We only need to update the locations if it doesn't get handled in
+ // #onDropCompleted.
+ if (d.dragSource != this) {
+ updateItemLocationsInDatabaseBatch();
}
}
@@ -1226,22 +1226,29 @@
// to correspond to the animation of the icon back into the folder. This is
public void hideItem(WorkspaceItemInfo info) {
View v = getViewForInfo(info);
- v.setVisibility(INVISIBLE);
+ if (v != null) {
+ v.setVisibility(INVISIBLE);
+ }
}
public void showItem(WorkspaceItemInfo info) {
View v = getViewForInfo(info);
- v.setVisibility(VISIBLE);
+ if (v != null) {
+ v.setVisibility(VISIBLE);
+ }
}
@Override
public void onAdd(WorkspaceItemInfo item, int rank) {
- View view = mContent.createAndAddViewForRank(item, rank);
+ FolderGridOrganizer verifier = new FolderGridOrganizer(
+ mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+ verifier.updateRankAndPos(item, rank);
mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
item.cellY);
+ updateItemLocationsInDatabaseBatch();
- ArrayList<View> items = new ArrayList<>(getItemsInReadingOrder());
- items.add(rank, view);
- mContent.arrangeChildren(items, items.size());
+ if (mContent.areViewsBound()) {
+ mContent.createAndAddViewForRank(item, rank);
+ }
mItemsInvalidated = true;
}
@@ -1264,13 +1271,7 @@
}
private View getViewForInfo(final WorkspaceItemInfo item) {
- return mContent.iterateOverItems(new ItemOperator() {
-
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- return info == item;
- }
- });
+ return mContent.iterateOverItems((info, view) -> info == item);
}
@Override
@@ -1278,32 +1279,27 @@
updateTextViewFocus();
}
- @Override
- public void prepareAutoUpdate() {
- close(false);
+ /**
+ * Utility methods to iterate over items of the view
+ */
+ public void iterateOverItems(ItemOperator op) {
+ mContent.iterateOverItems(op);
}
- public void onTitleChanged(CharSequence title) {
- }
-
- public ArrayList<View> getItemsInReadingOrder() {
+ /**
+ * Returns the sorted list of all the icons in the folder
+ */
+ public ArrayList<View> getIconsInReadingOrder() {
if (mItemsInvalidated) {
mItemsInReadingOrder.clear();
- mContent.iterateOverItems(new ItemOperator() {
-
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- mItemsInReadingOrder.add(view);
- return false;
- }
- });
+ mContent.iterateOverItems((i, v) -> !mItemsInReadingOrder.add(v));
mItemsInvalidated = false;
}
return mItemsInReadingOrder;
}
public List<BubbleTextView> getItemsOnPage(int page) {
- ArrayList<View> allItems = getItemsInReadingOrder();
+ ArrayList<View> allItems = getIconsInReadingOrder();
int lastPage = mContent.getPageCount() - 1;
int totalItemsInFolder = allItems.size();
int itemsPerPage = mContent.itemsPerPage();
@@ -1482,15 +1478,6 @@
return false;
}
- public static void setLocaleDependentFields(Resources res, boolean force) {
- if (sDefaultFolderName == null || force) {
- sDefaultFolderName = res.getString(R.string.folder_name);
- }
- if (sHintText == null || force) {
- sHintText = res.getString(R.string.folder_hint_text);
- }
- }
-
/**
* Alternative to using {@link #getClipToOutline()} as it only works with derivatives of
* rounded rect.
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 962f215..9eb0693 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -79,7 +79,7 @@
private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator;
private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
-
+ private final FolderGridOrganizer mPreviewVerifier;
public FolderAnimationManager(Folder folder, boolean isOpening) {
mFolder = folder;
@@ -91,6 +91,7 @@
mContext = folder.getContext();
mLauncher = folder.mLauncher;
+ mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv);
mIsOpening = isOpening;
@@ -113,7 +114,7 @@
public AnimatorSet getAnimator() {
final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
- final List<BubbleTextView> itemsInPreview = mFolderIcon.getPreviewItems();
+ final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
// Match position of the FolderIcon
final Rect folderIconPos = new Rect();
@@ -226,15 +227,22 @@
}
/**
+ * Returns the list of "preview items" on {@param page}.
+ */
+ private List<BubbleTextView> getPreviewIconsOnPage(int page) {
+ return mPreviewVerifier.setFolderInfo(mFolder.mInfo)
+ .previewItemsForPage(page, mFolder.getIconsInReadingOrder());
+ }
+
+ /**
* Animate the items on the current page.
*/
private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale,
int previewItemOffsetX, int previewItemOffsetY) {
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0;
- final List<BubbleTextView> itemsInPreview = isOnFirstPage
- ? mFolderIcon.getPreviewItems()
- : mFolderIcon.getPreviewItemsOnPage(mFolder.mContent.getCurrentPage());
+ final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(
+ isOnFirstPage ? 0 : mFolder.mContent.getCurrentPage());
final int numItemsInPreview = itemsInPreview.size();
final int numItemsInFirstPagePreview = isOnFirstPage
? numItemsInPreview : MAX_NUM_ITEMS_IN_PREVIEW;
diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java
new file mode 100644
index 0000000..9d14a5f
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java
@@ -0,0 +1,196 @@
+/*
+ * 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 static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+
+import android.graphics.Point;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for managing item positions in a folder based on rank
+ */
+public class FolderGridOrganizer {
+
+ private final Point mPoint = new Point();
+ private final int mMaxCountX;
+ private final int mMaxCountY;
+ private final int mMaxItemsPerPage;
+
+ private int mNumItemsInFolder;
+ private int mCountX;
+ private int mCountY;
+ private boolean mDisplayingUpperLeftQuadrant = false;
+
+ /**
+ * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work.
+ */
+ public FolderGridOrganizer(InvariantDeviceProfile profile) {
+ mMaxCountX = profile.numFolderColumns;
+ mMaxCountY = profile.numFolderRows;
+ mMaxItemsPerPage = mMaxCountX * mMaxCountY;
+ }
+
+ /**
+ * Updates the organizer with the provided folder info
+ */
+ public FolderGridOrganizer setFolderInfo(FolderInfo info) {
+ return setContentSize(info.contents.size());
+ }
+
+ /**
+ * Updates the organizer to reflect the content size
+ */
+ public FolderGridOrganizer setContentSize(int contentSize) {
+ if (contentSize != mNumItemsInFolder) {
+ calculateGridSize(contentSize);
+
+ mDisplayingUpperLeftQuadrant = contentSize > MAX_NUM_ITEMS_IN_PREVIEW;
+ mNumItemsInFolder = contentSize;
+ }
+ return this;
+ }
+
+ public int getCountX() {
+ return mCountX;
+ }
+
+ public int getCountY() {
+ return mCountY;
+ }
+
+ public int getMaxItemsPerPage() {
+ return mMaxItemsPerPage;
+ }
+
+ /**
+ * Calculates the grid size such that {@param count} items can fit in the grid.
+ * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
+ * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}.
+ */
+ private void calculateGridSize(int count) {
+ boolean done;
+ int gridCountX = mCountX;
+ int gridCountY = mCountY;
+
+ if (count >= mMaxItemsPerPage) {
+ gridCountX = mMaxCountX;
+ gridCountY = mMaxCountY;
+ done = true;
+ } else {
+ done = false;
+ }
+
+ while (!done) {
+ int oldCountX = gridCountX;
+ int oldCountY = gridCountY;
+ if (gridCountX * gridCountY < count) {
+ // Current grid is too small, expand it
+ if ((gridCountX <= gridCountY || gridCountY == mMaxCountY)
+ && gridCountX < mMaxCountX) {
+ gridCountX++;
+ } else if (gridCountY < mMaxCountY) {
+ gridCountY++;
+ }
+ if (gridCountY == 0) gridCountY++;
+ } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
+ gridCountY = Math.max(0, gridCountY - 1);
+ } else if ((gridCountX - 1) * gridCountY >= count) {
+ gridCountX = Math.max(0, gridCountX - 1);
+ }
+ done = gridCountX == oldCountX && gridCountY == oldCountY;
+ }
+
+ mCountX = gridCountX;
+ mCountY = gridCountY;
+ }
+
+ /**
+ * Updates the item's cellX, cellY and rank corresponding to the provided rank.
+ * @return true if there was any change
+ */
+ public boolean updateRankAndPos(ItemInfo item, int rank) {
+ Point pos = getPosForRank(rank);
+ if (!pos.equals(item.cellX, item.cellY) || rank != item.rank) {
+ item.rank = rank;
+ item.cellX = pos.x;
+ item.cellY = pos.y;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the position of the item in the grid
+ */
+ public Point getPosForRank(int rank) {
+ int pagePos = rank % mMaxItemsPerPage;
+ mPoint.x = pagePos % mCountX;
+ mPoint.y = pagePos / mCountX;
+ return mPoint;
+ }
+
+ /**
+ * Returns the preview items for the provided pageNo using the full list of contents
+ */
+ public <T, R extends T> ArrayList<R> previewItemsForPage(int page, List<T> contents) {
+ ArrayList<R> result = new ArrayList<>();
+ int itemsPerPage = mCountX * mCountY;
+ int start = itemsPerPage * page;
+ int end = Math.min(start + itemsPerPage, contents.size());
+
+ for (int i = start, rank = 0; i < end; i++, rank++) {
+ if (isItemInPreview(page, rank)) {
+ result.add((R) contents.get(i));
+ }
+
+ if (result.size() == MAX_NUM_ITEMS_IN_PREVIEW) {
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns whether the item with rank is in the default Folder icon preview.
+ */
+ public boolean isItemInPreview(int rank) {
+ return isItemInPreview(0, rank);
+ }
+
+ /**
+ * @param page The page the item is on.
+ * @param rank The rank of the item.
+ * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
+ */
+ public boolean isItemInPreview(int page, int rank) {
+ // First page items are laid out such that the first 4 items are always in the upper
+ // left quadrant. For all other pages, we need to check the row and col.
+ if (page > 0 || mDisplayingUpperLeftQuadrant) {
+ int col = rank % mCountX;
+ int row = rank / mCountX;
+ return col < 2 && row < 2;
+ }
+ return rank < MAX_NUM_ITEMS_IN_PREVIEW;
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 0e2d467..3840639 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -70,6 +70,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Predicate;
/**
* An icon that can appear on in the workspace representing an {@link Folder}.
@@ -96,11 +97,11 @@
PreviewBackground mBackground = new PreviewBackground();
private boolean mBackgroundIsVisible = true;
- FolderIconPreviewVerifier mPreviewVerifier;
+ FolderGridOrganizer mPreviewVerifier;
ClippedFolderIconLayoutRule mPreviewLayoutRule;
private PreviewItemManager mPreviewItemManager;
private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
- private List<BubbleTextView> mCurrentPreviewItems = new ArrayList<>();
+ private List<WorkspaceItemInfo> mCurrentPreviewItems = new ArrayList<>();
boolean mAnimating = false;
@@ -175,7 +176,7 @@
icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.mInfo = folderInfo;
icon.mLauncher = launcher;
- icon.mDotRenderer = grid.mDotRenderer;
+ icon.mDotRenderer = grid.mDotRendererWorkSpace;
icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title));
Folder folder = Folder.fromXml(launcher);
folder.setDragController(launcher.getDragController());
@@ -214,7 +215,7 @@
private void setFolder(Folder folder) {
mFolder = folder;
- mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+ mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv);
mPreviewVerifier.setFolderInfo(mFolder.getInfo());
updatePreviewItems(false);
}
@@ -232,11 +233,7 @@
}
public void addItem(WorkspaceItemInfo item) {
- addItem(item, true);
- }
-
- public void addItem(WorkspaceItemInfo item, boolean animate) {
- mInfo.add(item, animate);
+ mInfo.add(item, true);
}
public void removeItem(WorkspaceItemInfo item, boolean animate) {
@@ -261,7 +258,6 @@
OnAlarmListener mOnOpenListener = new OnAlarmListener() {
public void onAlarm(Alarm alarm) {
mFolder.beginExternalDrag();
- mFolder.animateOpen();
}
};
@@ -296,8 +292,7 @@
}
private void onDrop(final WorkspaceItemInfo item, DragView animateView, Rect finalRect,
- float scaleRelativeToDragLayer, int index,
- boolean itemReturnedOnFailedDrop) {
+ float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) {
item.cellX = -1;
item.cellY = -1;
@@ -328,18 +323,17 @@
int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1);
boolean itemAdded = false;
if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
- List<BubbleTextView> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
- addItem(item, false);
+ List<WorkspaceItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
+ mInfo.add(item, index, false);
mCurrentPreviewItems.clear();
- mCurrentPreviewItems.addAll(getPreviewItems());
+ mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0));
if (!oldPreviewItems.equals(mCurrentPreviewItems)) {
- for (int i = 0; i < mCurrentPreviewItems.size(); ++i) {
- if (mCurrentPreviewItems.get(i).getTag().equals(item)) {
- // If the item dropped is going to be in the preview, we update the
- // index here to reflect its position in the preview.
- index = i;
- }
+ int newIndex = mCurrentPreviewItems.indexOf(item);
+ if (newIndex >= 0) {
+ // If the item dropped is going to be in the preview, we update the
+ // index here to reflect its position in the preview.
+ index = newIndex;
}
mPreviewItemManager.hidePreviewItem(index, true);
@@ -351,13 +345,13 @@
}
if (!itemAdded) {
- addItem(item);
+ mInfo.add(item, index, true);
}
int[] center = new int[2];
float scale = getLocalCenterForIndex(index, numItemsInPreview, center);
- center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
- center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
+ center[0] = Math.round(scaleRelativeToDragLayer * center[0]);
+ center[1] = Math.round(scaleRelativeToDragLayer * center[1]);
to.offset(center[0] - animateView.getMeasuredWidth() / 2,
center[1] - animateView.getMeasuredHeight() / 2);
@@ -398,7 +392,8 @@
item = (WorkspaceItemInfo) d.dragInfo;
}
mFolder.notifyDrop();
- onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(),
+ onDrop(item, d.dragView, null, 1.0f,
+ itemReturnedOnFailedDrop ? item.rank : mInfo.contents.size(),
itemReturnedOnFailedDrop);
}
@@ -510,8 +505,7 @@
mBackground.drawBackground(canvas);
}
- if (mFolder == null) return;
- if (mFolder.getItemCount() == 0 && !mAnimating) return;
+ if (mCurrentPreviewItems.isEmpty() && !mAnimating) return;
final int saveCount = canvas.save();
canvas.clipPath(mBackground.getClipPath());
@@ -553,31 +547,10 @@
}
/**
- * Returns the list of preview items displayed in the icon.
+ * Returns the list of items which should be visible in the preview
*/
- public List<BubbleTextView> getPreviewItems() {
- return getPreviewItemsOnPage(0);
- }
-
- /**
- * Returns the list of "preview items" on {@param page}.
- */
- public List<BubbleTextView> getPreviewItemsOnPage(int page) {
- mPreviewVerifier.setFolderInfo(mFolder.getInfo());
-
- List<BubbleTextView> itemsToDisplay = new ArrayList<>();
- List<BubbleTextView> itemsOnPage = mFolder.getItemsOnPage(page);
- int numItems = itemsOnPage.size();
- for (int rank = 0; rank < numItems; ++rank) {
- if (mPreviewVerifier.isItemInPreview(page, rank)) {
- itemsToDisplay.add(itemsOnPage.get(rank));
- }
-
- if (itemsToDisplay.size() == MAX_NUM_ITEMS_IN_PREVIEW) {
- break;
- }
- }
- return itemsToDisplay;
+ public List<WorkspaceItemInfo> getPreviewItemsOnPage(int page) {
+ return mPreviewVerifier.setFolderInfo(mInfo).previewItemsForPage(page, mInfo.contents);
}
@Override
@@ -595,11 +568,14 @@
private void updatePreviewItems(boolean animate) {
mPreviewItemManager.updatePreviewItems(animate);
mCurrentPreviewItems.clear();
- mCurrentPreviewItems.addAll(getPreviewItems());
+ mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0));
}
- @Override
- public void prepareAutoUpdate() {
+ /**
+ * Updates the preview items which match the provided condition
+ */
+ public void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) {
+ mPreviewItemManager.updatePreviewItems(itemCheck);
}
@Override
@@ -622,7 +598,6 @@
requestLayout();
}
- @Override
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title);
setContentDescription(getContext().getString(R.string.folder_name_format, title));
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
deleted file mode 100644
index 4c84e35..0000000
--- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
+++ /dev/null
@@ -1,89 +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.folder;
-
-import android.util.Log;
-
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.InvariantDeviceProfile;
-
-import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
-
-/**
- * Verifies whether an item in a Folder is displayed in the FolderIcon preview.
- */
-public class FolderIconPreviewVerifier {
-
- private static final String TAG = "FolderPreviewVerifier";
-
- private final int mMaxGridCountX;
- private final int mMaxGridCountY;
- private final int mMaxItemsPerPage;
- private final int[] mGridSize = new int[] { 1, 1 };
-
- private int mNumItemsInFolder;
- private int mGridCountX;
- private boolean mDisplayingUpperLeftQuadrant = false;
-
- /**
- * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work.
- */
- public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
- mMaxGridCountX = profile.numFolderColumns;
- mMaxGridCountY = profile.numFolderRows;
- mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY;
- }
-
- public void setFolderInfo(FolderInfo info) {
- int numItemsInFolder = info.contents.size();
- if (numItemsInFolder != mNumItemsInFolder) {
- FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX,
- mMaxGridCountY, mMaxItemsPerPage, mGridSize);
- mGridCountX = mGridSize[0];
-
- mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW;
- mNumItemsInFolder = numItemsInFolder;
- }
- }
-
- /**
- * Returns whether the item with {@param rank} is in the default Folder icon preview.
- */
- public boolean isItemInPreview(int rank) {
- return isItemInPreview(0, rank);
- }
-
- /**
- * @param page The page the item is on.
- * @param rank The rank of the item.
- * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
- */
- public boolean isItemInPreview(int page, int rank) {
- if (mGridSize[0] == 1) {
- Log.w(TAG, "setFolderInfo not called before checking if item is in preview.");
- }
-
- // First page items are laid out such that the first 4 items are always in the upper
- // left quadrant. For all other pages, we need to check the row and col.
- if (page > 0 || mDisplayingUpperLeftQuadrant) {
- int col = rank % mGridCountX;
- int row = rank / mGridCountX;
- return col < 2 && row < 2;
- }
- return rank < MAX_NUM_ITEMS_IN_PREVIEW;
- }
-}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 57105e7..54b363e 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -24,10 +24,10 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewDebug;
+import com.android.launcher3.BaseActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
@@ -38,18 +38,22 @@
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.ViewCache;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
+import java.util.function.ToIntFunction;
+import java.util.stream.Collectors;
public class FolderPagedView extends PagedView<PageIndicatorDots> {
@@ -68,17 +72,12 @@
public final boolean mIsRtl;
- private final LayoutInflater mInflater;
private final ViewGroupFocusHelper mFocusIndicatorHelper;
@Thunk final ArrayMap<View, Runnable> mPendingAnimations = new ArrayMap<>();
- @ViewDebug.ExportedProperty(category = "launcher")
- private final int mMaxCountX;
- @ViewDebug.ExportedProperty(category = "launcher")
- private final int mMaxCountY;
- @ViewDebug.ExportedProperty(category = "launcher")
- private final int mMaxItemsPerPage;
+ private final FolderGridOrganizer mOrganizer;
+ private final ViewCache mViewCache;
private int mAllocatedContentSize;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -88,20 +87,20 @@
private Folder mFolder;
+ // If the views are attached to the folder or not. A folder should be bound when its
+ // animating or is open.
+ private boolean mViewsBound = false;
+
public FolderPagedView(Context context, AttributeSet attrs) {
super(context, attrs);
InvariantDeviceProfile profile = LauncherAppState.getIDP(context);
- mMaxCountX = profile.numFolderColumns;
- mMaxCountY = profile.numFolderRows;
-
- mMaxItemsPerPage = mMaxCountX * mMaxCountY;
-
- mInflater = LayoutInflater.from(context);
+ mOrganizer = new FolderGridOrganizer(profile);
mIsRtl = Utilities.isRtl(getResources());
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
+ mViewCache = BaseActivity.fromContext(context).getViewCache();
}
public void setFolder(Folder folder) {
@@ -111,57 +110,13 @@
}
/**
- * Calculates the grid size such that {@param count} items can fit in the grid.
- * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
- * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}.
- */
- public static void calculateGridSize(int count, int countX, int countY, int maxCountX,
- int maxCountY, int maxItemsPerPage, int[] out) {
- boolean done;
- int gridCountX = countX;
- int gridCountY = countY;
-
- if (count >= maxItemsPerPage) {
- gridCountX = maxCountX;
- gridCountY = maxCountY;
- done = true;
- } else {
- done = false;
- }
-
- while (!done) {
- int oldCountX = gridCountX;
- int oldCountY = gridCountY;
- if (gridCountX * gridCountY < count) {
- // Current grid is too small, expand it
- if ((gridCountX <= gridCountY || gridCountY == maxCountY)
- && gridCountX < maxCountX) {
- gridCountX++;
- } else if (gridCountY < maxCountY) {
- gridCountY++;
- }
- if (gridCountY == 0) gridCountY++;
- } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
- gridCountY = Math.max(0, gridCountY - 1);
- } else if ((gridCountX - 1) * gridCountY >= count) {
- gridCountX = Math.max(0, gridCountX - 1);
- }
- done = gridCountX == oldCountX && gridCountY == oldCountY;
- }
-
- out[0] = gridCountX;
- out[1] = gridCountY;
- }
-
- /**
* Sets up the grid size such that {@param count} items can fit in the grid.
*/
- public void setupContentDimensions(int count) {
+ private void setupContentDimensions(int count) {
mAllocatedContentSize = count;
- calculateGridSize(count, mGridCountX, mGridCountY, mMaxCountX, mMaxCountY, mMaxItemsPerPage,
- sTmpArray);
- mGridCountX = sTmpArray[0];
- mGridCountY = sTmpArray[1];
+ mOrganizer.setContentSize(count);
+ mGridCountX = mOrganizer.getCountX();
+ mGridCountY = mOrganizer.getCountY();
// Update grid size
for (int i = getPageCount() - 1; i >= 0; i--) {
@@ -178,35 +133,50 @@
/**
* Binds items to the layout.
*/
- public void bindItems(ArrayList<WorkspaceItemInfo> items) {
- ArrayList<View> icons = new ArrayList<>();
- for (WorkspaceItemInfo item : items) {
- icons.add(createNewView(item));
+ public void bindItems(List<WorkspaceItemInfo> items) {
+ if (mViewsBound) {
+ unbindItems();
}
- arrangeChildren(icons, icons.size(), false);
- }
-
- public void allocateSpaceForRank(int rank) {
- ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
- views.add(rank, null);
- arrangeChildren(views, views.size(), false);
+ arrangeChildren(items.stream().map(this::createNewView).collect(Collectors.toList()));
+ mViewsBound = true;
}
/**
- * Create space for a new item at the end, and returns the rank for that item.
- * Also sets the current page to the last page.
+ * Removes all the icons from the folder
*/
- public int allocateRankForNewItem() {
- int rank = getItemCount();
- allocateSpaceForRank(rank);
- setCurrentPage(rank / mMaxItemsPerPage);
- return rank;
+ public void unbindItems() {
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ CellLayout page = (CellLayout) getChildAt(i);
+ ShortcutAndWidgetContainer container = page.getShortcutsAndWidgets();
+ for (int j = container.getChildCount() - 1; j >= 0; j--) {
+ mViewCache.recycleView(R.layout.folder_application, container.getChildAt(j));
+ }
+ page.removeAllViews();
+ mViewCache.recycleView(R.layout.folder_page, page);
+ }
+ removeAllViews();
+ mViewsBound = false;
}
+ /**
+ * Returns true if the icons are bound to the folder
+ */
+ public boolean areViewsBound() {
+ return mViewsBound;
+ }
+
+ /**
+ * Creates and adds an icon corresponding to the provided rank
+ * @return the created icon
+ */
public View createAndAddViewForRank(WorkspaceItemInfo item, int rank) {
View icon = createNewView(item);
- allocateSpaceForRank(rank);
- addViewForRank(icon, item, rank);
+ if (!mViewsBound) {
+ return icon;
+ }
+ ArrayList<View> views = new ArrayList<>(mFolder.getIconsInReadingOrder());
+ views.add(rank, icon);
+ arrangeChildren(views);
return icon;
}
@@ -215,31 +185,33 @@
* related attributes. It assumes that {@param item} is already attached to the view.
*/
public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
- int pagePos = rank % mMaxItemsPerPage;
- int pageNo = rank / mMaxItemsPerPage;
-
- item.rank = rank;
- item.cellX = pagePos % mGridCountX;
- item.cellY = pagePos / mGridCountX;
+ int pageNo = rank / mOrganizer.getMaxItemsPerPage();
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
- lp.cellX = item.cellX;
- lp.cellY = item.cellY;
+ lp.setXY(mOrganizer.getPosForRank(rank));
getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true);
}
@SuppressLint("InflateParams")
public View createNewView(WorkspaceItemInfo item) {
- final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
- R.layout.folder_application, null, false);
+ if (item == null) {
+ return null;
+ }
+ final BubbleTextView textView = mViewCache.getView(
+ R.layout.folder_application, getContext(), null);
textView.applyFromWorkspaceItem(item);
- textView.setHapticFeedbackEnabled(false);
textView.setOnClickListener(ItemClickHandler.INSTANCE);
textView.setOnLongClickListener(mFolder);
textView.setOnFocusChangeListener(mFocusIndicatorHelper);
-
- textView.setLayoutParams(new CellLayout.LayoutParams(
- item.cellX, item.cellY, item.spanX, item.spanY));
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) textView.getLayoutParams();
+ if (lp == null) {
+ textView.setLayoutParams(new CellLayout.LayoutParams(
+ item.cellX, item.cellY, item.spanX, item.spanY));
+ } else {
+ lp.cellX = item.cellX;
+ lp.cellY = item.cellY;
+ lp.cellHSpan = lp.cellVSpan = 1;
+ }
return textView;
}
@@ -254,7 +226,7 @@
private CellLayout createAndAddNewPage() {
DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- CellLayout page = (CellLayout) mInflater.inflate(R.layout.folder_page, this, false);
+ CellLayout page = mViewCache.getView(R.layout.folder_page, getContext(), this);
page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
page.setInvertIfRtl(true);
@@ -295,16 +267,10 @@
* page.
*
* @param list the ordered list of children.
- * @param itemCount if greater than the total children count, empty spaces are left
- * at the end, otherwise it is ignored.
- *
*/
- public void arrangeChildren(ArrayList<View> list, int itemCount) {
- arrangeChildren(list, itemCount, true);
- }
-
@SuppressLint("RtlHardcoded")
- private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) {
+ public void arrangeChildren(List<View> list) {
+ int itemCount = list.size();
ArrayList<CellLayout> pages = new ArrayList<>();
for (int i = 0; i < getChildCount(); i++) {
CellLayout page = (CellLayout) getChildAt(i);
@@ -317,15 +283,12 @@
CellLayout currentPage = null;
int position = 0;
- int newX, newY, rank;
+ int rank = 0;
- FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(
- Launcher.getLauncher(getContext()).getDeviceProfile().inv);
- verifier.setFolderInfo(mFolder.getInfo());
- 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 >= mMaxItemsPerPage) {
+ if (currentPage == null || position >= mOrganizer.getMaxItemsPerPage()) {
// Next page
if (pageItr.hasNext()) {
currentPage = pageItr.next();
@@ -337,28 +300,16 @@
if (v != null) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
- newX = position % mGridCountX;
- newY = position / mGridCountX;
ItemInfo info = (ItemInfo) v.getTag();
- if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
- info.cellX = newX;
- info.cellY = newY;
- info.rank = rank;
- if (saveChanges) {
- mFolder.mLauncher.getModelWriter().addOrMoveItemInDatabase(info,
- mFolder.mInfo.id, 0, info.cellX, info.cellY);
- }
- }
- lp.cellX = info.cellX;
- lp.cellY = info.cellY;
+ lp.setXY(mOrganizer.getPosForRank(rank));
currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true);
- if (verifier.isItemInPreview(rank) && v instanceof BubbleTextView) {
+ if (mOrganizer.isItemInPreview(rank) && v instanceof BubbleTextView) {
((BubbleTextView) v).verifyHighRes();
}
}
- rank ++;
+ rank++;
position++;
}
@@ -391,16 +342,6 @@
(getPageAt(0).getDesiredHeight() + getPaddingTop() + getPaddingBottom()) : 0;
}
- public int getItemCount() {
- int lastPageIndex = getChildCount() - 1;
- if (lastPageIndex < 0) {
- // If there are no pages, nothing has yet been added to the folder.
- return 0;
- }
- return getPageAt(lastPageIndex).getShortcutsAndWidgets().getChildCount()
- + lastPageIndex * mMaxItemsPerPage;
- }
-
/**
* @return the rank of the cell nearest to the provided pixel position.
*/
@@ -412,31 +353,28 @@
sTmpArray[0] = page.getCountX() - sTmpArray[0] - 1;
}
return Math.min(mAllocatedContentSize - 1,
- pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]);
+ pageIndex * mOrganizer.getMaxItemsPerPage()
+ + sTmpArray[1] * mGridCountX + sTmpArray[0]);
}
public View getFirstItem() {
- if (getChildCount() < 1) {
- return null;
- }
- ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
- if (mGridCountX > 0) {
- return currContainer.getChildAt(0, 0);
- } else {
- return currContainer.getChildAt(0);
- }
+ return getViewInCurrentPage(c -> 0);
}
public View getLastItem() {
+ return getViewInCurrentPage(c -> c.getChildCount() - 1);
+ }
+
+ private View getViewInCurrentPage(ToIntFunction<ShortcutAndWidgetContainer> rankProvider) {
if (getChildCount() < 1) {
return null;
}
- ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
- int lastRank = currContainer.getChildCount() - 1;
+ ShortcutAndWidgetContainer container = getCurrentCellLayout().getShortcutsAndWidgets();
+ int rank = rankProvider.applyAsInt(container);
if (mGridCountX > 0) {
- return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
+ return container.getChildAt(rank % mGridCountX, rank / mGridCountX);
} else {
- return currContainer.getChildAt(lastRank);
+ return container.getChildAt(rank);
}
}
@@ -517,7 +455,7 @@
}
public boolean rankOnCurrentPage(int rank) {
- int p = rank / mMaxItemsPerPage;
+ int p = rank / mOrganizer.getMaxItemsPerPage();
return p == getNextPage();
}
@@ -563,15 +501,16 @@
// Animation only happens on the current page.
int pageToAnimate = getNextPage();
+ int maxItemsPerPage = mOrganizer.getMaxItemsPerPage();
- int pageT = target / mMaxItemsPerPage;
- int pagePosT = target % mMaxItemsPerPage;
+ int pageT = target / maxItemsPerPage;
+ int pagePosT = target % maxItemsPerPage;
if (pageT != pageToAnimate) {
Log.e(TAG, "Cannot animate when the target cell is invisible");
}
- int pagePosE = empty % mMaxItemsPerPage;
- int pageE = empty / mMaxItemsPerPage;
+ int pagePosE = empty % maxItemsPerPage;
+ int pageE = empty / maxItemsPerPage;
int startPos, endPos;
int moveStart, moveEnd;
@@ -588,7 +527,7 @@
if (pageE < pageToAnimate) {
moveStart = empty;
// Instantly move the first item in the current page.
- moveEnd = pageToAnimate * mMaxItemsPerPage;
+ moveEnd = pageToAnimate * maxItemsPerPage;
// Animate the 2nd item in the current page, as the first item was already moved to
// the last page.
startPos = 0;
@@ -606,10 +545,10 @@
// Move the items immediately.
moveStart = empty;
// Instantly move the last item in the current page.
- moveEnd = (pageToAnimate + 1) * mMaxItemsPerPage - 1;
+ moveEnd = (pageToAnimate + 1) * maxItemsPerPage - 1;
// Animations start with the second last item in the page
- startPos = mMaxItemsPerPage - 1;
+ startPos = maxItemsPerPage - 1;
} else {
moveStart = moveEnd = -1;
startPos = pagePosE;
@@ -621,8 +560,8 @@
// Instant moving views.
while (moveStart != moveEnd) {
int rankToMove = moveStart + direction;
- int p = rankToMove / mMaxItemsPerPage;
- int pagePos = rankToMove % mMaxItemsPerPage;
+ int p = rankToMove / maxItemsPerPage;
+ int pagePos = rankToMove % maxItemsPerPage;
int x = pagePos % mGridCountX;
int y = pagePos / mGridCountX;
@@ -667,9 +606,6 @@
for (int i = startPos; i != endPos; i += direction) {
int nextPos = i + direction;
View v = page.getChildAt(nextPos % mGridCountX, nextPos / mGridCountX);
- if (v != null) {
- ((ItemInfo) v.getTag()).rank -= direction;
- }
if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX,
REORDER_ANIMATION_DURATION, delay, true, true)) {
delay += delayAmount;
@@ -679,6 +615,6 @@
}
public int itemsPerPage() {
- return mMaxItemsPerPage;
+ return mOrganizer.getMaxItemsPerPage();
}
}
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
index c818462..caf6e55 100644
--- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -17,6 +17,8 @@
import android.graphics.drawable.Drawable;
+import com.android.launcher3.WorkspaceItemInfo;
+
/**
* Manages the parameters used to draw a Folder preview item.
*/
@@ -25,9 +27,10 @@
float transY;
float scale;
float overlayAlpha;
- FolderPreviewItemAnim anim;
+ public FolderPreviewItemAnim anim;
public boolean hidden;
- Drawable drawable;
+ public Drawable drawable;
+ public WorkspaceItemInfo item;
PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
this.transX = transX;
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 49763ba..2d817e6 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -23,28 +23,51 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
import android.view.View;
import android.widget.TextView;
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.WorkspaceItemInfo;
+import androidx.annotation.NonNull;
+
+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;
import java.util.List;
-
-import androidx.annotation.NonNull;
+import java.util.function.Predicate;
/**
* Manages the drawing and animations of {@link PreviewItemDrawingParams} for a {@link FolderIcon}.
*/
public class PreviewItemManager {
- private FolderIcon mIcon;
+ private static final FloatProperty<PreviewItemManager> CURRENT_PAGE_ITEMS_TRANS_X =
+ new FloatProperty<PreviewItemManager>("currentPageItemsTransX") {
+ @Override
+ public void setValue(PreviewItemManager manager, float v) {
+ manager.mCurrentPageItemsTransX = v;
+ manager.onParamsChanged();
+ }
+
+ @Override
+ public Float get(PreviewItemManager manager) {
+ return manager.mCurrentPageItemsTransX;
+ }
+ };
+
+ 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
// as member variables for shared usage and to avoid computation on each frame
@@ -69,7 +92,10 @@
private static final int ITEM_SLIDE_IN_OUT_DISTANCE_PX = 200;
public PreviewItemManager(FolderIcon icon) {
+ mContext = icon.getContext();
mIcon = icon;
+ mDrawableFactory = DrawableFactory.INSTANCE.get(mContext);
+ mIconSize = Launcher.getLauncher(mContext).getDeviceProfile().folderChildIconSizePx;
}
/**
@@ -200,7 +226,7 @@
}
void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) {
- List<BubbleTextView> items = mIcon.getPreviewItemsOnPage(page);
+ List<WorkspaceItemInfo> items = mIcon.getPreviewItemsOnPage(page);
int prevNumItems = params.size();
// We adjust the size of the list to match the number of items in the preview.
@@ -214,13 +240,7 @@
int numItemsInFirstPagePreview = page == 0 ? items.size() : MAX_NUM_ITEMS_IN_PREVIEW;
for (int i = 0; i < params.size(); i++) {
PreviewItemDrawingParams p = params.get(i);
- p.drawable = items.get(i).getCompoundDrawables()[1];
-
- if (p.drawable != null && !mIcon.mFolder.isOpen()) {
- // Set the callback to FolderIcon as it is responsible to drawing the icon. The
- // callback will be released when the folder is opened.
- p.drawable.setCallback(mIcon);
- }
+ setDrawable(p, items.get(i));
if (!animate) {
computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, p);
@@ -253,14 +273,8 @@
buildParamsForPage(currentPage, mCurrentPageParams, false);
onParamsChanged();
- ValueAnimator slideAnimator = ValueAnimator.ofFloat(0, ITEM_SLIDE_IN_OUT_DISTANCE_PX);
- slideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- mCurrentPageItemsTransX = (float) valueAnimator.getAnimatedValue();
- onParamsChanged();
- }
- });
+ ValueAnimator slideAnimator = ObjectAnimator
+ .ofFloat(this, CURRENT_PAGE_ITEMS_TRANS_X, 0, ITEM_SLIDE_IN_OUT_DISTANCE_PX);
slideAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -277,6 +291,25 @@
buildParamsForPage(0, mFirstPageParams, animate);
}
+ void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) {
+ boolean modified = false;
+ for (PreviewItemDrawingParams param : mFirstPageParams) {
+ if (itemCheck.test(param.item)) {
+ setDrawable(param, param.item);
+ modified = true;
+ }
+ }
+ for (PreviewItemDrawingParams param : mCurrentPageParams) {
+ if (itemCheck.test(param.item)) {
+ setDrawable(param, param.item);
+ modified = true;
+ }
+ }
+ if (modified) {
+ mIcon.invalidate();
+ }
+ }
+
boolean verifyDrawable(@NonNull Drawable who) {
for (int i = 0; i < mFirstPageParams.size(); i++) {
if (mFirstPageParams.get(i).drawable == who) {
@@ -296,46 +329,46 @@
* - Moving into a new position
* - Moving out of the preview
*
- * @param oldParams The list of items in the old preview.
- * @param newParams The list of items in the new preview.
+ * @param oldItems The list of items in the old preview.
+ * @param newItems The list of items in the new preview.
* @param dropped The item that was dropped onto the FolderIcon.
*/
- public void onDrop(List<BubbleTextView> oldParams, List<BubbleTextView> newParams,
+ public void onDrop(List<WorkspaceItemInfo> oldItems, List<WorkspaceItemInfo> newItems,
WorkspaceItemInfo dropped) {
- int numItems = newParams.size();
+ int numItems = newItems.size();
final ArrayList<PreviewItemDrawingParams> params = mFirstPageParams;
buildParamsForPage(0, params, false);
// New preview items for items that are moving in (except for the dropped item).
- List<BubbleTextView> moveIn = new ArrayList<>();
- for (BubbleTextView btv : newParams) {
- if (!oldParams.contains(btv) && !btv.getTag().equals(dropped)) {
- moveIn.add(btv);
+ List<WorkspaceItemInfo> moveIn = new ArrayList<>();
+ for (WorkspaceItemInfo newItem : newItems) {
+ if (!oldItems.contains(newItem) && !newItem.equals(dropped)) {
+ moveIn.add(newItem);
}
}
for (int i = 0; i < moveIn.size(); ++i) {
- int prevIndex = newParams.indexOf(moveIn.get(i));
+ int prevIndex = newItems.indexOf(moveIn.get(i));
PreviewItemDrawingParams p = params.get(prevIndex);
computePreviewItemDrawingParams(prevIndex, numItems, p);
- updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newParams.indexOf(moveIn.get(i)),
+ updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newItems.indexOf(moveIn.get(i)),
numItems);
}
// Items that are moving into new positions within the preview.
- for (int newIndex = 0; newIndex < newParams.size(); ++newIndex) {
- int oldIndex = oldParams.indexOf(newParams.get(newIndex));
+ for (int newIndex = 0; newIndex < newItems.size(); ++newIndex) {
+ int oldIndex = oldItems.indexOf(newItems.get(newIndex));
if (oldIndex >= 0 && newIndex != oldIndex) {
PreviewItemDrawingParams p = params.get(newIndex);
- updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex, numItems);
+ updateTransitionParam(p, newItems.get(newIndex), oldIndex, newIndex, numItems);
}
}
// Old preview items that need to be moved out.
- List<BubbleTextView> moveOut = new ArrayList<>(oldParams);
- moveOut.removeAll(newParams);
+ List<WorkspaceItemInfo> moveOut = new ArrayList<>(oldItems);
+ moveOut.removeAll(newItems);
for (int i = 0; i < moveOut.size(); ++i) {
- BubbleTextView item = moveOut.get(i);
- int oldIndex = oldParams.indexOf(item);
+ WorkspaceItemInfo item = moveOut.get(i);
+ int oldIndex = oldItems.indexOf(item);
PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null);
updateTransitionParam(p, item, oldIndex, EXIT_INDEX, numItems);
params.add(0, p); // We want these items first so that they are on drawn last.
@@ -348,14 +381,9 @@
}
}
- private void updateTransitionParam(final PreviewItemDrawingParams p, BubbleTextView btv,
+ private void updateTransitionParam(final PreviewItemDrawingParams p, WorkspaceItemInfo item,
int prevIndex, int newIndex, int numItems) {
- p.drawable = btv.getCompoundDrawables()[1];
- if (!mIcon.mFolder.isOpen()) {
- // Set the callback to FolderIcon as it is responsible to drawing the icon. The
- // callback will be released when the folder is opened.
- p.drawable.setCallback(mIcon);
- }
+ setDrawable(p, item);
FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex, numItems,
newIndex, numItems, DROP_IN_ANIMATION_DURATION, null);
@@ -364,4 +392,20 @@
}
p.anim = anim;
}
+
+ private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
+ if (item.hasPromiseIconUi()) {
+ PreloadIconDrawable drawable = mDrawableFactory.newPendingIcon(mContext, item);
+ drawable.setLevel(item.getInstallProgress());
+ p.drawable = drawable;
+ } else {
+ p.drawable = mDrawableFactory.newIcon(mContext, item);
+ }
+ p.drawable.setBounds(0, 0, mIconSize, mIconSize);
+ p.item = item;
+
+ // Set the callback to FolderIcon as it is responsible to drawing the icon. The
+ // callback will be released when the folder is opened.
+ p.drawable.setCallback(mIcon);
+ }
}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 9263a2a..f579451 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -16,6 +16,8 @@
package com.android.launcher3.graphics;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
@@ -25,7 +27,6 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
import android.view.View;
import com.android.launcher3.BubbleTextView;
@@ -35,7 +36,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BitmapRenderer;
-import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.PendingAppWidgetHostView;
@@ -157,7 +157,7 @@
}
mOutlineGeneratorCallback = new OutlineGeneratorCallback(preview);
- new Handler(UiThreadHelper.getBackgroundLooper()).post(mOutlineGeneratorCallback);
+ UI_HELPER_EXECUTOR.post(mOutlineGeneratorCallback);
}
protected static Rect getDrawableBounds(Drawable d) {
@@ -195,15 +195,22 @@
private final Bitmap mPreviewSnapshot;
private final Context mContext;
+ private final boolean mIsIcon;
OutlineGeneratorCallback(Bitmap preview) {
mPreviewSnapshot = preview;
mContext = mView.getContext();
+ mIsIcon = mView instanceof BubbleTextView;
}
@Override
public void run() {
Bitmap preview = convertPreviewToAlphaBitmap(mPreviewSnapshot);
+ if (mIsIcon) {
+ int size = Launcher.getLauncher(mContext).getDeviceProfile().iconSizePx;
+ preview = Bitmap.createScaledBitmap(preview, size, size, false);
+ }
+ //else case covers AppWidgetHost (doesn't drag/drop across different device profiles)
// We start by removing most of the alpha channel so as to ignore shadows, and
// other types of partial transparency when defining the shape of the object
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 288749f..837301f 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -80,26 +80,25 @@
* Returns a drawable that can be used as a badge for the user or null.
*/
@UiThread
- public Drawable getBadgeForUser(UserHandle user, Context context) {
+ public Drawable getBadgeForUser(UserHandle user, Context context, int badgeSize) {
if (mMyUser.equals(user)) {
return null;
}
- Bitmap badgeBitmap = getUserBadge(user, context);
+ 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) {
+ 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();
- int badgeSize = res.getDimensionPixelSize(R.dimen.profile_badge_size);
badgeBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
Drawable drawable = context.getPackageManager().getUserBadgedDrawableForDensity(
diff --git a/src/com/android/launcher3/graphics/GridOptionsProvider.java b/src/com/android/launcher3/graphics/GridOptionsProvider.java
index efd39ee..71b4366 100644
--- a/src/com/android/launcher3/graphics/GridOptionsProvider.java
+++ b/src/com/android/launcher3/graphics/GridOptionsProvider.java
@@ -1,5 +1,7 @@
package com.android.launcher3.graphics;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.res.XmlResourceParser;
@@ -17,8 +19,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.GridOption;
import com.android.launcher3.R;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.UiThreadHelper;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -180,10 +180,10 @@
throw new FileNotFoundException(e.getMessage());
}
- LooperExecutor executor = new LooperExecutor(UiThreadHelper.getBackgroundLooper());
try {
return openPipeHelper(uri, MIME_TYPE_PNG, null,
- executor.submit(new LauncherPreviewRenderer(getContext(), idp)), BITMAP_WRITER);
+ UI_HELPER_EXECUTOR.submit(new LauncherPreviewRenderer(getContext(), idp)),
+ BITMAP_WRITER);
} catch (Exception e) {
throw new FileNotFoundException(e.getMessage());
}
diff --git a/src/com/android/launcher3/icons/ComponentWithLabel.java b/src/com/android/launcher3/icons/ComponentWithLabel.java
index 46b5002..832956d 100644
--- a/src/com/android/launcher3/icons/ComponentWithLabel.java
+++ b/src/com/android/launcher3/icons/ComponentWithLabel.java
@@ -34,9 +34,11 @@
class ComponentCachingLogic implements CachingLogic<ComponentWithLabel> {
private final PackageManager mPackageManager;
+ private final boolean mAddToMemCache;
- public ComponentCachingLogic(Context context) {
+ public ComponentCachingLogic(Context context, boolean addToMemCache) {
mPackageManager = context.getPackageManager();
+ mAddToMemCache = addToMemCache;
}
@Override
@@ -60,5 +62,10 @@
// Do not load icon.
target.icon = BitmapInfo.LOW_RES_ICON;
}
+
+ @Override
+ public boolean addToMemCache() {
+ return mAddToMemCache;
+ }
}
}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index abff237..ad01f9f 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -16,13 +16,19 @@
package com.android.launcher3.icons;
+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.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Process;
@@ -36,11 +42,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherFiles;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
@@ -48,7 +51,9 @@
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.InstantAppResolver;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import java.util.function.Supplier;
@@ -60,12 +65,11 @@
private static final String TAG = "Launcher.IconCache";
- private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
-
private final CachingLogic<ComponentWithLabel> mComponentWithLabelCachingLogic;
private final CachingLogic<LauncherActivityInfo> mLauncherActivityInfoCachingLogic;
+ private final CachingLogic<ShortcutInfo> mShortcutCachingLogic;
- private final LauncherAppsCompat mLauncherApps;
+ private final LauncherApps mLauncherApps;
private final UserManagerCompat mUserManager;
private final InstantAppResolver mInstantAppResolver;
private final IconProvider mIconProvider;
@@ -73,11 +77,12 @@
private int mPendingIconRequestCount = 0;
public IconCache(Context context, InvariantDeviceProfile inv) {
- super(context, LauncherFiles.APP_ICONS_DB, LauncherModel.getWorkerLooper(),
+ super(context, LauncherFiles.APP_ICONS_DB, MODEL_EXECUTOR.getLooper(),
inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */);
- mComponentWithLabelCachingLogic = new ComponentCachingLogic(context);
+ mComponentWithLabelCachingLogic = new ComponentCachingLogic(context, false);
mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context);
- mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+ mShortcutCachingLogic = new ShortcutCachingLogic();
+ mLauncherApps = mContext.getSystemService(LauncherApps.class);
mUserManager = UserManagerCompat.getInstance(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
mIconProvider = IconProvider.INSTANCE.get(context);
@@ -124,7 +129,7 @@
final ItemInfoWithIcon info) {
Preconditions.assertUIThread();
if (mPendingIconRequestCount <= 0) {
- LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_FOREGROUND);
+ MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
}
mPendingIconRequestCount ++;
@@ -136,7 +141,7 @@
} else if (info instanceof PackageItemInfo) {
getTitleAndIconForApp((PackageItemInfo) info, false);
}
- mMainThreadExecutor.execute(() -> {
+ MAIN_EXECUTOR.execute(() -> {
caller.reapplyItemInfo(info);
onEnd();
});
@@ -149,7 +154,7 @@
private void onIconRequestEnd() {
mPendingIconRequestCount --;
if (mPendingIconRequestCount <= 0) {
- LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
@@ -175,6 +180,14 @@
}
/**
+ * Fill in info with the icon and label for deep shortcut.
+ */
+ public synchronized CacheEntry getDeepShortcutTitleAndIcon(ShortcutInfo info) {
+ return cacheLocked(ShortcutKey.fromInfo(info).componentName, info.getUserHandle(),
+ () -> info, mShortcutCachingLogic, false, false);
+ }
+
+ /**
* Fill in {@param info} with the icon and label. If the
* corresponding activity is not found, it reverts to the package icon.
*/
@@ -195,7 +208,7 @@
public synchronized String getTitleNoCache(ComponentWithLabel info) {
CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), () -> info,
mComponentWithLabelCachingLogic, false /* usePackageIcon */,
- true /* useLowResIcon */, false /* addToMemCache */);
+ true /* useLowResIcon */);
return Utilities.trim(entry.title);
}
@@ -236,6 +249,10 @@
return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
}
+ public void updateSessionCache(PackageUserKey key, PackageInstaller.SessionInfo info) {
+ cachePackageInstallInfo(key.mPackageName, key.mUser, info.getAppIcon(), info.getAppLabel());
+ }
+
@Override
protected String getIconSystemState(String packageName) {
return mIconProvider.getSystemStateForPackage(mSystemState, packageName)
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 7632408..44c67e9 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -24,6 +24,9 @@
import android.graphics.drawable.Drawable;
import android.os.Process;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.AppInfo;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.InvariantDeviceProfile;
@@ -37,8 +40,6 @@
import java.util.function.Supplier;
-import androidx.annotation.Nullable;
-
/**
* Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
* that are threadsafe.
@@ -115,24 +116,37 @@
}
// below methods should also migrate to BaseIconFactory
-
+ @WorkerThread
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
return createShortcutIcon(shortcutInfo, true /* badged */);
}
+ @WorkerThread
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged) {
return createShortcutIcon(shortcutInfo, badged, null);
}
- public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo,
- boolean badged, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
- Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext)
- .getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
+ @WorkerThread
+ public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged,
+ @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
+ return createShortcutIcon(shortcutInfo, badged, true, fallbackIconProvider);
+ }
+
+ @WorkerThread
+ 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);
+ } else {
+ bitmapInfo = new BitmapInfo();
+ new ShortcutCachingLogic().loadIcon(mContext, shortcutInfo, bitmapInfo);
+ }
final Bitmap unbadgedBitmap;
- if (unbadgedDrawable != null) {
- unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
+ if (bitmapInfo.icon != null) {
+ unbadgedBitmap = bitmapInfo.icon;
} else {
if (fallbackIconProvider != null) {
// Fallback icons are already badged and with appropriate shadow
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
new file mode 100644
index 0000000..5d696fd
--- /dev/null
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutKey;
+
+/**
+ * Caching logic for shortcuts.
+ */
+public class ShortcutCachingLogic implements CachingLogic<ShortcutInfo> {
+
+ @Override
+ public ComponentName getComponent(ShortcutInfo info) {
+ return ShortcutKey.fromInfo(info).componentName;
+ }
+
+ @Override
+ public UserHandle getUser(ShortcutInfo info) {
+ return info.getUserHandle();
+ }
+
+ @Override
+ public CharSequence getLabel(ShortcutInfo info) {
+ return info.getShortLabel();
+ }
+
+ @Override
+ 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) return info.lastUpdateTime;
+ return Math.max(shortcutInfo.getLastChangedTimestamp(), info.lastUpdateTime);
+ }
+
+ @Override
+ public boolean addToMemCache() {
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/logging/EventLogArray.java b/src/com/android/launcher3/logging/EventLogArray.java
index f20f365..3ecfb23 100644
--- a/src/com/android/launcher3/logging/EventLogArray.java
+++ b/src/com/android/launcher3/logging/EventLogArray.java
@@ -16,11 +16,13 @@
package com.android.launcher3.logging;
+import android.util.Log;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;
+import java.util.Random;
/**
* A utility class to record and log events. Events are stored in a fixed size array and old logs
@@ -37,6 +39,7 @@
private final String name;
private final EventEntry[] logs;
private int nextIndex;
+ private int mLogId;
public EventLogArray(String name, int size) {
this.name = name;
@@ -52,10 +55,6 @@
addLog(TYPE_INTEGER, event, extras);
}
- public void addLog(String event, float extras) {
- addLog(TYPE_FLOAT, event, extras);
- }
-
public void addLog(String event, boolean extras) {
addLog(extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE, event, 0);
}
@@ -65,7 +64,7 @@
int last = (nextIndex + logs.length - 1) % logs.length;
int secondLast = (nextIndex + logs.length - 2) % logs.length;
if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) {
- logs[last].update(type, event, extras);
+ logs[last].update(type, event, extras, mLogId);
logs[secondLast].duplicateCount++;
return;
}
@@ -73,7 +72,7 @@
if (logs[nextIndex] == null) {
logs[nextIndex] = new EventEntry();
}
- logs[nextIndex].update(type, event, extras);
+ logs[nextIndex].update(type, event, extras, mLogId);
nextIndex = (nextIndex + 1) % logs.length;
}
@@ -113,10 +112,18 @@
if (log.duplicateCount > 0) {
msg.append(" & ").append(log.duplicateCount).append(" similar events");
}
+ msg.append(" traceId: ").append(log.traceId);
writer.println(msg);
}
}
+ /** Returns a 3 digit random number between 100-999 */
+ public int generateAndSetLogId() {
+ Random r = new Random();
+ mLogId = r.nextInt(900) + 100;
+ return mLogId;
+ }
+
private boolean isEntrySame(EventEntry entry, int type, String event) {
return entry != null && entry.type == type && entry.event.equals(event);
}
@@ -129,11 +136,13 @@
private float extras;
private long time;
private int duplicateCount;
+ private int traceId;
- public void update(int type, String event, float extras) {
+ public void update(int type, String event, float extras, int traceId) {
this.type = type;
this.event = event;
this.extras = extras;
+ this.traceId = traceId;
time = System.currentTimeMillis();
duplicateCount = 0;
}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index f7f8ef1..04cf20a 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -1,13 +1,16 @@
package com.android.launcher3.logging;
+import static com.android.launcher3.util.Executors.createAndStartNewLooper;
+
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
import android.util.Pair;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.launcher3.util.IOUtils;
import java.io.BufferedReader;
import java.io.File;
@@ -29,8 +32,7 @@
*/
public final class FileLog {
- protected static final boolean ENABLED =
- FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE;
+ protected static final boolean ENABLED = true;
private static final String FILE_NAME_PREFIX = "log-";
private static final DateFormat DATE_FORMAT =
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
@@ -88,12 +90,12 @@
Message.obtain(getHandler(), LogWriterCallback.MSG_WRITE, out).sendToTarget();
}
- private static Handler getHandler() {
+ @VisibleForTesting
+ static Handler getHandler() {
synchronized (DATE_FORMAT) {
if (sHandler == null) {
- HandlerThread thread = new HandlerThread("file-logger");
- thread.start();
- sHandler = new Handler(thread.getLooper(), new LogWriterCallback());
+ sHandler = new Handler(createAndStartNewLooper("file-logger"),
+ new LogWriterCallback());
}
}
return sHandler;
@@ -103,15 +105,16 @@
* Blocks until all the pending logs are written to the disk
* @param out if not null, all the persisted logs are copied to the writer.
*/
- public static void flushAll(PrintWriter out) throws InterruptedException {
+ public static boolean flushAll(PrintWriter out) throws InterruptedException {
if (!ENABLED) {
- return;
+ return false;
}
CountDownLatch latch = new CountDownLatch(1);
Message.obtain(getHandler(), LogWriterCallback.MSG_FLUSH,
Pair.create(out, latch)).sendToTarget();
latch.await(2, TimeUnit.SECONDS);
+ return latch.getCount() == 0;
}
/**
@@ -131,7 +134,7 @@
private PrintWriter mCurrentWriter = null;
private void closeWriter() {
- Utilities.closeSilently(mCurrentWriter);
+ IOUtils.closeSilently(mCurrentWriter);
mCurrentWriter = null;
}
@@ -219,7 +222,7 @@
} catch (Exception e) {
// ignore
} finally {
- Utilities.closeSilently(in);
+ IOUtils.closeSilently(in);
}
}
}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index c72b07a..21ca74e 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -427,10 +427,16 @@
mAppOrTaskLaunch = false;
ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
-
if (!IS_VERBOSE) {
return;
}
+ Log.d(TAG, generateLog(ev));
+ }
+
+ /**
+ * Returns a human-readable log for given user event.
+ */
+ public static String generateLog(LauncherEvent ev) {
String log = "\n-----------------------------------------------------"
+ "\naction:" + LoggerUtils.getActionStr(ev.action);
if (ev.srcTarget != null && ev.srcTarget.length > 0) {
@@ -445,8 +451,7 @@
ev.elapsedSessionMillis,
ev.actionDurationMillis);
log += "\n\n";
- Log.d(TAG, log);
- return;
+ return log;
}
private static String getTargetsStr(Target[] targets) {
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 7d4f2f7..227bb22 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -17,12 +17,12 @@
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller.SessionInfo;
import android.os.UserHandle;
import android.util.LongSparseArray;
import android.util.Pair;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InvariantDeviceProfile;
@@ -30,14 +30,13 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.PackageManagerHelper;
import java.util.ArrayList;
import java.util.List;
@@ -91,7 +90,7 @@
PackageInstallerCompat packageInstaller =
PackageInstallerCompat.getInstance(app.getContext());
- LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(app.getContext());
+ LauncherApps launcherApps = app.getContext().getSystemService(LauncherApps.class);
for (ItemInfo item : filteredItems) {
// Find appropriate space for the item.
@@ -200,7 +199,7 @@
intentWithoutPkg = intent.toUri(0);
}
- boolean isLauncherAppTarget = Utilities.isLauncherAppTarget(intent);
+ boolean isLauncherAppTarget = PackageManagerHelper.isLauncherAppTarget(intent);
synchronized (dataModel) {
for (ItemInfo item : dataModel.itemsIdMap) {
if (item instanceof WorkspaceItemInfo) {
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
similarity index 63%
rename from src/com/android/launcher3/AllAppsList.java
rename to src/com/android/launcher3/model/AllAppsList.java
index 8b49c06..9f1843f 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -14,57 +14,80 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.model;
+
+import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
+import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.os.LocaleList;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.util.FlagOp;
-import com.android.launcher3.util.ItemInfoMatcher;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.AppFilter;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.PromiseAppInfo;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.util.FlagOp;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.SafeCloseable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.function.Consumer;
+
/**
* Stores the list of all applications for the all apps view.
*/
public class AllAppsList {
+
private static final String TAG = "AllAppsList";
+ private static final Consumer<AppInfo> NO_OP_CONSUMER = a -> { };
+
public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
/** The list off all apps. */
public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
- /** The list of apps that have been added since the last notify() call. */
- public ArrayList<AppInfo> added = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
- /** The list of apps that have been removed since the last notify() call. */
- public ArrayList<AppInfo> removed = new ArrayList<>();
- /** The list of apps that have been modified since the last notify() call. */
- public ArrayList<AppInfo> modified = new ArrayList<>();
private IconCache mIconCache;
-
private AppFilter mAppFilter;
+ private boolean mDataChanged = false;
+ private Consumer<AppInfo> mRemoveListener = NO_OP_CONSUMER;
+
+ private AlphabeticIndexCompat mIndex;
+
/**
* Boring constructor.
*/
public AllAppsList(IconCache iconCache, AppFilter appFilter) {
mIconCache = iconCache;
mAppFilter = appFilter;
+ mIndex = new AlphabeticIndexCompat(LocaleList.getDefault());
+ }
+
+ /**
+ * Returns true if there have been any changes since last call.
+ */
+ public boolean getAndResetChangeFlag() {
+ boolean result = mDataChanged;
+ mDataChanged = false;
+ return result;
}
/**
@@ -81,55 +104,67 @@
return;
}
mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
+ info.sectionName = mIndex.computeSectionName(info.title);
data.add(info);
- added.add(info);
+ mDataChanged = true;
}
- public void addPromiseApp(Context context,
- PackageInstallerCompat.PackageInstallInfo installInfo) {
- ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context)
- .getApplicationInfo(installInfo.packageName, 0, installInfo.user);
+ public void addPromiseApp(Context context, PackageInstallInfo installInfo) {
+ ApplicationInfo applicationInfo = new PackageManagerHelper(context)
+ .getApplicationInfo(installInfo.packageName, installInfo.user, 0);
// only if not yet installed
if (applicationInfo == null) {
PromiseAppInfo info = new PromiseAppInfo(installInfo);
mIconCache.getTitleAndIcon(info, info.usingLowResIcon());
+ info.sectionName = mIndex.computeSectionName(info.title);
+
data.add(info);
- added.add(info);
+ mDataChanged = true;
}
}
- public void removePromiseApp(AppInfo appInfo) {
- // the <em>removed</em> list is handled by the caller
- // so not adding it here
- data.remove(appInfo);
+ public PromiseAppInfo updatePromiseInstallInfo(PackageInstallInfo installInfo) {
+ UserHandle user = Process.myUserHandle();
+ for (int i=0; i < data.size(); i++) {
+ final AppInfo appInfo = data.get(i);
+ final ComponentName tgtComp = appInfo.getTargetComponent();
+ if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName)
+ && appInfo.user.equals(user)
+ && appInfo instanceof PromiseAppInfo) {
+ final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
+ if (installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
+ promiseAppInfo.level = installInfo.progress;
+ return promiseAppInfo;
+ } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED) {
+ removeApp(i);
+ }
+ }
+ }
+ return null;
+ }
+
+ private void removeApp(int index) {
+ AppInfo removed = data.remove(index);
+ if (removed != null) {
+ mDataChanged = true;
+ mRemoveListener.accept(removed);
+ }
}
public void clear() {
data.clear();
- // TODO: do we clear these too?
- added.clear();
- removed.clear();
- modified.clear();
- }
-
- public int size() {
- return data.size();
- }
-
- public AppInfo get(int index) {
- return data.get(index);
+ mDataChanged = false;
+ // Reset the index as locales might have changed
+ mIndex = new AlphabeticIndexCompat(LocaleList.getDefault());
}
/**
* Add the icons for the supplied apk called packageName.
*/
public void addPackage(Context context, String packageName, UserHandle user) {
- final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
- final List<LauncherActivityInfo> matches = launcherApps.getActivityList(packageName,
- user);
-
- for (LauncherActivityInfo info : matches) {
+ for (LauncherActivityInfo info : context.getSystemService(LauncherApps.class)
+ .getActivityList(packageName, user)) {
add(new AppInfo(context, info, user), info);
}
}
@@ -142,8 +177,7 @@
for (int i = data.size() - 1; i >= 0; i--) {
AppInfo info = data.get(i);
if (info.user.equals(user) && packageName.equals(info.componentName.getPackageName())) {
- removed.add(info);
- data.remove(i);
+ removeApp(i);
}
}
}
@@ -157,17 +191,17 @@
AppInfo info = data.get(i);
if (matcher.matches(info, info.componentName)) {
info.runtimeStatusFlags = op.apply(info.runtimeStatusFlags);
- modified.add(info);
+ mDataChanged = true;
}
}
}
- public void updateIconsAndLabels(HashSet<String> packages, UserHandle user,
- ArrayList<AppInfo> outUpdates) {
+ public void updateIconsAndLabels(HashSet<String> packages, UserHandle user) {
for (AppInfo info : data) {
if (info.user.equals(user) && packages.contains(info.componentName.getPackageName())) {
mIconCache.updateTitleAndIcon(info);
- outUpdates.add(info);
+ info.sectionName = mIndex.computeSectionName(info.title);
+ mDataChanged = true;
}
}
}
@@ -176,9 +210,8 @@
* Add and remove icons for this package which has been updated.
*/
public void updatePackage(Context context, String packageName, UserHandle user) {
- final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
- final List<LauncherActivityInfo> matches = launcherApps.getActivityList(packageName,
- user);
+ final List<LauncherActivityInfo> matches = context.getSystemService(LauncherApps.class)
+ .getActivityList(packageName, user);
if (matches.size() > 0) {
// Find disabled/removed activities and remove them from data and add them
// to the removed list.
@@ -188,8 +221,7 @@
&& packageName.equals(applicationInfo.componentName.getPackageName())) {
if (!findActivity(matches, applicationInfo.componentName)) {
Log.w(TAG, "Changing shortcut target due to app component name change.");
- removed.add(applicationInfo);
- data.remove(i);
+ removeApp(i);
}
}
}
@@ -202,7 +234,9 @@
add(new AppInfo(context, info, user), info);
} else {
mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
- modified.add(applicationInfo);
+ applicationInfo.sectionName = mIndex.computeSectionName(applicationInfo.title);
+
+ mDataChanged = true;
}
}
} else {
@@ -211,15 +245,13 @@
final AppInfo applicationInfo = data.get(i);
if (user.equals(applicationInfo.user)
&& packageName.equals(applicationInfo.componentName.getPackageName())) {
- removed.add(applicationInfo);
mIconCache.remove(applicationInfo.componentName, user);
- data.remove(i);
+ removeApp(i);
}
}
}
}
-
/**
* Returns whether <em>apps</em> contains <em>component</em>.
*/
@@ -247,4 +279,16 @@
}
return null;
}
+
+ public AppInfo[] copyData() {
+ AppInfo[] result = data.toArray(EMPTY_ARRAY);
+ Arrays.sort(result, COMPONENT_KEY_COMPARATOR);
+ return result;
+ }
+
+ public SafeCloseable trackRemoves(Consumer<AppInfo> removeListener) {
+ mRemoveListener = removeListener;
+
+ return () -> mRemoveListener = NO_OP_CONSUMER;
+ }
}
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
index 29a46cf..13ab033 100644
--- a/src/com/android/launcher3/model/AppLaunchTracker.java
+++ b/src/com/android/launcher3/model/AppLaunchTracker.java
@@ -51,5 +51,8 @@
public void onStartApp(ComponentName componentName, UserHandle user,
@Nullable String container) { }
+ public void onDismissApp(ComponentName componentName, UserHandle user,
+ @Nullable String container){}
+
public void onReturnedToHome() { }
}
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 97cf267..0a4f005 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -16,21 +16,21 @@
package com.android.launcher3.model;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.os.Looper;
import android.util.Log;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.PagedView;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.LooperIdleLock;
@@ -65,7 +65,7 @@
public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, int pageToBindFirst, WeakReference<Callbacks> callbacks) {
- mUiExecutor = new MainThreadExecutor();
+ mUiExecutor = MAIN_EXECUTOR;
mApp = app;
mBgDataModel = dataModel;
mBgAllAppsList = allAppsList;
@@ -279,9 +279,8 @@
public void bindAllApps() {
// shallow copy
- @SuppressWarnings("unchecked")
- ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
- executeCallbacksTask(c -> c.bindAllApplications(list), mUiExecutor);
+ AppInfo[] apps = mBgAllAppsList.copyData();
+ executeCallbacksTask(c -> c.bindAllApplications(apps), mUiExecutor);
}
public abstract void bindWidgets();
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index eea3d8c..e12633b 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -17,12 +17,12 @@
import android.util.Log;
-import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -95,12 +96,7 @@
public void bindUpdatedWorkspaceItems(final ArrayList<WorkspaceItemInfo> updatedShortcuts) {
if (!updatedShortcuts.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindWorkspaceItemsChanged(updatedShortcuts);
- }
- });
+ scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(updatedShortcuts));
}
}
@@ -113,23 +109,20 @@
public void bindUpdatedWidgets(BgDataModel dataModel) {
final ArrayList<WidgetListRowEntry> widgets =
dataModel.widgetsModel.getWidgetsList(mApp.getContext());
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAllWidgets(widgets);
- }
- });
+ scheduleCallbackTask(c -> c.bindAllWidgets(widgets));
}
public void deleteAndBindComponentsRemoved(final ItemInfoMatcher matcher) {
getModelWriter().deleteItemsFromDatabase(matcher);
// Call the components-removed callback
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindWorkspaceComponentsRemoved(matcher);
- }
- });
+ scheduleCallbackTask(c -> c.bindWorkspaceComponentsRemoved(matcher));
+ }
+
+ public void bindApplicationsIfNeeded() {
+ if (mAllAppsList.getAndResetChangeFlag()) {
+ AppInfo[] apps = mAllAppsList.copyData();
+ scheduleCallbackTask(c -> c.bindAllApplications(apps));
+ }
}
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 8f0cd08..0e20270 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -22,11 +22,13 @@
import android.util.Log;
import android.util.MutableInt;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
@@ -40,6 +42,10 @@
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.WidgetListRowEntry;
+
import com.google.protobuf.nano.MessageNano;
import java.io.FileDescriptor;
@@ -49,6 +55,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -391,4 +398,30 @@
}
}
}
+
+ public interface Callbacks {
+ void rebindModel();
+
+ int getCurrentWorkspaceScreen();
+ void clearPendingBinds();
+ void startBinding();
+ void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons);
+ void bindScreens(IntArray orderedScreenIds);
+ void finishFirstPageBind(ViewOnDrawExecutor executor);
+ void finishBindingItems(int pageBoundFirst);
+ void preAddApps();
+ void bindAppsAdded(IntArray newScreens,
+ ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
+ void bindPromiseAppProgressUpdated(PromiseAppInfo app);
+ void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
+ void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
+ void bindRestoreItemsChange(HashSet<ItemInfo> updates);
+ void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
+ void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
+ void onPageBoundSynchronously(int page);
+ void executeOnNextDraw(ViewOnDrawExecutor executor);
+ void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
+
+ void bindAllApplications(AppInfo[] apps);
+ }
}
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 7852444..c1c8be3 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -18,14 +18,10 @@
import android.content.ComponentName;
import android.os.UserHandle;
-import com.android.launcher3.AllAppsList;
-import com.android.launcher3.AppInfo;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import java.util.ArrayList;
@@ -53,9 +49,9 @@
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IconCache iconCache = app.getIconCache();
- final ArrayList<AppInfo> updatedApps = new ArrayList<>();
ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
+
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
@@ -69,18 +65,10 @@
}
}
}
- apps.updateIconsAndLabels(mPackages, mUser, updatedApps);
+ apps.updateIconsAndLabels(mPackages, mUser);
}
bindUpdatedWorkspaceItems(updatedShortcuts);
-
- if (!updatedApps.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAppsAddedOrUpdated(updatedApps);
- }
- });
- }
+ bindApplicationsIfNeeded();
}
public boolean isValidShortcut(WorkspaceItemInfo si) {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 783e908..ac44b0e 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -17,6 +17,8 @@
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
@@ -27,21 +29,17 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
-import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
-import java.util.function.Consumer;
-
-import androidx.annotation.VisibleForTesting;
/**
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
@@ -973,7 +971,7 @@
validPackages.add(info.packageName);
}
PackageInstallerCompat.getInstance(context)
- .updateAndGetActiveSessionCache().keySet()
+ .getActiveSessions().keySet()
.forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName));
return validPackages;
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 1c39d1f..6154e7e 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.CursorWrapper;
@@ -33,17 +34,16 @@
import android.util.LongSparseArray;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
-import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.ContentWriter;
@@ -260,7 +260,7 @@
Intent newIntent = new Intent(Intent.ACTION_MAIN, null);
newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
newIntent.setComponent(componentName);
- LauncherActivityInfo lai = LauncherAppsCompat.getInstance(mContext)
+ LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
.resolveActivity(newIntent, user);
if ((lai == null) && !allowMissingTarget) {
Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 7593a33..81b701d 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -19,8 +19,9 @@
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
-import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle;
import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.PackageManagerHelper.isSystemApp;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -29,18 +30,18 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.ShortcutInfo;
-import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.MutableInt;
+import android.util.TimingLogger;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
@@ -53,23 +54,27 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherActivityCachingLogic;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.ShortcutCachingLogic;
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.provider.ImportDataTask;
+import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.IOUtils;
import com.android.launcher3.util.LooperIdleLock;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
@@ -103,7 +108,7 @@
private final LoaderResults mResults;
- private final LauncherAppsCompat mLauncherApps;
+ private final LauncherApps mLauncherApps;
private final UserManagerCompat mUserManager;
private final DeepShortcutManager mShortcutManager;
private final PackageInstallerCompat mPackageInstaller;
@@ -119,7 +124,7 @@
mBgDataModel = dataModel;
mResults = results;
- mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext());
+ mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
mUserManager = UserManagerCompat.getInstance(mApp.getContext());
mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext());
mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext());
@@ -160,88 +165,123 @@
}
public void run() {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "LoaderTask1 " + this);
+ }
synchronized (this) {
// Skip fast if we are already stopped.
if (mStopped) {
return;
}
}
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "LoaderTask2 " + this);
+ }
- TraceHelper.beginSection(TAG);
+ Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
+ 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)) {
- TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
- loadWorkspace();
+ List<ShortcutInfo> allShortcuts = new ArrayList<>();
+ loadWorkspace(allShortcuts);
+ logger.addSplit("loadWorkspace");
verifyNotStopped();
- TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
mResults.bindWorkspace();
+ logger.addSplit("bindWorkspace");
// Notify the installer packages of packages with active installs on the first screen.
- TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");
sendFirstScreenActiveInstallsBroadcast();
+ logger.addSplit("sendFirstScreenActiveInstallsBroadcast");
// Take a break
- TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
waitForIdle();
+ logger.addSplit("step 1 complete");
verifyNotStopped();
// second step
- TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
List<LauncherActivityInfo> allActivityList = loadAllApps();
+ logger.addSplit("loadAllApps");
- TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
verifyNotStopped();
mResults.bindAllApps();
+ logger.addSplit("bindAllApps");
verifyNotStopped();
- TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache");
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.newInstance(mApp.getContext()),
mApp.getModel()::onPackageIconsUpdated);
+ logger.addSplit("update icon cache");
+
+ verifyNotStopped();
+ logger.addSplit("save shortcuts in icon cache");
+ updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
+ mApp.getModel()::onPackageIconsUpdated);
// Take a break
- TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle");
waitForIdle();
+ logger.addSplit("step 2 complete");
verifyNotStopped();
// third step
- TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts");
- loadDeepShortcuts();
+ List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
+ logger.addSplit("loadDeepShortcuts");
verifyNotStopped();
- TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts");
mResults.bindDeepShortcuts();
+ logger.addSplit("bindDeepShortcuts");
+
+ verifyNotStopped();
+ logger.addSplit("save deep shortcuts in icon cache");
+ updateHandler.updateIcons(allDeepShortcuts,
+ new ShortcutCachingLogic(), (pkgs, user) -> { });
// Take a break
- TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle");
waitForIdle();
+ logger.addSplit("step 3 complete");
verifyNotStopped();
// fourth step
- TraceHelper.partitionSection(TAG, "step 4.1: loading widgets");
List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
+ logger.addSplit("load widgets");
verifyNotStopped();
- TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets");
mResults.bindWidgets();
+ logger.addSplit("bindWidgets");
+ verifyNotStopped();
+
+ updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(
+ mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated);
+ logger.addSplit("save widgets in icon cache");
verifyNotStopped();
- TraceHelper.partitionSection(TAG, "step 4.3: Update icon cache");
- updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(mApp.getContext()),
- mApp.getModel()::onWidgetLabelsUpdated);
-
- verifyNotStopped();
- TraceHelper.partitionSection(TAG, "step 5: Finish icon cache update");
updateHandler.finish();
+ logger.addSplit("finish icon update");
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
+ "LoaderTask3 " + this);
+ }
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
- TraceHelper.partitionSection(TAG, "Cancelled");
+ logger.addSplit("Cancelled");
+ } finally {
+ logger.dumpToLog();
}
- TraceHelper.endSection(TAG);
+ TraceHelper.INSTANCE.endSection(traceToken);
}
public synchronized void stopLocked() {
@@ -249,7 +289,7 @@
this.notify();
}
- private void loadWorkspace() {
+ private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
final Context context = mApp.getContext();
final ContentResolver contentResolver = context.getContentResolver();
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
@@ -284,7 +324,9 @@
mBgDataModel.clear();
final HashMap<PackageUserKey, SessionInfo> installingPkgs =
- mPackageInstaller.updateAndGetActiveSessionCache();
+ mPackageInstaller.getActiveSessions();
+ installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
+
final PackageUserKey tempPackageKey = new PackageUserKey(null, null);
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
@@ -320,9 +362,9 @@
// We can only query for shortcuts when the user is unlocked.
if (userUnlocked) {
- List<ShortcutInfo> pinnedShortcuts =
+ DeepShortcutManager.QueryResult pinnedShortcuts =
mShortcutManager.queryForPinnedShortcuts(null, user);
- if (mShortcutManager.wasLastCallSuccess()) {
+ if (pinnedShortcuts.wasSuccess()) {
for (ShortcutInfo shortcut : pinnedShortcuts) {
shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
shortcut);
@@ -385,14 +427,16 @@
// If there is no target package, its an implicit intent
// (legacy shortcut) which is always valid
boolean validTarget = TextUtils.isEmpty(targetPkg) ||
- mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user);
+ mLauncherApps.isPackageEnabled(targetPkg, c.user);
- if (cn != null && validTarget) {
+ // If it's a deep shortcut, we'll use pinned shortcuts to restore it
+ if (cn != null && validTarget && c.itemType
+ != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
// If the apk is present and the shortcut points to a specific
// component.
// If the component is already present
- if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) {
+ if (mLauncherApps.isActivityEnabled(cn, c.user)) {
// no special handling necessary for this item
c.markRestored();
} else {
@@ -500,6 +544,7 @@
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
}
intent = info.intent;
+ allDeepShortcuts.add(pinnedShortcut);
} else {
// Create a shortcut info in disabled mode for now.
info = c.loadSimpleWorkspaceItem();
@@ -535,7 +580,7 @@
info.spanX = 1;
info.spanY = 1;
info.runtimeStatusFlags |= disabledState;
- if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
+ if (isSafeMode && !isSystemApp(context, intent)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
}
@@ -572,7 +617,7 @@
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
c.markDeleted("Only legacy shortcuts can have null package");
continue;
}
@@ -584,10 +629,19 @@
int appWidgetId = c.getInt(appWidgetIdIndex);
String savedProvider = c.getString(appWidgetProviderIndex);
+ final ComponentName component;
- final ComponentName component =
- ComponentName.unflattenFromString(savedProvider);
-
+ boolean isSearchWidget = (c.getInt(optionsIndex)
+ & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0;
+ if (isSearchWidget) {
+ component = QsbContainerView.getSearchComponentName(context);
+ if (component == null) {
+ c.markDeleted("Discarding SearchWidget without packagename ");
+ continue;
+ }
+ } else {
+ component = ComponentName.unflattenFromString(savedProvider);
+ }
final boolean isIdValid = !c.hasRestoreFlag(
LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
final boolean wasProviderReady = !c.hasRestoreFlag(
@@ -597,9 +651,7 @@
widgetProvidersMap = mAppWidgetManager.getAllProvidersMap();
}
final AppWidgetProviderInfo provider = widgetProvidersMap.get(
- new ComponentKey(
- ComponentName.unflattenFromString(savedProvider),
- c.user));
+ new ComponentKey(component, c.user));
final boolean isProviderReady = isValidProvider(provider);
if (!isSafeMode && !customWidget &&
@@ -665,6 +717,7 @@
c.applyCommonProperties(appWidgetInfo);
appWidgetInfo.spanX = c.getInt(spanXIndex);
appWidgetInfo.spanY = c.getInt(spanYIndex);
+ appWidgetInfo.options = c.getInt(optionsIndex);
appWidgetInfo.user = c.user;
if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) {
@@ -710,7 +763,7 @@
}
}
} finally {
- Utilities.closeSilently(c);
+ IOUtils.closeSilently(c);
}
// Break early if we've stopped loading
@@ -750,8 +803,8 @@
}
// Sort the folder items, update ranks, and make sure all preview items are high res.
- FolderIconPreviewVerifier verifier =
- new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+ FolderGridOrganizer verifier =
+ new FolderGridOrganizer(mApp.getInvariantDeviceProfile());
for (FolderInfo folder : mBgDataModel.folders) {
Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
verifier.setFolderInfo(folder);
@@ -778,7 +831,7 @@
new SdCardAvailableReceiver(mApp, pendingPackages),
new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
null,
- new Handler(LauncherModel.getWorkerLooper()));
+ MODEL_EXECUTOR.getHandler());
}
}
}
@@ -827,20 +880,21 @@
allActivityList.addAll(apps);
}
- if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+ if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
// get all active sessions and add them to the all apps list
for (PackageInstaller.SessionInfo info :
mPackageInstaller.getAllVerifiedSessions()) {
mBgAllAppsList.addPromiseApp(mApp.getContext(),
- PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info));
+ PackageInstallInfo.fromInstallingState(info));
}
}
- mBgAllAppsList.added = new ArrayList<>();
+ mBgAllAppsList.getAndResetChangeFlag();
return allActivityList;
}
- private void loadDeepShortcuts() {
+ private List<ShortcutInfo> loadDeepShortcuts() {
+ List<ShortcutInfo> allShortcuts = new ArrayList<>();
mBgDataModel.deepShortcutMap.clear();
mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission();
if (mBgDataModel.hasShortcutHostPermission) {
@@ -848,10 +902,12 @@
if (mUserManager.isUserUnlocked(user)) {
List<ShortcutInfo> shortcuts =
mShortcutManager.queryForAllShortcuts(user);
+ allShortcuts.addAll(shortcuts);
mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
}
}
}
+ return allShortcuts;
}
public static boolean isValidProvider(AppWidgetProviderInfo provider) {
diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java
index b353810..2bd6cd4 100644
--- a/src/com/android/launcher3/model/ModelPreload.java
+++ b/src/com/android/launcher3/model/ModelPreload.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.util.Log;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 4ce2f4b..c69ace9 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -16,6 +16,8 @@
package com.android.launcher3.model;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -31,23 +33,25 @@
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LooperExecutor;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
/**
* Class for handling model updates.
@@ -61,7 +65,6 @@
private final BgDataModel mBgDataModel;
private final Handler mUiHandler;
- private final Executor mWorkerExecutor;
private final boolean mHasVerticalHotseat;
private final boolean mVerifyChanges;
@@ -74,7 +77,6 @@
mContext = context;
mModel = model;
mBgDataModel = dataModel;
- mWorkerExecutor = new LooperExecutor(LauncherModel.getWorkerLooper());
mHasVerticalHotseat = hasVerticalHotseat;
mVerifyChanges = verifyChanges;
mUiHandler = new Handler(Looper.getMainLooper());
@@ -194,7 +196,7 @@
item.spanX = spanX;
item.spanY = spanY;
- mWorkerExecutor.execute(new UpdateItemRunnable(item, () ->
+ ((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () ->
new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
.put(Favorites.CELLX, item.cellX)
@@ -209,7 +211,7 @@
* Update an item to the database in a specified container.
*/
public void updateItemInDatabase(ItemInfo item) {
- mWorkerExecutor.execute(new UpdateItemRunnable(item, () -> {
+ ((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () -> {
ContentWriter writer = new ContentWriter(mContext);
item.onAddToDatabase(writer);
return writer;
@@ -229,7 +231,7 @@
ModelVerifier verifier = new ModelVerifier();
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- mWorkerExecutor.execute(() -> {
+ ((Executor) MODEL_EXECUTOR).execute(() -> {
// Write the item on background thread, as some properties might have been updated in
// the background.
final ContentWriter writer = new ContentWriter(mContext);
@@ -263,9 +265,12 @@
/**
* Removes the specified items from the database
*/
- public void deleteItemsFromDatabase(final Iterable<? extends ItemInfo> items) {
+ public void deleteItemsFromDatabase(final Collection<? extends ItemInfo> items) {
ModelVerifier verifier = new ModelVerifier();
-
+ FileLog.d(TAG, "removing items from db " + items.stream().map(
+ (item) -> item.getTargetComponent() == null ? ""
+ : item.getTargetComponent().getPackageName()).collect(
+ Collectors.joining(",")), new Exception());
enqueueDeleteRunnable(() -> {
for (ItemInfo item : items) {
final Uri uri = Favorites.getContentUri(item.id);
@@ -333,14 +338,14 @@
if (mPreparingToUndo) {
mDeleteRunnables.add(r);
} else {
- mWorkerExecutor.execute(r);
+ ((Executor) MODEL_EXECUTOR).execute(r);
}
}
public void commitDelete() {
mPreparingToUndo = false;
for (Runnable runnable : mDeleteRunnables) {
- mWorkerExecutor.execute(runnable);
+ ((Executor) MODEL_EXECUTOR).execute(runnable);
}
mDeleteRunnables.clear();
}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 9fcab38..2832150 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -19,20 +19,16 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import com.android.launcher3.AllAppsList;
-import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.InstantAppResolver;
-import java.util.ArrayList;
import java.util.HashSet;
/**
@@ -48,7 +44,7 @@
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
+ if (mInstallInfo.state == PackageInstallInfo.STATUS_INSTALLED) {
try {
// For instant apps we do not get package-add. Use setting events to update
// any pinned icons.
@@ -65,41 +61,11 @@
}
synchronized (apps) {
- PromiseAppInfo updated = null;
- final ArrayList<AppInfo> removed = new ArrayList<>();
- for (int i=0; i < apps.size(); i++) {
- final AppInfo appInfo = apps.get(i);
- final ComponentName tgtComp = appInfo.getTargetComponent();
- if (tgtComp != null && tgtComp.getPackageName().equals(mInstallInfo.packageName)) {
- if (appInfo instanceof PromiseAppInfo) {
- final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
- if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLING) {
- promiseAppInfo.level = mInstallInfo.progress;
- updated = promiseAppInfo;
- } else if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
- apps.removePromiseApp(appInfo);
- removed.add(appInfo);
- }
- }
- }
- }
+ PromiseAppInfo updated = apps.updatePromiseInstallInfo(mInstallInfo);
if (updated != null) {
- final PromiseAppInfo updatedPromiseApp = updated;
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindPromiseAppProgressUpdated(updatedPromiseApp);
- }
- });
+ scheduleCallbackTask(c -> c.bindPromiseAppProgressUpdated(updated));
}
- if (!removed.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAppInfosRemoved(removed);
- }
- });
- }
+ bindApplicationsIfNeeded();
}
synchronized (dataModel) {
@@ -111,7 +77,7 @@
if (si.hasPromiseIconUi() && (cn != null)
&& mInstallInfo.packageName.equals(cn.getPackageName())) {
si.setInstallProgress(mInstallInfo.progress);
- if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
+ if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
// Mark this info as broken.
si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
}
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index baeaa94..3ef48cd 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -17,6 +17,7 @@
package com.android.launcher3.model;
import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.LauncherSettings;
/**
* Represents a {@link Package} in the widget tray section.
@@ -30,10 +31,21 @@
public PackageItemInfo(String packageName) {
this.packageName = packageName;
+ this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
+ }
+
+ public PackageItemInfo(PackageItemInfo copy) {
+ this.packageName = copy.packageName;
+ this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
}
@Override
protected String dumpProperties() {
return super.dumpProperties() + " packageName=" + packageName;
}
+
+ @Override
+ public PackageItemInfo clone() {
+ return new PackageItemInfo(this);
+ }
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 4428c8e..db63b7c 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -15,31 +15,30 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+import static com.android.launcher3.WorkspaceItemInfo.FLAG_RESTORED_ICON;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
-import com.android.launcher3.AllAppsList;
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -49,6 +48,7 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.SafeCloseable;
import java.util.ArrayList;
import java.util.Arrays;
@@ -56,8 +56,6 @@
import java.util.HashSet;
import java.util.List;
-import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
-
/**
* Handles updates due to changes in package manager (app installed/updated/removed)
* or when a user availability changes.
@@ -100,12 +98,14 @@
FlagOp flagOp = FlagOp.NO_OP;
final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
+ final HashSet<ComponentName> removedComponents = new HashSet<>();
+
switch (mOp) {
case OP_ADD: {
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
- if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+ if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
appsList.removePackage(packages[i], mUser);
}
appsList.addPackage(context, packages[i], mUser);
@@ -119,17 +119,21 @@
break;
}
case OP_UPDATE:
- for (int i = 0; i < N; i++) {
- if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
- iconCache.updateIconsForPkg(packages[i], mUser);
- appsList.updatePackage(context, packages[i], mUser);
- app.getWidgetCache().removePackage(packages[i], mUser);
+ try (SafeCloseable t =
+ appsList.trackRemoves(a -> removedComponents.add(a.componentName))) {
+ for (int i = 0; i < N; i++) {
+ if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
+ iconCache.updateIconsForPkg(packages[i], mUser);
+ appsList.updatePackage(context, packages[i], mUser);
+ app.getWidgetCache().removePackage(packages[i], mUser);
+ }
}
// Since package was just updated, the target must be available now.
flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
case OP_REMOVE: {
for (int i = 0; i < N; i++) {
+ FileLog.d(TAG, "Removing app icon" + packages[i]);
iconCache.removeIconsForPkg(packages[i], mUser);
}
// Fall through
@@ -160,23 +164,7 @@
break;
}
- final ArrayList<AppInfo> addedOrModified = new ArrayList<>();
- addedOrModified.addAll(appsList.added);
- appsList.added.clear();
- addedOrModified.addAll(appsList.modified);
- appsList.modified.clear();
- if (!addedOrModified.isEmpty()) {
- scheduleCallbackTask((callbacks) -> callbacks.bindAppsAddedOrUpdated(addedOrModified));
- }
-
- final ArrayList<AppInfo> removedApps = new ArrayList<>(appsList.removed);
- appsList.removed.clear();
- final HashSet<ComponentName> removedComponents = new HashSet<>();
- if (mOp == OP_UPDATE) {
- for (AppInfo ai : removedApps) {
- removedComponents.add(ai.componentName);
- }
- }
+ bindApplicationsIfNeeded();
final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
@@ -231,10 +219,10 @@
infoUpdated = true;
}
} else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
- isTargetValid = LauncherAppsCompat.getInstance(context)
- .isActivityEnabledForProfile(cn, mUser);
+ isTargetValid = context.getSystemService(LauncherApps.class)
+ .isActivityEnabled(cn, mUser);
}
- if (si.hasStatusFlag(FLAG_AUTOINSTALL_ICON)) {
+ if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
} else if (si.hasPromiseIconUi()) {
@@ -302,12 +290,7 @@
}
if (!widgets.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindWidgetsRestored(widgets);
- }
- });
+ scheduleCallbackTask(c -> c.bindWidgetsRestored(widgets));
}
}
@@ -320,9 +303,9 @@
// removedPackages is a super-set of removedComponents
} else if (mOp == OP_UPDATE) {
// Mark disabled packages in the broadcast to be removed
- final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ final LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
for (int i=0; i<N; i++) {
- if (!launcherApps.isPackageEnabledForProfile(packages[i], mUser)) {
+ if (!launcherApps.isPackageEnabled(packages[i], mUser)) {
removedPackages.add(packages[i]);
}
}
@@ -338,16 +321,6 @@
InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
}
- if (!removedApps.isEmpty()) {
- // Remove corresponding apps from All-Apps
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAppInfosRemoved(removedApps);
- }
- });
- }
-
if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
// Load widgets for the new package. Changes due to app updates are handled through
// AppWidgetHost events, this is just to initialize the long-press options.
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index 3aedae6..eb3cb52 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -19,11 +19,11 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps;
import android.os.UserHandle;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
@@ -53,7 +53,7 @@
@Override
public void onReceive(Context context, Intent intent) {
- final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ final LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
for (Entry<UserHandle, ArrayList<String>> entry : mPackages.entrySet()) {
UserHandle user = entry.getKey();
@@ -62,7 +62,7 @@
final ArrayList<String> packagesUnavailable = new ArrayList<>();
for (String pkg : new HashSet<>(entry.getValue())) {
- if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
+ if (!launcherApps.isPackageEnabled(pkg, user)) {
if (pmHelper.isAppOnSdcard(pkg, user)) {
packagesUnavailable.add(pkg);
} else {
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 8528228..6c358b1 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -19,7 +19,6 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
@@ -65,7 +64,7 @@
for (ItemInfo itemInfo : dataModel.itemsIdMap) {
if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
- if (si.getIntent().getPackage().equals(mPackageName) && si.user.equals(mUser)) {
+ if (mPackageName.equals(si.getIntent().getPackage()) && si.user.equals(mUser)) {
keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
allIds.add(si.getDeepShortcutId());
}
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 2cb256e..4b773d7 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -21,7 +21,6 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
@@ -37,7 +36,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
/**
* Task to handle changing of lock state of the user
@@ -58,9 +56,9 @@
HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
if (isUserUnlocked) {
- List<ShortcutInfo> shortcuts =
+ DeepShortcutManager.QueryResult shortcuts =
deepShortcutManager.queryForPinnedShortcuts(null, mUser);
- if (deepShortcutManager.wasLastCallSuccess()) {
+ if (shortcuts.wasSuccess()) {
for (ShortcutInfo shortcut : shortcuts) {
pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut);
}
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index e38529b..b442c42 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -8,8 +8,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.ComponentKey;
import java.text.Collator;
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 32410a6..717a7e9 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -16,6 +16,8 @@
package com.android.launcher3.notification;
+import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
+
import android.app.Notification;
import android.content.Context;
import android.graphics.Color;
@@ -33,8 +35,6 @@
import java.util.List;
-import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
-
/**
* Utility class to manage notification UI
*/
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index e57a051..10378ee 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,6 +16,7 @@
package com.android.launcher3.notification;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
import android.annotation.TargetApi;
@@ -31,7 +32,8 @@
import android.util.Log;
import android.util.Pair;
-import com.android.launcher3.LauncherModel;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.SecureSettingsObserver;
@@ -43,8 +45,6 @@
import java.util.List;
import java.util.Map;
-import androidx.annotation.Nullable;
-
/**
* A {@link NotificationListenerService} that sends updates to its
* {@link NotificationsChangedListener} when notifications are posted or canceled,
@@ -141,7 +141,7 @@
public NotificationListener() {
super();
- mWorkerHandler = new Handler(LauncherModel.getWorkerLooper(), mWorkerCallback);
+ mWorkerHandler = new Handler(MODEL_EXECUTOR.getLooper(), mWorkerCallback);
mUiHandler = new Handler(Looper.getMainLooper(), mUiCallback);
sNotificationListenerInstance = this;
}
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
new file mode 100644
index 0000000..f157603
--- /dev/null
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -0,0 +1,129 @@
+/*
+ * 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.pm;
+
+import static com.android.launcher3.pm.PackageInstallInfo.STATUS_FAILED;
+import static com.android.launcher3.pm.PackageInstallInfo.STATUS_INSTALLED;
+import static com.android.launcher3.pm.PackageInstallerCompat.getUserHandle;
+
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.os.UserHandle;
+import android.util.SparseArray;
+
+import com.android.launcher3.util.PackageUserKey;
+
+public class InstallSessionTracker extends PackageInstaller.SessionCallback {
+
+ // Lazily initialized
+ private SparseArray<PackageUserKey> mActiveSessions = null;
+
+ private final PackageInstallerCompat mInstallerCompat;
+ private final Callback mCallback;
+
+ InstallSessionTracker(PackageInstallerCompat installerCompat, Callback callback) {
+ mInstallerCompat = installerCompat;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onCreated(int sessionId) {
+ SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+ if (sessionInfo != null) {
+ mCallback.onInstallSessionCreated(PackageInstallInfo.fromInstallingState(sessionInfo));
+ }
+
+ mInstallerCompat.tryQueuePromiseAppIcon(sessionInfo);
+ }
+
+ @Override
+ public void onFinished(int sessionId, boolean success) {
+ // For a finished session, we can't get the session info. So use the
+ // packageName from our local cache.
+ SparseArray<PackageUserKey> activeSessions = getActiveSessionMap();
+ PackageUserKey key = activeSessions.get(sessionId);
+ activeSessions.remove(sessionId);
+
+ if (key != null && key.mPackageName != null) {
+ String packageName = key.mPackageName;
+ PackageInstallInfo info = PackageInstallInfo.fromState(
+ success ? STATUS_INSTALLED : STATUS_FAILED,
+ packageName, key.mUser);
+ mCallback.onPackageStateChanged(info);
+
+ if (!success && mInstallerCompat.promiseIconAddedForId(sessionId)) {
+ mCallback.onSessionFailure(packageName, key.mUser);
+ // If it is successful, the id is removed in the the package added flow.
+ mInstallerCompat.removePromiseIconId(sessionId);
+ }
+ }
+ }
+
+ @Override
+ public void onProgressChanged(int sessionId, float progress) {
+ SessionInfo session = mInstallerCompat.getVerifiedSessionInfo(sessionId);
+ if (session != null && session.getAppPackageName() != null) {
+ mCallback.onPackageStateChanged(PackageInstallInfo.fromInstallingState(session));
+ }
+ }
+
+ @Override
+ public void onActiveChanged(int sessionId, boolean active) { }
+
+ @Override
+ public void onBadgingChanged(int sessionId) {
+ SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+ if (sessionInfo != null) {
+ mInstallerCompat.tryQueuePromiseAppIcon(sessionInfo);
+ }
+ }
+
+ private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
+ SessionInfo session = mInstallerCompat.getVerifiedSessionInfo(sessionId);
+ if (session != null && session.getAppPackageName() != null) {
+ PackageUserKey key =
+ new PackageUserKey(session.getAppPackageName(), getUserHandle(session));
+ getActiveSessionMap().put(session.getSessionId(), key);
+ mCallback.onUpdateSessionDisplay(key, session);
+ return session;
+ }
+ return null;
+ }
+
+ private SparseArray<PackageUserKey> getActiveSessionMap() {
+ if (mActiveSessions == null) {
+ mActiveSessions = new SparseArray<>();
+ mInstallerCompat.getActiveSessions().forEach(
+ (key, si) -> mActiveSessions.put(si.getSessionId(), key));
+ }
+ return mActiveSessions;
+ }
+
+ public void unregister() {
+ mInstallerCompat.unregister(this);
+ }
+
+ public interface Callback {
+
+ void onSessionFailure(String packageName, UserHandle user);
+
+ void onUpdateSessionDisplay(PackageUserKey key, SessionInfo info);
+
+ void onPackageStateChanged(PackageInstallInfo info);
+
+ void onInstallSessionCreated(PackageInstallInfo info);
+ }
+}
diff --git a/src/com/android/launcher3/pm/PackageInstallInfo.java b/src/com/android/launcher3/pm/PackageInstallInfo.java
new file mode 100644
index 0000000..6776ec4
--- /dev/null
+++ b/src/com/android/launcher3/pm/PackageInstallInfo.java
@@ -0,0 +1,60 @@
+/*
+ * 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.pm;
+
+import android.content.ComponentName;
+import android.content.pm.PackageInstaller;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+
+public final class PackageInstallInfo {
+
+ public static final int STATUS_INSTALLED = 0;
+ public static final int STATUS_INSTALLING = 1;
+ public static final int STATUS_FAILED = 2;
+
+ public final ComponentName componentName;
+ public final String packageName;
+ public final int state;
+ public final int progress;
+ public final UserHandle user;
+
+ private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) {
+ this.state = STATUS_INSTALLING;
+ this.packageName = info.getAppPackageName();
+ this.componentName = new ComponentName(packageName, "");
+ this.progress = (int) (info.getProgress() * 100f);
+ this.user = PackageInstallerCompat.getUserHandle(info);
+ }
+
+ public PackageInstallInfo(String packageName, int state, int progress, UserHandle user) {
+ this.state = state;
+ this.packageName = packageName;
+ this.componentName = new ComponentName(packageName, "");
+ this.progress = progress;
+ this.user = user;
+ }
+
+ public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) {
+ return new PackageInstallInfo(info);
+ }
+
+ public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
+ return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
+ }
+
+}
diff --git a/src/com/android/launcher3/pm/PackageInstallerCompat.java b/src/com/android/launcher3/pm/PackageInstallerCompat.java
new file mode 100644
index 0000000..c7b27d9
--- /dev/null
+++ b/src/com/android/launcher3/pm/PackageInstallerCompat.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2014 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.pm;
+
+import static com.android.launcher3.Utilities.getPrefs;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.launcher3.SessionCommitReceiver;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.LooperExecutor;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+public class PackageInstallerCompat {
+
+ // Set<String> of session ids of promise icons that have been added to the home screen
+ // as FLAG_PROMISE_NEW_INSTALLS.
+ protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
+
+ private static final Object sInstanceLock = new Object();
+ private static final boolean DEBUG = false;
+ private static PackageInstallerCompat sInstance;
+ private final LauncherApps mLauncherApps;
+ private final Context mAppContext;
+ private final IntSet mPromiseIconIds;
+
+ private final PackageInstaller mInstaller;
+ private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>();
+
+ public PackageInstallerCompat(Context context) {
+ mInstaller = context.getPackageManager().getPackageInstaller();
+ mAppContext = context.getApplicationContext();
+ mLauncherApps = context.getSystemService(LauncherApps.class);
+
+ mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
+ getPrefs(context).getString(PROMISE_ICON_IDS, "")));
+
+ cleanUpPromiseIconIds();
+ }
+
+ public static PackageInstallerCompat getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ sInstance = new PackageInstallerCompat(context);
+ }
+ return sInstance;
+ }
+ }
+
+ public static UserHandle getUserHandle(SessionInfo info) {
+ return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
+ }
+
+ protected void cleanUpPromiseIconIds() {
+ IntArray existingIds = new IntArray();
+ for (SessionInfo info : getActiveSessions().values()) {
+ existingIds.add(info.getSessionId());
+ }
+ IntArray idsToRemove = new IntArray();
+
+ for (int i = mPromiseIconIds.size() - 1; i >= 0; --i) {
+ if (!existingIds.contains(mPromiseIconIds.getArray().get(i))) {
+ idsToRemove.add(mPromiseIconIds.getArray().get(i));
+ }
+ }
+ for (int i = idsToRemove.size() - 1; i >= 0; --i) {
+ mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
+ }
+ }
+
+ public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
+ HashMap<PackageUserKey, SessionInfo> activePackages = new HashMap<>();
+ for (SessionInfo info : getAllVerifiedSessions()) {
+ activePackages.put(new PackageUserKey(info.getAppPackageName(), getUserHandle(info)),
+ info);
+ }
+ return activePackages;
+ }
+
+ public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
+ for (SessionInfo info : getAllVerifiedSessions()) {
+ boolean match = pkg.equals(info.getAppPackageName());
+ if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) {
+ match = false;
+ }
+ if (match) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ private void updatePromiseIconPrefs() {
+ getPrefs(mAppContext).edit()
+ .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
+ .apply();
+ }
+
+ SessionInfo getVerifiedSessionInfo(int sessionId) {
+ return verify(mInstaller.getSessionInfo(sessionId));
+ }
+
+ private SessionInfo verify(SessionInfo sessionInfo) {
+ if (sessionInfo == null
+ || sessionInfo.getInstallerPackageName() == null
+ || TextUtils.isEmpty(sessionInfo.getAppPackageName())) {
+ return null;
+ }
+ String pkg = sessionInfo.getInstallerPackageName();
+ synchronized (mSessionVerifiedMap) {
+ if (!mSessionVerifiedMap.containsKey(pkg)) {
+ boolean hasSystemFlag = new PackageManagerHelper(mAppContext).getApplicationInfo(
+ pkg, getUserHandle(sessionInfo), ApplicationInfo.FLAG_SYSTEM) != null;
+ mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
+ }
+ }
+ return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
+ }
+
+ public List<SessionInfo> getAllVerifiedSessions() {
+ List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q
+ ? mLauncherApps.getAllPackageInstallerSessions()
+ : mInstaller.getAllSessions());
+ Iterator<SessionInfo> it = list.iterator();
+ while (it.hasNext()) {
+ if (verify(it.next()) == null) {
+ it.remove();
+ }
+ }
+ return list;
+ }
+
+ public boolean promiseIconAddedForId(int sessionId) {
+ return mPromiseIconIds.contains(sessionId);
+ }
+
+ public void removePromiseIconId(int sessionId) {
+ if (mPromiseIconIds.contains(sessionId)) {
+ mPromiseIconIds.getArray().removeValue(sessionId);
+ updatePromiseIconPrefs();
+ }
+ }
+
+ /**
+ * Add a promise app icon to the workspace iff:
+ * - The settings for it are enabled
+ * - The user installed the app
+ * - There is an app icon and label (For apps with no launching activity, no icon is provided).
+ * - The app is not already installed
+ * - A promise icon for the session has not already been created
+ */
+ void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) {
+ if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+ && SessionCommitReceiver.isEnabled(mAppContext)
+ && verify(sessionInfo) != null
+ && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
+ && sessionInfo.getAppIcon() != null
+ && !TextUtils.isEmpty(sessionInfo.getAppLabel())
+ && !mPromiseIconIds.contains(sessionInfo.getSessionId())
+ && new PackageManagerHelper(mAppContext).getApplicationInfo(
+ sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), 0) == null) {
+ SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
+ mPromiseIconIds.add(sessionInfo.getSessionId());
+ updatePromiseIconPrefs();
+ }
+ }
+
+ public InstallSessionTracker registerInstallTracker(
+ InstallSessionTracker.Callback callback, LooperExecutor executor) {
+ InstallSessionTracker tracker = new InstallSessionTracker(this, callback);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ mInstaller.registerSessionCallback(tracker, executor.getHandler());
+ } else {
+ mLauncherApps.registerPackageInstallerSessionCallback(executor, tracker);
+ }
+ return tracker;
+ }
+
+ void unregister(InstallSessionTracker tracker) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ mInstaller.unregisterSessionCallback(tracker);
+ } else {
+ mLauncherApps.unregisterPackageInstallerSessionCallback(tracker);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/pm/PinRequestHelper.java b/src/com/android/launcher3/pm/PinRequestHelper.java
new file mode 100644
index 0000000..5b6b56d
--- /dev/null
+++ b/src/com/android/launcher3/pm/PinRequestHelper.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 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.pm;
+
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.ShortcutUtil.fetchAndUpdateShortcutIconAsync;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.ShortcutInfo;
+import android.os.Build;
+import android.os.Parcelable;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.WorkspaceItemInfo;
+
+public class PinRequestHelper {
+
+ /**
+ * request.accept() will initiate the following flow:
+ * -> go-to-system-process for actual processing (a)
+ * -> callback-to-launcher on UI thread (b)
+ * -> post callback on the worker thread (c)
+ * -> Update model and unpin (in system) any shortcut not in out model. (d)
+ *
+ * Note that (b) will take at-least one frame as it involves posting callback from binder
+ * thread to UI thread.
+ * If (d) happens before we add this shortcut to our model, we will end up unpinning
+ * the shortcut in the system.
+ * Here its the caller's responsibility to add the newly created WorkspaceItemInfo immediately
+ * to the model (which may involves a single post-to-worker-thread). That will guarantee
+ * that (d) happens after model is updated.
+ */
+ @Nullable
+ @TargetApi(Build.VERSION_CODES.O)
+ public static WorkspaceItemInfo createWorkspaceItemFromPinItemRequest(
+ Context context, final PinItemRequest request, final long acceptDelay) {
+ if (request != null && request.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT
+ && request.isValid()) {
+
+ if (acceptDelay <= 0) {
+ if (!request.accept()) {
+ return null;
+ }
+ } else {
+ // Block the worker thread until the accept() is called.
+ MODEL_EXECUTOR.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(acceptDelay);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ if (request.isValid()) {
+ request.accept();
+ }
+ }
+ });
+ }
+
+ ShortcutInfo si = request.getShortcutInfo();
+ WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
+ // Apply the unbadged icon and fetch the actual icon asynchronously.
+ fetchAndUpdateShortcutIconAsync(context, info, si, false);
+ return info;
+ } else {
+ return null;
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.O)
+ public static PinItemRequest getPinItemRequest(Intent intent) {
+ Parcelable extra = intent.getParcelableExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST);
+ return extra instanceof PinItemRequest ? (PinItemRequest) extra : null;
+ }
+}
diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
similarity index 66%
rename from src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
rename to src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index ace5691..0922e41 100644
--- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -14,29 +14,40 @@
* limitations under the License.
*/
-package com.android.launcher3.compat;
+package com.android.launcher3.pm;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
import android.widget.Toast;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.ComponentWithLabel;
-import com.android.launcher3.icons.IconCache;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.util.PackageUserKey;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Wrapper class for representing a shortcut configure activity.
@@ -87,9 +98,9 @@
Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
} catch (SecurityException e) {
Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Launcher does not have the permission to launch " + intent +
- ". Make sure to create a MAIN intent-filter for the corresponding activity " +
- "or use the exported attribute for this activity.", e);
+ Log.e(TAG, "Launcher does not have the permission to launch " + intent
+ + ". Make sure to create a MAIN intent-filter for the corresponding activity "
+ + "or use the exported attribute for this activity.", e);
}
return false;
}
@@ -106,7 +117,7 @@
private final ActivityInfo mInfo;
- public ShortcutConfigActivityInfoVL(ActivityInfo info) {
+ ShortcutConfigActivityInfoVL(ActivityInfo info) {
super(new ComponentName(info.packageName, info.name), Process.myUserHandle());
mInfo = info;
}
@@ -158,4 +169,46 @@
}
}
}
+
+ public static List<ShortcutConfigActivityInfo> queryList(
+ Context context, @Nullable PackageUserKey packageUser) {
+ List<ShortcutConfigActivityInfo> result = new ArrayList<>();
+ UserHandle myUser = Process.myUserHandle();
+
+ if (Utilities.ATLEAST_OREO) {
+ final List<UserHandle> users;
+ final String packageName;
+ if (packageUser == null) {
+ users = UserManagerCompat.getInstance(context).getUserProfiles();
+ packageName = null;
+ } else {
+ users = new ArrayList<>(1);
+ users.add(packageUser.mUser);
+ packageName = packageUser.mPackageName;
+ }
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ for (UserHandle user : users) {
+ boolean ignoreTargetSdk = myUser.equals(user);
+ for (LauncherActivityInfo activityInfo :
+ launcherApps.getShortcutConfigActivityList(packageName, user)) {
+ if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion
+ >= Build.VERSION_CODES.O) {
+ result.add(new ShortcutConfigActivityInfoVO(activityInfo));
+ }
+ }
+ }
+ } else {
+ if (packageUser == null || packageUser.mUser.equals(myUser)) {
+ Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+ if (packageUser != null) {
+ intent.setPackage(packageUser.mPackageName);
+ }
+ for (ResolveInfo info :
+ context.getPackageManager().queryIntentActivities(intent, 0)) {
+ result.add(new ShortcutConfigActivityInfoVL(info.activityInfo));
+ }
+ }
+ }
+ return result;
+ }
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 15fb4ce..4833c26 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
@@ -36,6 +37,7 @@
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
@@ -50,7 +52,6 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
@@ -65,6 +66,7 @@
import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageUserKey;
@@ -166,7 +168,10 @@
}
public OnClickListener getItemClickListener() {
- return ItemClickHandler.INSTANCE;
+ return (view) -> {
+ ItemClickHandler.INSTANCE.onClick(view);
+ close(true);
+ };
}
@Override
@@ -191,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.
@@ -232,6 +240,9 @@
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),
@@ -303,8 +314,7 @@
setLayoutTransition(new LayoutTransition());
// Load the shortcuts on a background thread and update the container as it animates.
- final Looper workerLooper = LauncherModel.getWorkerLooper();
- new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
+ MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()),
this, mShortcuts, notificationKeys));
}
@@ -565,9 +575,9 @@
@Override
protected void closeComplete() {
- super.closeComplete();
mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
mOriginalIcon.setForceHideDot(false);
+ super.closeComplete();
}
@Override
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 78bd81b..a87b7b8 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -1,14 +1,11 @@
package com.android.launcher3.popup;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
@@ -20,23 +17,30 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.WidgetsBottomSheet;
import java.util.List;
-
/**
* Represents a system shortcut for a given app. The shortcut should have a label and icon, and an
* onClickListener that depends on the item that the shortcut services.
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
+ * @param <T>
*/
-public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo {
+public abstract class SystemShortcut<T extends BaseDraggingActivity>
+ extends ItemInfo {
private final int mIconResId;
private final int mLabelResId;
private final Icon mIcon;
@@ -202,6 +206,27 @@
}
}
+ public static class DismissPrediction extends SystemShortcut<Launcher> {
+ public DismissPrediction() {
+ super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label);
+ }
+
+ @Override
+ 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);
+ };
+ }
+ }
+
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
index 37a2092..dfcc2f8 100644
--- a/src/com/android/launcher3/popup/SystemShortcutFactory.java
+++ b/src/com/android/launcher3/popup/SystemShortcutFactory.java
@@ -39,7 +39,9 @@
@SuppressWarnings("unused")
public SystemShortcutFactory() {
this(new SystemShortcut.AppInfo(),
- new SystemShortcut.Widgets(), new SystemShortcut.Install());
+ new SystemShortcut.Widgets(),
+ new SystemShortcut.Install(),
+ new SystemShortcut.DismissPrediction());
}
protected SystemShortcutFactory(SystemShortcut... shortcuts) {
@@ -53,6 +55,7 @@
systemShortcuts.add(systemShortcut);
}
}
+
return systemShortcuts;
}
}
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index 7b62f53..970a03e 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -42,7 +42,6 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -50,6 +49,7 @@
import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.PackageManagerHelper;
import java.net.URISyntaxException;
import java.util.ArrayList;
@@ -223,7 +223,7 @@
case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_APPLICATION: {
intent = Intent.parseUri(c.getString(intentIndex), 0);
- if (Utilities.isLauncherAppTarget(intent)) {
+ if (PackageManagerHelper.isLauncherAppTarget(intent)) {
type = Favorites.ITEM_TYPE_APPLICATION;
} else {
values.put(Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index d643a0b..fb33551 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,8 +16,6 @@
package com.android.launcher3.provider;
-import static com.android.launcher3.Utilities.getIntArrayFromString;
-import static com.android.launcher3.Utilities.getStringFromIntArray;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import android.app.backup.BackupManager;
@@ -40,6 +38,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
+import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LogConfig;
import java.io.InvalidObjectException;
@@ -240,8 +239,8 @@
SharedPreferences prefs = Utilities.getPrefs(context);
if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) {
AppWidgetsRestoredReceiver.restoreAppWidgetIds(context,
- getIntArrayFromString(prefs.getString(APPWIDGET_OLD_IDS, "")),
- getIntArrayFromString(prefs.getString(APPWIDGET_IDS, "")));
+ IntArray.fromConcatString(prefs.getString(APPWIDGET_OLD_IDS, "")).toArray(),
+ IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray());
} else {
FileLog.d(TAG, "No app widget ids to restore.");
}
@@ -253,8 +252,8 @@
public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds,
@NonNull int[] newIds) {
Utilities.getPrefs(context).edit()
- .putString(APPWIDGET_OLD_IDS, getStringFromIntArray(oldIds))
- .putString(APPWIDGET_IDS, getStringFromIntArray(newIds))
+ .putString(APPWIDGET_OLD_IDS, IntArray.wrap(oldIds).toConcatString())
+ .putString(APPWIDGET_IDS, IntArray.wrap(newIds).toConcatString())
.commit();
}
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 857ea05..0eb4285 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -32,12 +32,16 @@
import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
@@ -55,6 +59,74 @@
*/
public class QsbContainerView extends FrameLayout {
+ public static final String SEARCH_PROVIDER_SETTINGS_KEY = "SEARCH_PROVIDER_PACKAGE_NAME";
+
+ /**
+ * Returns the package name for user configured search provider or from searchManager
+ * @param context
+ * @return String
+ */
+ @Nullable
+ public static String getSearchWidgetPackageName(@NonNull Context context) {
+ String providerPkg = Settings.Global.getString(context.getContentResolver(),
+ SEARCH_PROVIDER_SETTINGS_KEY);
+ if (providerPkg == null) {
+ SearchManager searchManager = context.getSystemService(SearchManager.class);
+ ComponentName componentName = searchManager.getGlobalSearchActivity();
+ if (componentName != null) {
+ providerPkg = searchManager.getGlobalSearchActivity().getPackageName();
+ }
+ }
+ return providerPkg;
+ }
+
+ /**
+ * returns it's AppWidgetProviderInfo using package name from getSearchWidgetPackageName
+ * @param context
+ * @return AppWidgetProviderInfo
+ */
+ @Nullable
+ public static AppWidgetProviderInfo getSearchWidgetProviderInfo(@NonNull Context context) {
+ String providerPkg = getSearchWidgetPackageName(context);
+ if (providerPkg == null) {
+ return null;
+ }
+
+ AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+ for (AppWidgetProviderInfo info :
+ appWidgetManager.getInstalledProvidersForPackage(providerPkg, null)) {
+ if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
+ if ((info.widgetCategory
+ & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
+ return info;
+ } else if (defaultWidgetForSearchPackage == null) {
+ defaultWidgetForSearchPackage = info;
+ }
+ }
+ }
+ return defaultWidgetForSearchPackage;
+ }
+
+ /**
+ * returns componentName for searchWidget if package name is known.
+ */
+ @Nullable
+ public static ComponentName getSearchComponentName(@NonNull Context context) {
+ AppWidgetProviderInfo providerInfo =
+ QsbContainerView.getSearchWidgetProviderInfo(context);
+ if (providerInfo != null) {
+ return providerInfo.provider;
+ } else {
+ String pkgName = QsbContainerView.getSearchWidgetPackageName(context);
+ if (pkgName != null) {
+ //we don't know the class name yet. we'll put the package name as placeholder
+ return new ComponentName(pkgName, pkgName);
+ }
+ return null;
+ }
+ }
+
public QsbContainerView(Context context) {
super(context);
}
@@ -101,7 +173,7 @@
protected QsbWidgetHost createHost() {
return new QsbWidgetHost(getContext(), QSB_WIDGET_HOST_ID,
- (c) -> new QsbWidgetHostView(c));
+ (c) -> new QsbWidgetHostView(c), this::rebindFragment);
}
private FrameLayout mWrapper;
@@ -111,9 +183,9 @@
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mWrapper = new FrameLayout(getContext());
-
// Only add the view when enabled
if (isQsbEnabled()) {
+ mQsbWidgetHost.startListening();
mWrapper.addView(createQsb(mWrapper));
}
return mWrapper;
@@ -155,7 +227,8 @@
}
if (isWidgetBound) {
- mQsb = (QsbWidgetHostView) mQsbWidgetHost.createView(context, widgetId, mWidgetInfo);
+ mQsb = (QsbWidgetHostView) mQsbWidgetHost.createView(context, widgetId,
+ mWidgetInfo);
mQsb.setId(R.id.qsb_widget);
if (!isInPreviewMode()) {
@@ -163,7 +236,6 @@
.getAppWidgetOptions(widgetId), opts)) {
mQsb.updateAppWidgetOptions(opts);
}
- mQsbWidgetHost.startListening();
}
return mQsb;
}
@@ -246,43 +318,32 @@
return v;
}
+
/**
* Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
- * provided by the same package which is set to be global search activity.
+ * provided by the package from getSearchProviderPackageName
* If widgetCategory is not supported, or no such widget is found, returns the first widget
* provided by the package.
*/
protected AppWidgetProviderInfo getSearchWidgetProvider() {
- SearchManager searchManager =
- (SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE);
- ComponentName searchComponent = searchManager.getGlobalSearchActivity();
- if (searchComponent == null) return null;
- String providerPkg = searchComponent.getPackageName();
-
- AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
-
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext());
- for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
- if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
- if ((info.widgetCategory
- & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
- return info;
- } else if (defaultWidgetForSearchPackage == null) {
- defaultWidgetForSearchPackage = info;
- }
- }
- }
- return defaultWidgetForSearchPackage;
+ return getSearchWidgetProviderInfo(getContext());
}
}
public static class QsbWidgetHost extends AppWidgetHost {
private final WidgetViewFactory mViewFactory;
+ private final WidgetProvidersUpdateCallback mWidgetsUpdateCallback;
- public QsbWidgetHost(Context context, int hostId, WidgetViewFactory viewFactory) {
+ public QsbWidgetHost(Context context, int hostId, WidgetViewFactory viewFactory,
+ WidgetProvidersUpdateCallback widgetProvidersUpdateCallback) {
super(context, hostId);
mViewFactory = viewFactory;
+ mWidgetsUpdateCallback = widgetProvidersUpdateCallback;
+ }
+
+ public QsbWidgetHost(Context context, int hostId, WidgetViewFactory viewFactory) {
+ this(context, hostId, viewFactory, null);
}
@Override
@@ -290,6 +351,14 @@
Context context, int appWidgetId, AppWidgetProviderInfo appWidget) {
return mViewFactory.newView(context);
}
+
+ @Override
+ protected void onProvidersChanged() {
+ super.onProvidersChanged();
+ if (mWidgetsUpdateCallback != null) {
+ mWidgetsUpdateCallback.onProvidersUpdated();
+ }
+ }
}
public interface WidgetViewFactory {
@@ -298,6 +367,17 @@
}
/**
+ * Callback interface for packages list update.
+ */
+ @FunctionalInterface
+ public interface WidgetProvidersUpdateCallback {
+ /**
+ * Gets called when widget providers list changes
+ */
+ void onProvidersUpdated();
+ }
+
+ /**
* Returns true if {@param original} contains all entries defined in {@param updates} and
* have the same value.
* The comparison uses {@link Object#equals(Object)} to compare the values.
@@ -316,4 +396,5 @@
}
return true;
}
+
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index ee97641..408ced2 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -26,6 +26,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.BitmapRenderer;
/**
* Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
@@ -39,22 +40,22 @@
mPositionShift = shift;
}
+ @Override
public Bitmap createDragBitmap() {
+ int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
+ return BitmapRenderer.createHardwareBitmap(
+ size + blurSizeOutline,
+ size + blurSizeOutline,
+ (c) -> drawDragViewOnBackground(c, size));
+ }
+
+ private void drawDragViewOnBackground(Canvas canvas, float size) {
Drawable d = mView.getBackground();
Rect bounds = getDrawableBounds(d);
-
- int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
- final Bitmap b = Bitmap.createBitmap(
- size + blurSizeOutline,
- size + blurSizeOutline,
- Bitmap.Config.ARGB_8888);
-
- Canvas canvas = new Canvas(b);
canvas.translate(blurSizeOutline / 2, blurSizeOutline / 2);
- canvas.scale(((float) size) / bounds.width(), ((float) size) / bounds.height(), 0, 0);
+ canvas.scale(size / bounds.width(), size / bounds.height(), 0, 0);
canvas.translate(bounds.left, bounds.top);
d.draw(canvas);
- return b;
}
@Override
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
deleted file mode 100644
index c6370c5..0000000
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ /dev/null
@@ -1,148 +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.states;
-
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.MainThreadExecutor;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Utility class to sending state handling logic to Launcher from within the same process.
- *
- * Extending {@link Binder} ensures that the platform maintains a single instance of each object
- * which allows this object to safely navigate the system process.
- */
-public abstract class InternalStateHandler extends Binder {
-
- public static final String EXTRA_STATE_HANDLER = "launcher.state_handler";
-
- private static final Scheduler sScheduler = new Scheduler();
-
- /**
- * Initializes the handler when the launcher is ready.
- * @return true if the handler wants to stay alive.
- */
- protected abstract boolean init(Launcher launcher, boolean alreadyOnHome);
-
- public final Intent addToIntent(Intent intent) {
- Bundle extras = new Bundle();
- extras.putBinder(EXTRA_STATE_HANDLER, this);
- intent.putExtras(extras);
- return intent;
- }
-
- public final void initWhenReady() {
- sScheduler.schedule(this);
- }
-
- public boolean clearReference() {
- return sScheduler.clearReference(this);
- }
-
- public static boolean hasPending() {
- return sScheduler.hasPending();
- }
-
- public static boolean handleCreate(Launcher launcher, Intent intent) {
- return handleIntent(launcher, intent, false, false);
- }
-
- public static boolean handleNewIntent(Launcher launcher, Intent intent, boolean alreadyOnHome) {
- return handleIntent(launcher, intent, alreadyOnHome, true);
- }
-
- private static boolean handleIntent(
- Launcher launcher, Intent intent, boolean alreadyOnHome, boolean explicitIntent) {
- boolean result = false;
- if (intent != null && intent.getExtras() != null) {
- IBinder stateBinder = intent.getExtras().getBinder(EXTRA_STATE_HANDLER);
- if (stateBinder instanceof InternalStateHandler) {
- InternalStateHandler handler = (InternalStateHandler) stateBinder;
- if (!handler.init(launcher, alreadyOnHome)) {
- intent.getExtras().remove(EXTRA_STATE_HANDLER);
- }
- result = true;
- }
- }
- if (!result && !explicitIntent) {
- result = sScheduler.initIfPending(launcher, alreadyOnHome);
- }
- return result;
- }
-
- private static class Scheduler implements Runnable {
-
- private WeakReference<InternalStateHandler> mPendingHandler = new WeakReference<>(null);
- private MainThreadExecutor mMainThreadExecutor;
-
- public void schedule(InternalStateHandler handler) {
- synchronized (this) {
- mPendingHandler = new WeakReference<>(handler);
- if (mMainThreadExecutor == null) {
- mMainThreadExecutor = new MainThreadExecutor();
- }
- }
- mMainThreadExecutor.execute(this);
- }
-
- @Override
- public void run() {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app == null) {
- return;
- }
- Callbacks cb = app.getModel().getCallback();
- if (!(cb instanceof Launcher)) {
- return;
- }
- Launcher launcher = (Launcher) cb;
- initIfPending(launcher, launcher.isStarted());
- }
-
- public boolean initIfPending(Launcher launcher, boolean alreadyOnHome) {
- InternalStateHandler pendingHandler = mPendingHandler.get();
- if (pendingHandler != null) {
- if (!pendingHandler.init(launcher, alreadyOnHome)) {
- clearReference(pendingHandler);
- }
- return true;
- }
- return false;
- }
-
- public boolean clearReference(InternalStateHandler handler) {
- synchronized (this) {
- if (mPendingHandler.get() == handler) {
- mPendingHandler.clear();
- return true;
- }
- return false;
- }
- }
-
- public boolean hasPending() {
- return mPendingHandler.get() != null;
- }
- }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index abf90e2..852928b 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -24,7 +24,6 @@
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -59,7 +58,9 @@
private boolean mAutoRotateEnabled;
/**
- * Rotation request made by {@link InternalStateHandler}. This supersedes any other request.
+ * Rotation request made by
+ * {@link com.android.launcher3.util.ActivityTracker.SchedulerCallback}.
+ * This supersedes any other request.
*/
private int mStateHandlerRequest = REQUEST_NONE;
/**
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 790a2e8..d0e648f 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -17,24 +17,29 @@
import static android.graphics.Bitmap.Config.ARGB_8888;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Debug;
+import android.util.Log;
+import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.util.ResourceBasedOverride;
import java.util.LinkedList;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
public class TestInformationHandler implements ResourceBasedOverride {
@@ -54,8 +59,7 @@
mDeviceProfile = InvariantDeviceProfile.INSTANCE.
get(context).getDeviceProfile(context);
mLauncherAppState = LauncherAppState.getInstanceNoCreate();
- mLauncher = mLauncherAppState != null ?
- (Launcher) mLauncherAppState.getModel().getCallback() : null;
+ mLauncher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
}
public Bundle call(String method) {
@@ -82,7 +86,7 @@
}
case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: {
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, true);
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, isLauncherInitialized());
break;
}
@@ -95,32 +99,44 @@
break;
case TestProtocol.REQUEST_FREEZE_APP_LIST:
- new MainThreadExecutor().execute(() ->
+ MAIN_EXECUTOR.execute(() ->
mLauncher.getAppsView().getAppsStore().enableDeferUpdates(
AllAppsStore.DEFER_UPDATES_TEST));
break;
case TestProtocol.REQUEST_UNFREEZE_APP_LIST:
- new MainThreadExecutor().execute(() ->
+ MAIN_EXECUTOR.execute(() ->
mLauncher.getAppsView().getAppsStore().disableDeferUpdates(
AllAppsStore.DEFER_UPDATES_TEST));
break;
case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
try {
- final int deferUpdatesFlags = new MainThreadExecutor().submit(() ->
+ final int deferUpdatesFlags = MAIN_EXECUTOR.submit(() ->
mLauncher.getAppsView().getAppsStore().getDeferUpdatesFlags()).get();
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
deferUpdatesFlags);
- } catch (ExecutionException e) {
+ } catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
- } catch (InterruptedException e) {
+ }
+ break;
+ }
+
+ case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
+ try {
+ final int deferUpdatesFlags = MAIN_EXECUTOR.submit(() ->
+ mLauncher.getAppsView().getActiveRecyclerView().getCurrentScrollY())
+ .get();
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ deferUpdatesFlags);
+ } catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
break;
}
case TestProtocol.REQUEST_TOTAL_PSS_KB: {
+ runGcAndFinalizersSync();
Debug.MemoryInfo mem = new Debug.MemoryInfo();
Debug.getMemoryInfo(mem);
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss());
@@ -149,7 +165,49 @@
mLeaks.add(bitmap);
break;
}
+
+ case TestProtocol.REQUEST_VIEW_LEAK: {
+ if (mLeaks == null) mLeaks = new LinkedList();
+
+ mLeaks.add(new View(mContext));
+ break;
+ }
}
return response;
}
-}
\ No newline at end of file
+
+ 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();
+ }
+
+ private static void runGcAndFinalizersSync() {
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+
+ final CountDownLatch fence = new CountDownLatch(1);
+ new Object() {
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ fence.countDown();
+ } finally {
+ super.finalize();
+ }
+ }
+ };
+ 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 232a764..923c466 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -20,13 +20,10 @@
* Protocol for custom accessibility events for communication with UI Automation tests.
*/
public final class TestProtocol {
- public static final String GET_SCROLL_MESSAGE = "TAPL_GET_SCROLL";
- public static final String SCROLL_Y_FIELD = "scrollY";
public static final String STATE_FIELD = "state";
public static final String SWITCHED_TO_STATE_MESSAGE = "TAPL_SWITCHED_TO_STATE";
public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED";
public static final String PAUSE_DETECTED_MESSAGE = "TAPL_PAUSE_DETECTED";
- public static final String RESPONSE_MESSAGE_POSTFIX = "_RESPONSE";
public static final int NORMAL_STATE_ORDINAL = 0;
public static final int SPRING_LOADED_STATE_ORDINAL = 1;
public static final int OVERVIEW_STATE_ORDINAL = 2;
@@ -71,11 +68,14 @@
public static final String REQUEST_FREEZE_APP_LIST = "freeze-app-list";
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
+ public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin";
public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin";
public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
public static final String REQUEST_JAVA_LEAK = "java-leak";
public static final String REQUEST_NATIVE_LEAK = "native-leak";
+ public static final String REQUEST_VIEW_LEAK = "view-leak";
+ public static final String REQUEST_RECENT_TASKS_LIST = "recent-tasks-list";
public static boolean sDebugTracing = false;
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
@@ -84,6 +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 ALL_APPS_UPON_RECENTS = "b/139941530";
- public static final String STABLE_STATE_MISMATCH = "b/140311911";
+ 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/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 03493a5..455af5a 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -25,8 +25,6 @@
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
import android.app.AlertDialog;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -52,9 +50,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.widget.PendingAppWidgetHostView;
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index babbcdd..86d2b39 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -17,10 +17,12 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
+
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import android.util.Log;
import android.view.View;
import android.view.View.OnLongClickListener;
@@ -32,6 +34,9 @@
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
+import com.android.launcher3.testing.TestProtocol;
+
+import java.util.Arrays;
/**
* Class to handle long-clicks on workspace items and start drag as a result.
@@ -60,7 +65,7 @@
if (info.container >= 0) {
Folder folder = Folder.getOpen(launcher);
if (folder != null) {
- if (!folder.getItemsInReadingOrder().contains(v)) {
+ if (!folder.getIconsInReadingOrder().contains(v)) {
folder.close(true);
} else {
folder.startDrag(v, dragOptions);
@@ -74,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/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index 3777a41..c38ca24 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -24,12 +24,11 @@
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
-
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.Utilities;
+
/**
* One dimensional scroll/drag/swipe gesture detector.
*
@@ -41,47 +40,14 @@
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;
- private int mScrollConditions;
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;
- private static final float ANIMATION_DURATION = 1200;
-
- protected int mActivePointerId = INVALID_POINTER_ID;
-
- /**
- * The minimum release velocity in pixels per millisecond that triggers fling..
- */
- public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
-
- /* Scroll state, this is set to true during dragging and animation. */
- private ScrollState mState = ScrollState.IDLE;
-
- enum ScrollState {
- IDLE,
- DRAGGING, // onDragStart, onDrag
- SETTLING // onDragEnd
- }
-
- public static abstract 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);
- }
-
public static final Direction VERTICAL = new Direction() {
@Override
@@ -150,35 +116,54 @@
}
};
- //------------------- 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 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 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();
- }
+ 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;
- mState = newState;
+ private enum ScrollState {
+ IDLE,
+ DRAGGING, // onDragStart, onDrag
+ SETTLING // onDragEnd
}
- public boolean isDraggingOrSettling() {
- return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
+ 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() {
@@ -203,73 +188,31 @@
return mState == ScrollState.DRAGGING;
}
- 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 VelocityTracker mVelocityTracker;
-
- private float mLastDisplacement;
- private float mDisplacement;
-
- private float mSubtractDisplacement;
- private boolean mIgnoreSlopWhenSettling;
-
- 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 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 boolean isDraggingOrSettling() {
+ return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
}
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
- mScrollConditions = scrollDirectionFlags;
+ mScrollDirections = scrollDirectionFlags;
mIgnoreSlopWhenSettling = ignoreSlop;
}
public int getScrollDirections() {
- return mScrollConditions;
+ return mScrollDirections;
}
- 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;
- }
+ public void finishedScrolling() {
+ setState(ScrollState.IDLE);
+ }
- // Check if the client is interested in scroll in current direction.
- if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(mDisplacement)) ||
- ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDir.isPositive(mDisplacement))) {
- return true;
- }
- return false;
+ /**
+ * 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) {
@@ -338,49 +281,71 @@
return true;
}
- public void finishedScrolling() {
- setState(ScrollState.IDLE);
+ //------------------- 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 reportDragStart(boolean recatch) {
+ 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);
}
- return true;
}
private void initializeDragging() {
if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
mSubtractDisplacement = 0;
- }
- if (mDisplacement > 0) {
+ } else if (mDisplacement > 0) {
mSubtractDisplacement = mTouchSlop;
} else {
mSubtractDisplacement = -mTouchSlop;
}
}
- /**
- * 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);
- }
-
- private boolean reportDragging(MotionEvent event) {
+ private void reportDragging(MotionEvent event) {
if (mDisplacement != mLastDisplacement) {
if (DBG) {
Log.d(TAG, String.format("onDrag disp=%.1f", mDisplacement));
}
mLastDisplacement = mDisplacement;
- return mListener.onDrag(mDisplacement - mSubtractDisplacement, event);
+ mListener.onDrag(mDisplacement - mSubtractDisplacement, event);
}
- return true;
}
private void reportDragEnd() {
@@ -394,14 +359,33 @@
mListener.onDragEnd(velocity, Math.abs(velocity) > RELEASE_VELOCITY_PX_MS);
}
- 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));
+ /** 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);
}
- return duration;
+
+ 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
new file mode 100644
index 0000000..b4f361f
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -0,0 +1,143 @@
+/*
+ * 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 com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.testing.TestProtocol;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Helper class to statically track activity creation
+ */
+public final class ActivityTracker<T extends BaseActivity> implements Runnable {
+
+ private WeakReference<T> mCurrentActivity = new WeakReference<>(null);
+ private WeakReference<SchedulerCallback<T>> mPendingCallback = new WeakReference<>(null);
+
+ private static final String EXTRA_SCHEDULER_CALLBACK = "launcher.scheduler_callback";
+
+ @Nullable
+ public <R extends T> R getCreatedActivity() {
+ return (R) mCurrentActivity.get();
+ }
+
+ 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();
+ }
+ }
+
+ public void schedule(SchedulerCallback<? extends T> callback) {
+ synchronized (this) {
+ mPendingCallback = new WeakReference<>((SchedulerCallback<T>) callback);
+ }
+ MAIN_EXECUTOR.execute(this);
+ }
+
+ @Override
+ public void run() {
+ T activity = mCurrentActivity.get();
+ if (activity != null) {
+ initIfPending(activity, activity.isStarted());
+ }
+ }
+
+ public boolean initIfPending(T activity, boolean alreadyOnHome) {
+ SchedulerCallback<T> pendingCallback = mPendingCallback.get();
+ if (pendingCallback != null) {
+ if (!pendingCallback.init(activity, alreadyOnHome)) {
+ clearReference(pendingCallback);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean clearReference(SchedulerCallback<? extends T> handler) {
+ synchronized (this) {
+ if (mPendingCallback.get() == handler) {
+ mPendingCallback.clear();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public boolean hasPending() {
+ return mPendingCallback.get() != null;
+ }
+
+ 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);
+ }
+
+ public boolean handleNewIntent(T activity, Intent intent) {
+ return handleIntent(activity, intent, activity.isStarted(), true);
+ }
+
+ private boolean handleIntent(
+ T activity, Intent intent, boolean alreadyOnHome, boolean explicitIntent) {
+ boolean result = false;
+ if (intent != null && intent.getExtras() != null) {
+ IBinder stateBinder = intent.getExtras().getBinder(EXTRA_SCHEDULER_CALLBACK);
+ if (stateBinder instanceof ObjectWrapper) {
+ SchedulerCallback<T> handler =
+ ((ObjectWrapper<SchedulerCallback>) stateBinder).get();
+ if (!handler.init(activity, alreadyOnHome)) {
+ intent.getExtras().remove(EXTRA_SCHEDULER_CALLBACK);
+ }
+ result = true;
+ }
+ }
+ if (!result && !explicitIntent) {
+ result = initIfPending(activity, alreadyOnHome);
+ }
+ return result;
+ }
+
+ public interface SchedulerCallback<T extends BaseActivity> {
+
+ boolean init(T activity, boolean alreadyOnHome);
+
+ default Intent addToIntent(Intent intent) {
+ Bundle extras = new Bundle();
+ extras.putBinder(EXTRA_SCHEDULER_CALLBACK, ObjectWrapper.wrap(this));
+ intent.putExtras(extras);
+ return intent;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
index 12d35e9..0f81520 100644
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -16,20 +16,15 @@
* limitations under the License.
*/
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.Point;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.Handler;
import android.util.Log;
-import android.view.Display;
-import android.view.WindowManager;
-
-import com.android.launcher3.MainThreadExecutor;
import java.util.function.Consumer;
@@ -37,7 +32,8 @@
* {@link BroadcastReceiver} which watches configuration changes and
* notifies the callback in case changes which affect the device profile occur.
*/
-public class ConfigMonitor extends BroadcastReceiver implements DisplayListener {
+public class ConfigMonitor extends BroadcastReceiver implements
+ DefaultDisplay.DisplayInfoChangeListener {
private static final String TAG = "ConfigMonitor";
@@ -61,24 +57,19 @@
mFontScale = config.fontScale;
mDensity = config.densityDpi;
- Display display = getDefaultDisplay(context);
- mDisplayId = display.getDisplayId();
+ DefaultDisplay display = DefaultDisplay.INSTANCE.get(context);
+ display.addChangeListener(this);
+ DefaultDisplay.Info displayInfo = display.getInfo();
+ mDisplayId = displayInfo.id;
- mRealSize = new Point();
- display.getRealSize(mRealSize);
-
- mSmallestSize = new Point();
- mLargestSize = new Point();
- display.getCurrentSizeRange(mSmallestSize, mLargestSize);
+ mRealSize = new Point(displayInfo.realSize);
+ mSmallestSize = new Point(displayInfo.smallestSize);
+ mLargestSize = new Point(displayInfo.largestSize);
mCallback = callback;
// Listen for configuration change
mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
-
- // Listen for display manager change
- mContext.getSystemService(DisplayManager.class)
- .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
}
@Override
@@ -91,26 +82,19 @@
}
@Override
- public void onDisplayAdded(int displayId) { }
-
- @Override
- public void onDisplayRemoved(int displayId) { }
-
- @Override
- public void onDisplayChanged(int displayId) {
- if (displayId != mDisplayId) {
+ public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
+ if (info.id != mDisplayId) {
return;
}
- Display display = getDefaultDisplay(mContext);
- display.getRealSize(mTmpPoint1);
-
+ mTmpPoint1.set(info.realSize.x, info.realSize.y);
if (!mRealSize.equals(mTmpPoint1) && !mRealSize.equals(mTmpPoint1.y, mTmpPoint1.x)) {
Log.d(TAG, String.format("Display size changed from %s to %s", mRealSize, mTmpPoint1));
notifyChange();
return;
}
- display.getCurrentSizeRange(mTmpPoint1, mTmpPoint2);
+ mTmpPoint1.set(info.smallestSize.x, info.smallestSize.y);
+ mTmpPoint2.set(info.largestSize.x, info.largestSize.y);
if (!mSmallestSize.equals(mTmpPoint1) || !mLargestSize.equals(mTmpPoint2)) {
Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
mSmallestSize, mLargestSize, mTmpPoint1, mTmpPoint2));
@@ -122,18 +106,15 @@
if (mCallback != null) {
Consumer<Context> callback = mCallback;
mCallback = null;
- new MainThreadExecutor().execute(() -> callback.accept(mContext));
+ MAIN_EXECUTOR.execute(() -> callback.accept(mContext));
}
}
- private Display getDefaultDisplay(Context context) {
- return context.getSystemService(WindowManager.class).getDefaultDisplay();
- }
-
public void unregister() {
try {
mContext.unregisterReceiver(this);
- mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+ DefaultDisplay display = DefaultDisplay.INSTANCE.get(mContext);
+ display.removeChangeListener(this);
} catch (Exception e) {
Log.e(TAG, "Failed to unregister config monitor", e);
}
diff --git a/src/com/android/launcher3/util/DefaultDisplay.java b/src/com/android/launcher3/util/DefaultDisplay.java
index 7719f08..8529d50 100644
--- a/src/com/android/launcher3/util/DefaultDisplay.java
+++ b/src/com/android/launcher3/util/DefaultDisplay.java
@@ -15,12 +15,15 @@
*/
package com.android.launcher3.util;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.content.Context;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Handler;
import android.os.Message;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
@@ -54,7 +57,7 @@
mChangeHandler = new Handler(this::onChange);
context.getSystemService(DisplayManager.class)
- .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
+ .registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
}
@Override
@@ -122,6 +125,8 @@
public final Point smallestSize;
public final Point largestSize;
+ public final DisplayMetrics metrics;
+
private Info(Context context) {
Display display = context.getSystemService(WindowManager.class).getDefaultDisplay();
@@ -136,6 +141,9 @@
largestSize = new Point();
display.getRealSize(realSize);
display.getCurrentSizeRange(smallestSize, largestSize);
+
+ metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
}
private boolean hasDifferentSize(Info info) {
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
new file mode 100644
index 0000000..4d5ee49
--- /dev/null
+++ b/src/com/android/launcher3/util/Executors.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Various different executors used in Launcher
+ */
+public class Executors {
+
+ // These values are same as that in {@link AsyncTask}.
+ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+ private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
+ private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+ private static final int KEEP_ALIVE = 1;
+
+ /**
+ * An {@link Executor} to be used with async task with no limit on the queue size.
+ */
+ public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
+ CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
+ TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+
+ /**
+ * Returns the executor for running tasks on the main thread.
+ */
+ public static final LooperExecutor MAIN_EXECUTOR =
+ new LooperExecutor(Looper.getMainLooper());
+
+ /**
+ * A background executor for using time sensitive actions where user is waiting for response.
+ */
+ public static final LooperExecutor UI_HELPER_EXECUTOR =
+ new LooperExecutor(createAndStartNewForegroundLooper("UiThreadHelper"));
+
+ /**
+ * Utility method to get a started handler thread statically
+ */
+ public static Looper createAndStartNewLooper(String name) {
+ return createAndStartNewLooper(name, Process.THREAD_PRIORITY_DEFAULT);
+ }
+
+ /**
+ * Utility method to get a started handler thread statically with the provided priority
+ */
+ public static Looper createAndStartNewLooper(String name, int priority) {
+ HandlerThread thread = new HandlerThread(name, priority);
+ thread.start();
+ return thread.getLooper();
+ }
+
+ /**
+ * Similar to {@link #createAndStartNewLooper(String)}, but starts the thread with
+ * foreground priority.
+ * Think before using
+ */
+ public static Looper createAndStartNewForegroundLooper(String name) {
+ return createAndStartNewLooper(name, Process.THREAD_PRIORITY_FOREGROUND);
+ }
+
+ /**
+ * Executor used for running Launcher model related tasks (eg loading icons or updated db)
+ */
+ public static final LooperExecutor MODEL_EXECUTOR =
+ new LooperExecutor(createAndStartNewLooper("launcher-loader"));
+}
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index f95f74d..4a4a5ca 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -17,10 +17,13 @@
package com.android.launcher3.util;
import android.content.Context;
+import android.util.Log;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -35,6 +38,7 @@
public class IOUtils {
private static final int BUF_SIZE = 0x1000; // 4K
+ private static final String TAG = "IOUtils";
public static byte[] toByteArray(File file) throws IOException {
try (InputStream in = new FileInputStream(file)) {
@@ -77,4 +81,16 @@
}
return file.getAbsolutePath();
}
+
+ public static void closeSilently(Closeable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ Log.d(TAG, "Error closing", e);
+ }
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java
index d2a551f..7252f7a 100644
--- a/src/com/android/launcher3/util/IntArray.java
+++ b/src/com/android/launcher3/util/IntArray.java
@@ -17,6 +17,7 @@
package com.android.launcher3.util;
import java.util.Arrays;
+import java.util.StringTokenizer;
/**
* Copy of the platform hidden implementation of android.util.IntArray.
@@ -248,6 +249,17 @@
return b.toString();
}
+ public static IntArray fromConcatString(String concatString) {
+ StringTokenizer tokenizer = new StringTokenizer(concatString, ",");
+ int[] array = new int[tokenizer.countTokens()];
+ int count = 0;
+ while (tokenizer.hasMoreTokens()) {
+ array[count] = Integer.parseInt(tokenizer.nextToken().trim());
+ count++;
+ }
+ return new IntArray(array, array.length);
+ }
+
/**
* Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
*
diff --git a/src/com/android/launcher3/util/LooperExecutor.java b/src/com/android/launcher3/util/LooperExecutor.java
index cc07469..8ac600f 100644
--- a/src/com/android/launcher3/util/LooperExecutor.java
+++ b/src/com/android/launcher3/util/LooperExecutor.java
@@ -16,7 +16,9 @@
package com.android.launcher3.util;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
+import android.os.Process;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
@@ -47,6 +49,13 @@
}
/**
+ * Same as execute, but never runs the action inline.
+ */
+ public void post(Runnable runnable) {
+ mHandler.post(runnable);
+ }
+
+ /**
* Not supported and throws an exception when used.
*/
@Override
@@ -79,7 +88,31 @@
*/
@Override
@Deprecated
- public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException {
+ public boolean awaitTermination(long l, TimeUnit timeUnit) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * Returns the thread for this executor
+ */
+ public Thread getThread() {
+ return mHandler.getLooper().getThread();
+ }
+
+ /**
+ * Returns the looper for this executor
+ */
+ public Looper getLooper() {
+ return mHandler.getLooper();
+ }
+
+ /**
+ * Set the priority of a thread, based on Linux priorities.
+ * @param priority Linux priority level, from -20 for highest scheduling priority
+ * to 19 for lowest scheduling priority.
+ * @see Process#setThreadPriority(int, int)
+ */
+ public void setThreadPriority(int priority) {
+ Process.setThreadPriority(((HandlerThread) getThread()).getThreadId(), priority);
+ }
}
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index e185a31..520a9ed 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -15,12 +15,13 @@
*/
package com.android.launcher3.util;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.content.Context;
import android.os.Looper;
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import java.util.concurrent.ExecutionException;
@@ -40,10 +41,11 @@
public T get(Context context) {
if (mValue == null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
- mValue = mProvider.get(context.getApplicationContext());
+ mValue = TraceHelper.whitelistIpcs("main.thread.object",
+ () -> mProvider.get(context.getApplicationContext()));
} else {
try {
- return new MainThreadExecutor().submit(() -> get(context)).get();
+ return MAIN_EXECUTOR.submit(() -> get(context)).get();
} catch (InterruptedException|ExecutionException e) {
throw new RuntimeException(e);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java b/src/com/android/launcher3/util/ObjectWrapper.java
similarity index 96%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java
rename to src/com/android/launcher3/util/ObjectWrapper.java
index abfe3ad..b692431 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java
+++ b/src/com/android/launcher3/util/ObjectWrapper.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep.util;
+package com.android.launcher3.util;
import android.os.Binder;
import android.os.IBinder;
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 7d3a941..7b4e0c6 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -24,17 +24,22 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
+import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.widget.Toast;
import com.android.launcher3.AppInfo;
@@ -43,8 +48,8 @@
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.compat.LauncherAppsCompat;
import java.net.URISyntaxException;
import java.util.List;
@@ -58,12 +63,12 @@
private final Context mContext;
private final PackageManager mPm;
- private final LauncherAppsCompat mLauncherApps;
+ private final LauncherApps mLauncherApps;
public PackageManagerHelper(Context context) {
mContext = context;
mPm = context.getPackageManager();
- mLauncherApps = LauncherAppsCompat.getInstance(context);
+ mLauncherApps = context.getSystemService(LauncherApps.class);
}
/**
@@ -71,8 +76,8 @@
* guarantee that the app is on SD card.
*/
public boolean isAppOnSdcard(String packageName, UserHandle user) {
- ApplicationInfo info = mLauncherApps.getApplicationInfo(
- packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, user);
+ ApplicationInfo info = getApplicationInfo(
+ packageName, user, PackageManager.MATCH_UNINSTALLED_PACKAGES);
return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
@@ -81,10 +86,47 @@
* {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
*/
public boolean isAppSuspended(String packageName, UserHandle user) {
- ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, 0, user);
+ ApplicationInfo info = getApplicationInfo(packageName, user, 0);
return info != null && isAppSuspended(info);
}
+ /**
+ * Returns the application info for the provided package or null
+ */
+ public ApplicationInfo getApplicationInfo(String packageName, UserHandle user, int flags) {
+ if (Utilities.ATLEAST_OREO) {
+ try {
+ ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
+ return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
+ ? null : info;
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ } else {
+ final boolean isPrimaryUser = Process.myUserHandle().equals(user);
+ if (!isPrimaryUser && (flags == 0)) {
+ // We are looking for an installed app on a secondary profile. Prior to O, the only
+ // entry point for work profiles is through the LauncherActivity.
+ List<LauncherActivityInfo> activityList =
+ mLauncherApps.getActivityList(packageName, user);
+ return activityList.size() > 0 ? activityList.get(0).getApplicationInfo() : null;
+ }
+ try {
+ ApplicationInfo info = mPm.getApplicationInfo(packageName, flags);
+ // There is no way to check if the app is installed for managed profile. But for
+ // primary profile, we can still have this check.
+ if (isPrimaryUser && ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0)
+ || !info.enabled) {
+ return null;
+ }
+ return info;
+ } catch (PackageManager.NameNotFoundException e) {
+ // Package not found
+ return null;
+ }
+ }
+ }
+
public boolean isSafeMode() {
return mContext.getPackageManager().isSafeMode();
}
@@ -199,8 +241,7 @@
}
if (componentName != null) {
try {
- mLauncherApps.showAppDetailsForProfile(
- componentName, info.user, sourceBounds, opts);
+ mLauncherApps.startAppDetailsActivity(componentName, info.user, sourceBounds, opts);
} catch (SecurityException | ActivityNotFoundException e) {
Toast.makeText(mContext, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch settings", e);
@@ -220,4 +261,73 @@
packageFilter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL);
return packageFilter;
}
+
+ public static boolean isSystemApp(Context context, Intent intent) {
+ PackageManager pm = context.getPackageManager();
+ ComponentName cn = intent.getComponent();
+ String packageName = null;
+ if (cn == null) {
+ ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if ((info != null) && (info.activityInfo != null)) {
+ packageName = info.activityInfo.packageName;
+ }
+ } else {
+ packageName = cn.getPackageName();
+ }
+ if (packageName != null) {
+ try {
+ PackageInfo info = pm.getPackageInfo(packageName, 0);
+ return (info != null) && (info.applicationInfo != null) &&
+ ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Finds a system apk which had a broadcast receiver listening to a particular action.
+ * @param action intent action used to find the apk
+ * @return a pair of apk package name and the resources.
+ */
+ public static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
+ final Intent intent = new Intent(action);
+ for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
+ if (info.activityInfo != null &&
+ (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ final String packageName = info.activityInfo.packageName;
+ try {
+ final Resources res = pm.getResourcesForApplication(packageName);
+ return Pair.create(packageName, res);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Failed to find resources for " + packageName);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the intent is a valid launch intent for a launcher activity of an app.
+ * This is used to identify shortcuts which are different from the ones exposed by the
+ * applications' manifest file.
+ *
+ * @param launchIntent The intent that will be launched when the shortcut is clicked.
+ */
+ public static boolean isLauncherAppTarget(Intent launchIntent) {
+ if (launchIntent != null
+ && Intent.ACTION_MAIN.equals(launchIntent.getAction())
+ && launchIntent.getComponent() != null
+ && launchIntent.getCategories() != null
+ && launchIntent.getCategories().size() == 1
+ && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
+ && TextUtils.isEmpty(launchIntent.getDataString())) {
+ // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE.
+ Bundle extras = launchIntent.getExtras();
+ return extras == null || extras.keySet().isEmpty();
+ }
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java
index aa11968..f243ca6 100644
--- a/src/com/android/launcher3/util/PackageUserKey.java
+++ b/src/com/android/launcher3/util/PackageUserKey.java
@@ -6,7 +6,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
import java.util.Arrays;
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
index 7ab0d31..ed66422 100644
--- a/src/com/android/launcher3/util/Preconditions.java
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -16,9 +16,10 @@
package com.android.launcher3.util;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.os.Looper;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.config.FeatureFlags;
/**
@@ -33,7 +34,7 @@
}
public static void assertWorkerThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) {
throw new IllegalStateException();
}
}
diff --git a/src/com/android/launcher3/util/RaceConditionTracker.java b/src/com/android/launcher3/util/RaceConditionTracker.java
deleted file mode 100644
index 6954d0e..0000000
--- a/src/com/android/launcher3/util/RaceConditionTracker.java
+++ /dev/null
@@ -1,61 +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.util;
-
-/**
- * Event tracker for reliably reproducing race conditions in tests.
- * The app should call onEvent() for events that the test will try to reproduce in all possible
- * orders.
- */
-public class RaceConditionTracker {
- public final static boolean ENTER = true;
- public final static boolean EXIT = false;
- static final String ENTER_POSTFIX = "enter";
- static final String EXIT_POSTFIX = "exit";
-
- public interface EventProcessor {
- void onEvent(String eventName);
- }
-
- private static EventProcessor sEventProcessor;
-
- static void setEventProcessor(EventProcessor eventProcessor) {
- sEventProcessor = eventProcessor;
- }
-
- public static void onEvent(String eventName) {
- if (sEventProcessor != null) sEventProcessor.onEvent(eventName);
- }
-
- public static void onEvent(String eventName, boolean isEnter) {
- if (sEventProcessor != null) {
- sEventProcessor.onEvent(enterExitEvt(eventName, isEnter));
- }
- }
-
- public static String enterExitEvt(String eventName, boolean isEnter) {
- return eventName + ":" + (isEnter ? ENTER_POSTFIX : EXIT_POSTFIX);
- }
-
- public static String enterEvt(String eventName) {
- return enterExitEvt(eventName, ENTER);
- }
-
- public static String exitEvt(String eventName) {
- return enterExitEvt(eventName, EXIT);
- }
-}
diff --git a/src_flags/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/util/SafeCloseable.java
similarity index 64%
rename from src_flags/com/android/launcher3/config/FeatureFlags.java
rename to src/com/android/launcher3/util/SafeCloseable.java
index 73c6996..ba8ee04 100644
--- a/src_flags/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/util/SafeCloseable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-package com.android.launcher3.config;
-
-import android.content.Context;
+package com.android.launcher3.util;
/**
- * Defines a set of flags used to control various launcher behaviors
+ * Extension of closeable which does not throw an exception
*/
-public final class FeatureFlags extends BaseFlags {
- private FeatureFlags() {
- // Prevent instantiation
- }
+public interface SafeCloseable extends AutoCloseable {
+
+ @Override
+ void close();
}
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index af99713..a69cd6c 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -15,11 +15,21 @@
*/
package com.android.launcher3.util;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+
+import androidx.annotation.NonNull;
+
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.shortcuts.ShortcutKey;
public class ShortcutUtil {
@@ -61,10 +71,30 @@
&& info instanceof WorkspaceItemInfo;
}
+ /**
+ * Fetch the shortcut icon in background, then update the UI.
+ */
+ 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(() -> {
+ 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);
+ });
+ }
+
private static boolean isActive(ItemInfo info) {
boolean isLoading = info instanceof WorkspaceItemInfo
&& ((WorkspaceItemInfo) info).hasPromiseIconUi();
- return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
+ return !isLoading && !info.isDisabled() && !WidgetsModel.GO_DISABLE_WIDGETS;
}
private static boolean isApp(ItemInfo info) {
@@ -76,4 +106,4 @@
&& info.container != ItemInfo.NO_ID
&& info instanceof WorkspaceItemInfo;
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
new file mode 100644
index 0000000..465a0e8
--- /dev/null
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import java.util.function.Consumer;
+
+public class SimpleBroadcastReceiver extends BroadcastReceiver {
+
+ private final Consumer<Intent> mIntentConsumer;
+
+ public SimpleBroadcastReceiver(Consumer<Intent> intentConsumer) {
+ mIntentConsumer = intentConsumer;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mIntentConsumer.accept(intent);
+ }
+
+ /**
+ * Helper method to register multiple actions
+ */
+ public void register(Context context, String... actions) {
+ IntentFilter filter = new IntentFilter();
+ for (String action : actions) {
+ filter.addAction(action);
+ }
+ context.registerReceiver(this, filter);
+ }
+}
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index c24bb67..168227d 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -15,19 +15,14 @@
*/
package com.android.launcher3.util;
-import static android.util.Log.VERBOSE;
-import static android.util.Log.isLoggable;
-
-import android.os.SystemClock;
import android.os.Trace;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.MutableLong;
-import com.android.launcher3.config.FeatureFlags;
+import androidx.annotation.MainThread;
+
+import java.util.function.Supplier;
/**
- * A wrapper around {@link Trace} with some utility information.
+ * A wrapper around {@link Trace} to allow better testing.
*
* To enable any tracing log, execute the following command:
* $ adb shell setprop log.tag.LAUNCHER_TRACE VERBOSE
@@ -35,65 +30,60 @@
*/
public class TraceHelper {
- private static final boolean ENABLED = isLoggable("LAUNCHER_TRACE", VERBOSE);
+ // Track binder class for this trace
+ public static final int FLAG_ALLOW_BINDER_TRACKING = 1 << 0;
- private static final boolean SYSTEM_TRACE = ENABLED;
- private static final ArrayMap<String, MutableLong> sUpTimes = ENABLED ? new ArrayMap<>() : null;
+ // Temporarily ignore blocking binder calls for this trace.
+ public static final int FLAG_IGNORE_BINDERS = 1 << 1;
- public static void beginSection(String sectionName) {
- if (ENABLED) {
- synchronized (sUpTimes) {
- MutableLong time = sUpTimes.get(sectionName);
- if (time == null) {
- time = new MutableLong(isLoggable(sectionName, VERBOSE) ? 0 : -1);
- sUpTimes.put(sectionName, time);
- }
- if (time.value >= 0) {
- if (SYSTEM_TRACE) {
- Trace.beginSection(sectionName);
- }
- time.value = SystemClock.uptimeMillis();
- }
- }
- }
+ public static final int FLAG_CHECK_FOR_RACE_CONDITIONS = 1 << 2;
+
+ public static final int FLAG_UI_EVENT =
+ FLAG_ALLOW_BINDER_TRACKING | FLAG_CHECK_FOR_RACE_CONDITIONS;
+
+ /**
+ * Static instance of Trace helper, overridden in tests.
+ */
+ public static TraceHelper INSTANCE = new TraceHelper();
+
+ /**
+ * @return a token to pass into {@link #endSection(Object)}.
+ */
+ public Object beginSection(String sectionName) {
+ return beginSection(sectionName, 0);
}
- public static void partitionSection(String sectionName, String partition) {
- if (ENABLED) {
- synchronized (sUpTimes) {
- MutableLong time = sUpTimes.get(sectionName);
- if (time != null && time.value >= 0) {
-
- if (SYSTEM_TRACE) {
- Trace.endSection();
- Trace.beginSection(sectionName);
- }
-
- long now = SystemClock.uptimeMillis();
- Log.d(sectionName, partition + " : " + (now - time.value));
- time.value = now;
- }
- }
- }
+ public Object beginSection(String sectionName, int flags) {
+ Trace.beginSection(sectionName);
+ return null;
}
- public static void endSection(String sectionName) {
- if (ENABLED) {
- endSection(sectionName, "End");
- }
+ /**
+ * @param token the token returned from {@link #beginSection(String, int)}
+ */
+ public void endSection(Object token) {
+ Trace.endSection();
}
- public static void endSection(String sectionName, String msg) {
- if (ENABLED) {
- synchronized (sUpTimes) {
- MutableLong time = sUpTimes.get(sectionName);
- if (time != null && time.value >= 0) {
- if (SYSTEM_TRACE) {
- Trace.endSection();
- }
- Log.d(sectionName, msg + " : " + (SystemClock.uptimeMillis() - time.value));
- }
- }
+ /**
+ * Similar to {@link #beginSection} but doesn't add a trace section.
+ */
+ public Object beginFlagsOverride(int flags) {
+ return null;
+ }
+
+ public void endFlagsOverride(Object token) { }
+
+ /**
+ * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
+ */
+ @MainThread
+ public static <T> T whitelistIpcs(String rpcName, Supplier<T> supplier) {
+ Object traceToken = INSTANCE.beginSection(rpcName, FLAG_IGNORE_BINDERS);
+ try {
+ return supplier.get();
+ } finally {
+ INSTANCE.endSection(traceToken);
}
}
}
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index cc442f9..a133f01 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -15,40 +15,30 @@
*/
package com.android.launcher3.util;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
-import android.os.Process;
import android.view.inputmethod.InputMethodManager;
+import com.android.launcher3.uioverrides.UiFactory;
/**
* Utility class for offloading some class from UI thread
*/
public class UiThreadHelper {
- private static HandlerThread sHandlerThread;
private static Handler sHandler;
private static final int MSG_HIDE_KEYBOARD = 1;
private static final int MSG_SET_ORIENTATION = 2;
private static final int MSG_RUN_COMMAND = 3;
- public static Looper getBackgroundLooper() {
- if (sHandlerThread == null) {
- sHandlerThread =
- new HandlerThread("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND);
- sHandlerThread.start();
- }
- return sHandlerThread.getLooper();
- }
-
private static Handler getHandler(Context context) {
if (sHandler == null) {
- sHandler = new Handler(getBackgroundLooper(),
+ sHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(),
new UiCallbacks(context.getApplicationContext()));
}
return sHandler;
@@ -63,15 +53,22 @@
.sendToTarget();
}
+ public static void setBackButtonAlphaAsync(Context context, AsyncCommand command, float alpha,
+ boolean animate) {
+ runAsyncCommand(context, command, Float.floatToIntBits(alpha), animate ? 1 : 0);
+ }
+
public static void runAsyncCommand(Context context, AsyncCommand command, int arg1, int arg2) {
Message.obtain(getHandler(context), MSG_RUN_COMMAND, arg1, arg2, command).sendToTarget();
}
private static class UiCallbacks implements Handler.Callback {
+ private final Context mContext;
private final InputMethodManager mIMM;
UiCallbacks(Context context) {
+ mContext = context;
mIMM = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
}
@@ -85,7 +82,7 @@
((Activity) message.obj).setRequestedOrientation(message.arg1);
return true;
case MSG_RUN_COMMAND:
- ((AsyncCommand) message.obj).execute(message.arg1, message.arg2);
+ ((AsyncCommand) message.obj).execute(mContext, message.arg1, message.arg2);
return true;
}
return false;
@@ -93,7 +90,6 @@
}
public interface AsyncCommand {
-
- void execute(int arg1, int arg2);
+ void execute(Context proxy, int arg1, int arg2);
}
}
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index acce308..61ba4e5 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -16,13 +16,14 @@
package com.android.launcher3.util;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.os.Process;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewTreeObserver.OnDrawListener;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherModel;
import java.util.ArrayList;
import java.util.concurrent.Executor;
@@ -66,7 +67,7 @@
@Override
public void execute(Runnable command) {
mTasks.add(command);
- LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
@Override
@@ -108,7 +109,7 @@
if (mLauncher != null) {
mLauncher.clearPendingExecutor(this);
}
- LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_DEFAULT);
+ MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
protected boolean isCompleted() {
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index 5c24687..2ad80cf 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -1,5 +1,7 @@
package com.android.launcher3.util;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -198,7 +200,7 @@
private float mOffsetX;
public OffsetHandler(Context context) {
- super(UiThreadHelper.getBackgroundLooper());
+ super(UI_HELPER_EXECUTOR.getLooper());
mInterpolator = Interpolators.DEACCEL_1_5;
mWM = WallpaperManager.getInstance(context);
}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index c08b659..2a4c5a7 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -465,7 +465,7 @@
}
public void dump(String prefix, PrintWriter writer) {
- writer.println(prefix + "DragLayer");
+ writer.println(prefix + "DragLayer:");
if (mActiveController != null) {
writer.println(prefix + "\tactiveController: " + mActiveController);
mActiveController.dump(prefix + "\t", writer);
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index f728a67..45c0d90 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -41,7 +42,6 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.CancellationSignal;
-import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -50,11 +50,17 @@
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
@@ -66,13 +72,6 @@
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.DeepShortcutView;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-import androidx.dynamicanimation.animation.FloatPropertyCompat;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
/**
* A view that is created to look like another view with the purpose of creating fluid animations.
*/
@@ -722,7 +721,7 @@
@UiThread
public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) {
IconLoadResult result = new IconLoadResult(info);
- new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
+ MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> {
RectF position = new RectF();
getLocationBoundsForView(l, v, isOpening, position);
getIconResult(l, v, info, position, result);
@@ -768,11 +767,6 @@
// Match the position of the original view.
view.matchPositionOf(launcher, originalView, isOpening, positionOut);
- // Must be called after matchPositionOf so that we know what size to load.
- if (shouldLoadIcon) {
- view.checkIconResult(originalView, isOpening);
- }
-
// We need to add it to the overlay, but keep it invisible until animation starts..
view.setVisibility(INVISIBLE);
parent.addView(view);
@@ -799,6 +793,14 @@
view.finish(dragLayer);
}
};
+
+ // Must be called after matchPositionOf so that we know what size to load.
+ // Must be called after the fastFinish listener and end runnable is created so that
+ // the icon is not left in a hidden state.
+ if (shouldLoadIcon) {
+ view.checkIconResult(originalView, isOpening);
+ }
+
return view;
}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 465df44..88d34da 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -37,7 +37,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.popup.ArrowPopup;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -158,7 +158,7 @@
R.drawable.ic_palette : R.drawable.ic_wallpaper;
options.add(new OptionItem(resString, resDrawable,
ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker));
- if (!FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (!WidgetsModel.GO_DISABLE_WIDGETS) {
options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked));
}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index da1df3f..9f59d78 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -288,6 +288,7 @@
anim.addUpdateListener((v) -> invalidate(invalidateRegion));
getOverlay().add(drawable);
anim.start();
+ return true;
}
return value;
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index dce839f..f3fd7ca 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -44,6 +44,7 @@
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
@@ -96,7 +97,7 @@
setBackgroundResource(R.drawable.widget_internal_focus_bg);
if (Utilities.ATLEAST_OREO) {
- setExecutor(Utilities.THREAD_POOL_EXECUTOR);
+ setExecutor(Executors.THREAD_POOL_EXECUTOR);
}
if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
setOnLightBackground(true);
@@ -332,12 +333,7 @@
if (shouldRegisterAutoAdvance != mIsAutoAdvanceRegistered) {
mIsAutoAdvanceRegistered = shouldRegisterAutoAdvance;
if (mAutoAdvanceRunnable == null) {
- mAutoAdvanceRunnable = new Runnable() {
- @Override
- public void run() {
- runAutoAdvance();
- }
- };
+ mAutoAdvanceRunnable = this::runAutoAdvance;
}
handler.removeCallbacks(mAutoAdvanceRunnable);
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
index 62b6903..6e21a41 100644
--- a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -16,7 +16,7 @@
package com.android.launcher3.widget;
import com.android.launcher3.PendingAddItemInfo;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
+import com.android.launcher3.pm.ShortcutConfigActivityInfo;
/**
* Meta data used for late binding of the short cuts.
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 8ea9bd4..662e627 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -91,8 +91,8 @@
createWidgetInfo.info, maxWidth, previewSizeBeforeScale);
}
if (preview == null) {
- preview = app.getWidgetCache().generateWidgetPreview(
- launcher, createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale);
+ preview = app.getWidgetCache().generateWidgetPreview(launcher,
+ createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale).first;
}
if (previewSizeBeforeScale[0] < previewBitmapWidth) {
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index dc4af8c..6944879 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -37,6 +37,7 @@
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;
/**
@@ -80,6 +81,7 @@
private Bitmap mDeferredBitmap;
protected final BaseActivity mActivity;
+ protected DeviceProfile mDeviceProfile;
public WidgetCell(Context context) {
this(context, null);
@@ -93,6 +95,7 @@
super(context, attrs, defStyle);
mActivity = BaseActivity.fromContext(context);
+ mDeviceProfile = mActivity.getDeviceProfile();
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
setContainerWidth();
@@ -102,8 +105,7 @@
}
private void setContainerWidth() {
- DeviceProfile profile = mActivity.getDeviceProfile();
- mCellSize = (int) (profile.cellWidthPx * WIDTH_SCALE);
+ mCellSize = (int) (mDeviceProfile.allAppsCellWidthPx * WIDTH_SCALE);
mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
}
@@ -180,8 +182,10 @@
return;
}
if (bitmap != null) {
- mWidgetImage.setBitmap(bitmap, DrawableFactory.INSTANCE.get(getContext())
- .getBadgeForUser(mItem.user, getContext()));
+ 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/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
new file mode 100644
index 0000000..b3569f2
--- /dev/null
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
@@ -0,0 +1,172 @@
+/*
+ * 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.widget.custom;
+
+import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Process;
+import android.util.SparseArray;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.systemui.plugins.CustomWidgetPlugin;
+import com.android.systemui.plugins.PluginListener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * CustomWidgetManager handles custom widgets implemented as a plugin.
+ */
+public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin> {
+
+ public static final MainThreadInitializedObject<CustomWidgetManager> INSTANCE =
+ new MainThreadInitializedObject<>(CustomWidgetManager::new);
+
+ /**
+ * auto provider Id is an ever-increasing number that serves as the providerId whenever a new
+ * custom widget has been connected.
+ */
+ private int mAutoProviderId = 0;
+ private final SparseArray<CustomWidgetPlugin> mPlugins;
+ private final List<CustomAppWidgetProviderInfo> mCustomWidgets;
+ private final SparseArray<ComponentName> mWidgetsIdMap;
+ private Consumer<PackageUserKey> mWidgetRefreshCallback;
+
+ private CustomWidgetManager(Context context) {
+ mPlugins = new SparseArray<>();
+ mCustomWidgets = new ArrayList<>();
+ mWidgetsIdMap = new SparseArray<>();
+ PluginManagerWrapper.INSTANCE.get(context)
+ .addPluginListener(this, CustomWidgetPlugin.class, true);
+ }
+
+ @Override
+ public void onPluginConnected(CustomWidgetPlugin plugin, Context context) {
+ mPlugins.put(mAutoProviderId, plugin);
+ List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(context)
+ .getInstalledProvidersForProfile(Process.myUserHandle());
+ if (providers.isEmpty()) return;
+ Parcel parcel = Parcel.obtain();
+ providers.get(0).writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CustomAppWidgetProviderInfo info = newInfo(mAutoProviderId, plugin, parcel, context);
+ parcel.recycle();
+ mCustomWidgets.add(info);
+ mWidgetsIdMap.put(mAutoProviderId, info.provider);
+ mWidgetRefreshCallback.accept(null);
+ mAutoProviderId++;
+ }
+
+ @Override
+ public void onPluginDisconnected(CustomWidgetPlugin plugin) {
+ int providerId = findProviderId(plugin);
+ if (providerId == -1) return;
+ mPlugins.remove(providerId);
+ mCustomWidgets.remove(getWidgetProvider(providerId));
+ mWidgetsIdMap.remove(providerId);
+ }
+
+ /**
+ * Inject a callback function to refresh the widgets.
+ */
+ public void setWidgetRefreshCallback(Consumer<PackageUserKey> cb) {
+ mWidgetRefreshCallback = cb;
+ }
+
+ /**
+ * Callback method to inform a plugin it's corresponding widget has been created.
+ */
+ public void onViewCreated(LauncherAppWidgetHostView view) {
+ CustomAppWidgetProviderInfo info = (CustomAppWidgetProviderInfo) view.getAppWidgetInfo();
+ CustomWidgetPlugin plugin = mPlugins.get(info.providerId);
+ if (plugin == null) return;
+ plugin.onViewCreated(view);
+ }
+
+ /**
+ * Returns the list of custom widgets.
+ */
+ @NonNull
+ public List<CustomAppWidgetProviderInfo> getCustomWidgets() {
+ return mCustomWidgets;
+ }
+
+ /**
+ * Returns the widget id for a specific provider.
+ */
+ public int getWidgetIdForCustomProvider(@NonNull ComponentName provider) {
+ int index = mWidgetsIdMap.indexOfValue(provider);
+ if (index >= 0) {
+ return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - mWidgetsIdMap.keyAt(index);
+ } else {
+ return AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+ }
+
+ /**
+ * Returns the widget provider in respect to given widget id.
+ */
+ @Nullable
+ public LauncherAppWidgetProviderInfo getWidgetProvider(int widgetId) {
+ ComponentName cn = mWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId);
+ for (LauncherAppWidgetProviderInfo info : mCustomWidgets) {
+ if (info.provider.equals(cn)) return info;
+ }
+ return null;
+ }
+
+ private static CustomAppWidgetProviderInfo newInfo(int providerId, CustomWidgetPlugin plugin,
+ Parcel parcel, Context context) {
+ CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(
+ parcel, false, providerId);
+ info.provider = new ComponentName(
+ context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId);
+
+ info.label = plugin.getLabel();
+ info.resizeMode = plugin.getResizeMode();
+
+ info.spanX = plugin.getSpanX();
+ info.spanY = plugin.getSpanY();
+ info.minSpanX = plugin.getMinSpanX();
+ info.minSpanY = plugin.getMinSpanY();
+ return info;
+ }
+
+ private int findProviderId(CustomWidgetPlugin plugin) {
+ for (int i = 0; i < mPlugins.size(); i++) {
+ int providerId = mPlugins.keyAt(i);
+ if (mPlugins.get(providerId) == plugin) {
+ return providerId;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetParser.java b/src/com/android/launcher3/widget/custom/CustomWidgetParser.java
deleted file mode 100644
index 00720c4..0000000
--- a/src/com/android/launcher3/widget/custom/CustomWidgetParser.java
+++ /dev/null
@@ -1,142 +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.widget.custom;
-
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Parcel;
-import android.os.Process;
-import android.util.SparseArray;
-import android.util.Xml;
-
-import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX;
-
-/**
- * Utility class to parse {@ink CustomAppWidgetProviderInfo} definitions from xml
- */
-public class CustomWidgetParser {
-
- private static List<LauncherAppWidgetProviderInfo> sCustomWidgets;
- private static SparseArray<ComponentName> sWidgetsIdMap;
-
- public static List<LauncherAppWidgetProviderInfo> getCustomWidgets(Context context) {
- if (sCustomWidgets == null) {
- // Synchronization not needed as it it safe to load multiple times
- parseCustomWidgets(context);
- }
-
- return sCustomWidgets;
- }
-
- public static int getWidgetIdForCustomProvider(Context context, ComponentName provider) {
- if (sWidgetsIdMap == null) {
- parseCustomWidgets(context);
- }
- int index = sWidgetsIdMap.indexOfValue(provider);
- if (index >= 0) {
- return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - sWidgetsIdMap.keyAt(index);
- } else {
- return AppWidgetManager.INVALID_APPWIDGET_ID;
- }
- }
-
- public static LauncherAppWidgetProviderInfo getWidgetProvider(Context context, int widgetId) {
- if (sWidgetsIdMap == null || sCustomWidgets == null) {
- parseCustomWidgets(context);
- }
- ComponentName cn = sWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId);
- for (LauncherAppWidgetProviderInfo info : sCustomWidgets) {
- if (info.provider.equals(cn)) {
- return info;
- }
- }
- return null;
- }
-
- private static void parseCustomWidgets(Context context) {
- ArrayList<LauncherAppWidgetProviderInfo> widgets = new ArrayList<>();
- SparseArray<ComponentName> idMap = new SparseArray<>();
-
- List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(context)
- .getInstalledProvidersForProfile(Process.myUserHandle());
- if (providers.isEmpty()) {
- sCustomWidgets = widgets;
- sWidgetsIdMap = idMap;
- return;
- }
-
- Parcel parcel = Parcel.obtain();
- providers.get(0).writeToParcel(parcel, 0);
-
- try (XmlResourceParser parser = context.getResources().getXml(R.xml.custom_widgets)) {
- final int depth = parser.getDepth();
- int type;
-
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
- if ((type == XmlPullParser.START_TAG) && "widget".equals(parser.getName())) {
- TypedArray a = context.obtainStyledAttributes(
- Xml.asAttributeSet(parser), R.styleable.CustomAppWidgetProviderInfo);
-
- parcel.setDataPosition(0);
- CustomAppWidgetProviderInfo info = newInfo(a, parcel, context);
- widgets.add(info);
- a.recycle();
-
- idMap.put(info.providerId, info.provider);
- }
- }
- } catch (IOException | XmlPullParserException e) {
- throw new RuntimeException(e);
- }
- parcel.recycle();
- sCustomWidgets = widgets;
- sWidgetsIdMap = idMap;
- }
-
- private static CustomAppWidgetProviderInfo newInfo(TypedArray a, Parcel parcel, Context context) {
- int providerId = a.getInt(R.styleable.CustomAppWidgetProviderInfo_providerId, 0);
- CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(parcel, false, providerId);
- info.provider = new ComponentName(context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId);
-
- info.label = a.getString(R.styleable.CustomAppWidgetProviderInfo_android_label);
- info.initialLayout = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_initialLayout, 0);
- info.icon = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_icon, 0);
- info.previewImage = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_previewImage, 0);
- info.resizeMode = a.getInt(R.styleable.CustomAppWidgetProviderInfo_android_resizeMode, 0);
-
- info.spanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numColumns, 1);
- info.spanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numRows, 1);
- info.minSpanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinColumns, 1);
- info.minSpanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinRows, 1);
- return info;
- }
-}
diff --git a/src_build_config/BuildConfig.java b/src_build_config/BuildConfig.java
index 36d7f4b..49aadf6 100644
--- a/src_build_config/BuildConfig.java
+++ b/src_build_config/BuildConfig.java
@@ -18,4 +18,5 @@
public final class BuildConfig {
public static final String APPLICATION_ID = "com.android.launcher3";
+ public static final boolean DEBUG = false;
}
diff --git a/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java b/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java
new file mode 100644
index 0000000..15a0ffa
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.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.systemui.plugins;
+
+import android.content.ComponentName;
+import android.os.UserHandle;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Plugin interface which sends app launch events.
+ */
+@ProvidesInterface(action = AppLaunchEventsPlugin.ACTION, version = AppLaunchEventsPlugin.VERSION)
+public interface AppLaunchEventsPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_APP_EVENTS";
+ int VERSION = 1;
+
+ /**
+ * Receives onStartShortcut event from
+ * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
+ */
+ void onStartShortcut(String packageName, String shortcutId, UserHandle user, String container);
+
+ /**
+ * Receives onStartApp event from
+ * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
+ */
+ void onStartApp(ComponentName componentName, UserHandle user, String container);
+
+ /**
+ * Receives onDismissApp event from
+ * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
+ */
+ void onDismissApp(ComponentName componentName, UserHandle user, String container);
+
+ /**
+ * Receives onReturnedToHome event from
+ * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
+ */
+ void onReturnedToHome();
+}
diff --git a/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java b/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java
new file mode 100644
index 0000000..56ebcc5
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.appwidget.AppWidgetHostView;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this plugin interface to add a custom widget.
+ */
+@ProvidesInterface(action = CustomWidgetPlugin.ACTION, version = CustomWidgetPlugin.VERSION)
+public interface CustomWidgetPlugin extends Plugin {
+
+ String ACTION = "com.android.systemui.action.PLUGIN_CUSTOM_WIDGET";
+ int VERSION = 1;
+
+ /**
+ * The label to display to the user in the AppWidget picker.
+ */
+ String getLabel();
+
+ /**
+ * The default width of the widget when added to a host, in dp. The widget will get
+ * at least this width, and will often be given more, depending on the host.
+ */
+ int getSpanX();
+
+ /**
+ * The default height of the widget when added to a host, in dp. The widget will get
+ * at least this height, and will often be given more, depending on the host.
+ */
+ int getSpanY();
+
+ /**
+ * Minimum width (in dp) which the widget can be resized to. This field has no effect if it
+ * is greater than minWidth or if horizontal resizing isn't enabled.
+ */
+ int getMinSpanX();
+
+ /**
+ * Minimum height (in dp) which the widget can be resized to. This field has no effect if it
+ * is greater than minHeight or if vertical resizing isn't enabled.
+ */
+ int getMinSpanY();
+
+ /**
+ * The rules by which a widget can be resized.
+ */
+ int getResizeMode();
+
+ /**
+ * Notify the plugin that container of the widget has been rendered, where the custom widget
+ * can be attached to.
+ */
+ void onViewCreated(AppWidgetHostView parent);
+}
diff --git a/src_plugins/com/android/systemui/plugins/HotseatPlugin.java b/src_plugins/com/android/systemui/plugins/HotseatPlugin.java
new file mode 100644
index 0000000..1264e0d
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/HotseatPlugin.java
@@ -0,0 +1,20 @@
+package com.android.systemui.plugins;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this plugin interface to add a sub-view in the Hotseat.
+ */
+@ProvidesInterface(action = HotseatPlugin.ACTION, version = HotseatPlugin.VERSION)
+public interface HotseatPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_HOTSEAT";
+ int VERSION = 1;
+
+ /**
+ * Creates a plugin view which will be added to the Hotseat.
+ */
+ View createView(ViewGroup parent);
+}
diff --git a/src_plugins/com/android/systemui/plugins/OverlayPlugin.java b/src_plugins/com/android/systemui/plugins/OverlayPlugin.java
new file mode 100644
index 0000000..1edb692
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/OverlayPlugin.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.systemui.plugins;
+
+import android.app.Activity;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.shared.LauncherExterns;
+import com.android.systemui.plugins.shared.LauncherOverlayManager;
+
+/**
+ * Implement this interface to add a -1 content on the home screen.
+ */
+@ProvidesInterface(action = OverlayPlugin.ACTION, version = OverlayPlugin.VERSION)
+public interface OverlayPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERLAY";
+ int VERSION = 1;
+
+ LauncherOverlayManager createOverlayManager(Activity activity, LauncherExterns externs);
+
+}
diff --git a/src_plugins/com/android/systemui/plugins/OverviewScreenshotActions.java b/src_plugins/com/android/systemui/plugins/OverviewScreenshotActions.java
new file mode 100644
index 0000000..8d9c0f4
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/OverviewScreenshotActions.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.app.Activity;
+import android.graphics.Bitmap;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this interface to add action buttons for overview screenshots, e.g. share, edit etc.
+ */
+@ProvidesInterface(
+ action = OverviewScreenshotActions.ACTION, version = OverviewScreenshotActions.VERSION)
+public interface OverviewScreenshotActions extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_OVERVIEW_SCREENSHOT_ACTIONS";
+ int VERSION = 1;
+
+ /**
+ * Setup the actions for the screenshot, including edit, save, etc.
+ * @param parent The parent view to add buttons on.
+ * @param screenshot The screenshot we will do actions on.
+ * @param activity THe host activity.
+ */
+ void setupActions(ViewGroup parent, Bitmap screenshot, Activity activity);
+}
diff --git a/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java b/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
new file mode 100644
index 0000000..0ebea3d
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
@@ -0,0 +1,42 @@
+/*
+ * 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 android.app.Activity;
+import android.content.Context;
+import android.widget.FrameLayout;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this interface to allow extra card on recents overview.
+ */
+@ProvidesInterface(action = RecentsExtraCard.ACTION, version = RecentsExtraCard.VERSION)
+public interface RecentsExtraCard extends Plugin {
+
+ String ACTION = "com.android.systemui.action.PLUGIN_RECENTS_EXTRA_CARD";
+ int VERSION = 1;
+
+ /**
+ * Sets up the recents overview extra card and fills in data.
+ *
+ * @param context Plugin context
+ * @param frameLayout PlaceholderView
+ * @param activity Recents activity to hold extra view
+ */
+ void setupView(Context context, FrameLayout frameLayout, Activity activity);
+}
diff --git a/src_plugins/com/android/systemui/plugins/UserEventPlugin.java b/src_plugins/com/android/systemui/plugins/UserEventPlugin.java
new file mode 100644
index 0000000..0e3664a
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/UserEventPlugin.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.systemui.plugins;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this plugin interface to access user event log on the device for prototype purpose.
+ * NOTE: plugin is for internal prototype only and is not visible in production environment.
+ */
+@ProvidesInterface(action = UserEventPlugin.ACTION, version = UserEventPlugin.VERSION)
+public interface UserEventPlugin extends Plugin {
+ String ACTION = "com.android.launcher3.action.PLUGIN_USER_EVENT_LOG";
+ int VERSION = 1;
+
+ /**
+ * Callback to be triggered whenever an user event occurs.
+ */
+ void onUserEvent(Object event);
+}
diff --git a/src/com/android/launcher3/LauncherExterns.java b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java
similarity index 63%
rename from src/com/android/launcher3/LauncherExterns.java
rename to src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java
index 272bbf6..13e4999 100644
--- a/src/com/android/launcher3/LauncherExterns.java
+++ b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java
@@ -14,19 +14,36 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.systemui.plugins.shared;
import android.content.SharedPreferences;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
+
/**
* This interface defines the set of methods that the Launcher activity exposes. Methods
* here should be safe to call from classes outside of com.android.launcher3.*
*/
public interface LauncherExterns {
- boolean setLauncherCallbacks(LauncherCallbacks callbacks);
-
+ /**
+ * Returns the shared main preference
+ */
SharedPreferences getSharedPrefs();
- void setLauncherOverlay(Launcher.LauncherOverlay overlay);
+ /**
+ * Returns the device specific preference
+ */
+ SharedPreferences getDevicePrefs();
+
+ /**
+ * Sets the overlay on the target activity
+ */
+ void setLauncherOverlay(LauncherOverlay overlay);
+
+ /**
+ * Executes the command, next time the overlay is hidden
+ */
+ void runOnOverlayHidden(Runnable runnable);
+
}
diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
new file mode 100644
index 0000000..ac02ba4
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
@@ -0,0 +1,98 @@
+/*
+ * 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.shared;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+import java.io.PrintWriter;
+
+/**
+ * Interface to control the overlay on Launcher
+ */
+public interface LauncherOverlayManager extends Application.ActivityLifecycleCallbacks {
+
+ default void onDeviceProvideChanged() { }
+
+ default void onAttachedToWindow() { }
+ default void onDetachedFromWindow() { }
+
+ default void dump(String prefix, PrintWriter w) { }
+
+ default void openOverlay() { }
+
+ default void hideOverlay(boolean animate) {
+ hideOverlay(animate ? 200 : 0);
+ }
+
+ default void hideOverlay(int duration) { }
+
+ default boolean startSearch(byte[] config, Bundle extras) {
+ return false;
+ }
+
+ @Override
+ default void onActivityCreated(Activity activity, Bundle bundle) { }
+
+ @Override
+ default void onActivityStarted(Activity activity) { }
+
+ @Override
+ default void onActivityResumed(Activity activity) { }
+
+ @Override
+ default void onActivityPaused(Activity activity) { }
+
+ @Override
+ default void onActivityStopped(Activity activity) { }
+
+ @Override
+ default void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
+
+ @Override
+ default void onActivityDestroyed(Activity activity) { }
+
+ interface LauncherOverlay {
+
+ /**
+ * Touch interaction leading to overscroll has begun
+ */
+ void onScrollInteractionBegin();
+
+ /**
+ * Touch interaction related to overscroll has ended
+ */
+ void onScrollInteractionEnd();
+
+ /**
+ * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
+ * screen (or in the case of RTL, the rightmost screen).
+ */
+ void onScrollChange(float progress, boolean rtl);
+
+ /**
+ * Called when the launcher is ready to use the overlay
+ * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
+ */
+ void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
+ }
+
+ interface LauncherOverlayCallbacks {
+
+ void onScrollChanged(float progress);
+ }
+}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
index 1710aef..789bfd8 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
@@ -16,9 +16,8 @@
package com.android.launcher3.model;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.widget.WidgetListRowEntry;
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 7a7f828..b8af8ed 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -3,6 +3,8 @@
import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER;
+import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
+
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -10,18 +12,19 @@
import android.os.UserHandle;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AppFilter;
-import com.android.launcher3.icons.ComponentWithLabel;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
@@ -37,8 +40,6 @@
import java.util.Map.Entry;
import java.util.Set;
-import androidx.annotation.Nullable;
-
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
*
@@ -46,6 +47,9 @@
*/
public class WidgetsModel {
+ // True is the widget support is disabled.
+ public static final boolean GO_DISABLE_WIDGETS = false;
+
private static final String TAG = "WidgetsModel";
private static final boolean DEBUG = false;
@@ -103,8 +107,8 @@
}
// Shortcuts
- for (ShortcutConfigActivityInfo info : LauncherAppsCompat.getInstance(context)
- .getCustomShortcutActivityList(packageUser)) {
+ for (ShortcutConfigActivityInfo info :
+ queryList(context, packageUser)) {
widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache(), pm));
updatedItems.add(info);
}
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 09b1890..57f4164 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -43,35 +43,27 @@
| ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_PINNED;
private static DeepShortcutManager sInstance;
- private static final Object sInstanceLock = new Object();
public static DeepShortcutManager getInstance(Context context) {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new DeepShortcutManager(context.getApplicationContext());
- }
- return sInstance;
+ if (sInstance == null) {
+ sInstance = new DeepShortcutManager(context.getApplicationContext());
}
+ return sInstance;
}
private final LauncherApps mLauncherApps;
- private boolean mWasLastCallSuccess;
private DeepShortcutManager(Context context) {
mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
}
- public boolean wasLastCallSuccess() {
- return mWasLastCallSuccess;
- }
-
/**
* Queries for the shortcuts with the package name and provided ids.
*
* This method is intended to get the full details for shortcuts when they are added or updated,
* because we only get "key" fields in onShortcutsChanged().
*/
- public List<ShortcutInfo> queryForFullDetails(String packageName,
+ public QueryResult queryForFullDetails(String packageName,
List<String> shortcutIds, UserHandle user) {
return query(FLAG_GET_ALL, packageName, null, shortcutIds, user);
}
@@ -80,9 +72,9 @@
* Gets all the manifest and dynamic shortcuts associated with the given package and user,
* to be displayed in the shortcuts container on long press.
*/
- public List<ShortcutInfo> queryForShortcutsContainer(@Nullable ComponentName activity,
+ public QueryResult queryForShortcutsContainer(@Nullable ComponentName activity,
UserHandle user) {
- if (activity == null) return Collections.EMPTY_LIST;
+ if (activity == null) return QueryResult.FAILURE;
return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
activity.getPackageName(), activity, null, user);
}
@@ -99,10 +91,8 @@
pinnedIds.remove(id);
try {
mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
- mWasLastCallSuccess = true;
} catch (SecurityException|IllegalStateException e) {
Log.w(TAG, "Failed to unpin shortcut", e);
- mWasLastCallSuccess = false;
}
}
@@ -118,10 +108,8 @@
pinnedIds.add(id);
try {
mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
- mWasLastCallSuccess = true;
} catch (SecurityException|IllegalStateException e) {
Log.w(TAG, "Failed to pin shortcut", e);
- mWasLastCallSuccess = false;
}
}
@@ -130,23 +118,18 @@
try {
mLauncherApps.startShortcut(packageName, id, sourceBounds,
startActivityOptions, user);
- mWasLastCallSuccess = true;
} catch (SecurityException|IllegalStateException e) {
Log.e(TAG, "Failed to start shortcut", e);
- mWasLastCallSuccess = false;
}
}
public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) {
try {
- Drawable icon = mLauncherApps.getShortcutIconDrawable(shortcutInfo, density);
- mWasLastCallSuccess = true;
- return icon;
+ return mLauncherApps.getShortcutIconDrawable(shortcutInfo, density);
} catch (SecurityException|IllegalStateException e) {
Log.e(TAG, "Failed to get shortcut icon", e);
- mWasLastCallSuccess = false;
+ return null;
}
- return null;
}
/**
@@ -154,20 +137,20 @@
*
* If packageName is null, returns all pinned shortcuts regardless of package.
*/
- public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
+ public QueryResult queryForPinnedShortcuts(String packageName, UserHandle user) {
return queryForPinnedShortcuts(packageName, null, user);
}
- public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
- List<String> shortcutIds, UserHandle user) {
+ public QueryResult queryForPinnedShortcuts(String packageName, List<String> shortcutIds,
+ UserHandle user) {
return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, shortcutIds, user);
}
- public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
+ public QueryResult queryForAllShortcuts(UserHandle user) {
return query(FLAG_GET_ALL, null, null, null, user);
}
- private List<String> extractIds(List<ShortcutInfo> shortcuts) {
+ private static List<String> extractIds(List<ShortcutInfo> shortcuts) {
List<String> shortcutIds = new ArrayList<>(shortcuts.size());
for (ShortcutInfo shortcut : shortcuts) {
shortcutIds.add(shortcut.getId());
@@ -181,8 +164,8 @@
*
* TODO: Use the cache to optimize this so we don't make an RPC every time.
*/
- private List<ShortcutInfo> query(int flags, String packageName,
- ComponentName activity, List<String> shortcutIds, UserHandle user) {
+ private QueryResult query(int flags, String packageName, ComponentName activity,
+ List<String> shortcutIds, UserHandle user) {
ShortcutQuery q = new ShortcutQuery();
q.setQueryFlags(flags);
if (packageName != null) {
@@ -190,18 +173,12 @@
q.setActivity(activity);
q.setShortcutIds(shortcutIds);
}
- List<ShortcutInfo> shortcutInfos = null;
try {
- shortcutInfos = mLauncherApps.getShortcuts(q, user);
- mWasLastCallSuccess = true;
+ return new QueryResult(mLauncherApps.getShortcuts(q, user));
} catch (SecurityException|IllegalStateException e) {
Log.e(TAG, "Failed to query for shortcuts", e);
- mWasLastCallSuccess = false;
+ return QueryResult.FAILURE;
}
- if (shortcutInfos == null) {
- return Collections.EMPTY_LIST;
- }
- return shortcutInfos;
}
public boolean hasHostPermission() {
@@ -212,4 +189,25 @@
}
return false;
}
+
+ public static class QueryResult extends ArrayList<ShortcutInfo> {
+
+ static QueryResult FAILURE = new QueryResult();
+
+ private final boolean mWasSuccess;
+
+ QueryResult(List<ShortcutInfo> result) {
+ super(result == null ? Collections.emptyList() : result);
+ mWasSuccess = true;
+ }
+
+ QueryResult() {
+ mWasSuccess = false;
+ }
+
+
+ public boolean wasSuccess() {
+ return mWasSuccess;
+ }
+ }
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
index 60f12d8..d7bb293 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -17,7 +17,8 @@
package com.android.launcher3.uioverrides;
import android.content.Context;
-import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+
+import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
public class TogglableFlag extends BaseTogglableFlag {
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 467ae02..6d9ed88 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -96,7 +96,8 @@
public static void resetPendingActivityResults(Launcher launcher, int requestCode) { }
- public static void clearSwipeSharedState(boolean finishAnimation) {}
+ /** 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/Android.mk b/tests/Android.mk
index 0c41241..31a9960 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -27,7 +27,7 @@
ifneq (,$(wildcard frameworks/base))
else
- LOCAL_STATIC_JAVA_LIBRARIES += libSharedSystemUI
+ LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib
LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
../src/com/android/launcher3/ResourceUtils.java \
@@ -36,7 +36,7 @@
endif
LOCAL_MODULE := ub-launcher-aosp-tapl
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index c6f55a7..56eca6d 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -62,6 +62,12 @@
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
+ <activity android:name="com.android.launcher3.testcomponent.CustomShortcutConfigActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_SHORTCUT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
<activity
android:name="com.android.launcher3.testcomponent.RequestPinItemActivity"
android:icon="@drawable/test_drawable_pin_item"
@@ -73,8 +79,12 @@
</intent-filter>
</activity>
+ <service
+ android:name="com.android.launcher3.testcomponent.ListViewService"
+ android:permission="android.permission.BIND_REMOTEVIEWS" />
+
<provider
- android:name="com.android.launcher3.testcomponent.TestCommandReceiver"
+ android:name="com.android.launcher3.testcomponent.TestCommandProvider"
android:authorities="${packageName}.commands"
android:exported="true"/>
diff --git a/tests/res/layout/test_layout_widget_list.xml b/tests/res/layout/test_layout_widget_list.xml
new file mode 100644
index 0000000..0152040
--- /dev/null
+++ b/tests/res/layout/test_layout_widget_list.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FFFFFF">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#FF0000FF"
+ android:id="@android:id/text1"
+ android:padding="10dp" />
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:id="@android:id/list" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 7d60ad6..7029ad5 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -1,29 +1,5 @@
package com.android.launcher3.model;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.database.MatrixCursor;
-import android.graphics.Bitmap;
-import android.os.Process;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.icons.BitmapInfo;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
import static com.android.launcher3.LauncherSettings.Favorites.CELLY;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
@@ -32,6 +8,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.ICON;
import static com.android.launcher3.LauncherSettings.Favorites.ICON_PACKAGE;
import static com.android.launcher3.LauncherSettings.Favorites.ICON_RESOURCE;
+import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -40,15 +17,41 @@
import static com.android.launcher3.LauncherSettings.Favorites.SCREEN;
import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
import static com.android.launcher3.LauncherSettings.Favorites._ID;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.os.Process;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.util.PackageManagerHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Tests for {@link LoaderCursor}
*/
@@ -62,7 +65,7 @@
private MatrixCursor mCursor;
private InvariantDeviceProfile mIDP;
private Context mContext;
- private LauncherAppsCompat mLauncherApps;
+ private LauncherApps mLauncherApps;
private LoaderCursor mLoaderCursor;
@@ -81,7 +84,7 @@
when(mMockApp.getIconCache()).thenReturn(mMockIconCache);
when(mMockApp.getInvariantDeviceProfile()).thenReturn(mIDP);
when(mMockApp.getContext()).thenReturn(mContext);
- mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+ mLauncherApps = mContext.getSystemService(LauncherApps.class);
mLoaderCursor = new LoaderCursor(mCursor, mMockApp);
mLoaderCursor.allUsers.put(0, Process.myUserHandle());
@@ -115,7 +118,7 @@
WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
new Intent().setComponent(cn), false /* allowMissingTarget */, true);
assertNotNull(info);
- assertTrue(Utilities.isLauncherAppTarget(info.intent));
+ assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
}
@Test
@@ -127,7 +130,7 @@
WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
new Intent().setComponent(cn), true /* allowMissingTarget */, true);
assertNotNull(info);
- assertTrue(Utilities.isLauncherAppTarget(info.intent));
+ assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
}
@Test
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 6fa8d62..27990f4 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -1,8 +1,11 @@
package com.android.launcher3.provider;
+import static org.junit.Assert.assertEquals;
+
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -13,8 +16,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertEquals;
-
/**
* Tests for {@link RestoreDbTask}
*/
@@ -82,7 +83,7 @@
private final long mProfileId;
MyDatabaseHelper(long profileId) {
- super(InstrumentationRegistry.getContext(), null, null);
+ super(InstrumentationRegistry.getContext(), null);
mProfileId = profileId;
}
diff --git a/tests/src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java b/tests/src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java
new file mode 100644
index 0000000..b673faa
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java
@@ -0,0 +1,66 @@
+/*
+ * 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.testcomponent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+import com.android.launcher3.R;
+
+import java.util.UUID;
+
+/**
+ * A custom shortcut is a 1x1 widget that launches a specific intent when user tap on it.
+ * Custom shortcuts are replaced by deep shortcuts after api 25.
+ */
+public class CustomShortcutConfigActivity extends BaseTestingActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent launchIntent = new Intent(this, BaseTestingActivity.class)
+ .setAction("com.android.launcher3.intent.action.test_shortcut");
+ Intent shortcutIntent = createShortcutResultIntent(
+ this, UUID.randomUUID().toString(), "Shortcut",
+ R.drawable.ic_widget, launchIntent);
+ setResult(RESULT_OK, shortcutIntent);
+ finish();
+ }
+
+ private static Intent createShortcutResultIntent(
+ Context context, String uniqueId, String name, int iconId, Intent launchIntent) {
+ ShortcutInfo shortcutInfo =
+ createShortcutInfo(context, uniqueId, name, iconId, launchIntent);
+ ShortcutManager sm = context.getSystemService(ShortcutManager.class);
+ return sm.createShortcutResultIntent(shortcutInfo);
+ }
+
+ private static ShortcutInfo createShortcutInfo(
+ Context context, String uniqueId, String name, int iconId, Intent launchIntent) {
+ return new ShortcutInfo.Builder(context, uniqueId)
+ .setShortLabel(name)
+ .setLongLabel(name)
+ .setIcon(Icon.createWithResource(context, iconId))
+ .setIntent(launchIntent)
+ .build();
+ }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/ListViewService.java b/tests/src/com/android/launcher3/testcomponent/ListViewService.java
new file mode 100644
index 0000000..3da20e0
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/ListViewService.java
@@ -0,0 +1,95 @@
+/*
+ * 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.testcomponent;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+public class ListViewService extends RemoteViewsService {
+
+ public static IBinder sBinderForTest;
+
+ @Override
+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
+ return new SimpleViewsFactory();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return sBinderForTest != null ? sBinderForTest : super.onBind(intent);
+ }
+
+ public static class SimpleViewsFactory implements RemoteViewsFactory {
+
+ public int viewCount = 0;
+
+ @Override
+ public void onCreate() { }
+
+ @Override
+ public void onDataSetChanged() { }
+
+ @Override
+ public void onDestroy() { }
+
+ @Override
+ public int getCount() {
+ return viewCount;
+ }
+
+ @Override
+ public RemoteViews getViewAt(int i) {
+ RemoteViews views = new RemoteViews("android", android.R.layout.simple_list_item_1);
+ views.setTextViewText(android.R.id.text1, getLabel(i));
+ return views;
+ }
+
+ public String getLabel(int i) {
+ return "Item " + i;
+ }
+
+ @Override
+ public RemoteViews getLoadingView() {
+ return null;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public long getItemId(int i) {
+ return i;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return false;
+ }
+
+ public IBinder toBinder() {
+ return new RemoteViewsService() {
+ @Override
+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
+ return SimpleViewsFactory.this;
+ }
+ }.onBind(new Intent("dummy_intent"));
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandProvider.java b/tests/src/com/android/launcher3/testcomponent/TestCommandProvider.java
new file mode 100644
index 0000000..f9981a9
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/TestCommandProvider.java
@@ -0,0 +1,131 @@
+/*
+ * 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.testcomponent;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DONT_KILL_APP;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+
+import static com.android.launcher3.testcomponent.TestCommandReceiver.DISABLE_TEST_LAUNCHER;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.ENABLE_TEST_LAUNCHER;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.KILL_PROCESS;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Base64;
+
+import com.android.launcher3.tapl.TestHelpers;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+public class TestCommandProvider extends ContentProvider {
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ switch (method) {
+ case ENABLE_TEST_LAUNCHER: {
+ getContext().getPackageManager().setComponentEnabledSetting(
+ new ComponentName(getContext(), TestLauncherActivity.class),
+ COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+ return null;
+ }
+ case DISABLE_TEST_LAUNCHER: {
+ getContext().getPackageManager().setComponentEnabledSetting(
+ new ComponentName(getContext(), TestLauncherActivity.class),
+ COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
+ return null;
+ }
+ case KILL_PROCESS: {
+ ((ActivityManager) getContext().getSystemService(Activity.ACTIVITY_SERVICE))
+ .killBackgroundProcesses(arg);
+ return null;
+ }
+
+ case GET_SYSTEM_HEALTH_MESSAGE: {
+ final Bundle response = new Bundle();
+ response.putString("result",
+ TestHelpers.getSystemHealthMessage(getContext(), Long.parseLong(arg)));
+ return response;
+ }
+
+ case SET_LIST_VIEW_SERVICE_BINDER: {
+ ListViewService.sBinderForTest = extras.getBinder(EXTRA_VALUE);
+ return null;
+ }
+ }
+ return super.call(method, arg, extras);
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ String path = Base64.encodeToString(uri.getPath().getBytes(),
+ Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
+ File file = new File(getContext().getCacheDir(), path);
+ if (!file.exists()) {
+ // Create an empty file so that we can pass its descriptor
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ }
+ }
+
+ return ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+ }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
index 6a6916e..eb6c3ed 100644
--- a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
+++ b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
@@ -15,125 +15,36 @@
*/
package com.android.launcher3.testcomponent;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.DONT_KILL_APP;
-import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
-
-import android.app.Activity;
-import android.app.ActivityManager;
import android.app.Instrumentation;
-import android.content.ComponentName;
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-import android.util.Base64;
import androidx.test.InstrumentationRegistry;
-import com.android.launcher3.tapl.TestHelpers;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
/**
* Content provider to receive commands from tests
*/
-public class TestCommandReceiver extends ContentProvider {
+public class TestCommandReceiver {
public static final String ENABLE_TEST_LAUNCHER = "enable-test-launcher";
public static final String DISABLE_TEST_LAUNCHER = "disable-test-launcher";
public static final String KILL_PROCESS = "kill-process";
public static final String GET_SYSTEM_HEALTH_MESSAGE = "get-system-health-message";
+ public static final String SET_LIST_VIEW_SERVICE_BINDER = "set-list-view-service-binder";
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- throw new UnsupportedOperationException("unimplemented mock method");
- }
-
- @Override
- public String getType(Uri uri) {
- throw new UnsupportedOperationException("unimplemented mock method");
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- throw new UnsupportedOperationException("unimplemented mock method");
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- throw new UnsupportedOperationException("unimplemented mock method");
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- throw new UnsupportedOperationException("unimplemented mock method");
- }
-
- @Override
- public Bundle call(String method, String arg, Bundle extras) {
- switch (method) {
- case ENABLE_TEST_LAUNCHER: {
- getContext().getPackageManager().setComponentEnabledSetting(
- new ComponentName(getContext(), TestLauncherActivity.class),
- COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
- return null;
- }
- case DISABLE_TEST_LAUNCHER: {
- getContext().getPackageManager().setComponentEnabledSetting(
- new ComponentName(getContext(), TestLauncherActivity.class),
- COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
- return null;
- }
- case KILL_PROCESS: {
- ((ActivityManager) getContext().getSystemService(Activity.ACTIVITY_SERVICE)).
- killBackgroundProcesses(arg);
- return null;
- }
-
- case GET_SYSTEM_HEALTH_MESSAGE: {
- final Bundle response = new Bundle();
- response.putString("result", TestHelpers.getSystemHealthMessage(getContext()));
- return response;
- }
- }
- return super.call(method, arg, extras);
- }
+ public static final String EXTRA_VALUE = "value";
public static Bundle callCommand(String command) {
return callCommand(command, null);
}
public static Bundle callCommand(String command, String arg) {
- Instrumentation inst = InstrumentationRegistry.getInstrumentation();
- Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands");
- return inst.getTargetContext().getContentResolver().call(uri, command, arg, null);
+ return callCommand(command, arg, null);
}
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- String path = Base64.encodeToString(uri.getPath().getBytes(),
- Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
- File file = new File(getContext().getCacheDir(), path);
- if (!file.exists()) {
- // Create an empty file so that we can pass its descriptor
- try {
- file.createNewFile();
- } catch (IOException e) {
- }
- }
-
- return ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+ public static Bundle callCommand(String command, String arg, Bundle extras) {
+ Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands");
+ return inst.getTargetContext().getContentResolver().call(uri, command, arg, extras);
}
}
diff --git a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
index e042357..f209fae 100644
--- a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
@@ -25,6 +25,10 @@
import android.util.Log;
import android.view.ViewConfiguration;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.launcher3.testcomponent.TouchEventGenerator;
import org.junit.Before;
@@ -33,10 +37,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SwipeDetectorTest {
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index dc890bb..62989a3 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -17,8 +17,10 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
import static com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static org.junit.Assert.assertTrue;
@@ -26,10 +28,13 @@
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.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.RemoteException;
@@ -41,25 +46,26 @@
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
+import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.LauncherActivityRule;
import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.util.rule.TestStabilityRule;
import org.junit.After;
import org.junit.Before;
@@ -89,10 +95,9 @@
public static final long DEFAULT_UI_TIMEOUT = 60000; // b/136278866
private static final String TAG = "AbstractLauncherUiTest";
- protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
+ protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
- protected final LauncherInstrumentation mLauncher =
- new LauncherInstrumentation(getInstrumentation());
+ protected final LauncherInstrumentation mLauncher = new LauncherInstrumentation();
protected Context mTargetContext;
protected String mTargetPackage;
@@ -104,8 +109,9 @@
}
if (TestHelpers.isInLauncherProcess()) {
Utilities.enableRunningInTestHarnessForTests();
- mLauncher.setSystemHealthSupplier(() -> TestCommandReceiver.callCommand(
- TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE).getString("result"));
+ mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand(
+ TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()).
+ getString("result"));
mLauncher.setOnSettledStateAction(
containerType -> executeOnLauncher(
launcher ->
@@ -155,7 +161,8 @@
@Rule
public TestRule mOrderSensitiveRules = RuleChain.
- outerRule(mActivityMonitor).
+ outerRule(new TestStabilityRule()).
+ around(mActivityMonitor).
around(getRulesInsideActivityMonitor());
public UiDevice getDevice() {
@@ -169,15 +176,13 @@
mTargetContext = InstrumentationRegistry.getTargetContext();
mTargetPackage = mTargetContext.getPackageName();
- // Unlock the phone
- mDevice.executeShellCommand("input keyevent 82");
}
@After
public void verifyLauncherState() {
try {
// Limits UI tests affecting tests running after them.
- waitForModelLoaded();
+ mLauncher.waitForLauncherInitialized();
} catch (Throwable t) {
Log.e(TAG,
"Couldn't deinit after a test, exiting tests, see logs for failures that "
@@ -216,14 +221,36 @@
} catch (Throwable t) {
throw new IllegalArgumentException(t);
}
- waitForModelLoaded();
+ mLauncher.waitForLauncherInitialized();
}
- protected void waitForModelLoaded() {
- waitForLauncherCondition("Launcher model didn't load", launcher -> {
- final LauncherModel model = LauncherAppState.getInstance(mTargetContext).getModel();
- return model.getCallback() == null || model.isModelLoaded();
- });
+ /**
+ * Adds {@param item} on the homescreen on the 0th screen
+ */
+ protected void addItemToScreen(ItemInfo item) {
+ ContentResolver resolver = mTargetContext.getContentResolver();
+ int screenId = FIRST_SCREEN_ID;
+ // Update the screen id counter for the provider.
+ LauncherSettings.Settings.call(resolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+
+ if (screenId > FIRST_SCREEN_ID) {
+ screenId = FIRST_SCREEN_ID;
+ }
+
+ // Insert the item
+ ContentWriter writer = new ContentWriter(mTargetContext);
+ item.id = LauncherSettings.Settings.call(
+ resolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+ .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+ item.screenId = screenId;
+ item.onAddToDatabase(writer);
+ writer.put(LauncherSettings.Favorites._ID, item.id);
+ resolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext));
+ resetLoaderState();
+
+ // Launch the home activity
+ mDevice.pressHome();
+ mLauncher.waitForLauncherInitialized();
}
/**
@@ -308,9 +335,8 @@
}
protected LauncherActivityInfo getSettingsApp() {
- return LauncherAppsCompat.getInstance(mTargetContext)
- .getActivityList("com.android.settings",
- Process.myUserHandle()).get(0);
+ return mTargetContext.getSystemService(LauncherApps.class)
+ .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
}
/**
@@ -343,14 +369,14 @@
}
}
- protected void startAppFast(String packageName) {
+ public static void startAppFast(String packageName) {
startIntent(
getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
packageName),
By.pkg(packageName).depth(0));
}
- protected void startTestActivity(int activityNumber) {
+ public static void startTestActivity(int activityNumber) {
final String packageName = getAppPackageName();
final Intent intent = getInstrumentation().getContext().getPackageManager().
getLaunchIntentForPackage(packageName);
@@ -359,19 +385,25 @@
startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber));
}
- private void startIntent(Intent intent, BySelector selector) {
+ private static void startIntent(Intent intent, BySelector selector) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
- mDevice.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
+ UiDevice.getInstance(getInstrumentation())
+ .wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
}
- public static String resolveSystemApp(String category) {
+ public static ActivityInfo resolveSystemAppInfo(String category) {
return getInstrumentation().getContext().getPackageManager().resolveActivity(
new Intent(Intent.ACTION_MAIN).addCategory(category),
PackageManager.MATCH_SYSTEM_ONLY).
- activityInfo.packageName;
+ activityInfo;
+ }
+
+
+ public static String resolveSystemApp(String category) {
+ return resolveSystemAppInfo(category).packageName;
}
protected void closeLauncherActivity() {
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
index a76b4a4..1d89d6e 100644
--- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -30,7 +30,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.testcomponent.TestCommandProvider;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.rule.ShellCommandRule;
@@ -63,7 +63,7 @@
PackageManager pm = mTargetContext.getPackageManager();
ProviderInfo pi = pm.getProviderInfo(new ComponentName(mContext,
- TestCommandReceiver.class), 0);
+ TestCommandProvider.class), 0);
mAuthority = pi.authority;
}
@@ -73,7 +73,6 @@
// Launch the home activity
mDevice.pressHome();
- waitForModelLoaded();
mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
}
@@ -89,7 +88,6 @@
// Launch the home activity
mDevice.pressHome();
- waitForModelLoaded();
// Verify widget present
assertTrue("Widget is not present",
@@ -106,7 +104,6 @@
// Launch the home activity
mDevice.pressHome();
- waitForModelLoaded();
mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index c2a3c1c..5e87612 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -34,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;
@@ -340,6 +339,23 @@
}
}
+ /**
+ * Test dragging a custom shortcut to the workspace and launch it.
+ *
+ * A custom shortcut is a 1x1 widget that launches a specific intent when user tap on it.
+ * Custom shortcuts are replaced by deep shortcuts after api 25.
+ */
+ @Test
+ @Ignore("Temporarily disabled to unblock merging to master")
+ @PortraitLandscape
+ public void testDragCustomShortcut() {
+ mLauncher.getWorkspace().openAllWidgets()
+ .getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
+ .dragToWorkspace();
+ mLauncher.getWorkspace().getWorkspaceAppIcon("Shortcut")
+ .launch(getAppPackageName());
+ }
+
public static String getAppPackageName() {
return getInstrumentation().getContext().getPackageName();
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 3f35a3a..e1b3ede 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -41,7 +41,6 @@
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,7 +52,8 @@
@RunWith(AndroidJUnit4.class)
public class AddConfigWidgetTest extends AbstractLauncherUiTest {
- @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+ @Rule
+ public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
private LauncherAppWidgetProviderInfo mWidgetInfo;
private AppWidgetManager mAppWidgetManager;
@@ -70,14 +70,12 @@
@Test
@PortraitLandscape
- @org.junit.Ignore
public void testWidgetConfig() throws Throwable {
runTest(true);
}
@Test
@PortraitLandscape
- @org.junit.Ignore
public void testConfigCancelled() throws Throwable {
runTest(false);
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 1edce22..b8ca5de 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -30,7 +30,6 @@
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,11 +41,11 @@
@RunWith(AndroidJUnit4.class)
public class AddWidgetTest extends AbstractLauncherUiTest {
- @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+ @Rule
+ public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
@Test
@PortraitLandscape
- @org.junit.Ignore
public void testDragIcon() throws Throwable {
clearHomescreen();
mDevice.pressHome();
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index e6348d9..3d691da 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -15,7 +15,9 @@
*/
package com.android.launcher3.ui.widget;
-import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -25,6 +27,7 @@
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
@@ -39,15 +42,12 @@
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.PendingAddWidgetInfo;
-import com.android.launcher3.widget.WidgetHostViewLoader;
import org.junit.After;
import org.junit.Before;
@@ -57,7 +57,6 @@
import java.util.HashSet;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Tests for bind widget flow.
@@ -72,7 +71,6 @@
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
private ContentResolver mResolver;
- private AppWidgetManagerCompat mWidgetManager;
// Objects created during test, which should be cleaned up in the end.
private Cursor mCursor;
@@ -85,7 +83,6 @@
super.setUp();
mResolver = mTargetContext.getContentResolver();
- mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext);
// Clear all existing data
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
@@ -108,7 +105,7 @@
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
LauncherAppWidgetInfo item = createWidgetInfo(info, true);
- setupContents(item);
+ addItemToScreen(item);
verifyWidgetPresent(info);
}
@@ -117,7 +114,7 @@
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
LauncherAppWidgetInfo item = createWidgetInfo(info, true);
- setupContents(item);
+ addItemToScreen(item);
verifyWidgetPresent(info);
}
@@ -127,7 +124,7 @@
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
item.appWidgetId = -33;
- setupContents(item);
+ addItemToScreen(item);
final Workspace workspace = mLauncher.getWorkspace();
// Item deleted from db
@@ -148,7 +145,7 @@
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
- setupContents(item);
+ addItemToScreen(item);
verifyWidgetPresent(info);
}
@@ -161,7 +158,7 @@
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
- setupContents(item);
+ addItemToScreen(item);
verifyPendingWidgetPresent();
// Item deleted from db
@@ -183,7 +180,7 @@
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
- setupContents(item);
+ addItemToScreen(item);
assertTrue("Pending widget exists",
mLauncher.getWorkspace().tryGetPendingWidget(0) == null);
@@ -202,7 +199,7 @@
| LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
- setupContents(item);
+ addItemToScreen(item);
verifyPendingWidgetPresent();
// Verify item still exists in db
@@ -230,7 +227,7 @@
PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
mSessionId = installer.createSession(params);
- setupContents(item);
+ addItemToScreen(item);
verifyPendingWidgetPresent();
// Verify item still exists in db
@@ -245,35 +242,6 @@
& LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
}
- /**
- * Adds {@param item} on the homescreen on the 0th screen at 0,0, and verifies that the
- * widget class is displayed on the homescreen.
- */
- private void setupContents(LauncherAppWidgetInfo item) {
- int screenId = FIRST_SCREEN_ID;
- // Update the screen id counter for the provider.
- LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
-
- if (screenId > FIRST_SCREEN_ID) {
- screenId = FIRST_SCREEN_ID;
- }
-
- // Insert the item
- ContentWriter writer = new ContentWriter(mTargetContext);
- item.id = LauncherSettings.Settings.call(
- mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
- item.screenId = screenId;
- item.onAddToDatabase(writer);
- writer.put(LauncherSettings.Favorites._ID, item.id);
- mResolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext));
- resetLoaderState();
-
- // Launch the home activity
- mDevice.pressHome();
- waitForModelLoaded();
- }
-
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
assertTrue("Widget is not present",
mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
@@ -289,8 +257,10 @@
* @param bindWidget if true the info is bound and a valid widgetId is assigned to
* the LauncherAppWidgetInfo
*/
- private LauncherAppWidgetInfo createWidgetInfo(
+ public static LauncherAppWidgetInfo createWidgetInfo(
LauncherAppWidgetProviderInfo info, boolean bindWidget) {
+ Context targetContext = getTargetContext();
+
LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(
LauncherAppWidgetInfo.NO_ID, info.provider);
item.spanX = info.minSpanX;
@@ -308,11 +278,12 @@
pendingInfo.spanY = item.spanY;
pendingInfo.minSpanX = item.minSpanX;
pendingInfo.minSpanY = item.minSpanY;
- Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(mTargetContext, pendingInfo);
+ Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo);
- AppWidgetHost host = new LauncherAppWidgetHost(mTargetContext);
+ AppWidgetHost host = new LauncherAppWidgetHost(targetContext);
int widgetId = host.allocateAppWidgetId();
- if (!mWidgetManager.bindAppWidgetIdIfAllowed(widgetId, info, options)) {
+ if (!AppWidgetManagerCompat.getInstance(targetContext)
+ .bindAppWidgetIdIfAllowed(widgetId, info, options)) {
host.deleteAppWidgetId(widgetId);
throw new IllegalArgumentException("Unable to bind widget id");
}
@@ -331,7 +302,7 @@
Set<String> activePackage = getOnUiThread(() -> {
Set<String> packages = new HashSet<>();
- PackageInstallerCompat.getInstance(mTargetContext).updateAndGetActiveSessionCache()
+ PackageInstallerCompat.getInstance(mTargetContext).getActiveSessions()
.keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName));
return packages;
});
diff --git a/tests/src/com/android/launcher3/util/Condition.java b/tests/src/com/android/launcher3/util/Condition.java
index b564a1a..d85dd3a 100644
--- a/tests/src/com/android/launcher3/util/Condition.java
+++ b/tests/src/com/android/launcher3/util/Condition.java
@@ -1,8 +1,8 @@
package com.android.launcher3.util;
-import androidx.test.uiautomator.UiObject2;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import com.android.launcher3.MainThreadExecutor;
+import androidx.test.uiautomator.UiObject2;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -16,7 +16,7 @@
* Converts the condition to be run on UI thread.
*/
static Condition runOnUiThread(final Condition condition) {
- final MainThreadExecutor executor = new MainThreadExecutor();
+ final LooperExecutor executor = MAIN_EXECUTOR;
return () -> {
final AtomicBoolean value = new AtomicBoolean(false);
final Throwable[] exceptions = new Throwable[1];
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
index 0235f95..ed2ec7b 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
@@ -16,14 +16,12 @@
package com.android.launcher3.util;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER_POSTFIX;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT_POSTFIX;
+import static com.android.launcher3.util.Executors.createAndStartNewLooper;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.os.Handler;
-import android.os.HandlerThread;
import android.util.Log;
import java.util.ArrayList;
@@ -64,17 +62,30 @@
*
* When we register event XXX:enter, we hold all other events until we register XXX:exit.
*/
-public class RaceConditionReproducer implements RaceConditionTracker.EventProcessor {
+public class RaceConditionReproducer {
private static final String TAG = "RaceConditionReproducer";
+
+ private static final boolean ENTER = true;
+ private static final boolean EXIT = false;
+ private static final String ENTER_POSTFIX = "enter";
+ private static final String EXIT_POSTFIX = "exit";
+
private static final long SHORT_TIMEOUT_MS = 2000;
private static final long LONG_TIMEOUT_MS = 60000;
// Handler used to resume postponed events.
- private static final Handler POSTPONED_EVENT_RESUME_HANDLER = createEventResumeHandler();
+ private static final Handler POSTPONED_EVENT_RESUME_HANDLER =
+ new Handler(createAndStartNewLooper("RaceConditionEventResumer"));
- private static Handler createEventResumeHandler() {
- final HandlerThread thread = new HandlerThread("RaceConditionEventResumer");
- thread.start();
- return new Handler(thread.getLooper());
+ public static String enterExitEvt(String eventName, boolean isEnter) {
+ return eventName + ":" + (isEnter ? ENTER_POSTFIX : EXIT_POSTFIX);
+ }
+
+ public static String enterEvt(String eventName) {
+ return enterExitEvt(eventName, ENTER);
+ }
+
+ public static String exitEvt(String eventName) {
+ return enterExitEvt(eventName, EXIT);
}
/**
@@ -211,7 +222,8 @@
parseReproString(mReproString) : generateSequenceToFollowLocked();
Log.e(TAG, "---- Start of iteration; state:\n" + dumpStateLocked());
checkIfCompletedSequenceToFollowLocked();
- RaceConditionTracker.setEventProcessor(this);
+
+ TraceHelperForTest.setRaceConditionReproducer(this);
}
/**
@@ -220,7 +232,8 @@
* Returns whether we need more iterations.
*/
public synchronized boolean finishIteration() {
- RaceConditionTracker.setEventProcessor(null);
+ TraceHelperForTest.setRaceConditionReproducer(null);
+
runResumeAllEventsCallbackLocked();
assertTrue("Non-empty postponed events", mPostponedEvents.isEmpty());
assertTrue("Last registered event is :enter", lastEventAsEnter() == null);
@@ -245,7 +258,6 @@
/**
* Called when the app issues an event.
*/
- @Override
public void onEvent(String event) {
final Semaphore waitObject = tryRegisterEvent(event);
if (waitObject != null) {
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
index 3fc268e..59f2173 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
@@ -22,6 +22,8 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,17 +39,29 @@
return res;
}
- private static void run3_3_TestAction() throws InterruptedException {
+ RaceConditionReproducer eventProcessor;
+
+ @Before
+ public void setup() {
+ eventProcessor = new RaceConditionReproducer();
+ }
+
+ @After
+ public void tearDown() {
+ TraceHelperForTest.cleanup();
+ }
+
+ private void run3_3_TestAction() throws InterruptedException {
Thread tb = new Thread(() -> {
- RaceConditionTracker.onEvent("B1");
- RaceConditionTracker.onEvent("B2");
- RaceConditionTracker.onEvent("B3");
+ eventProcessor.onEvent("B1");
+ eventProcessor.onEvent("B2");
+ eventProcessor.onEvent("B3");
});
tb.start();
- RaceConditionTracker.onEvent("A1");
- RaceConditionTracker.onEvent("A2");
- RaceConditionTracker.onEvent("A3");
+ eventProcessor.onEvent("A1");
+ eventProcessor.onEvent("A2");
+ eventProcessor.onEvent("A3");
tb.join();
}
@@ -56,7 +70,6 @@
@Ignore // The test is too long for continuous testing.
// 2 threads, 3 events each.
public void test3_3() throws Exception {
- final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
boolean sawTheValidSequence = false;
for (; ; ) {
@@ -80,25 +93,24 @@
@Ignore // The test is too long for continuous testing.
// 2 threads, 3 events, including enter-exit pairs each.
public void test3_3_enter_exit() throws Exception {
- final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
boolean sawTheValidSequence = false;
for (; ; ) {
eventProcessor.startIteration();
Thread tb = new Thread(() -> {
- RaceConditionTracker.onEvent("B1:enter");
- RaceConditionTracker.onEvent("B1:exit");
- RaceConditionTracker.onEvent("B2");
- RaceConditionTracker.onEvent("B3:enter");
- RaceConditionTracker.onEvent("B3:exit");
+ eventProcessor.onEvent("B1:enter");
+ eventProcessor.onEvent("B1:exit");
+ eventProcessor.onEvent("B2");
+ eventProcessor.onEvent("B3:enter");
+ eventProcessor.onEvent("B3:exit");
});
tb.start();
- RaceConditionTracker.onEvent("A1");
- RaceConditionTracker.onEvent("A2:enter");
- RaceConditionTracker.onEvent("A2:exit");
- RaceConditionTracker.onEvent("A3:enter");
- RaceConditionTracker.onEvent("A3:exit");
+ eventProcessor.onEvent("A1");
+ eventProcessor.onEvent("A2:enter");
+ eventProcessor.onEvent("A2:exit");
+ eventProcessor.onEvent("A3:enter");
+ eventProcessor.onEvent("A3:exit");
tb.join();
final boolean needMoreIterations = eventProcessor.finishIteration();
@@ -119,9 +131,7 @@
@Test
// 2 threads, 3 events each; reproducing a particular event sequence.
public void test3_3_ReproMode() throws Exception {
- final RaceConditionReproducer eventProcessor = new RaceConditionReproducer(
- SOME_VALID_SEQUENCE_3_3);
-
+ eventProcessor = new RaceConditionReproducer(SOME_VALID_SEQUENCE_3_3);
eventProcessor.startIteration();
run3_3_TestAction();
assertTrue(!eventProcessor.finishIteration());
@@ -134,23 +144,21 @@
@Ignore // The test is too long for continuous testing.
// 2 threads with 2 events; 1 thread with 1 event.
public void test2_1_2() throws Exception {
- final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
-
for (; ; ) {
eventProcessor.startIteration();
Thread tb = new Thread(() -> {
- RaceConditionTracker.onEvent("B1");
- RaceConditionTracker.onEvent("B2");
+ eventProcessor.onEvent("B1");
+ eventProcessor.onEvent("B2");
});
tb.start();
Thread tc = new Thread(() -> {
- RaceConditionTracker.onEvent("C1");
+ eventProcessor.onEvent("C1");
});
tc.start();
- RaceConditionTracker.onEvent("A1");
- RaceConditionTracker.onEvent("A2");
+ eventProcessor.onEvent("A1");
+ eventProcessor.onEvent("A2");
tb.join();
tc.join();
@@ -167,28 +175,26 @@
@Ignore // The test is too long for continuous testing.
// 2 threads with 2 events; 1 thread with 1 event. Includes enter-exit pairs.
public void test2_1_2_enter_exit() throws Exception {
- final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
-
for (; ; ) {
eventProcessor.startIteration();
Thread tb = new Thread(() -> {
- RaceConditionTracker.onEvent("B1:enter");
- RaceConditionTracker.onEvent("B1:exit");
- RaceConditionTracker.onEvent("B2:enter");
- RaceConditionTracker.onEvent("B2:exit");
+ eventProcessor.onEvent("B1:enter");
+ eventProcessor.onEvent("B1:exit");
+ eventProcessor.onEvent("B2:enter");
+ eventProcessor.onEvent("B2:exit");
});
tb.start();
Thread tc = new Thread(() -> {
- RaceConditionTracker.onEvent("C1:enter");
- RaceConditionTracker.onEvent("C1:exit");
+ eventProcessor.onEvent("C1:enter");
+ eventProcessor.onEvent("C1:exit");
});
tc.start();
- RaceConditionTracker.onEvent("A1:enter");
- RaceConditionTracker.onEvent("A1:exit");
- RaceConditionTracker.onEvent("A2:enter");
- RaceConditionTracker.onEvent("A2:exit");
+ eventProcessor.onEvent("A1:enter");
+ eventProcessor.onEvent("A1:exit");
+ eventProcessor.onEvent("A2:enter");
+ eventProcessor.onEvent("A2:exit");
tb.join();
tc.join();
diff --git a/tests/src/com/android/launcher3/util/TraceHelperForTest.java b/tests/src/com/android/launcher3/util/TraceHelperForTest.java
new file mode 100644
index 0000000..f1c8a67
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/TraceHelperForTest.java
@@ -0,0 +1,116 @@
+/**
+ * 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 java.util.LinkedList;
+import java.util.function.IntConsumer;
+
+public class TraceHelperForTest extends TraceHelper {
+
+ private static final TraceHelperForTest INSTANCE_FOR_TEST = new TraceHelperForTest();
+
+ private final ThreadLocal<LinkedList<TraceInfo>> mStack =
+ ThreadLocal.withInitial(LinkedList::new);
+
+ private RaceConditionReproducer mRaceConditionReproducer;
+ private IntConsumer mFlagsChangeListener;
+
+ public static void setRaceConditionReproducer(RaceConditionReproducer reproducer) {
+ TraceHelper.INSTANCE = INSTANCE_FOR_TEST;
+ INSTANCE_FOR_TEST.mRaceConditionReproducer = reproducer;
+ }
+
+ public static void cleanup() {
+ INSTANCE_FOR_TEST.mRaceConditionReproducer = null;
+ INSTANCE_FOR_TEST.mFlagsChangeListener = null;
+ }
+
+ public static void setFlagsChangeListener(IntConsumer listener) {
+ TraceHelper.INSTANCE = INSTANCE_FOR_TEST;
+ INSTANCE_FOR_TEST.mFlagsChangeListener = listener;
+ }
+
+ private TraceHelperForTest() { }
+
+ @Override
+ public Object beginSection(String sectionName, int flags) {
+ LinkedList<TraceInfo> stack = mStack.get();
+ TraceInfo info = new TraceInfo(sectionName, flags);
+ stack.add(info);
+
+ if ((flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0
+ && mRaceConditionReproducer != null) {
+ mRaceConditionReproducer.onEvent(RaceConditionReproducer.enterEvt(sectionName));
+ }
+ updateBinderTracking(stack);
+
+ super.beginSection(sectionName, flags);
+ return info;
+ }
+
+ @Override
+ public void endSection(Object token) {
+ LinkedList<TraceInfo> stack = mStack.get();
+ if (stack.size() == 0) {
+ new Throwable().printStackTrace();
+ }
+ TraceInfo info = (TraceInfo) token;
+ stack.remove(info);
+ if ((info.flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0
+ && mRaceConditionReproducer != null) {
+ mRaceConditionReproducer.onEvent(RaceConditionReproducer.exitEvt(info.sectionName));
+ }
+ updateBinderTracking(stack);
+
+ super.endSection(token);
+ }
+
+ @Override
+ public Object beginFlagsOverride(int flags) {
+ LinkedList<TraceInfo> stack = mStack.get();
+ TraceInfo info = new TraceInfo(null, flags);
+ stack.add(info);
+ updateBinderTracking(stack);
+ super.beginFlagsOverride(flags);
+ return info;
+ }
+
+ @Override
+ public void endFlagsOverride(Object token) {
+ super.endFlagsOverride(token);
+ LinkedList<TraceInfo> stack = mStack.get();
+ TraceInfo info = (TraceInfo) token;
+ stack.remove(info);
+ updateBinderTracking(stack);
+ }
+
+ private void updateBinderTracking(LinkedList<TraceInfo> stack) {
+ if (mFlagsChangeListener != null) {
+ mFlagsChangeListener.accept(stack.stream()
+ .mapToInt(info -> info.flags).reduce(0, (a, b) -> a | b));
+ }
+ }
+
+ private static class TraceInfo {
+ public final String sectionName;
+ public final int flags;
+
+ TraceInfo(String sectionName, int flags) {
+ this.sectionName = sectionName;
+ this.flags = flags;
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index 62fe26d..6a6ec3e 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -16,16 +16,10 @@
package com.android.launcher3.util.rule;
import android.app.Activity;
-import android.app.Application;
-import android.app.Application.ActivityLifecycleCallbacks;
-import android.os.Bundle;
-
-import androidx.test.InstrumentationRegistry;
import com.android.launcher3.Launcher;
import com.android.launcher3.Workspace.ItemOperator;
-import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -34,17 +28,23 @@
/**
* Test rule to get the current Launcher activity.
*/
-public class LauncherActivityRule implements TestRule {
+public class LauncherActivityRule extends SimpleActivityRule<Launcher> {
- private Launcher mActivity;
+ public LauncherActivityRule() {
+ super(Launcher.class);
+ }
@Override
public Statement apply(Statement base, Description description) {
- return new MyStatement(base);
- }
- public Launcher getActivity() {
- return mActivity;
+ return new MyStatement(base) {
+ @Override
+ public void onActivityStarted(Activity activity) {
+ if (activity instanceof Launcher) {
+ ((Launcher) activity).getRotationHelper().forceAllowRotationForTesting(true);
+ }
+ }
+ };
}
public Callable<Boolean> itemExists(final ItemOperator op) {
@@ -56,62 +56,4 @@
return launcher.getWorkspace().getFirstMatch(op) != null;
};
}
-
- private class MyStatement extends Statement implements ActivityLifecycleCallbacks {
-
- private final Statement mBase;
-
- public MyStatement(Statement base) {
- mBase = base;
- }
-
- @Override
- public void evaluate() throws Throwable {
- Application app = (Application)
- InstrumentationRegistry.getTargetContext().getApplicationContext();
- app.registerActivityLifecycleCallbacks(this);
- try {
- mBase.evaluate();
- } finally {
- app.unregisterActivityLifecycleCallbacks(this);
- }
- }
-
- @Override
- public void onActivityCreated(Activity activity, Bundle bundle) {
- if (activity instanceof Launcher) {
- mActivity = (Launcher) activity;
- }
- }
-
- @Override
- public void onActivityStarted(Activity activity) {
- if (activity instanceof Launcher) {
- mActivity.getRotationHelper().forceAllowRotationForTesting(true);
- }
- }
-
- @Override
- public void onActivityResumed(Activity activity) {
- }
-
- @Override
- public void onActivityPaused(Activity activity) {
- }
-
- @Override
- public void onActivityStopped(Activity activity) {
- }
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
- }
-
- @Override
- public void onActivityDestroyed(Activity activity) {
- if (activity == mActivity) {
- mActivity = null;
- }
- }
- }
}
diff --git a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java
new file mode 100644
index 0000000..33a6cf9
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java
@@ -0,0 +1,104 @@
+/*
+ * 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.rule;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.os.Bundle;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Test rule to get the current activity.
+ */
+public class SimpleActivityRule<T extends Activity> implements TestRule {
+
+ private final Class<T> mClass;
+ private T mActivity;
+
+ public SimpleActivityRule(Class<T> clazz) {
+ mClass = clazz;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new MyStatement(base);
+ }
+
+ public T getActivity() {
+ return mActivity;
+ }
+
+ protected class MyStatement extends Statement implements ActivityLifecycleCallbacks {
+
+ private final Statement mBase;
+
+ public MyStatement(Statement base) {
+ mBase = base;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ Application app = (Application)
+ InstrumentationRegistry.getTargetContext().getApplicationContext();
+ app.registerActivityLifecycleCallbacks(this);
+ try {
+ mBase.evaluate();
+ } finally {
+ app.unregisterActivityLifecycleCallbacks(this);
+ }
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle bundle) {
+ if (activity != null && mClass.isInstance(activity)) {
+ mActivity = (T) activity;
+ }
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ if (activity == mActivity) {
+ mActivity = null;
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
new file mode 100644
index 0000000..69bf01d
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -0,0 +1,147 @@
+/*
+ * 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.rule;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class TestStabilityRule implements TestRule {
+ private static final String TAG = "TestStabilityRule";
+ private static final Pattern LAUNCHER_BUILD =
+ Pattern.compile("^("
+ + "(?<local>(BuildFromAndroidStudio|"
+ + "([0-9]+|[A-Z])-eng\\.[a-z]+\\.[0-9]+\\.[0-9]+))|"
+ + "(?<presubmit>([0-9]+|[A-Z])-P[0-9]+)|"
+ + "(?<postsubmit>([0-9]+|[A-Z])-[0-9]+)|"
+ + "(?<platform>[0-9]+|[A-Z])"
+ + ")$");
+ private static final Pattern PLATFORM_BUILD =
+ Pattern.compile("^("
+ + "(?<commandLine>eng\\.[a-z]+\\.[0-9]+\\.[0-9]+)|"
+ + "(?<presubmit>P[0-9]+)|"
+ + "(?<postsubmit>[0-9]+)"
+ + ")$");
+
+ public static final int LOCAL = 0x1;
+ public static final int UNBUNDLED_PRESUBMIT = 0x2;
+ public static final int UNBUNDLED_POSTSUBMIT = 0x4;
+ public static final int PLATFORM_PRESUBMIT = 0x8;
+ public static final int PLATFORM_POSTSUBMIT = 0x10;
+
+ private static final int RUN_FLAFOR = getRunFlavor();
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.METHOD)
+ public @interface Stability {
+ int flavors();
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ final Stability stability = description.getAnnotation(Stability.class);
+ if (stability != null) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ if ((stability.flavors() & RUN_FLAFOR) != 0) {
+ Log.d(TAG, "Running " + description.getDisplayName());
+ base.evaluate();
+ } else {
+ Log.d(TAG, "Skipping " + description.getDisplayName());
+ }
+ }
+ };
+ } else {
+ return base;
+ }
+ }
+
+ private static int getRunFlavor() {
+ final String launcherVersion;
+ try {
+ launcherVersion = getInstrumentation().
+ getContext().
+ getPackageManager().
+ getPackageInfo(
+ UiDevice.getInstance(getInstrumentation()).
+ getLauncherPackageName(),
+ 0).
+ versionName;
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ final Matcher launcherBuildMatcher = LAUNCHER_BUILD.matcher(launcherVersion);
+
+ if (!launcherBuildMatcher.find()) {
+ throw new AssertionError("Launcher build match not found");
+ }
+
+ final String platformVersion = Build.VERSION.INCREMENTAL;
+ final Matcher platformBuildMatcher = PLATFORM_BUILD.matcher(platformVersion);
+
+ if (!platformBuildMatcher.find()) {
+ throw new AssertionError("Platform build match not found");
+ }
+
+ Log.d(TAG, "Launcher: " + launcherVersion + ", platform: " + platformVersion);
+
+ final int runFlavor;
+
+ if (launcherBuildMatcher.group("local") != null && (
+ platformBuildMatcher.group("commandLine") != null ||
+ platformBuildMatcher.group("postsubmit") != null)) {
+ Log.d(TAG, "LOCAL RUN");
+ runFlavor = LOCAL;
+ } else if (launcherBuildMatcher.group("presubmit") != null
+ && platformBuildMatcher.group("postsubmit") != null) {
+ Log.d(TAG, "UNBUNDLED PRESUBMIT");
+ runFlavor = UNBUNDLED_PRESUBMIT;
+ } else if (launcherBuildMatcher.group("postsubmit") != null
+ && platformBuildMatcher.group("postsubmit") != null) {
+ Log.d(TAG, "UNBUNDLED POSTSUBMIT");
+ runFlavor = UNBUNDLED_POSTSUBMIT;
+ } else if (launcherBuildMatcher.group("platform") != null
+ && platformBuildMatcher.group("presubmit") != null) {
+ Log.d(TAG, "PLATFORM PRESUBMIT");
+ runFlavor = PLATFORM_PRESUBMIT;
+ } else if (launcherBuildMatcher.group("platform") != null
+ && platformBuildMatcher.group("postsubmit") != null) {
+ Log.d(TAG, "PLATFORM POSTSUBMIT");
+ runFlavor = PLATFORM_POSTSUBMIT;
+ } else {
+ throw new AssertionError("Unrecognized run flavor");
+ }
+
+ return runFlavor;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index f070280..e1e9b8d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -53,8 +53,8 @@
return LauncherInstrumentation.ContainerType.ALL_APPS;
}
- private boolean hasClickableIcon(
- UiObject2 allAppsContainer, UiObject2 appListRecycler, BySelector appIconSelector) {
+ private boolean hasClickableIcon(UiObject2 allAppsContainer, UiObject2 appListRecycler,
+ BySelector appIconSelector, int displayBottom) {
final UiObject2 icon = appListRecycler.findObject(appIconSelector);
if (icon == null) {
LauncherInstrumentation.log("hasClickableIcon: icon not visible");
@@ -66,6 +66,10 @@
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
return false;
}
+ if (iconBounds.bottom > displayBottom) {
+ LauncherInstrumentation.log("hasClickableIcon: icon center bellow bottom offset");
+ return false;
+ }
LauncherInstrumentation.log("hasClickableIcon: icon is clickable");
return true;
}
@@ -90,21 +94,32 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
"apps_list_view");
+ final UiObject2 searchBox = getSearchBox(allAppsContainer);
+
+ int bottomGestureMargin = ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
+ int deviceHeight = mLauncher.getDevice().getDisplayHeight();
+ int displayBottom = deviceHeight - bottomGestureMargin;
allAppsContainer.setGestureMargins(
0,
getSearchBox(allAppsContainer).getVisibleBounds().bottom + 1,
0,
- ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
- mLauncher.getResources()) + 1);
+ bottomGestureMargin);
final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
- if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
+ if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+ displayBottom)) {
scrollBackToBeginning();
int attempts = 0;
- int scroll = getScroll(allAppsContainer);
+ int scroll = getAllAppsScroll();
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled")) {
- while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
- mLauncher.scroll(allAppsContainer, Direction.DOWN, 0.8f, null, 50);
- final int newScroll = getScroll(allAppsContainer);
+ while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+ displayBottom)) {
+ mLauncher.scrollToLastVisibleRow(
+ allAppsContainer,
+ mLauncher.getObjectsInContainer(allAppsContainer, "icon"),
+ searchBox.getVisibleBounds().bottom
+ - allAppsContainer.getVisibleBounds().top);
+ final int newScroll = getAllAppsScroll();
if (newScroll == scroll) break;
mLauncher.assertTrue(
@@ -117,8 +132,11 @@
verifyActiveContainer();
}
+ // Ignore bottom offset selection here as there might not be any scroll more scroll
+ // region available.
mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
- hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector));
+ hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+ deviceHeight));
final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
appIconSelector);
@@ -136,16 +154,16 @@
int attempts = 0;
final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5);
- for (int scroll = getScroll(allAppsContainer);
+ for (int scroll = getAllAppsScroll();
scroll != 0;
- scroll = getScroll(allAppsContainer)) {
+ scroll = getAllAppsScroll()) {
mLauncher.assertTrue("Negative scroll position", scroll > 0);
mLauncher.assertTrue(
"Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
++attempts <= MAX_SCROLL_ATTEMPTS);
- mLauncher.scroll(allAppsContainer, Direction.UP, 1, margins, 50);
+ mLauncher.scroll(allAppsContainer, Direction.UP, margins, 50);
}
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
@@ -154,9 +172,10 @@
}
}
- private int getScroll(UiObject2 allAppsContainer) {
- return mLauncher.getAnswerFromLauncher(allAppsContainer, TestProtocol.GET_SCROLL_MESSAGE).
- getInt(TestProtocol.SCROLL_Y_FIELD, -1);
+ private int getAllAppsScroll() {
+ return mLauncher.getTestInfo(
+ TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
private UiObject2 getSearchBox(UiObject2 allAppsContainer) {
@@ -172,7 +191,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center to avoid starting at elements near the top.
mLauncher.scroll(
- allAppsContainer, Direction.DOWN, 1, new Rect(0, 0, 0, mHeight / 2), 10);
+ allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10);
verifyActiveContainer();
}
}
@@ -186,7 +205,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center, for symmetry with forward.
mLauncher.scroll(
- allAppsContainer, Direction.UP, 1, new Rect(0, mHeight / 2, 0, 0), 10);
+ allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10);
verifyActiveContainer();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index bcce8ef..0d9038f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -23,6 +23,7 @@
import android.view.MotionEvent;
import androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.testing.TestProtocol;
@@ -104,7 +105,12 @@
startY = endY = mLauncher.getDevice().getDisplayHeight() / 2;
}
- mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
+ if (mLauncher.isFallbackOverview()) {
+ mLauncher.linearGesture(startX, startY, endX, endY, 10);
+ new BaseOverview(mLauncher);
+ } else {
+ mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
+ }
break;
}
@@ -114,6 +120,60 @@
}
}
+ /**
+ * Swipes right or double presses the square button to switch to the previous app.
+ */
+ public Background quickSwitchToPreviousApp() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to quick switch to the previous app")) {
+ verifyActiveContainer();
+ quickSwitchToPreviousApp(getExpectedStateForQuickSwitch());
+ return new Background(mLauncher);
+ }
+ }
+
+ protected int getExpectedStateForQuickSwitch() {
+ return BACKGROUND_APP_STATE_ORDINAL;
+ }
+
+ protected void quickSwitchToPreviousApp(int expectedState) {
+ boolean transposeInLandscape = false;
+ switch (mLauncher.getNavigationModel()) {
+ case TWO_BUTTON:
+ transposeInLandscape = true;
+ // Fall through, zero button and two button modes behave the same.
+ case ZERO_BUTTON: {
+ final int startX;
+ final int startY;
+ final int endX;
+ final int endY;
+ if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
+ // Swipe from the bottom left to the bottom right of the screen.
+ startX = 0;
+ startY = getSwipeStartY();
+ endX = mLauncher.getDevice().getDisplayWidth();
+ endY = startY;
+ } else {
+ // Swipe from the bottom right to the top right of the screen.
+ startX = getSwipeStartX();
+ startY = mLauncher.getRealDisplaySize().y - 1;
+ endX = startX;
+ endY = 0;
+ }
+ mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState);
+ break;
+ }
+
+ case THREE_BUTTON:
+ // Double press the recents button.
+ UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
+ recentsButton.click();
+ mLauncher.getOverview();
+ recentsButton.click();
+ break;
+ }
+ }
+
protected String getSwipeHeightRequestName() {
return TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT;
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 25e6e8c..8ccfc05 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -55,7 +55,7 @@
final int leftMargin = mLauncher.getTestInfo(
TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
- mLauncher.scroll(overview, Direction.LEFT, 1, new Rect(leftMargin, 0, 0, 0), 20);
+ mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20);
verifyActiveContainer();
}
}
@@ -63,10 +63,10 @@
/**
* Dismissed all tasks by scrolling to Clear-all button and pressing it.
*/
- public Workspace dismissAllTasks() {
+ public void dismissAllTasks() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"dismissing all tasks")) {
- final BySelector clearAllSelector = mLauncher.getLauncherObjectSelector("clear_all");
+ final BySelector clearAllSelector = mLauncher.getOverviewObjectSelector("clear_all");
for (int i = 0;
i < FLINGS_FOR_DISMISS_LIMIT
&& !verifyActiveContainer().hasObject(clearAllSelector);
@@ -75,10 +75,6 @@
}
mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
- try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "dismissed all tasks")) {
- return new Workspace(mLauncher);
- }
}
}
@@ -93,7 +89,7 @@
final int rightMargin = mLauncher.getTestInfo(
TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
- mLauncher.scroll(overview, Direction.RIGHT, 1, new Rect(0, 0, rightMargin, 0), 20);
+ mLauncher.scroll(overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20);
verifyActiveContainer();
}
}
@@ -109,7 +105,7 @@
"want to get current task")) {
verifyActiveContainer();
final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
- mLauncher.getLauncherObjectSelector("snapshot"));
+ mLauncher.getOverviewObjectSelector("snapshot"));
mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
// taskViews contains up to 3 task views: the 'main' (having the widest visible
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index cfc4374..e0fe933 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -17,6 +17,7 @@
package com.android.launcher3.tapl;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
import androidx.annotation.NonNull;
@@ -58,4 +59,9 @@
}
}
}
+
+ @Override
+ protected int getExpectedStateForQuickSwitch() {
+ return QUICK_SWITCH_STATE_ORDINAL;
+ }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 15615fc..0879404 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -37,7 +37,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -52,6 +51,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Configurator;
@@ -60,20 +60,24 @@
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
+import com.android.launcher3.ResourceUtils;
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.Collection;
+import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
-import java.util.function.Supplier;
+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
@@ -84,6 +88,7 @@
private static final String TAG = "Tapl";
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
private static final int GESTURE_STEP_MS = 16;
+ private static long START_TIME = System.currentTimeMillis();
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
@@ -134,13 +139,22 @@
private int mExpectedRotation = Surface.ROTATION_0;
private final Uri mTestProviderUri;
private final Deque<String> mDiagnosticContext = new LinkedList<>();
- private Supplier<String> mSystemHealthSupplier;
+ private Function<Long, String> mSystemHealthSupplier;
private Consumer<ContainerType> mOnSettledStateAction;
/**
* Constructs the root of TAPL hierarchy. You get all other objects from it.
*/
+ public LauncherInstrumentation() {
+ this(InstrumentationRegistry.getInstrumentation());
+ }
+
+ /**
+ * Constructs the root of TAPL hierarchy. You get all other objects from it.
+ * Deprecated: use the constructor without parameters instead.
+ */
+ @Deprecated
public LauncherInstrumentation(Instrumentation instrumentation) {
mInstrumentation = instrumentation;
mDevice = UiDevice.getInstance(instrumentation);
@@ -238,10 +252,6 @@
return null;
}
- public static boolean isAvd() {
- return Build.MODEL.contains("Cuttlefish");
- }
-
static void log(String message) {
Log.d(TAG, message);
}
@@ -296,7 +306,7 @@
return "Background";
}
- public void setSystemHealthSupplier(Supplier<String> supplier) {
+ public void setSystemHealthSupplier(Function<Long, String> supplier) {
this.mSystemHealthSupplier = supplier;
}
@@ -316,8 +326,8 @@
}
return mSystemHealthSupplier != null
- ? mSystemHealthSupplier.get()
- : TestHelpers.getSystemHealthMessage(getContext());
+ ? mSystemHealthSupplier.apply(START_TIME)
+ : TestHelpers.getSystemHealthMessage(getContext(), START_TIME);
}
private void fail(String message) {
@@ -486,8 +496,8 @@
}
}
- private void waitForLauncherInitialized() {
- for (int i = 0; i < 100; ++i) {
+ public void waitForLauncherInitialized() {
+ for (int i = 0; i < 600; ++i) {
if (getTestInfo(
TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED).
getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD)) {
@@ -512,15 +522,6 @@
}
}
- Bundle getAnswerFromLauncher(UiObject2 view, String requestTag) {
- // Send a fake set-text request to Launcher to initiate a response with requested data.
- final String responseTag = requestTag + TestProtocol.RESPONSE_MESSAGE_POSTFIX;
- return (Bundle) executeAndWaitForEvent(
- () -> view.setText(requestTag),
- event -> responseTag.equals(event.getClassName()),
- "Launcher didn't respond to request: " + requestTag);
- }
-
/**
* Presses nav bar home button.
*
@@ -534,6 +535,9 @@
// accessibility events prior to pressing Home.
final String action;
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
+ final String anomaly = getAnomalyMessage();
+ if (anomaly != null) fail("Can't swipe up to Home: " + anomaly);
+
final Point displaySize = getRealDisplaySize();
if (hasLauncherObject("deep_shortcuts_container")) {
@@ -541,13 +545,17 @@
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
- assertTrue("Context menu is still visible afterswiping up to home",
- !hasLauncherObject("deep_shortcuts_container"));
+ try (LauncherInstrumentation.Closable c = addContextLayer(
+ "Swiped up from context menu to home")) {
+ waitUntilGone("deep_shortcuts_container");
+ }
}
if (hasLauncherObject(WORKSPACE_RES_ID)) {
log(action = "already at home");
} else {
- log(action = "swiping up to home");
+ log("Hierarchy before swiping up to home");
+ dumpViewHierarchy();
+ log(action = "swiping up to home from " + getVisibleStateMessage());
final int finalState = mDevice.hasObject(By.pkg(getLauncherPackageName()))
? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL;
@@ -725,7 +733,7 @@
@NonNull
UiObject2 waitForFallbackLauncherObject(String resName) {
- return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName));
+ return waitForObjectBySelector(getOverviewObjectSelector(resName));
}
private UiObject2 waitForObjectBySelector(BySelector selector) {
@@ -742,7 +750,7 @@
return By.res(getLauncherPackageName(), resName);
}
- BySelector getFallbackLauncherObjectSelector(String resName) {
+ BySelector getOverviewObjectSelector(String resName) {
return By.res(getOverviewPackageName(), resName);
}
@@ -769,7 +777,36 @@
TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD)));
}
- void scroll(UiObject2 container, Direction direction, float percent, Rect margins, int steps) {
+ int getBottomGestureSize() {
+ return ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources()) + 1;
+ }
+
+ int getBottomGestureMargin(UiObject2 container) {
+ return container.getVisibleBounds().bottom - getRealDisplaySize().y
+ + getBottomGestureSize();
+ }
+
+ void scrollToLastVisibleRow(UiObject2 container, Collection<UiObject2> items, int topPadding) {
+ final UiObject2 lowestItem = Collections.max(items, (i1, i2) ->
+ Integer.compare(i1.getVisibleBounds().top, i2.getVisibleBounds().top));
+
+ final int gestureStart = lowestItem.getVisibleBounds().top + getTouchSlop();
+ final int distance = gestureStart - container.getVisibleBounds().top - topPadding;
+ final int bottomMargin = container.getVisibleBounds().height() - distance;
+
+ scroll(
+ container,
+ Direction.DOWN,
+ new Rect(
+ 0,
+ 0,
+ 0,
+ Math.max(bottomMargin, getBottomGestureMargin(container))),
+ 150);
+ }
+
+ void scroll(UiObject2 container, Direction direction, Rect margins, int steps) {
final Rect rect = container.getVisibleBounds();
if (margins != null) {
rect.left += margins.left;
@@ -786,34 +823,26 @@
switch (direction) {
case UP: {
startX = endX = rect.centerX();
- final int vertCenter = rect.centerY();
- final float halfGestureHeight = rect.height() * percent / 2.0f;
- startY = (int) (vertCenter - halfGestureHeight) + 1;
- endY = (int) (vertCenter + halfGestureHeight);
+ startY = rect.top;
+ endY = rect.bottom - 1;
}
break;
case DOWN: {
startX = endX = rect.centerX();
- final int vertCenter = rect.centerY();
- final float halfGestureHeight = rect.height() * percent / 2.0f;
- startY = (int) (vertCenter + halfGestureHeight) - 1;
- endY = (int) (vertCenter - halfGestureHeight);
+ startY = rect.bottom - 1;
+ endY = rect.top;
}
break;
case LEFT: {
startY = endY = rect.centerY();
- final int horizCenter = rect.centerX();
- final float halfGestureWidth = rect.width() * percent / 2.0f;
- startX = (int) (horizCenter - halfGestureWidth) + 1;
- endX = (int) (horizCenter + halfGestureWidth);
+ startX = rect.left;
+ endX = rect.right - 1;
}
break;
case RIGHT: {
startY = endY = rect.centerY();
- final int horizCenter = rect.centerX();
- final float halfGestureWidth = rect.width() * percent / 2.0f;
- startX = (int) (horizCenter + halfGestureWidth) - 1;
- endX = (int) (horizCenter - halfGestureWidth);
+ startX = rect.right - 1;
+ endX = rect.left;
}
break;
default:
@@ -844,10 +873,6 @@
mDevice.waitForIdle();
}
- float getDisplayDensity() {
- return mInstrumentation.getTargetContext().getResources().getDisplayMetrics().density;
- }
-
int getTouchSlop() {
return ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@@ -969,4 +994,18 @@
public void produceNativeLeak() {
getTestInfo(TestProtocol.REQUEST_NATIVE_LEAK);
}
-}
\ No newline at end of file
+
+ public void produceViewLeak() {
+ getTestInfo(TestProtocol.REQUEST_VIEW_LEAK);
+ }
+
+ public ArrayList<ComponentName> getRecentTasks() {
+ ArrayList<ComponentName> tasks = new ArrayList<>();
+ ArrayList<String> components = getTestInfo(TestProtocol.REQUEST_RECENT_TASKS_LIST)
+ .getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ for (String s : components) {
+ tasks.add(ComponentName.unflattenFromString(s));
+ }
+ return tasks;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index da68da3..4f8aeb1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -67,4 +67,13 @@
}
}
}
+
+ @Override
+ public void dismissAllTasks() {
+ super.dismissAllTasks();
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "dismissed all tasks")) {
+ new Workspace(mLauncher);
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index a089a52..e882171 100644
--- a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
+++ b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
@@ -77,13 +77,18 @@
return launchers.get(0).activityInfo;
}
- public static String getOverviewPackageName() {
+ public static ComponentName getOverviewComponentName() {
Resources res = Resources.getSystem();
int id = res.getIdentifier("config_recentsComponentName", "string", "android");
if (id != 0) {
- return ComponentName.unflattenFromString(res.getString(id)).getPackageName();
+ return ComponentName.unflattenFromString(res.getString(id));
}
- return "com.android.systemui";
+ return new ComponentName("com.android.systemui",
+ "com.android.systemui.recents.RecentsActivity");
+ }
+
+ public static String getOverviewPackageName() {
+ return getOverviewComponentName().getPackageName();
}
private static String truncateCrash(String text, int maxLines) {
@@ -101,11 +106,11 @@
return ret.toString();
}
- private static String checkCrash(Context context, String label) {
+ private static String checkCrash(Context context, String label, long startTime) {
DropBoxManager dropbox = (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);
Assert.assertNotNull("Unable access the DropBoxManager service", dropbox);
- long timestamp = System.currentTimeMillis() - 5 * 60000;
+ long timestamp = startTime;
DropBoxManager.Entry entry;
StringBuilder errorDetails = new StringBuilder();
while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
@@ -123,7 +128,7 @@
return errorDetails.length() != 0 ? errorDetails.toString() : null;
}
- public static String getSystemHealthMessage(Context context) {
+ public static String getSystemHealthMessage(Context context, long startTime) {
try {
StringBuilder errors = new StringBuilder();
@@ -131,7 +136,6 @@
"system_app_anr",
"system_app_crash",
"system_app_native_crash",
- "system_app_wtf",
"system_server_anr",
"system_server_crash",
"system_server_native_crash",
@@ -139,7 +143,7 @@
};
for (String label : labels) {
- final String crash = checkCrash(context, label);
+ final String crash = checkCrash(context, label, startTime);
if (crash != null) errors.append(crash);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 7d308af..5fcaa55 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -16,8 +16,6 @@
package com.android.launcher3.tapl;
-import static org.junit.Assert.fail;
-
import android.graphics.Point;
import android.graphics.Rect;
@@ -26,13 +24,12 @@
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
-import com.android.launcher3.ResourceUtils;
+import java.util.Collection;
/**
* All widgets container.
*/
public final class Widgets extends LauncherInstrumentation.VisibleContainer {
- private static final Rect MARGINS = new Rect(100, 100, 100, 100);
private static final int FLING_STEPS = 10;
Widgets(LauncherInstrumentation launcher) {
@@ -48,7 +45,11 @@
"want to fling forward in widgets")) {
LauncherInstrumentation.log("Widgets.flingForward enter");
final UiObject2 widgetsContainer = verifyActiveContainer();
- mLauncher.scroll(widgetsContainer, Direction.DOWN, 1f, MARGINS, FLING_STEPS);
+ mLauncher.scroll(
+ widgetsContainer,
+ Direction.DOWN,
+ new Rect(0, 0, 0, mLauncher.getBottomGestureMargin(widgetsContainer) + 1),
+ FLING_STEPS);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
verifyActiveContainer();
}
@@ -64,7 +65,11 @@
"want to fling backwards in widgets")) {
LauncherInstrumentation.log("Widgets.flingBackward enter");
final UiObject2 widgetsContainer = verifyActiveContainer();
- mLauncher.scroll(widgetsContainer, Direction.UP, 1f, MARGINS, FLING_STEPS);
+ mLauncher.scroll(
+ widgetsContainer,
+ Direction.UP,
+ new Rect(0, 0, widgetsContainer.getVisibleBounds().width(), 0),
+ FLING_STEPS);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
verifyActiveContainer();
}
@@ -78,32 +83,33 @@
}
public Widget getWidget(String labelText) {
- final int margin = ResourceUtils.getNavbarSize(
- ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
final UiObject2 widgetsContainer = verifyActiveContainer();
- widgetsContainer.setGestureMargins(0, 0, 0, margin);
-
final Point displaySize = mLauncher.getRealDisplaySize();
+ final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
int i = 0;
- final BySelector selector = By.clazz("android.widget.TextView").text(labelText);
-
for (; ; ) {
- final UiObject2 label = mLauncher.tryWaitForLauncherObject(selector, 300);
- if (label != null) {
+ final Collection<UiObject2> cells = mLauncher.getObjectsInContainer(
+ widgetsContainer, "widgets_cell_list_container");
+ mLauncher.assertTrue("Widgets doesn't have 2 rows", cells.size() >= 2);
+ for (UiObject2 cell : cells) {
+ final UiObject2 label = cell.findObject(labelSelector);
+ if (label == null) continue;
+
final UiObject2 widget = label.getParent().getParent();
mLauncher.assertEquals(
"View is not WidgetCell",
"com.android.launcher3.widget.WidgetCell",
widget.getClassName());
- if (widget.getVisibleBounds().bottom <= displaySize.y - margin) {
+ if (widget.getVisibleBounds().bottom
+ <= displaySize.y - mLauncher.getBottomGestureSize()) {
return new Widget(mLauncher, widget);
}
}
- if (++i > 40) fail("Too many attempts");
- mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.7f, MARGINS, 50);
+ mLauncher.assertTrue("Too many attempts", ++i <= 40);
+ mLauncher.scrollToLastVisibleRow(widgetsContainer, cells, 0);
}
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 510ea14..d1261e0 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -38,8 +38,6 @@
* Operations on the workspace screen.
*/
public final class Workspace extends Home {
- private static final float FLING_SPEED =
- LauncherInstrumentation.isAvd() ? 1500.0F : 3500.0F;
private static final int DRAG_DURACTION = 2000;
private static final int FLING_STEPS = 10;
private final UiObject2 mHotseat;
@@ -142,7 +140,7 @@
}
private boolean isWorkspaceScrollable(UiObject2 workspace) {
- return workspace.isScrollable();
+ return workspace.getChildCount() > 1;
}
@NonNull
@@ -182,8 +180,8 @@
*/
public void flingForward() {
final UiObject2 workspace = verifyActiveContainer();
- mLauncher.scroll(workspace, Direction.RIGHT, 1f,
- new Rect(0, 0, mLauncher.getEdgeSensitivityWidth(), 0),
+ mLauncher.scroll(workspace, Direction.RIGHT,
+ new Rect(0, 0, mLauncher.getEdgeSensitivityWidth() + 1, 0),
FLING_STEPS);
verifyActiveContainer();
}
@@ -194,8 +192,8 @@
*/
public void flingBackward() {
final UiObject2 workspace = verifyActiveContainer();
- mLauncher.scroll(workspace, Direction.LEFT, 1f,
- new Rect(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0),
+ mLauncher.scroll(workspace, Direction.LEFT,
+ new Rect(mLauncher.getEdgeSensitivityWidth() + 1, 0, 0, 0),
FLING_STEPS);
verifyActiveContainer();
}